[poincare] Add test: do not expand multinome when reduction target is

System
This commit is contained in:
Émilie Feral
2019-11-15 16:49:55 +01:00
committed by LeaNumworks
parent 11b8ed72a1
commit ccf848a9eb
14 changed files with 75 additions and 44 deletions

View File

@@ -18,14 +18,14 @@ void Model::tidy() {
Poincare::Expression Model::simplifiedExpression(double * modelCoefficients, Poincare::Context * context) {
Expression e = expression(modelCoefficients);
if (!e.isUninitialized()) {
PoincareHelpers::Simplify(&e, context);
PoincareHelpers::Simplify(&e, context, ExpressionNode::ReductionTarget::SystemForApproximation);
}
return e;
}
double Model::levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) {
Expression yExpression = Number::DecimalNumber(y);
PoincareHelpers::Simplify(&yExpression, context);
PoincareHelpers::Simplify(&yExpression, context, ExpressionNode::ReductionTarget::SystemForApproximation);
Expression modelExpression = simplifiedExpression(modelCoefficients, context);
double result = PoincareHelpers::NextIntersection(modelExpression, "x", xMin, step, xMax, context, yExpression).x1();
return result;

View File

@@ -47,7 +47,7 @@ Expression ExpressionModel::expressionReduced(const Storage::Record * record, Po
m_expression = Undefined::Builder();
} else {
m_expression = Expression::ExpressionFromAddress(expressionAddress(record), expressionSize(record));
PoincareHelpers::Simplify(&m_expression, context);
PoincareHelpers::Simplify(&m_expression, context, ExpressionNode::ReductionTarget::SystemForApproximation);
// simplify might return an uninitialized Expression if interrupted
if (m_expression.isUninitialized()) {
m_expression = Expression::ExpressionFromAddress(expressionAddress(record), expressionSize(record));

View File

@@ -62,10 +62,10 @@ inline Poincare::Expression ParseAndSimplify(const char * text, Poincare::Contex
return Poincare::Expression::ParseAndSimplify(text, context, complexFormat, preferences->angleUnit(), symbolicComputation);
}
inline void Simplify(Poincare::Expression * e, Poincare::Context * context, bool symbolicComputation = true) {
inline void Simplify(Poincare::Expression * e, Poincare::Context * context, Poincare::ExpressionNode::ReductionTarget target, bool symbolicComputation = true) {
Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences();
Poincare::Preferences::ComplexFormat complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(preferences->complexFormat(), *e, context);
*e = e->simplify(context, complexFormat, preferences->angleUnit(), symbolicComputation);
*e = e->simplify(context, complexFormat, preferences->angleUnit(), target, symbolicComputation);
}
inline void ParseAndSimplifyAndApproximate(const char * text, Poincare::Expression * simplifiedExpression, Poincare::Expression * approximateExpression, Poincare::Context * context, bool symbolicComputation = true) {

View File

@@ -276,7 +276,7 @@ EquationStore::Error EquationStore::oneDimensialPolynomialSolve(Expression exact
assert(degree == 2);
// Compute delta = b*b-4ac
Expression delta = Subtraction::Builder(Power::Builder(coefficients[1].clone(), Rational::Builder(2)), Multiplication::Builder(Rational::Builder(4), coefficients[0].clone(), coefficients[2].clone()));
delta = delta.simplify(context, updatedComplexFormat(context), Poincare::Preferences::sharedPreferences()->angleUnit());
delta = delta.simplify(context, updatedComplexFormat(context), Poincare::Preferences::sharedPreferences()->angleUnit(), ExpressionNode::ReductionTarget::SystemForApproximation);
if (delta.isUninitialized()) {
delta = Poincare::Undefined::Builder();
}

View File

@@ -221,11 +221,11 @@ public:
* (For instance, in Polar mode, they return an expression of the form
* r*e^(i*th) reduced and approximated.) */
static Expression ParseAndSimplify(const char * text, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation = true);
Expression simplify(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation = true);
Expression simplify(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target, bool symbolicComputation = true);
static void ParseAndSimplifyAndApproximate(const char * text, Expression * simplifiedExpression, Expression * approximateExpression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation = true);
void simplifyAndApproximate(Expression * simplifiedExpression, Expression * approximateExpression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation = true);
Expression reduce(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit);
Expression reduce(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target = ExpressionNode::ReductionTarget::SystemForApproximation);
Expression mapOnMatrixFirstChild(ExpressionNode::ReductionContext reductionContext);
static Expression ExpressionWithoutSymbols(Expression expressionWithSymbols, Context * context);

View File

@@ -110,7 +110,15 @@ public:
/* Properties */
enum class ReductionTarget {
System = 0,
/* Minimal reduction: this at least reduces rationals operations as
* "1-0.3-0.7 --> 0" */
SystemForApproximation = 0,
/* Expansion of Newton multinome to be able to identify polynoms */
SystemForAnalysis,
/* Additional features as:
* - factorizing on a common denominator
* - turning complex expression into the form a+ib
* - identifying tangent in cos/sin polynoms ... */
User
};
enum class Sign {

View File

@@ -46,7 +46,11 @@ Evaluation<T> EqualNode::templatedApproximate(Context * context, Preferences::Co
Expression Equal::standardEquation(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
Expression sub = Subtraction::Builder(childAtIndex(0).clone(), childAtIndex(1).clone());
return sub.reduce(context, complexFormat, angleUnit);
/* When reducing the equation, we specify the reduction target to be
* SystemForAnalysis. This enables to expand Newton multinom to be able to
* detect polynom correctly ("(x=2)^2" in this form won't be detected
* unless expanded). */
return sub.reduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::SystemForAnalysis);
}
Expression Equal::shallowReduce() {

View File

@@ -519,7 +519,7 @@ Expression Expression::ParseAndSimplify(const char * text, Context * context, Pr
if (exp.isUninitialized()) {
return Undefined::Builder();
}
exp = exp.simplify(context, complexFormat, angleUnit, symbolicSimplification);
exp = exp.simplify(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User, symbolicSimplification);
/* simplify might have been interrupted, in which case the resulting
* expression is uninitialized, so we need to check that. */
if (exp.isUninitialized()) {
@@ -547,9 +547,9 @@ void Expression::ParseAndSimplifyAndApproximate(const char * text, Expression *
}
}
Expression Expression::simplify(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation) {
Expression Expression::simplify(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target, bool symbolicComputation) {
sSimplificationHasBeenInterrupted = false;
ExpressionNode::ReductionContext c = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System, symbolicComputation);
ExpressionNode::ReductionContext c = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, target, symbolicComputation);
Expression e = deepReduce(c);
if (!sSimplificationHasBeenInterrupted) {
e = e.deepBeautify(c);
@@ -618,7 +618,7 @@ void Expression::simplifyAndApproximate(Expression * simplifiedExpression, Expre
Expression e = clone().deepReduce(userReductionContext);
if (sSimplificationHasBeenInterrupted) {
sSimplificationHasBeenInterrupted = false;
ExpressionNode::ReductionContext systemReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System, symbolicComputation);
ExpressionNode::ReductionContext systemReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::SystemForApproximation, symbolicComputation);
e = deepReduce(systemReductionContext);
}
*simplifiedExpression = Expression();
@@ -730,9 +730,9 @@ Expression Expression::angleUnitToRadian(Preferences::AngleUnit angleUnit) {
return *this;
}
Expression Expression::reduce(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) {
Expression Expression::reduce(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) {
sSimplificationHasBeenInterrupted = false;
return deepReduce(ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System, true));
return deepReduce(ExpressionNode::ReductionContext(context, complexFormat, angleUnit, target, true));
}
Expression Expression::deepReduce(ExpressionNode::ReductionContext reductionContext) {

View File

@@ -115,7 +115,7 @@ void Matrix::addChildrenAsRowInPlace(TreeHandle t, int i) {
int Matrix::rank(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool inPlace) {
Matrix m = inPlace ? *this : clone().convert<Matrix>();
ExpressionNode::ReductionContext systemReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System);
ExpressionNode::ReductionContext systemReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::SystemForApproximation);
m = m.rowCanonize(systemReductionContext, nullptr);
int rank = m.numberOfRows();
int i = rank-1;

View File

@@ -744,11 +744,11 @@ Expression Power::shallowReduce(ExpressionNode::ReductionContext reductionContex
/* Step 13: (a0+a1+...am)^n with n integer
* -> a^n+?a^(n-1)*b+?a^(n-2)*b^2+...+b^n (Multinome)
* We apply this rule only when the target is the User. Indeed, developing
* the multinome is likely to increase the numbers of operations and to
* lead to precision loss. */
* We don't apply this rule when the target is the SystemForApproximation.
* Indeed, developing the multinome is likely to increase the numbers of
* operations and lead to precision loss. */
if (!letPowerAtRoot
&& reductionContext.target() == ExpressionNode::ReductionTarget::User
&& reductionContext.target() != ExpressionNode::ReductionTarget::SystemForApproximation
&& indexType == ExpressionNode::Type::Rational
&& !static_cast<Rational &>(index).signedIntegerNumerator().isZero()
&& static_cast<Rational &>(index).isInteger()
@@ -871,12 +871,12 @@ Expression Power::shallowBeautify(ExpressionNode::ReductionContext reductionCont
return result;
}
/* Optional Step 3: if the ReductionTarget is the System, turn a^(p/q) into
* (root(a, q))^p
/* Optional Step 3: if the ReductionTarget is the SystemForApproximation,
* turn a^(p/q) into (root(a, q))^p
* Indeed, root(a, q) can have a real root which is not the principale angle
* but that we want to return in real complex format. This special case is
* handled in NthRoot approximation but not in Power approximation. */
if (reductionContext.target() == ExpressionNode::ReductionTarget::System && childAtIndex(1).type() == ExpressionNode::Type::Rational) {
if (reductionContext.target() != ExpressionNode::ReductionTarget::User && childAtIndex(1).type() == ExpressionNode::Type::Rational) {
Integer p = childAtIndex(1).convert<Rational>().signedIntegerNumerator();
Integer q = childAtIndex(1).convert<Rational>().integerDenominator();
Expression nthRoot = q.isOne() ? childAtIndex(0) : NthRoot::Builder(childAtIndex(0), Rational::Builder(q));

View File

@@ -311,7 +311,7 @@ QUIZ_CASE(poincare_preperties_get_variables) {
void assert_reduced_expression_has_polynomial_coefficient(const char * expression, const char * symbolName, const char ** coefficients, Preferences::ComplexFormat complexFormat = Cartesian, Preferences::AngleUnit angleUnit = Radian) {
Shared::GlobalContext globalContext;
Expression e = parse_expression(expression, false);
e = e.reduce(&globalContext, complexFormat, angleUnit);
e = e.reduce(&globalContext, complexFormat, angleUnit, SystemForAnalysis);
Expression coefficientBuffer[Poincare::Expression::k_maxNumberOfPolynomialCoefficients];
int d = e.getPolynomialReducedCoefficients(symbolName, coefficientBuffer, &globalContext, complexFormat, Radian);
for (int i = 0; i <= d; i++) {

View File

@@ -54,11 +54,11 @@ Poincare::Expression parse_expression(const char * expression, bool addParenthes
return result;
}
void assert_simplify(const char * expression, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat) {
void assert_simplify(const char * expression, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat, ExpressionNode::ReductionTarget target) {
Shared::GlobalContext globalContext;
Expression e = parse_expression(expression, false);
quiz_assert_print_if_failure(!e.isUninitialized(), expression);
e = e.simplify(&globalContext, complexFormat, angleUnit);
e = e.simplify(&globalContext, complexFormat, angleUnit, target);
quiz_assert_print_if_failure(!(e.isUninitialized()), expression);
}
@@ -68,7 +68,7 @@ void assert_parsed_expression_simplify_to(const char * expression, const char *
if (target == ExpressionNode::ReductionTarget::User) {
copy.simplifyAndApproximate(&copy, nullptr, context, complexFormat, angleUnit, symbolicComputation);
} else {
copy = copy.simplify(context, complexFormat, angleUnit, symbolicComputation);
copy = copy.simplify(context, complexFormat, angleUnit, target, symbolicComputation);
}
if (copy.isUninitialized()) {
return e;
@@ -81,7 +81,7 @@ template<typename T>
void assert_expression_approximates_to(const char * expression, const char * approximation, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat, int numberOfSignificantDigits) {
int numberOfDigits = sizeof(T) == sizeof(double) ? PrintFloat::k_numberOfStoredSignificantDigits : PrintFloat::k_numberOfPrintedSignificantDigits;
numberOfDigits = numberOfSignificantDigits > 0 ? numberOfSignificantDigits : numberOfDigits;
assert_parsed_expression_process_to(expression, approximation, ExpressionNode::ReductionTarget::System, complexFormat, angleUnit, false, [](Expression e, Context * context, ExpressionNode::ReductionTarget target, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation) {
assert_parsed_expression_process_to(expression, approximation, ExpressionNode::ReductionTarget::SystemForApproximation, complexFormat, angleUnit, false, [](Expression e, Context * context, ExpressionNode::ReductionTarget target, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation) {
return e.approximate<T>(context, complexFormat, angleUnit);
}, numberOfDigits);
}

View File

@@ -5,7 +5,8 @@ const char * MaxIntegerString(); // (2^32)^k_maxNumberOfDigits-1
const char * OverflowedIntegerString(); // (2^32)^k_maxNumberOfDigits
const char * BigOverflowedIntegerString(); // OverflowedIntegerString with a 2 on first digit
constexpr Poincare::ExpressionNode::ReductionTarget System = Poincare::ExpressionNode::ReductionTarget::System;
constexpr Poincare::ExpressionNode::ReductionTarget SystemForApproximation = Poincare::ExpressionNode::ReductionTarget::SystemForApproximation;
constexpr Poincare::ExpressionNode::ReductionTarget SystemForAnalysis = Poincare::ExpressionNode::ReductionTarget::SystemForAnalysis;
constexpr Poincare::ExpressionNode::ReductionTarget User = Poincare::ExpressionNode::ReductionTarget::User;
constexpr Poincare::Preferences::AngleUnit Degree = Poincare::Preferences::AngleUnit::Degree;
constexpr Poincare::Preferences::AngleUnit Radian = Poincare::Preferences::AngleUnit::Radian;
@@ -30,7 +31,7 @@ Poincare::Expression parse_expression(const char * expression, bool addParenthes
// Simplification
void assert_simplify(const char * expression, Poincare::Preferences::AngleUnit angleUnit = Radian, Poincare::Preferences::ComplexFormat complexFormat = Cartesian);
void assert_simplify(const char * expression, Poincare::Preferences::AngleUnit angleUnit = Radian, Poincare::Preferences::ComplexFormat complexFormat = Cartesian, Poincare::ExpressionNode::ReductionTarget target = User);
void assert_parsed_expression_simplify_to(const char * expression, const char * simplifiedExpression, Poincare::ExpressionNode::ReductionTarget target = User, Poincare::Preferences::AngleUnit angleUnit = Radian, Poincare::Preferences::ComplexFormat complexFormat = Cartesian, bool symbolicComputation = true);

View File

@@ -959,36 +959,54 @@ QUIZ_CASE(poincare_simplification_complex_format) {
}
QUIZ_CASE(poincare_simplification_reduction_target) {
assert_parsed_expression_simplify_to("1/π+1/x", "1/x+1/π", System);
// Factorize on the same denominator only for ReductionTarget = User
assert_parsed_expression_simplify_to("1/π+1/x", "1/x+1/π", SystemForAnalysis);
assert_parsed_expression_simplify_to("1/π+1/x", "1/x+1/π", SystemForApproximation);
assert_parsed_expression_simplify_to("1/π+1/x", "\u0012x+π\u0013/\u0012π×x\u0013", User);
assert_parsed_expression_simplify_to("1/(1+𝐢)", "1/\u0012𝐢+1\u0013", System);
// Display in the form a+ib only for ReductionTarget = User
assert_parsed_expression_simplify_to("1/(1+𝐢)", "1/\u0012𝐢+1\u0013", SystemForAnalysis);
assert_parsed_expression_simplify_to("1/(1+𝐢)", "1/\u0012𝐢+1\u0013", SystemForApproximation);
assert_parsed_expression_simplify_to("1/(1+𝐢)", "1/2-1/2×𝐢", User);
assert_parsed_expression_simplify_to("sin(x)/(cos(x)×cos(x))", "sin(x)/cos(x)^2", System);
// Replace sin/cos-->tan for ReductionTarget = User
assert_parsed_expression_simplify_to("sin(x)/(cos(x)×cos(x))", "sin(x)/cos(x)^2", SystemForAnalysis);
assert_parsed_expression_simplify_to("sin(x)/(cos(x)×cos(x))", "sin(x)/cos(x)^2", SystemForApproximation);
assert_parsed_expression_simplify_to("sin(x)/(cos(x)×cos(x))", "tan(x)/cos(x)", User);
assert_parsed_expression_simplify_to("x^0", "x^0", System);
// Apply rule x^0 --> 1 for ReductionTarget = User (because this is not always true)
assert_parsed_expression_simplify_to("x^0", "x^0", SystemForAnalysis);
assert_parsed_expression_simplify_to("x^0", "x^0", SystemForApproximation);
assert_parsed_expression_simplify_to("x^0", "1", User);
assert_parsed_expression_simplify_to("(1+x)/(1+x)", "(x+1)^0", SystemForApproximation);
assert_parsed_expression_simplify_to("(1+x)/(1+x)", "1", User);
assert_parsed_expression_simplify_to("x^(2/3)", "root(x,3)^2", System);
// Apply rule x^(2/3) --> root(x,3)^2 for ReductionTarget = System
assert_parsed_expression_simplify_to("x^(2/3)", "root(x,3)^2", SystemForApproximation);
assert_parsed_expression_simplify_to("x^(2/3)", "root(x,3)^2", SystemForAnalysis);
assert_parsed_expression_simplify_to("x^(2/3)", "x^\u00122/3\u0013", User);
assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", System);
assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", System);
assert_parsed_expression_simplify_to("x^2", "x^2", System);
assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", SystemForApproximation);
assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", SystemForAnalysis);
assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", User);
assert_parsed_expression_simplify_to("x^2", "x^2", SystemForApproximation);
assert_parsed_expression_simplify_to("x^2", "x^2", User);
assert_parsed_expression_simplify_to("1/(√(2)+√(3))", "1/\u0012√(3)+√(2)\u0013", System);
// Remove square root at denominator for ReductionTarget = User
assert_parsed_expression_simplify_to("1/(√(2)+√(3))", "1/\u0012√(3)+√(2)\u0013", SystemForApproximation);
assert_parsed_expression_simplify_to("1/(√(2)+√(3))", "√(3)-√(2)", User);
assert_parsed_expression_simplify_to("sign(abs(x))", "sign(abs(x))", System);
// Always reduce sign for ReductionTarget = User
assert_parsed_expression_simplify_to("sign(abs(x))", "sign(abs(x))", SystemForApproximation);
assert_parsed_expression_simplify_to("sign(abs(x))", "1", User);
assert_parsed_expression_simplify_to("atan(1/x)", "atan(1/x)", System);
// Apply rule atan(1/x)-> (π×sign(x)-2×atan(x))/2 for ReductionTarget = User (as it is not always true)
assert_parsed_expression_simplify_to("atan(1/x)", "atan(1/x)", SystemForApproximation);
assert_parsed_expression_simplify_to("atan(1/x)", "\u0012π×sign(x)-2×atan(x)\u0013/2", User);
assert_parsed_expression_simplify_to("(1+x)/(1+x)", "(x+1)^0", System);
assert_parsed_expression_simplify_to("(1+x)/(1+x)", "1", User);
// Expand multinome when ReductionTarget is not SystemForApproximation as it increases precision loss
assert_parsed_expression_simplify_to("(2+x)^2", "(x+2)^2", SystemForApproximation);
assert_parsed_expression_simplify_to("(2+x)^2", "x^2+4×x+4", SystemForAnalysis);
assert_parsed_expression_simplify_to("(2+x)^2", "x^2+4×x+4", User);
}
QUIZ_CASE(poincare_simplification_mix) {