Files
Upsilon/poincare/src/addition.cpp

461 lines
19 KiB
C++

#include <poincare/addition.h>
#include <poincare/complex_cartesian.h>
#include <poincare/layout_helper.h>
#include <poincare/matrix.h>
#include <poincare/multiplication.h>
#include <poincare/opposite.h>
#include <poincare/power.h>
#include <poincare/serialization_helper.h>
#include <poincare/subtraction.h>
#include <poincare/undefined.h>
#include <assert.h>
namespace Poincare {
int AdditionNode::polynomialDegree(Context * context, const char * symbolName) const {
int degree = 0;
for (ExpressionNode * e : children()) {
int d = e->polynomialDegree(context, symbolName);
if (d < 0) {
return -1;
}
degree = d > degree ? d : degree;
}
return degree;
}
int AdditionNode::getPolynomialCoefficients(Context * context, const char * symbolName, Expression coefficients[]) const {
return Addition(this).getPolynomialCoefficients(context, symbolName, coefficients);
}
// Layout
Layout AdditionNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const {
return LayoutHelper::Infix(Addition(this), floatDisplayMode, numberOfSignificantDigits, "+");
}
int AdditionNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const {
return SerializationHelper::Infix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, "+");
}
// Simplication
Expression AdditionNode::shallowReduce(ReductionContext reductionContext) {
return Addition(this).shallowReduce(reductionContext);
}
Expression AdditionNode::shallowBeautify(ReductionContext reductionContext) {
return Addition(this).shallowBeautify(reductionContext);
}
// Addition
const Number Addition::NumeralFactor(const Expression & e) {
if (e.type() == ExpressionNode::Type::Multiplication && e.childAtIndex(0).isNumber()) {
Number result = e.childAtIndex(0).convert<Number>();
return result;
}
return Rational::Builder(1);
}
int Addition::getPolynomialCoefficients(Context * context, const char * symbolName, Expression coefficients[]) const {
int deg = polynomialDegree(context, symbolName);
if (deg < 0 || deg > Expression::k_maxPolynomialDegree) {
return -1;
}
for (int k = 0; k < deg+1; k++) {
coefficients[k] = Addition::Builder();
}
Expression intermediateCoefficients[Expression::k_maxNumberOfPolynomialCoefficients];
for (int i = 0; i < numberOfChildren(); i++) {
int d = childAtIndex(i).getPolynomialCoefficients(context, symbolName, intermediateCoefficients);
assert(d < Expression::k_maxNumberOfPolynomialCoefficients);
for (int j = 0; j < d+1; j++) {
static_cast<Addition&>(coefficients[j]).addChildAtIndexInPlace(intermediateCoefficients[j], coefficients[j].numberOfChildren(), coefficients[j].numberOfChildren());
}
}
return deg;
}
Expression Addition::shallowBeautify(ExpressionNode::ReductionContext reductionContext) {
/* Beautifying AdditionNode essentially consists in adding Subtractions if
* needed.
* In practice, we want to turn "a+(-1)*b" into "a-b". Or, more precisely, any
* "a+(-r)*b" into "a-r*b" where r is a positive Rational.
* Note: the process will slightly differ if the negative product occurs on
* the first term: we want to turn "Addition(Multiplication(-1,b))" into
* "Opposite(b)".
* Last but not least, special care must be taken when iterating over children
* since we may remove some during the process. */
/* Sort children in decreasing order:
* 1+x+x^2 --> x^2+x+1
* 1+R(2) --> R(2)+1 */
sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, false, canBeInterrupted); }, reductionContext.context(), true);
for (int i = 0; i < numberOfChildren(); i++) {
// Try to make the child i positive if any negative numeral factor is found
Expression subtractant = childAtIndex(i).makePositiveAnyNegativeNumeralFactor(reductionContext);
if (subtractant.isUninitialized())
{
// if subtractant is not initialized, it means the child i had no negative numeral factor
// we ignore terms which are not like "(-r)*a"
continue;
}
if (i == 0) {
Opposite o = Opposite::Builder(subtractant);
replaceChildAtIndexInPlace(i, o);
} else {
Expression leftSibling = childAtIndex(i-1);
removeChildAtIndexInPlace(i-1);
/* CAUTION: we removed a child. So we need to decrement i to make sure
* the next iteration is actually on the next child. */
i--;
Subtraction s = Subtraction::Builder(leftSibling, subtractant);
/* We stole subtractant from this which replaced it by a ghost. We thus
* need to put the subtraction at the previous index of subtractant, which
* is still i because we updated i after removing a child. */
replaceChildAtIndexInPlace(i, s);
}
}
Expression result = squashUnaryHierarchyInPlace();
return result;
}
Expression Addition::shallowReduce(ExpressionNode::ReductionContext reductionContext) {
{
Expression e = Expression::defaultShallowReduce();
if (e.isUndefined()) {
return e;
}
}
// Step 0: Let's remove the addition if it has a single child
assert(numberOfChildren() > 0);
if (numberOfChildren() == 1) {
return squashUnaryHierarchyInPlace();
}
/* Step 1: Addition is associative, so let's start by merging children which
* are additions. */
int i = 0;
while (i < numberOfChildren()) {
if (childAtIndex(i).type() == ExpressionNode::Type::Addition) {
mergeChildrenAtIndexInPlace(childAtIndex(i), i);
continue;
}
i++;
}
// Step 2: Sort the children
sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, reductionContext.context(), true);
/* Step 3: Handle matrices. We return undef for a scalar added to a matrix.
* Thanks to the simplification order, all matrix children (if any) are the
* last children. */
int childrenCount = numberOfChildren();
assert(childrenCount > 1);
{
Expression lastChild = childAtIndex(childrenCount - 1);
if (lastChild.deepIsMatrix(reductionContext.context())) {
if (!childAtIndex(0).deepIsMatrix(reductionContext.context())) {
/* If there is a matrix in the children, the last child is a matrix. If
* there is a a scalar, the first child is a scalar. We forbid the
* addition of a matrix and a scalar. */
return replaceWithUndefinedInPlace();
}
if (lastChild.type() != ExpressionNode::Type::Matrix) {
/* All children are matrices that are not of type Matrix (for instance a
* ConfidenceInterval that cannot be reduced). We cannot reduce the
* addition more. */
return *this;
}
// Create the addition matrix (in place of the last child)
Matrix resultMatrix = static_cast<Matrix &>(lastChild);
int n = resultMatrix.numberOfRows();
int m = resultMatrix.numberOfColumns();
// Scan to add the other children, which are matrices
for (int i = childrenCount - 2; i >= 0; i--) {
if (childAtIndex(i).type() != ExpressionNode::Type::Matrix) {
break;
}
Matrix currentMatrix = childAtIndex(i).convert<Matrix>();
int currentN = currentMatrix.numberOfRows();
int currentM = currentMatrix.numberOfColumns();
if (currentN != n || currentM != m) {
// Addition of matrices of different dimensions -> undef
return replaceWithUndefinedInPlace();
}
// Dispatch the current matrix children in the created addition matrix
for (int j = 0; j < n*m; j++) {
Expression resultEntryJ = resultMatrix.childAtIndex(j);
Expression currentEntryJ = currentMatrix.childAtIndex(j);
if (resultEntryJ.type() == ExpressionNode::Type::Addition) {
static_cast<Addition &>(resultEntryJ).addChildAtIndexInPlace(currentEntryJ, resultEntryJ.numberOfChildren(), resultEntryJ.numberOfChildren());
} else {
Addition a = Addition::Builder(resultEntryJ, currentEntryJ);
resultMatrix.replaceChildAtIndexInPlace(j, a);
}
}
removeChildInPlace(currentMatrix, currentMatrix.numberOfChildren());
}
for (int j = 0; j < n*m; j++) {
resultMatrix.childAtIndex(j).shallowReduce(reductionContext);
}
return squashUnaryHierarchyInPlace();
}
}
/* Step 4: Factorize like terms. Thanks to the simplification order, those are
* next to each other at this point. */
i = 0;
while (i < numberOfChildren()-1) {
Expression e1 = childAtIndex(i);
Expression e2 = childAtIndex(i+1);
if (e1.isNumber() && e2.isNumber()) {
Number r1 = static_cast<Number&>(e1);
Number r2 = static_cast<Number&>(e2);
Number a = Number::Addition(r1, r2);
replaceChildAtIndexInPlace(i, a);
removeChildAtIndexInPlace(i+1);
continue;
}
if (TermsHaveIdenticalNonNumeralFactors(e1, e2, reductionContext.context())) {
factorizeChildrenAtIndexesInPlace(i, i+1, reductionContext);
continue;
}
i++;
}
/* Step 5: Let's remove any zero. It's important to do this after having
* factorized because factorization can lead to new zeroes. For example
* pi+(-1)*pi. We don't remove the last zero if it's the only child left
* though. */
i = 0;
while (i < numberOfChildren()) {
Expression e = childAtIndex(i);
if (e.type() == ExpressionNode::Type::Rational && static_cast<Rational&>(e).isZero() && numberOfChildren() > 1) {
removeChildAtIndexInPlace(i);
continue;
}
i++;
}
// Step 6: Let's remove the addition altogether if it has a single child
Expression result = squashUnaryHierarchyInPlace();
if (result != *this) {
return result;
}
/* Step 7: 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(reductionContext.context()) == 0) {
/* We turn (a+ib)+(c+id) into (a+c)+i(c+d)*/
Addition imag = Addition::Builder(); // we store all imaginary parts in 'imag'
Addition real = *this; // we store all real parts in 'real'
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(reductionContext);
imag.shallowReduce(reductionContext);
return newComplexCartesian.shallowReduce();
}
/* Step 8: 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();
if (reductionContext.target() == ExpressionNode::ReductionTarget::User && result == *this && (p.isUninitialized() || p.type() != ExpressionNode::Type::Addition)) {
// squashUnaryHierarchy didn't do anything: we're not an unary hierarchy
result = factorizeOnCommonDenominator(reductionContext);
}
return result;
}
int Addition::NumberOfNonNumeralFactors(const Expression & e) {
if (e.type() != ExpressionNode::Type::Multiplication) {
return 1; // Or (e->type() != Type::Rational);
}
int result = e.numberOfChildren();
if (e.childAtIndex(0).isNumber()) {
result--;
}
return result;
}
const Expression Addition::FirstNonNumeralFactor(const Expression & e) {
if (e.type() != ExpressionNode::Type::Multiplication) {
return e;
}
if (e.childAtIndex(0).isNumber()) {
return e.numberOfChildren() > 1 ? e.childAtIndex(1) : Expression();
}
return e.childAtIndex(0);
}
bool Addition::TermsHaveIdenticalNonNumeralFactors(const Expression & e1, const Expression & e2, Context * context) {
/* Return true if two expressions differ only by a rational factor. For
* example, 2*pi and pi do, 2*pi and 2*ln(2) don't. We do not want to
* factorize random(). */
int numberOfNonNumeralFactorsInE1 = NumberOfNonNumeralFactors(e1);
int numberOfNonNumeralFactorsInE2 = NumberOfNonNumeralFactors(e2);
if (numberOfNonNumeralFactorsInE1 != numberOfNonNumeralFactorsInE2) {
return false;
}
int numberOfNonNumeralFactors = numberOfNonNumeralFactorsInE1;
if (numberOfNonNumeralFactors == 1) {
Expression nonNumeralFactor = FirstNonNumeralFactor(e1);
if (nonNumeralFactor.recursivelyMatches(Expression::IsRandom, context, true)) {
return false;
}
return FirstNonNumeralFactor(e1).isIdenticalTo(FirstNonNumeralFactor(e2));
} else {
assert(numberOfNonNumeralFactors > 1);
return Multiplication::HaveSameNonNumeralFactors(e1, e2);
}
}
Expression Addition::factorizeOnCommonDenominator(ExpressionNode::ReductionContext reductionContext) {
/* We want to turn (a/b+c/d+e/b) into (a*d+b*c+e*d)/(b*d), except if one of
* the denominators contains random, in which case the factors with random
* should stay appart. */
/* a will store the random factors, which should not be put to the same
* denominator. */
Addition a = Addition::Builder();
// Step 1: We want to compute the common denominator, b*d
Multiplication commonDenominator = Multiplication::Builder();
for (int i = 0; i < numberOfChildren(); i++) {
Expression childI = childAtIndex(i);
Expression currentDenominator = childI.denominator(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit());
if (!currentDenominator.isUninitialized()) {
if (currentDenominator.recursivelyMatches(Expression::IsRandom, reductionContext.context(), true)) {
// Remove "random" factors
removeChildInPlace(childI, childI.numberOfChildren());
a.addChildAtIndexInPlace(childI, a.numberOfChildren(), a.numberOfChildren());
i--;
continue;
}
// Make commonDenominator = LeastCommonMultiple(commonDenominator, denominator);
commonDenominator.addMissingFactors(currentDenominator, reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit());
}
}
if (commonDenominator.numberOfChildren() == 0) {
// If commonDenominator is empty this means that no child was a fraction.
while (a.numberOfChildren() > 0) {
// Put back the "random" children
addChildAtIndexInPlace(a.childAtIndex(0), numberOfChildren(), numberOfChildren());
}
return *this;
}
/* Step 2: Create the numerator. We start with this being a/b+c/d+e/b and we
* want to create numerator = a/b*b*d + c/d*b*d + e/b*b*d = a*d + c*b + e*d */
assert(reductionContext.target() == ExpressionNode::ReductionTarget::User); // Else, before, the algorithm used User target -> put back ?
Addition numerator = Addition::Builder();
for (int i = 0; i < numberOfChildren(); i++) {
Multiplication m = Multiplication::Builder(childAtIndex(i), commonDenominator.clone());
numerator.addChildAtIndexInPlace(m, numerator.numberOfChildren(), numerator.numberOfChildren());
m.privateShallowReduce(reductionContext, true, false);
}
// Step 3: Add the denominator
Power inverseDenominator = Power::Builder(commonDenominator, Rational::Builder(-1));
Multiplication result = Multiplication::Builder(numerator, inverseDenominator);
// Step 4: Simplify the numerator
numerator.shallowReduce(reductionContext);
// Step 5: Simplify the denominator (in case it's a rational number)
inverseDenominator.deepReduce(reductionContext);
/* Step 6: We simplify the resulting multiplication forbidding any
* distribution of multiplication on additions (to avoid an infinite loop).
* Handle the removed random factors. */
int aChildrenCount = a.numberOfChildren();
if (aChildrenCount == 0) {
replaceWithInPlace(result);
return result.privateShallowReduce(reductionContext, false, true);
}
a.addChildAtIndexInPlace(result, aChildrenCount, aChildrenCount);
result.privateShallowReduce(reductionContext, false, true);
replaceWithInPlace(a);
return a;
}
void Addition::factorizeChildrenAtIndexesInPlace(int index1, int index2, ExpressionNode::ReductionContext reductionContext) {
/* This function factorizes two children which only differ by a rational
* factor. For example, if this is AdditionNode(2*pi, 3*pi), then 2*pi and 3*pi
* could be merged, and this turned into AdditionNode(5*pi). */
assert(index1 >= 0 && index1 < numberOfChildren());
assert(index2 >= 0 && index2 < numberOfChildren());
Expression e1 = childAtIndex(index1);
Expression e2 = childAtIndex(index2);
// Step 1: Compute the new numeral factor
Number r = Number::Addition(NumeralFactor(e1), NumeralFactor(e2));
// Step 2: Get rid of one of the children
removeChildAtIndexInPlace(index2);
// Step 3: Create a multiplication
Multiplication m = Multiplication::Builder();
if (e1.type() == ExpressionNode::Type::Multiplication) {
m = static_cast<Multiplication&>(e1);
} else {
replaceChildAtIndexInPlace(index1, m);
m.addChildAtIndexInPlace(e1, 0, 0);
}
// Step 4: Use the new rational factor
assert(m.numberOfChildren() > 0);
if (m.childAtIndex(0).isNumber()) {
/* The children were ordered before, so ant rational child of m would be on
* the first position */
m.replaceChildAtIndexInPlace(0, r);
} else {
m.addChildAtIndexInPlace(r, 0, m.numberOfChildren());
}
// Step 5: Reduce the multiplication (in case the new rational factor is zero)
m.shallowReduce(reductionContext);
}
template Complex<float> Poincare::AdditionNode::compute<float>(std::complex<float>, std::complex<float>, Preferences::ComplexFormat);
template Complex<double> Poincare::AdditionNode::compute<double>(std::complex<double>, std::complex<double>, Preferences::ComplexFormat);
template MatrixComplex<float> AdditionNode::computeOnMatrices<float>(const MatrixComplex<float>,const MatrixComplex<float>, Preferences::ComplexFormat complexFormat);
template MatrixComplex<double> AdditionNode::computeOnMatrices<double>(const MatrixComplex<double>,const MatrixComplex<double>, Preferences::ComplexFormat complexFormat);
template MatrixComplex<float> AdditionNode::computeOnComplexAndMatrix<float>(std::complex<float> const, const MatrixComplex<float>, Preferences::ComplexFormat complexFormat);
template MatrixComplex<double> AdditionNode::computeOnComplexAndMatrix<double>(std::complex<double> const, const MatrixComplex<double>, Preferences::ComplexFormat complexFormat);
}