[poincare] Sketch of complex reduction as part of the reduce routine

This commit is contained in:
Émilie Feral
2018-12-13 11:08:12 +01:00
committed by Léa Saviot
parent e11b7ca631
commit 3292bdfd44
41 changed files with 542 additions and 26 deletions

View File

@@ -49,6 +49,7 @@ objs += $(addprefix poincare/src/,\
ceiling.o\
complex.o\
complex_argument.o\
complex_cartesian.o\
complex_helper.o\
confidence_interval.o\
conjugate.o\

View File

@@ -26,6 +26,7 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; }
// Approximation
template<typename T> static Complex<T> computeOnComplex(const std::complex<T> c, Preferences::AngleUnit angleUnit) {

View File

@@ -25,6 +25,7 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianComplexFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return childAtIndex(0)->isReal(context, angleUnit); }
private:
// Layout

View File

@@ -21,6 +21,7 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; }
// Properties
Type type() const override{ return Type::BinomialCoefficient; }

View File

@@ -21,6 +21,7 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; }
// Properties
Type type() const override { return Type::Ceiling; }

View File

@@ -20,6 +20,7 @@ public:
#endif
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; }
// Properties
Type type() const override { return Type::ComplexArgument; }

View File

@@ -2,7 +2,7 @@
#define POINCARE_COMPLEX_CARTESIAN_H
#include <poincare/expression.h>
#include <poincare/complex_helper.h>
#include <poincare/multiplication.h>
namespace Poincare {
@@ -26,19 +26,42 @@ private:
// Evaluation
Evaluation<float> approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { assert(false); return Evaluation<float>(); }
Evaluation<double> approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { assert(false); return Evaluation<double>(); }
// Simplification
Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override;
Expression shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) override;
};
class ComplexCartesian final : public Expression {
public:
ComplexCartesian() : Expression() {}
ComplexCartesian(const ComplexCartesianNode * node) : Expression(node) {}
static ComplexCartesian Builder() { ComplexCartesianNode * node = TreePool::sharedPool()->createTreeNode<ComplexCartesianNode>(); return ComplexCartesian(node); }
static ComplexCartesian Builder(Expression child0, Expression child1) { return ComplexCartesian(child0, child1); }
// Getters
Expression real() { return childAtIndex(0); }
Expression imag() { return childAtIndex(1); }
// Simplification
Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit);
Expression shallowBeautify(Context & context, Preferences::AngleUnit angleUnit);
// Common operations (done in-place)
Expression squareNorm(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target);
Expression norm(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target);
Expression argument(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target);
ComplexCartesian inverse(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target);
ComplexCartesian squareRoot(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target);
ComplexCartesian powerInteger(int n, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target);
ComplexCartesian multiply(ComplexCartesian & other, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target);
ComplexCartesian power(ComplexCartesian & other, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target);
private:
ComplexCartesian(Expression child0, Expression child1) : Expression(TreePool::sharedPool()->createTreeNode<ComplexCartesianNode>()) {
replaceChildAtIndexInPlace(0, child0);
replaceChildAtIndexInPlace(1, child1);
}
static Multiplication squareRootHelper(Expression e, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target);
static Expression powerHelper(Expression norm, Expression trigo, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target);
};

View File

@@ -18,6 +18,7 @@ public:
#endif
// Complex
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override;
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override;
// Expression Properties
@@ -36,6 +37,9 @@ public:
bool isPi() const { return isConstantChar(Ion::Charset::SmallPi); }
bool isExponential() const { return isConstantChar(Ion::Charset::Exponential); }
bool isIComplex() const { return isConstantChar(Ion::Charset::IComplex); }
// Simplification
Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override;
private:
char m_name[0]; // MUST be the last member variable
@@ -54,6 +58,9 @@ public:
bool isExponential() const { return node()->isExponential(); }
bool isIComplex() const { return node()->isIComplex(); }
// Simplification
Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit);
private:
ConstantNode * node() const { return static_cast<ConstantNode *>(Expression::node()); }
};

View File

@@ -22,6 +22,7 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianComplexFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return childAtIndex(0)->isReal(context, angleUnit); }
// Properties
Type type() const override { return Type::Cosine; }

View File

@@ -22,6 +22,7 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; }
// Properties
Type type() const override { return Type::Derivative; }

View File

@@ -23,6 +23,8 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; }
private:
// Layout
Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override;

View File

@@ -24,6 +24,8 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; }
private:
// Layout
Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override;

View File

@@ -29,6 +29,7 @@ class Expression : public TreeHandle {
template<typename T>
friend class ComplexNode;
friend class ComplexArgument;
friend class ComplexCartesian;
friend class ComplexHelper;
friend class ConfidenceInterval;
friend class Conjugate;
@@ -166,6 +167,7 @@ public:
Expression defaultReplaceUnknown(const Symbol & symbol);
/* Complex */
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const { return node()->isReal(context, angleUnit); }
bool isPureReal(Context & context, Preferences::AngleUnit angleUnit) const;
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const;
ComplexPolar complexPolar(Context & context, Preferences::AngleUnit angleUnit) const;
@@ -186,8 +188,8 @@ public:
Expression simplify(Context & context, Preferences::AngleUnit angleUnit);
Expression reduce(Context & context, Preferences::AngleUnit angleUnit);
static Expression ExpressionWithoutSymbols(Expression expressionWithSymbols, Context & context);
Expression radianToDegree(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target);
Expression degreeToRadian(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target);
Expression radianToDegree();
Expression degreeToRadian();
/* Approximation Helper */
template<typename U> static U epsilon();

View File

@@ -48,7 +48,6 @@ public:
BinomialCoefficient,
Ceiling,
ComplexArgument,
ComplexCartesian,
ComplexPolar,
Conjugate,
Derivative,
@@ -88,6 +87,8 @@ public:
Symbol,
Constant,
ComplexCartesian,
Matrix,
ConfidenceInterval,
MatrixDimension,
@@ -128,6 +129,7 @@ public:
* ComplexCartesian::shallowBeautify. This would enable us to do only one
* scan of the tree in ParseAndSimplifyForComplexFormat instead of Simplifying
* and then extracting ComplexCartesian. */
virtual bool isReal(Context & context, Preferences::AngleUnit angleUnit) const { return false; }
virtual ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const;
virtual ComplexPolar complexPolar(Context & context, Preferences::AngleUnit angleUnit) const;

View File

@@ -26,6 +26,8 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; }
private:
// Layout
bool childNeedsParenthesis(const TreeNode * child) const override;

View File

@@ -25,6 +25,8 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; }
private:
// Layout
Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override;

View File

@@ -25,6 +25,8 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; }
private:
// Layout
Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override;

View File

@@ -22,6 +22,8 @@ public:
Type type() const override { return Type::GreatCommonDivisor; }
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; }
private:
// Layout
Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override;

View File

@@ -24,6 +24,8 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; }
private:
// Layout
Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override;

View File

@@ -26,6 +26,8 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; }
private:
// Layout
Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override;

View File

@@ -22,6 +22,8 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; }
private:
/* Layout */
Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override;

View File

@@ -23,6 +23,7 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); }
private:
// Layout
Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override;

View File

@@ -18,6 +18,9 @@ public:
}
void eraseNumberOfChildren() override { m_numberOfChildren = 0; }
// Complex
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override;
// Comparison
typedef int (*ExpressionOrder)(const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted);
@@ -48,6 +51,11 @@ public:
Expression squashUnaryHierarchyInPlace() {
return node()->squashUnaryHierarchyInPlace();
}
/* allChildrenAreReal returns:
* - 1 if all children are real
* - 0 if all non real children are ComplexCartesian
* - -1 if some chidren are non-real and non ComplexCartesian */
int allChildrenAreReal(Context & context, Preferences::AngleUnit angleUnit) const;
protected:
NAryExpressionNode * node() const { return static_cast<NAryExpressionNode *>(Expression::node()); }
};

View File

@@ -25,6 +25,7 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; }
};
class Number : public Expression {

View File

@@ -27,6 +27,8 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; }
private:
// Layout
Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override;

View File

@@ -22,6 +22,7 @@ public:
#endif
// Complex
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override;
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override;
ComplexPolar complexPolar(Context & context, Preferences::AngleUnit angleUnit) const override;

View File

@@ -23,6 +23,7 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; }
private:
// Layout

View File

@@ -21,6 +21,7 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; }
// Properties
Type type() const override { return Type::Random; }

View File

@@ -24,6 +24,7 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; }
private:
// Layout

View File

@@ -21,6 +21,7 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; }
// Properties
Type type() const override { return Type::Round; }

View File

@@ -26,6 +26,7 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; }
private:
// Layout

View File

@@ -22,6 +22,7 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianComplexFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return childAtIndex(0)->isReal(context, angleUnit); }
// Properties
Type type() const override { return Type::Sine; }

View File

@@ -25,6 +25,8 @@ public:
// Complex
ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianComplexFunction(this, context, angleUnit); }
bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return childAtIndex(0)->isReal(context, angleUnit); }
private:
// Layout
Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override;

View File

@@ -266,8 +266,43 @@ Expression Addition::shallowReduce(Context & context, Preferences::AngleUnit ang
// Step 5: Let's remove the addition altogether if it has a single child
Expression result = squashUnaryHierarchyInPlace();
if (result != *this) {
return result;
}
/* Step 6: Let's put everything under a common denominator.
/* Step 6: Let's bubble up the complex operator if possible
* 3 cases:
* - All children are real, we do nothing (allChildrenAreReal == 1)
* - One of the child is non-real and not a ComplexCartesian: it means a
* complex expression could not be resolved as a ComplexCartesian, we cannot
* do anything about it now (allChildrenAreReal == -1)
* - All children are either real or ComplexCartesian (allChildrenAreReal == 0)
* We can bubble up ComplexCartesian nodes. */
if (allChildrenAreReal(context, angleUnit) == 0) {
Addition imag;
Addition real = *this;
i = numberOfChildren() - 1;
while (i >= 0) {
Expression c = childAtIndex(i);
if (c.type() == ExpressionNode::Type::ComplexCartesian) {
real.replaceChildAtIndexInPlace(i, c.childAtIndex(0));
imag.addChildAtIndexInPlace(c.childAtIndex(1), imag.numberOfChildren(), imag.numberOfChildren());
} else {
// the Addition is sorted so ComplexCartesian nodes are the last ones
break;
}
i--;
}
ComplexCartesian newComplexCartesian = ComplexCartesian::Builder();
replaceWithInPlace(newComplexCartesian);
newComplexCartesian.replaceChildAtIndexInPlace(0, real);
newComplexCartesian.replaceChildAtIndexInPlace(1, imag);
real.shallowReduce(context, angleUnit, target);
imag.shallowReduce(context, angleUnit, target);
return newComplexCartesian.shallowReduce(context, angleUnit);
}
/* Step 7: Let's put everything under a common denominator.
* This step is done only for ReductionTarget::User if the parent expression
* is not an addition. */
Expression p = result.parent();

View File

@@ -0,0 +1,259 @@
#include <poincare/complex_cartesian.h>
#include <poincare/addition.h>
#include <poincare/arc_tangent.h>
#include <poincare/cosine.h>
#include <poincare/constant.h>
#include <poincare/division.h>
#include <poincare/multiplication.h>
#include <poincare/naperian_logarithm.h>
#include <poincare/rational.h>
#include <poincare/square_root.h>
#include <poincare/sine.h>
#include <poincare/sign_function.h>
#include <poincare/subtraction.h>
#include <poincare/power.h>
#include <assert.h>
#include <cmath>
namespace Poincare {
Expression ComplexCartesianNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) {
return ComplexCartesian(this).shallowReduce(context, angleUnit);
}
Expression ComplexCartesianNode::shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) {
return ComplexCartesian(this).shallowBeautify(context, angleUnit);
}
bool isZero(const Expression e) {
return e.type() == ExpressionNode::Type::Rational && static_cast<const Rational &>(e).isZero();
}
bool isOne(const Expression e) {
return e.type() == ExpressionNode::Type::Rational && static_cast<const Rational &>(e).isOne();
}
bool isMinusOne(const Expression e) {
return e.type() == ExpressionNode::Type::Rational && static_cast<const Rational &>(e).isMinusOne();
}
Expression ComplexCartesian::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) {
if (imag().isRationalZero()) {
Expression r = real();
replaceWithInPlace(r);
return r;
}
return *this;
}
Expression ComplexCartesian::shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) {
Expression a = real();
Expression b = imag();
Expression oppositeA = a.makePositiveAnyNegativeNumeralFactor(context, angleUnit);
Expression oppositeB = b.makePositiveAnyNegativeNumeralFactor(context, angleUnit);
a = oppositeA.isUninitialized() ? a : oppositeA;
b = oppositeB.isUninitialized() ? b : oppositeB;
Expression e = Expression::CreateComplexExpression(a, b, Preferences::ComplexFormat::Cartesian,
a.type() == ExpressionNode::Type::Undefined || b.type() == ExpressionNode::Type::Undefined,
isZero(a), isOne(a), isZero(b), isOne(b), isMinusOne(b),
!oppositeA.isUninitialized(),
!oppositeB.isUninitialized()
);
replaceWithInPlace(e);
return e;
}
Expression ComplexCartesian::squareNorm(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) {
Expression a = real();
Expression b = imag();
Expression a2 = Power(a, Rational(2));
Expression b2 = Power(b, Rational(2));
Addition add(a2, b2);
a2.shallowReduce(context, angleUnit, target);
b2.shallowReduce(context, angleUnit, target);
return add;
}
Expression ComplexCartesian::norm(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) {
Expression n2 = squareNorm(context, angleUnit, target);
Expression n = SquareRoot::Builder(n2);
n2.shallowReduce(context, angleUnit, target);
return n;
}
Expression ComplexCartesian::argument(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) {
Expression a = real();
Expression b = imag();
if (!b.isRationalZero()) {
// if b != 0, argument = sign(b) * Pi/2 - arctan(a/b)
// First, compute arctan(a/b) or (Pi/180)*arctan(a/b)
Expression divab = Division(a, b.clone());
Expression arcTangent = ArcTangent::Builder(divab);
divab.shallowReduce(context, angleUnit, target);
if (angleUnit == Preferences::AngleUnit::Degree) {
Expression temp = arcTangent.degreeToRadian();
arcTangent.shallowReduce(context, angleUnit, target);
arcTangent = temp;
}
// Then, compute sign(b) * Pi/2 - arctan(a/b)
Expression signb = SignFunction::Builder(b);
Expression signbPi2 = Multiplication(Rational(1,2), signb, Constant(Ion::Charset::SmallPi));
signb.shallowReduce(context, angleUnit, target);
Expression sub = Subtraction(signbPi2, arcTangent);
signbPi2.shallowReduce(context, angleUnit, target);
arcTangent.shallowReduce(context, angleUnit, target);
return sub;
} else {
// if b == 0, argument = (1-sign(a))*Pi/2
Expression signa = SignFunction::Builder(a).shallowReduce(context, angleUnit);
Subtraction sub(Rational(1), signa);
signa.shallowReduce(context, angleUnit, target);
Multiplication mul(Rational(1,2), Constant(Ion::Charset::SmallPi), sub);
sub.shallowReduce(context, angleUnit, target);
return mul;
}
}
ComplexCartesian ComplexCartesian::inverse(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) {
Expression a = real();
Expression b = imag();
// 1/(a+ib) = a/(a^2+b^2)+i*(-b/(a^2+b^2))
Expression denominatorReal = clone().convert<ComplexCartesian>().squareNorm(context, angleUnit, target);
Expression denominatorImag = denominatorReal.clone();
Expression denominatorRealInv = Power(denominatorReal, Rational(-1));
denominatorReal.shallowReduce(context, angleUnit, target);
Expression denominatorImagInv = Power(denominatorImag, Rational(-1));
denominatorImag.shallowReduce(context, angleUnit, target);
Multiplication A(a, denominatorRealInv);
denominatorRealInv.shallowReduce(context, angleUnit, target);
Expression numeratorImag = Multiplication(Rational(-1), b);
Multiplication B(numeratorImag, denominatorImagInv);
numeratorImag.shallowReduce(context, angleUnit, target);
denominatorImagInv.shallowReduce(context, angleUnit, target);
ComplexCartesian result(A,B);
A.shallowReduce(context, angleUnit, target);
B.shallowReduce(context, angleUnit, target);
return result;
}
Multiplication ComplexCartesian::squareRootHelper(Expression e, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) {
//(1/2)*sqrt(2*e)
Multiplication doubleE(Rational(2), e);
e.shallowReduce(context, angleUnit, target);
Expression sqrt = SquareRoot::Builder(doubleE);
doubleE.shallowReduce(context, angleUnit, target);
Multiplication result(Rational(1,2), sqrt);
sqrt.shallowReduce(context, angleUnit, target);
return result;
}
ComplexCartesian ComplexCartesian::squareRoot(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) {
Expression a = real();
Expression b = imag();
// A: (1/2)*sqrt(2*(sqrt(a^2+b^2)+a))
// B: (1/2)*sqrt(2*(sqrt(a^2+b^2)-a))*sign(b)
Expression normA = clone().convert<ComplexCartesian>().norm(context, angleUnit, target);
Expression normB = normA.clone();
// A = (1/2)*sqrt(2*(sqrt(a^2+b^2)+a))
Addition normAdda(normA, a.clone());
normA.shallowReduce(context, angleUnit, target);
Multiplication A = squareRootHelper(normAdda, context, angleUnit, target);
// B = B: (1/2)*sqrt(2*(sqrt(a^2+b^2)-a))
Subtraction normSuba(normB, a);
normB.shallowReduce(context, angleUnit, target);
Multiplication B = squareRootHelper(normSuba, context, angleUnit, target);
// B = B: (1/2)*sqrt(2*(sqrt(a^2+b^2)-a))*sign(b)
Expression signb = SignFunction::Builder(b);
B.addChildAtIndexInPlace(signb, B.numberOfChildren(), B.numberOfChildren());
signb.shallowReduce(context, angleUnit, target);
ComplexCartesian result = ComplexCartesian::Builder(A, B);
A.shallowReduce(context, angleUnit, target);
B.shallowReduce(context, angleUnit, target);
return result;
}
ComplexCartesian ComplexCartesian::powerInteger(int n, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) {
return ComplexCartesian();
}
ComplexCartesian ComplexCartesian::multiply(ComplexCartesian & other, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) {
Expression a = real();
Expression b = imag();
Expression c = other.real();
Expression d = other.imag();
// (a+ib) * (c+id) = (ac-bd)+i*(ad+bc)
// Compute ac-bd
Expression ac = Multiplication(a.clone(), c.clone());
Expression bd = Multiplication(b.clone(), d.clone());
Subtraction A(ac, bd);
ac.shallowReduce(context, angleUnit, target);
bd.shallowReduce(context, angleUnit, target);
// Compute ad+bc
Expression ad = Multiplication(a, d);
Expression bc = Multiplication(b, c);
Addition B(ad, bc);
ad.shallowReduce(context, angleUnit, target);
bc.shallowReduce(context, angleUnit, target);
ComplexCartesian result = ComplexCartesian::Builder(A, B);
A.shallowReduce(context, angleUnit, target);
B.shallowReduce(context, angleUnit, target);
return result;
}
Expression ComplexCartesian::powerHelper(Expression norm, Expression trigo, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) {
Multiplication m(norm, trigo);
norm.shallowReduce(context, angleUnit, target);
trigo.shallowReduce(context, angleUnit, target);
return m;
}
ComplexCartesian ComplexCartesian::power(ComplexCartesian & other, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) {
Expression r = clone().convert<ComplexCartesian>().norm(context, angleUnit, target);
Expression rclone = r.clone();
Expression th = argument(context, angleUnit, target);
Expression thclone = th.clone();
Expression c = other.real();
Expression d = other.imag();
// R = r^c*e^(-th*d)
Expression rpowc = Power(rclone, c);
rclone.shallowReduce(context, angleUnit, target);
Expression thmuld = Multiplication(Rational(-1), thclone, d.clone());
thclone.shallowReduce(context, angleUnit, target);
Expression exp = Power(Constant(Ion::Charset::Exponential), thmuld);
thmuld.shallowReduce(context, angleUnit, target);
Multiplication norm(rpowc, exp);
rpowc.shallowReduce(context, angleUnit, target);
exp.shallowReduce(context, angleUnit, target);
// TH = d*ln(r)+c*th
Expression lnr = NaperianLogarithm::Builder(r);
r.shallowReduce(context, angleUnit, target);
Multiplication dlnr(d, lnr);
lnr.shallowReduce(context, angleUnit, target);
Multiplication thc(th, c);
th.shallowReduce(context, angleUnit, target);
Expression argument = Addition(thc, dlnr);
thc.shallowReduce(context, angleUnit, target);
dlnr.shallowReduce(context, angleUnit, target);
if (angleUnit == Preferences::AngleUnit::Degree) {
Expression temp = argument.radianToDegree();
argument.shallowReduce(context, angleUnit, target);
argument = temp;
}
// Result = (norm*cos(argument), norm*sin(argument))
Expression normClone = norm.clone();
Expression argClone = argument.clone();
Expression cos = Cosine::Builder(argClone);
argClone.shallowReduce(context, angleUnit, target);
Expression normcosarg = powerHelper(normClone, cos, context, angleUnit, target);
Expression sin = Sine::Builder(argument);
argument.shallowReduce(context, angleUnit, target);
Expression normsinarg = powerHelper(norm, sin, context, angleUnit, target);
ComplexCartesian result = ComplexCartesian::Builder(normcosarg, normsinarg);
normcosarg.shallowReduce(context, angleUnit, target);
normsinarg.shallowReduce(context, angleUnit, target);
return result;
}
}

View File

@@ -51,7 +51,7 @@ ComplexCartesian ComplexHelper::complexCartesianFromComplexPolar(const Expressio
Expression r = polar.norm();
Expression th = polar.arg();
assert(!r.isUninitialized() && !th.isUninitialized());
Expression argument = angleUnit == Preferences::AngleUnit::Radian ? th : th.radianToDegree(context, angleUnit, ExpressionNode::ReductionTarget::BottomUpComputation);
Expression argument = th;
return ComplexCartesian::Builder(
complexCartesianFromComplexPolarHelper(r.clone(), Cosine::Builder(argument.clone()), context, angleUnit),
complexCartesianFromComplexPolarHelper(r, Sine::Builder(argument), context, angleUnit)
@@ -93,9 +93,6 @@ ComplexPolar ComplexHelper::complexPolarFromComplexCartesian(const ExpressionNod
// if b != 0, argument = sign(b) * Pi/2 - arctan(a/b)
// First, compute arctan(a/b) or (Pi/180)*arctan(a/b)
Expression arcTangent = ArcTangent::Builder(Division(a, b.clone()).shallowReduce(context, angleUnit, ExpressionNode::ReductionTarget::BottomUpComputation)).shallowReduce(context, angleUnit, ExpressionNode::ReductionTarget::BottomUpComputation);
if (angleUnit == Preferences::AngleUnit::Degree) {
arcTangent = arcTangent.degreeToRadian(context, angleUnit, ExpressionNode::ReductionTarget::BottomUpComputation);
}
// Then, compute sign(b) * Pi/2 - arctan(a/b)
argument = Subtraction(
Multiplication(

View File

@@ -16,6 +16,10 @@ ExpressionNode::Sign ConstantNode::sign(Context * context, Preferences::AngleUni
return Sign::Unknown;
}
bool ConstantNode::isReal(Context & context, Preferences::AngleUnit angleUnit) const {
return !isIComplex();
}
ComplexCartesian ConstantNode::complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const {
if (isIComplex()) {
return ComplexCartesian::Builder(Rational(0), Rational(1));
@@ -47,8 +51,21 @@ Evaluation<T> ConstantNode::templatedApproximate(Context& context, Preferences::
return Complex<T>(M_E);
}
Expression ConstantNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) {
return Constant(this).shallowReduce(context, angleUnit);
}
Constant::Constant(char name) : SymbolAbstract(TreePool::sharedPool()->createTreeNode<ConstantNode>(SymbolAbstract::AlignedNodeSize(1, sizeof(ConstantNode)))) {
node()->setName(&name, 1);
}
Expression Constant::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) {
if (isIComplex()) {
ComplexCartesian c = ComplexCartesian::Builder(Rational(0), Rational(1));
replaceWithInPlace(c);
return c;
}
return *this;
}
}

View File

@@ -420,12 +420,14 @@ Expression Expression::ExpressionWithoutSymbols(Expression e, Context & context)
return e;
}
Expression Expression::radianToDegree(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) {
return Multiplication(*this, Division(Rational(180), Constant(Ion::Charset::SmallPi)).shallowReduce(context, angleUnit, target)).shallowReduce(context, angleUnit, target);
Expression Expression::radianToDegree() {
// e*180/Pi
return Multiplication(*this, Rational(180), Power(Constant(Ion::Charset::SmallPi), Rational(-1)));
}
Expression Expression::degreeToRadian(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) {
return Multiplication(*this, Division(Constant(Ion::Charset::SmallPi), Rational(180)).shallowReduce(context, angleUnit, target)).shallowReduce(context, angleUnit, target);
Expression Expression::degreeToRadian() {
// e*Pi/180
return Multiplication(*this, Power(Rational(180), Rational(-1)), Constant(Ion::Charset::SmallPi));
}
Expression Expression::reduce(Context & context, Preferences::AngleUnit angleUnit) {

View File

@@ -522,8 +522,49 @@ Expression Multiplication::privateShallowReduce(Context & context, Preferences::
}
}
// Step 8: Let's remove the multiplication altogether if it has one child
// Step 7: Let's remove the multiplication altogether if it has one child
Expression result = squashUnaryHierarchyInPlace();
if (result != *this) {
return result;
}
/* Step 8: Let's bubble up the complex operator if possible
* 3 cases:
* - All children are real, we do nothing (allChildrenAreReal == 1)
* - One of the child is non-real and not a ComplexCartesian: it means a
* complex expression could not be resolved as a ComplexCartesian, we cannot
* do anything about it now (allChildrenAreReal == -1)
* - All children are either real or ComplexCartesian (allChildrenAreReal == 0)
* We can bubble up ComplexCartesian nodes. */
if (allChildrenAreReal(context, angleUnit) == 0) {
int nbChildren = numberOfChildren();
int i = nbChildren-1;
assert(childAtIndex(i).type() == ExpressionNode::Type::ComplexCartesian);
ComplexCartesian child = childAtIndex(i).convert<ComplexCartesian>();
removeChildAtIndexInPlace(i);
i--;
while (i >= 0) {
Expression e = childAtIndex(i);
if (e.type() != ExpressionNode::Type::ComplexCartesian) {
// the Multiplication is sorted so ComplexCartesian nodes are the last ones
break;
}
child = child.multiply(static_cast<ComplexCartesian &>(e), context, angleUnit, target);
removeChildAtIndexInPlace(i);
i--;
}
Multiplication real = *this;
Multiplication imag = clone().convert<Multiplication>();
real.addChildAtIndexInPlace(child.real(), real.numberOfChildren(), real.numberOfChildren());
imag.addChildAtIndexInPlace(child.imag(), real.numberOfChildren(), real.numberOfChildren());
ComplexCartesian newComplexCartesian = ComplexCartesian::Builder();
replaceWithInPlace(newComplexCartesian);
newComplexCartesian.replaceChildAtIndexInPlace(0, real);
newComplexCartesian.replaceChildAtIndexInPlace(1, imag);
real.shallowReduce(context, angleUnit, target);
imag.shallowReduce(context, angleUnit, target);
return newComplexCartesian.shallowReduce(context, angleUnit);
}
return result;
}

View File

@@ -28,6 +28,10 @@ void NAryExpressionNode::sortChildrenInPlace(ExpressionOrder order, bool canBeIn
}
}
bool NAryExpressionNode::isReal(Context & context, Preferences::AngleUnit angleUnit) const {
return NAryExpression(this).allChildrenAreReal(context, angleUnit) == 1;
}
Expression NAryExpressionNode::squashUnaryHierarchyInPlace() {
NAryExpression reference = NAryExpression(this);
if (reference.numberOfChildren() == 1) {
@@ -76,4 +80,22 @@ int NAryExpressionNode::simplificationOrderGreaterType(const ExpressionNode * e,
return 0;
}
int NAryExpression::allChildrenAreReal(Context & context, Preferences::AngleUnit angleUnit) const {
int i = 0;
/* The addition children are assumed to be sorted. ComplexCartesian children
* are supposed to be the last ones before matrices. We just test children
* to be real until we reach the first ComplexCartesian. */
while (i < numberOfChildren()) {
Expression c = childAtIndex(i);
if (c.type() == ExpressionNode::Type::ComplexCartesian) {
return 0;
}
if (!c.isReal(context, angleUnit)) {
return -1;
}
i++;
}
return 1;
}
}

View File

@@ -79,7 +79,13 @@ int PowerNode::getPolynomialCoefficients(Context & context, const char * symbolN
return Power(this).getPolynomialCoefficients(context, symbolName, coefficients);
}
// Private
bool PowerNode::isReal(Context & context, Preferences::AngleUnit angleUnit) const {
ExpressionNode * base = childAtIndex(0);
if (base->isReal(context, angleUnit) && base->sign(&context, angleUnit) == Sign::Positive && childAtIndex(1)->isReal(context, angleUnit)) {
return true;
}
return false;
}
ComplexCartesian PowerNode::complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const {
Power p(this);
@@ -125,6 +131,8 @@ ComplexPolar PowerNode::complexPolar(Context & context, Preferences::AngleUnit a
return ComplexPolar::Builder(norm, argument);
}
// Private
template<typename T>
Complex<T> PowerNode::compute(const std::complex<T> c, const std::complex<T> d) {
std::complex<T> result;
@@ -359,22 +367,76 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU
#endif
#endif
/* Step 0: if both children are true complexes, the result is undefined. We
* can assert that evaluations are Complex, as matrix are not simplified */
Evaluation<float> c0Approximated = childAtIndex(0).node()->approximate(1.0f, context, angleUnit);
// TODO: do we need this?
/*Evaluation<float> c0Approximated = childAtIndex(0).node()->approximate(1.0f, context, angleUnit);
Evaluation<float> c1Approximated = childAtIndex(1).node()->approximate(1.0f, context, angleUnit);
Complex<float> c0 = static_cast<Complex<float>&>(c0Approximated);
Complex<float> c1 = static_cast<Complex<float>&>(c1Approximated);
bool bothChildrenComplexes = c0.imag() != 0 && c1.imag() != 0 && !std::isnan(c0.imag()) && !std::isnan(c1.imag());
bool nonComplexNegativeChild0 = c0.imag() == 0 && c0.real() < 0;
bool nonNullChild0 = !std::isnan(c0.real()) && !std::isnan(c0.imag()) && (c0.real() > Expression::epsilon<float>() || c0.imag() > Expression::epsilon<float>());
if (bothChildrenComplexes) {
return *this;
}*/
/* Step 0: if both children are true unresolved complexes, the result is not simplified. TODO? */
Expression power = *this;
Expression base = childAtIndex(0);
Expression index = childAtIndex(1);
/* Step 0: if both children are true unresolved complexes, the result is not simplified. TODO? */
if (!base.isReal(context, angleUnit) && !index.isReal(context, angleUnit)) {
return *this;
}
/* Step 1: we now bubble up ComplexCartesian, we handle different case */
ComplexCartesian complexBase;
ComplexCartesian complexIndex;
ComplexCartesian result;
// First, (x+iy)^q with q special values
// TODO: explain here why?
if (base.type() == ExpressionNode::Type::ComplexCartesian) {
complexBase = static_cast<ComplexCartesian &>(base);
Integer ten(10);
if (index.type() == ExpressionNode::Type::Rational) {
Rational r = static_cast<Rational &>(index);
if (r.isMinusOne()) {
// (x+iy)^(-1)
result = complexBase.inverse(context, angleUnit, target);
} else if (r.isHalf()) {
// (x+iy)^(1/2)
result = complexBase.squareRoot(context, angleUnit, target);
} else if (r.isMinusHalf()) {
// (x+iy)^(-1/2)
result = complexBase.squareRoot(context, angleUnit, target).inverse(context, angleUnit, target);
} else if (r.integerDenominator().isOne() && r.unsignedIntegerNumerator().isLowerThan(ten)) {
if (r.sign() == ExpressionNode::Sign::Positive) {
// (x+iy)^n, n integer positive n < 10
result = complexBase.powerInteger(r.unsignedIntegerNumerator().extractedInt(), context, angleUnit, target);
} else {
// (x+iy)^(-n), n integer positive n < 10
result = complexBase.powerInteger(r.unsignedIntegerNumerator().extractedInt(), context, angleUnit, target).inverse(context, angleUnit, target);
}
}
if (!result.isUninitialized()) {
replaceWithInPlace(result);
return result.shallowReduce(context, angleUnit);
}
}
}
// All other cases where one child at least is a ComplexCartesian
if ((base.isReal(context, angleUnit) && index.type() == ExpressionNode::Type::ComplexCartesian) ||
(base.type() == ExpressionNode::Type::ComplexCartesian && index.isReal(context, angleUnit)) ||
(base.type() == ExpressionNode::Type::ComplexCartesian && index.type() == ExpressionNode::Type::ComplexCartesian)) {
complexBase = base.type() == ExpressionNode::Type::ComplexCartesian ? static_cast<ComplexCartesian &>(base) : ComplexCartesian::Builder(base, Rational(0));
complexIndex = index.type() == ExpressionNode::Type::ComplexCartesian ? static_cast<ComplexCartesian &>(index) : ComplexCartesian::Builder(index, Rational(0));
result = complexBase.power(complexIndex, context, angleUnit, target);
replaceWithInPlace(result);
return result.shallowReduce(context, angleUnit);
}
/* Step 1: We handle simple cases as x^0, x^1, 0^x and 1^x first for 2 reasons:
* - we can assert this step that there is no division by 0:
* - we can assert after this step that there is no division by 0:
* for instance, 0^(-2)->undefined
* - we save computational time by early escaping for these cases. */
if (childAtIndex(1).type() == ExpressionNode::Type::Rational) {
@@ -388,7 +450,7 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU
return result;
}
// x^0
if (target == ExpressionNode::ReductionTarget::User || nonNullChild0) {
if (target == ExpressionNode::ReductionTarget::User || childAtIndex(0).isNumber()) {
/* Warning: if the ReductionTarget is User, in all other cases but 0^0,
* we replace x^0 by one. This is almost always true except when x = 0.
* However, not substituting x^0 by one would prevent from simplifying
@@ -437,7 +499,7 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU
}
}
if (target == ExpressionNode::ReductionTarget::User && childAtIndex(1).type() == ExpressionNode::Type::Rational) {
/*if (target == ExpressionNode::ReductionTarget::User && childAtIndex(1).type() == ExpressionNode::Type::Rational) {
const Rational b = childAtIndex(1).convert<Rational>();
// i^(p/q)
if (childAtIndex(0).type() == ExpressionNode::Type::Constant && childAtIndex(0).convert<Constant>().isIComplex()) {
@@ -446,7 +508,7 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU
replaceWithInPlace(result);
return result.shallowReduce(context, angleUnit, target);
}
}
}*/
// (±inf)^x
if (childAtIndex(0).type() == ExpressionNode::Type::Infinity) {
@@ -508,7 +570,7 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU
}
}
// e^(i*Pi*r) with r rational
if (!letPowerAtRoot && isNthRootOfUnity()) {
/*if (!letPowerAtRoot && isNthRootOfUnity()) {
Expression m = childAtIndex(1);
Expression i = m.childAtIndex(m.numberOfChildren()-1);
static_cast<Multiplication &>(m).removeChildAtIndexInPlace(m.numberOfChildren()-1);
@@ -525,7 +587,7 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU
complexPart.shallowReduce(context, angleUnit, target);
replaceWithInPlace(a);
return a.shallowReduce(context, angleUnit, target);
}
}*/
// x^log(y,x)->y if y > 0
if (childAtIndex(1).type() == ExpressionNode::Type::Logarithm) {
if (childAtIndex(1).numberOfChildren() == 2 && childAtIndex(0).isIdenticalTo(childAtIndex(1).childAtIndex(1))) {