mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 00:37:25 +01:00
[solver] Move Poincare::Equal::solve() to Solver app to consider system
instead of single expression
This commit is contained in:
@@ -18,7 +18,7 @@ public:
|
||||
virtual void removeAll();
|
||||
int numberOfModels() const { return m_numberOfModels; };
|
||||
virtual int maxNumberOfModels() const = 0;
|
||||
void tidy();
|
||||
virtual void tidy();
|
||||
protected:
|
||||
virtual ExpressionModel * emptyModel() = 0;
|
||||
virtual ExpressionModel * nullModel() = 0;
|
||||
|
||||
@@ -4,6 +4,7 @@ snapshot_headers += apps/solver/app.h
|
||||
app_objs += $(addprefix apps/solver/,\
|
||||
app.o\
|
||||
equation_models_parameter_controller.o\
|
||||
equation.o\
|
||||
equation_store.o\
|
||||
list_controller.o\
|
||||
)
|
||||
|
||||
45
apps/solver/equation.cpp
Normal file
45
apps/solver/equation.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "equation.h"
|
||||
|
||||
using namespace Poincare;
|
||||
|
||||
namespace Solver {
|
||||
|
||||
Equation::Equation() :
|
||||
Shared::ExpressionModel(),
|
||||
m_standardForm(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
Equation& Equation::operator=(const Equation& other) {
|
||||
Shared::ExpressionModel::operator=(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Equation::~Equation() {
|
||||
if (m_standardForm) {
|
||||
delete m_standardForm;
|
||||
m_standardForm = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Equation::setContent(const char * c) {
|
||||
tidy();
|
||||
ExpressionModel::setContent(c);
|
||||
}
|
||||
|
||||
void Equation::tidy() {
|
||||
ExpressionModel::tidy();
|
||||
if (m_standardForm) {
|
||||
delete m_standardForm;
|
||||
m_standardForm = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Expression * Equation::standardForm(Context * context) const {
|
||||
if (m_standardForm == nullptr) {
|
||||
m_standardForm = static_cast<const Equal *>(expression(context))->standardEquation(*context);
|
||||
}
|
||||
return m_standardForm;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,6 +6,18 @@
|
||||
namespace Solver {
|
||||
|
||||
class Equation : public Shared::ExpressionModel {
|
||||
public:
|
||||
Equation();
|
||||
~Equation();
|
||||
Equation& operator=(const Equation& other);
|
||||
Equation& operator=(Equation&& other) = delete;
|
||||
Equation(const Equation& other) = delete;
|
||||
Equation(Equation&& other) = delete;
|
||||
void setContent(const char * c) override;
|
||||
void tidy() override;
|
||||
Poincare::Expression * standardForm(Poincare::Context * context) const;
|
||||
private:
|
||||
mutable Poincare::Expression * m_standardForm;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
#include "equation_store.h"
|
||||
|
||||
using namespace Poincare;
|
||||
|
||||
namespace Solver {
|
||||
|
||||
EquationStore::EquationStore() :
|
||||
m_equations{},
|
||||
m_form(Form::LinearSystem),
|
||||
m_numberOfSolutions(0),
|
||||
m_exactSolutions{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}
|
||||
{
|
||||
}
|
||||
|
||||
EquationStore::~EquationStore() {
|
||||
tidySolution();
|
||||
}
|
||||
|
||||
Equation * EquationStore::emptyModel() {
|
||||
static Equation e;
|
||||
return &e;
|
||||
@@ -11,4 +25,168 @@ void EquationStore::setModelAtIndex(Shared::ExpressionModel * e, int i) {
|
||||
m_equations[i] = *(static_cast<Equation *>(e));;
|
||||
}
|
||||
|
||||
void EquationStore::tidy() {
|
||||
ExpressionModelStore::tidy();
|
||||
tidySolution();
|
||||
}
|
||||
|
||||
EquationStore::Error EquationStore::exactSolve(Poincare::Context * context) {
|
||||
tidySolution();
|
||||
char variables[Expression::k_maxNumberOfVariables+1] = {0};
|
||||
int numberOfVariables;
|
||||
for (int i = 0; i < numberOfModels(); i++) {
|
||||
numberOfVariables = m_equations[i].standardForm(context)->getVariables(variables);
|
||||
if (numberOfVariables < 0) {
|
||||
return Error::TooManyVariables;
|
||||
}
|
||||
}
|
||||
|
||||
// 1-- Linear System
|
||||
/* Create matrix coefficients and vector constants as:
|
||||
* coefficients*(x y z ...) = constants */
|
||||
Expression * coefficients[k_maxNumberOfEquations][Expression::k_maxNumberOfVariables];
|
||||
Expression * constants[k_maxNumberOfEquations];
|
||||
bool success = true;
|
||||
for (int i = 0; i < numberOfModels(); i++) {
|
||||
success = success && m_equations[i].standardForm(context)->getLinearCoefficients(variables, coefficients[i], &constants[i], *context);
|
||||
if (!success) {
|
||||
for (int j = 0; j < i; j++) {
|
||||
for (int k = 0; k < numberOfVariables; k++) {
|
||||
delete coefficients[j][k];
|
||||
}
|
||||
delete constants[j];
|
||||
}
|
||||
if (numberOfModels() > 1 || numberOfVariables > 1) {
|
||||
return Error::NonLinearSystem;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
for (int i = 0; i < numberOfModels(); i++) {
|
||||
for (int k = 0; k < numberOfVariables; k++) {
|
||||
Expression::Reduce(&coefficients[i][k], *context);
|
||||
}
|
||||
Expression::Reduce(&constants[i], *context);
|
||||
}
|
||||
m_form = Form::LinearSystem;
|
||||
return resolveLinearSystem(coefficients, constants, context);
|
||||
}
|
||||
assert(numberOfVariables == 1 && numberOfModels() == 1);
|
||||
char x = variables[0];
|
||||
Expression * polynomialCoefficients[Expression::k_maxNumberOfPolynomialCoefficients];
|
||||
int degree = m_equations[0].standardForm(context)->getPolynomialCoefficients(x, polynomialCoefficients);
|
||||
if (degree < 0) {
|
||||
m_form = Form::Monovariable;
|
||||
return Error::RequireApproximateSolution;
|
||||
}
|
||||
m_form = Form::PolynomialMonovariable;
|
||||
for (int i = 0; i <= degree; i++) {
|
||||
Expression::Reduce(&polynomialCoefficients[i], *context);
|
||||
}
|
||||
return oneDimensialPolynomialSolve(polynomialCoefficients, degree, context);
|
||||
}
|
||||
|
||||
EquationStore::Error EquationStore::resolveLinearSystem(Expression * coefficients[k_maxNumberOfEquations][Expression::k_maxNumberOfVariables], Expression * constants[k_maxNumberOfEquations], Context * context) {
|
||||
m_numberOfSolutions = 5;
|
||||
m_exactSolutions[0] = new Rational(1);
|
||||
m_exactSolutions[1] = new Rational(2);
|
||||
m_exactSolutions[2] = new Rational(3);
|
||||
m_exactSolutions[3] = new Rational(4);
|
||||
m_exactSolutions[4] = new Rational(5);
|
||||
return Error::NoError;
|
||||
}
|
||||
|
||||
EquationStore::Error EquationStore::oneDimensialPolynomialSolve(Expression * coefficients[Expression::k_maxNumberOfPolynomialCoefficients], int degree, Context * context) {
|
||||
assert(degree == 2);
|
||||
Expression * deltaDenominator[3] = {new Rational(4), coefficients[0]->clone(), coefficients[2]->clone()};
|
||||
Expression * delta = new Subtraction(new Power(coefficients[1]->clone(), new Rational(2), false), new Multiplication(deltaDenominator, 3, false), false);
|
||||
Expression::Simplify(&delta, *context);
|
||||
if (delta->isRationalZero()) {
|
||||
m_exactSolutions[0] = new Division(new Opposite(coefficients[1], false), new Multiplication(new Rational(2), coefficients[2]), false);
|
||||
m_numberOfSolutions = 1;
|
||||
} else {
|
||||
m_exactSolutions[0] = new Division(new Subtraction(new Opposite(coefficients[1]->clone(), false), new SquareRoot(delta->clone(), false), false), new Multiplication(new Rational(2), coefficients[2]->clone()), false);
|
||||
m_exactSolutions[1] = new Division(new Addition(new Opposite(coefficients[1], false), new SquareRoot(delta->clone(), false), false), new Multiplication(new Rational(2), coefficients[2]), false);
|
||||
m_numberOfSolutions = 2;
|
||||
}
|
||||
m_exactSolutions[m_numberOfSolutions] = delta;
|
||||
delete coefficients[0];
|
||||
return Error::NoError;
|
||||
#if 0
|
||||
if (degree == 3) {
|
||||
Expression * a = coefficients[3];
|
||||
Expression * b = coefficients[2];
|
||||
Expression * c = coefficients[1];
|
||||
Expression * d = coefficients[0];
|
||||
// Delta = b^2*c^2+18abcd-27a^2*d^2-4ac^3-4db^3
|
||||
Expression * mult0Operands[2] = {new Power(b->clone(), new Rational(2), false), new Power(c->clone(), new Rational(2), false)};
|
||||
Expression * mult1Operands[5] = {new Rational(18), a->clone(), b->clone(), c->clone(), d->clone()};
|
||||
Expression * mult2Operands[3] = {new Rational(-27), new Power(a->clone(), new Rational(2), false), new Power(d->clone(), new Rational(2), false)};
|
||||
Expression * mult3Operands[3] = {new Rational(-4), a->clone(), new Power(c->clone(), new Rational(3), false)};
|
||||
Expression * mult4Operands[3] = {new Rational(-4), d->clone(), new Power(b->clone(), new Rational(3), false)};
|
||||
Expression * add0Operands[5] = {new Multiplication(mult0Operands, 2, false), new Multiplication(mult1Operands, 5, false), new Multiplication(mult2Operands, 3, false), new Multiplication(mult3Operands, 3, false), new Multiplication(mult4Operands, 3, false)};
|
||||
Expression * delta = new Addition(add0Operands, 5, false);
|
||||
Simplify(&delta, *context);
|
||||
// Delta0 = b^2-3ac
|
||||
Expression * mult5Operands[3] = {new Rational(3), a->clone(), c->clone()};
|
||||
Expression * delta0 = new Subtraction(new Power(b->clone(), new Rational(2), false), new Multiplication(mult5Operands, 3, false), false);
|
||||
Reduce(&delta0, *context);
|
||||
if (delta->isRationalZero()) {
|
||||
if (delta0->isRationalZero()) {
|
||||
// delta0 = 0 && delta = 0 --> x0 = -b/(3a)
|
||||
delete delta0;
|
||||
m_exactSolutions[0] = new Opposite(new Division(b, new Multiplication(new Rational(3), a, false), false), false);
|
||||
m_numberOfSolutions = 1;
|
||||
delete c;
|
||||
delete d;
|
||||
} else {
|
||||
// delta = 0 --> x0 = (9ad-bc)/(2delta0)
|
||||
// --> x1 = (4abc-9a^2d-b^3)/(a*delta0)
|
||||
Expression * mult6Operands[3] = {new Rational(9), a, d};
|
||||
m_exactSolutions[0] = new Division(new Subtraction(new Multiplication(mult6Operands, 3, false), new Multiplication(b, c, false), false), new Multiplication(new Rational(2), delta0, false), false);
|
||||
Expression * mult7Operands[4] = {new Rational(4), a->clone(), b->clone(), c->clone()};
|
||||
Expression * mult8Operands[3] = {new Rational(-9), new Power(a->clone(), new Rational(2), false), d->clone()};
|
||||
Expression * add1Operands[3] = {new Multiplication(mult7Operands, 4, false), new Multiplication(mult8Operands,3, false), new Opposite(new Power(b->clone(), new Rational(3), false), false)};
|
||||
m_exactSolutions[1] = new Division(new Addition(add1Operands, 3, false), new Multiplication(a->clone(), delta0, false), false);
|
||||
m_numberOfSolutions = 2;
|
||||
}
|
||||
} else {
|
||||
// delta1 = 2b^3-9abc+27a^2*d
|
||||
Expression * mult9Operands[4] = {new Rational(-9), a, b, c};
|
||||
Expression * mult10Operands[3] = {new Rational(27), new Power(a->clone(), new Rational(2), false), d};
|
||||
Expression * add2Operands[3] = {new Multiplication(new Rational(2), new Power(b->clone(), new Rational(3), false), false), new Multiplication(mult9Operands, 4, false), new Multiplication(mult10Operands, 3, false)};
|
||||
Expression * delta1 = new Addition(add2Operands, 3, false);
|
||||
// C = Root((delta1+sqrt(-27a^2*delta))/2, 3)
|
||||
Expression * mult11Operands[3] = {new Rational(-27), new Power(a->clone(), new Rational(2), false), (*delta)->clone()};
|
||||
Expression * c = new Power(new Division(new Addition(delta1, new SquareRoot(new Multiplication(mult11Operands, 3, false), false), false), new Rational(2), false), new Rational(1,3), false);
|
||||
Expression * unary3roots[2] = {new Addition(new Rational(-1,2), new Division(new Multiplication(new SquareRoot(new Rational(3), false), new Symbol(Ion::Charset::IComplex), false), new Rational(2), false), false), new Subtraction(new Rational(-1,2), new Division(new Multiplication(new SquareRoot(new Rational(3), false), new Symbol(Ion::Charset::IComplex), false), new Rational(2), false), false)};
|
||||
// x_k = -1/(3a)*(b+C*z+delta0/(zC)) with z = unary cube root
|
||||
for (int k = 0; k < 3; k++) {
|
||||
Expression * ccopy = c;
|
||||
Expression * delta0copy = delta0;
|
||||
if (k < 2) {
|
||||
ccopy = new Multiplication(c->clone(), unary3roots[k], false);
|
||||
delta0copy = delta0->clone();
|
||||
}
|
||||
Expression * add3Operands[3] = {b->clone(), ccopy, new Division(delta0copy, ccopy->clone(), false)};
|
||||
m_exactSolutions[k] = new Multiplication(new Division(new Rational(-1), new Multiplication(new Rational(3), a->clone(), false), false), new Addition(add3Operands, 3, false), false);
|
||||
}
|
||||
m_numberOfSolutions = 3;
|
||||
}
|
||||
m_exactSolutions[m_numberOfSolutions] = delta;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void EquationStore::tidySolution() {
|
||||
for (int i = 0; i < k_maxNumberOfExactSolutions; i++) {
|
||||
if (m_exactSolutions[i]) {
|
||||
delete m_exactSolutions[i];
|
||||
m_exactSolutions[i] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,20 +9,44 @@ namespace Solver {
|
||||
|
||||
class EquationStore : public Shared::ExpressionModelStore {
|
||||
public:
|
||||
EquationStore() {}
|
||||
enum class Form {
|
||||
LinearSystem,
|
||||
PolynomialMonovariable,
|
||||
Monovariable,
|
||||
};
|
||||
enum class Error : int16_t {
|
||||
NoError = 0,
|
||||
TooManyVariables = -1,
|
||||
NonLinearSystem = -2,
|
||||
RequireApproximateSolution = -3
|
||||
};
|
||||
EquationStore();
|
||||
~EquationStore();
|
||||
Equation * modelAtIndex(int i) override {
|
||||
assert(i>=0 && i<m_numberOfModels);
|
||||
return &m_equations[i];
|
||||
}
|
||||
int maxNumberOfModels() const override { return k_maxNumberOfEquations; }
|
||||
void tidy() override;
|
||||
Error exactSolve(Poincare::Context * context);
|
||||
private:
|
||||
static constexpr int k_maxNumberOfEquations = Poincare::Expression::k_maxNumberOfVariables; // Enable the same number of equations as the number of unknown variables
|
||||
static constexpr int k_maxNumberOfExactSolutions = Poincare::Expression::k_maxNumberOfVariables > Poincare::Expression::k_maxPolynomialDegree ? Poincare::Expression::k_maxNumberOfVariables : Poincare::Expression::k_maxPolynomialDegree;
|
||||
static constexpr int k_maxNumberOfApproximateSolutions = 10;
|
||||
Equation * emptyModel() override;
|
||||
Equation * nullModel() override {
|
||||
return emptyModel();
|
||||
}
|
||||
void setModelAtIndex(Shared::ExpressionModel * f, int i) override;
|
||||
static constexpr int k_maxNumberOfEquations = Poincare::Expression::k_maxNumberOfVariables; // Enable the same number of equations as the number of unknown variables
|
||||
Equation m_equations[k_maxNumberOfEquations];;
|
||||
Error resolveLinearSystem(Poincare::Expression * coefficients[k_maxNumberOfEquations][Poincare::Expression::k_maxNumberOfVariables], Poincare::Expression * constants[k_maxNumberOfEquations], Poincare::Context * context);
|
||||
Error oneDimensialPolynomialSolve(Poincare::Expression * polynomialCoefficients[Poincare::Expression::k_maxNumberOfPolynomialCoefficients], int degree, Poincare::Context * context);
|
||||
void tidySolution();
|
||||
|
||||
Equation m_equations[k_maxNumberOfEquations];
|
||||
Form m_form;
|
||||
int m_numberOfSolutions;
|
||||
Poincare::Expression * m_exactSolutions[k_maxNumberOfExactSolutions];
|
||||
double m_approximateSolutions[k_maxNumberOfApproximateSolutions];
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public:
|
||||
Type type() const override;
|
||||
Expression * clone() const override;
|
||||
int polynomialDegree(char symbolName) const override;
|
||||
int getPolynomialCoefficients(char symbolName, Expression ** coefficients) const override;
|
||||
int getPolynomialCoefficients(char symbolName, Expression * coefficients[]) const override;
|
||||
/* Evaluation */
|
||||
template<typename T> static Complex<T> compute(const Complex<T> c, const Complex<T> d);
|
||||
template<typename T> static Matrix * computeOnMatrices(const Matrix * m, const Matrix * n) {
|
||||
|
||||
@@ -13,9 +13,9 @@ public:
|
||||
Type type() const override;
|
||||
Expression * clone() const override;
|
||||
int polynomialDegree(char symbolName) const override;
|
||||
int solve(Expression ** solutions, Expression ** delta, Context & context, AngleUnit angleUnit) const;
|
||||
// For the equation A = B, create the reduced expression A-B
|
||||
Expression * standardEquation(Context & context, AngleUnit angleUnit = AngleUnit::Default) const;
|
||||
private:
|
||||
int oneDimensialPolynomialSolve(Expression ** coefficients, int degree, Expression ** solutions, Expression ** delta, Context & context, AngleUnit angleUnit) const;
|
||||
/* Simplification */
|
||||
Expression * shallowReduce(Context& context, AngleUnit angleUnit) override;
|
||||
/* Layout */
|
||||
|
||||
@@ -196,6 +196,7 @@ public:
|
||||
Unknown = 0,
|
||||
Positive = 1
|
||||
};
|
||||
bool isRationalZero() const;
|
||||
virtual Sign sign() const { return Sign::Unknown; }
|
||||
typedef bool (*ExpressionTest)(const Expression * e, Context & context);
|
||||
bool recursivelyMatches(ExpressionTest test, Context & context) const;
|
||||
@@ -222,12 +223,18 @@ public:
|
||||
* variables would overflow the maxNumberOfVariables, getVariables return -1 */
|
||||
static constexpr int k_maxNumberOfVariables = 6;
|
||||
virtual int getVariables(char * variables) const;
|
||||
/* getLinearCoefficients return false if the expression is not linear with
|
||||
* the variables hold in 'variables'. Otherwise, it fills 'coefficients' with
|
||||
* the coefficients of the variables hold in 'variables' (following the same
|
||||
* order) and 'constant' with the constant of the expression. */
|
||||
bool getLinearCoefficients(char * variables, Expression * coefficients[], Expression * constant[], Context & context) const;
|
||||
/* getPolynomialCoefficients fills the table coefficients with the expressions
|
||||
* of the first 5 polynomial coefficients and return polynomialDegree.
|
||||
* coefficients has up to 5 entries. It supposed to be called on Reduced
|
||||
* expression. */
|
||||
static constexpr int k_maxNumberOfPolynomialCoefficients = 4+1;
|
||||
virtual int getPolynomialCoefficients(char symbolName, Expression ** coefficients) const;
|
||||
static constexpr int k_maxPolynomialDegree = 2;
|
||||
static constexpr int k_maxNumberOfPolynomialCoefficients = k_maxPolynomialDegree+1;
|
||||
virtual int getPolynomialCoefficients(char symbolName, Expression * coefficients[]) const;
|
||||
|
||||
/* Comparison */
|
||||
/* isIdenticalTo is the "easy" equality, it returns true if both trees have
|
||||
@@ -246,7 +253,7 @@ public:
|
||||
/* Simplification */
|
||||
static Expression * ParseAndSimplify(const char * text, Context & context, AngleUnit angleUnit = AngleUnit::Default);
|
||||
static void Simplify(Expression ** expressionAddress, Context & context, AngleUnit angleUnit = AngleUnit::Default);
|
||||
static void Reduce(Expression ** expressionAddress, Context & context, AngleUnit angleUnit, bool recursively = true);
|
||||
static void Reduce(Expression ** expressionAddress, Context & context, AngleUnit angleUnit = AngleUnit::Default, bool recursively = true);
|
||||
|
||||
/* Evaluation Engine
|
||||
* The function evaluate creates a new expression and thus mallocs memory.
|
||||
|
||||
@@ -23,7 +23,7 @@ public:
|
||||
Expression * clone() const override;
|
||||
Sign sign() const override;
|
||||
int polynomialDegree(char symbolName) const override;
|
||||
int getPolynomialCoefficients(char symbolName, Expression ** coefficients) const override;
|
||||
int getPolynomialCoefficients(char symbolName, Expression * coefficients[]) const override;
|
||||
/* Evaluation */
|
||||
template<typename T> static Complex<T> compute(const Complex<T> c, const Complex<T> d);
|
||||
template<typename T> static Matrix * computeOnComplexAndMatrix(const Complex<T> * c, const Matrix * m) {
|
||||
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
Expression * clone() const override;
|
||||
Sign sign() const override;
|
||||
int polynomialDegree(char symbolName) const override;
|
||||
int getPolynomialCoefficients(char symbolName, Expression ** coefficients) const override;
|
||||
int getPolynomialCoefficients(char symbolName, Expression * coefficients[]) const override;
|
||||
template<typename T> static Complex<T> compute(const Complex<T> c, const Complex<T> d);
|
||||
private:
|
||||
constexpr static int k_maxNumberOfTermsInExpandedMultinome = 25;
|
||||
|
||||
@@ -37,10 +37,11 @@ public:
|
||||
Expression * clone() const override;
|
||||
int polynomialDegree(char symbolName) const override;
|
||||
int getVariables(char * variables) const override;
|
||||
int getPolynomialCoefficients(char symbolName, Expression ** coefficients) const override;
|
||||
int getPolynomialCoefficients(char symbolName, Expression * coefficients[]) const override;
|
||||
Sign sign() const override;
|
||||
bool isMatrixSymbol() const;
|
||||
bool isScalarSymbol() const;
|
||||
bool isVariableSymbol() const;
|
||||
bool isApproximate(Context & context) const;
|
||||
float characteristicXRange(Context & context, AngleUnit angleUnit = AngleUnit::Default) const override;
|
||||
bool hasAnExactRepresentation(Context & context) const;
|
||||
|
||||
@@ -35,7 +35,7 @@ int Addition::polynomialDegree(char symbolName) const {
|
||||
return degree;
|
||||
}
|
||||
|
||||
int Addition::getPolynomialCoefficients(char symbolName, Expression ** coefficients) const {
|
||||
int Addition::getPolynomialCoefficients(char symbolName, Expression * coefficients[]) const {
|
||||
int deg = polynomialDegree(symbolName);
|
||||
if (deg < 0 || deg > k_maxNumberOfPolynomialCoefficients-1) {
|
||||
return -1;
|
||||
|
||||
@@ -33,44 +33,10 @@ int Equal::polynomialDegree(char symbolName) const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Equal::solve(Expression ** solutions, Expression ** delta, Context & context, AngleUnit angleUnit) const {
|
||||
Expression * Equal::standardEquation(Context & context, AngleUnit angleUnit) const {
|
||||
Expression * sub = new Subtraction(operand(0), operand(1), true);
|
||||
Reduce(&sub, context, angleUnit);
|
||||
char variables[k_maxNumberOfVariables+1] = {0};
|
||||
int var = sub->getVariables(variables);
|
||||
if (var < 0) {
|
||||
delete sub;
|
||||
// Too many unknown variables
|
||||
return -10;
|
||||
}
|
||||
if (var == 0) {
|
||||
int sol = -1;
|
||||
if (sub->type() == Type::Rational && static_cast<Rational *>(sub)->isZero()) {
|
||||
// 0 == 0
|
||||
sol = INT_MAX;
|
||||
}
|
||||
delete sub;
|
||||
return sol;
|
||||
}
|
||||
if (var == 1) {
|
||||
char variable = variables[0];
|
||||
Expression * coefficients[k_maxNumberOfPolynomialCoefficients];
|
||||
int deg = sub->getPolynomialCoefficients(variable, coefficients);
|
||||
if (deg < 0 || deg > 2) {
|
||||
// Case 0: 1 unknown variable, non-polynomial --> approximate solution
|
||||
return 0;
|
||||
}
|
||||
delete sub;
|
||||
for (int i = 0; i <= deg; i++) {
|
||||
Reduce(&coefficients[i], context, angleUnit);
|
||||
}
|
||||
// Case 1: 1 unknown variable, polynomial of degree <= 2 --> Exact solution
|
||||
return oneDimensialPolynomialSolve(coefficients, deg, solutions, delta, context, angleUnit);
|
||||
}
|
||||
assert(var > 1);
|
||||
// Case 2: several variables, linear --> ?
|
||||
delete sub;
|
||||
return 0;
|
||||
return sub;
|
||||
}
|
||||
|
||||
Expression * Equal::shallowReduce(Context& context, AngleUnit angleUnit) {
|
||||
@@ -84,119 +50,6 @@ Expression * Equal::shallowReduce(Context& context, AngleUnit angleUnit) {
|
||||
return this;
|
||||
}
|
||||
|
||||
int Equal::oneDimensialPolynomialSolve(Expression ** coefficients, int degree, Expression ** solutions, Expression ** delta, Context & context, AngleUnit angleUnit) const {
|
||||
int sol = -1;
|
||||
if (degree == 0) {
|
||||
if (coefficients[0]->type() == Type::Rational && static_cast<Rational *>(coefficients[0])->isZero()) {
|
||||
// 0 == 0
|
||||
sol = INT_MAX;
|
||||
}
|
||||
delete coefficients[0];
|
||||
return sol;
|
||||
}
|
||||
switch (degree) {
|
||||
case 1:
|
||||
{
|
||||
// ax+b = 0
|
||||
solutions[0] = new Division(new Opposite(coefficients[0], false), coefficients[1], false);
|
||||
sol = 1;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
Expression * deltaDenominator[3] = {new Rational(4), coefficients[0]->clone(), coefficients[2]->clone()};
|
||||
*delta = new Subtraction(new Power(coefficients[1]->clone(), new Rational(2), false), new Multiplication(deltaDenominator, 3, false), false);
|
||||
Simplify(delta, context, angleUnit);
|
||||
if ((*delta)->type() == Type::Rational && static_cast<Rational *>(*delta)->isZero()) {
|
||||
solutions[0] = new Division(new Opposite(coefficients[1], false), new Multiplication(new Rational(2), coefficients[2]), false);
|
||||
sol = 1;
|
||||
} else {
|
||||
solutions[0] = new Division(new Subtraction(new Opposite(coefficients[1]->clone(), false), new SquareRoot((*delta)->clone(), false), false), new Multiplication(new Rational(2), coefficients[2]->clone()), false);
|
||||
solutions[1] = new Division(new Addition(new Opposite(coefficients[1], false), new SquareRoot((*delta)->clone(), false), false), new Multiplication(new Rational(2), coefficients[2]), false);
|
||||
sol = 2;
|
||||
}
|
||||
delete coefficients[0];
|
||||
break;
|
||||
}
|
||||
#if 0
|
||||
case 3:
|
||||
{
|
||||
Expression * a = coefficients[3];
|
||||
Expression * b = coefficients[2];
|
||||
Expression * c = coefficients[1];
|
||||
Expression * d = coefficients[0];
|
||||
// Delta = b^2*c^2+18abcd-27a^2*d^2-4ac^3-4db^3
|
||||
Expression * mult0Operands[2] = {new Power(b->clone(), new Rational(2), false), new Power(c->clone(), new Rational(2), false)};
|
||||
Expression * mult1Operands[5] = {new Rational(18), a->clone(), b->clone(), c->clone(), d->clone()};
|
||||
Expression * mult2Operands[3] = {new Rational(-27), new Power(a->clone(), new Rational(2), false), new Power(d->clone(), new Rational(2), false)};
|
||||
Expression * mult3Operands[3] = {new Rational(-4), a->clone(), new Power(c->clone(), new Rational(3), false)};
|
||||
Expression * mult4Operands[3] = {new Rational(-4), d->clone(), new Power(b->clone(), new Rational(3), false)};
|
||||
Expression * add0Operands[5] = {new Multiplication(mult0Operands, 2, false), new Multiplication(mult1Operands, 5, false), new Multiplication(mult2Operands, 3, false), new Multiplication(mult3Operands, 3, false), new Multiplication(mult4Operands, 3, false)};
|
||||
*delta = new Addition(add0Operands, 5, false);
|
||||
Simplify(delta, context, angleUnit);
|
||||
// Delta0 = b^2-3ac
|
||||
Expression * mult5Operands[3] = {new Rational(3), a->clone(), c->clone()};
|
||||
Expression * delta0 = new Subtraction(new Power(b->clone(), new Rational(2), false), new Multiplication(mult5Operands, 3, false), false);
|
||||
Reduce(&delta0, context, angleUnit);
|
||||
if ((*delta)->type() == Type::Rational && static_cast<Rational *>(*delta)->isZero()) {
|
||||
if (delta0->type() == Type::Rational && static_cast<Rational *>(delta0)->isZero()) {
|
||||
// delta0 = 0 && delta = 0 --> x0 = -b/(3a)
|
||||
delete delta0;
|
||||
solutions[0] = new Opposite(new Division(b, new Multiplication(new Rational(3), a, false), false), false);
|
||||
sol = 1;
|
||||
delete c;
|
||||
delete d;
|
||||
break;
|
||||
}
|
||||
// delta = 0 --> x0 = (9ad-bc)/(2delta0)
|
||||
// --> x1 = (4abc-9a^2d-b^3)/(a*delta0)
|
||||
Expression * mult6Operands[3] = {new Rational(9), a, d};
|
||||
solutions[0] = new Division(new Subtraction(new Multiplication(mult6Operands, 3, false), new Multiplication(b, c, false), false), new Multiplication(new Rational(2), delta0, false), false);
|
||||
Expression * mult7Operands[4] = {new Rational(4), a->clone(), b->clone(), c->clone()};
|
||||
Expression * mult8Operands[3] = {new Rational(-9), new Power(a->clone(), new Rational(2), false), d->clone()};
|
||||
Expression * add1Operands[3] = {new Multiplication(mult7Operands, 4, false), new Multiplication(mult8Operands,3, false), new Opposite(new Power(b->clone(), new Rational(3), false), false)};
|
||||
solutions[1] = new Division(new Addition(add1Operands, 3, false), new Multiplication(a->clone(), delta0, false), false);
|
||||
sol = 2;
|
||||
break;
|
||||
} else {
|
||||
// delta1 = 2b^3-9abc+27a^2*d
|
||||
Expression * mult9Operands[4] = {new Rational(-9), a, b, c};
|
||||
Expression * mult10Operands[3] = {new Rational(27), new Power(a->clone(), new Rational(2), false), d};
|
||||
Expression * add2Operands[3] = {new Multiplication(new Rational(2), new Power(b->clone(), new Rational(3), false), false), new Multiplication(mult9Operands, 4, false), new Multiplication(mult10Operands, 3, false)};
|
||||
Expression * delta1 = new Addition(add2Operands, 3, false);
|
||||
// C = Root((delta1+sqrt(-27a^2*delta))/2, 3)
|
||||
Expression * mult11Operands[3] = {new Rational(-27), new Power(a->clone(), new Rational(2), false), (*delta)->clone()};
|
||||
Expression * c = new Power(new Division(new Addition(delta1, new SquareRoot(new Multiplication(mult11Operands, 3, false), false), false), new Rational(2), false), new Rational(1,3), false);
|
||||
Expression * unary3roots[2] = {new Addition(new Rational(-1,2), new Division(new Multiplication(new SquareRoot(new Rational(3), false), new Symbol(Ion::Charset::IComplex), false), new Rational(2), false), false), new Subtraction(new Rational(-1,2), new Division(new Multiplication(new SquareRoot(new Rational(3), false), new Symbol(Ion::Charset::IComplex), false), new Rational(2), false), false)};
|
||||
// x_k = -1/(3a)*(b+C*z+delta0/(zC)) with z = unary cube root
|
||||
for (int k = 0; k < 3; k++) {
|
||||
Expression * ccopy = c;
|
||||
Expression * delta0copy = delta0;
|
||||
if (k < 2) {
|
||||
ccopy = new Multiplication(c->clone(), unary3roots[k], false);
|
||||
delta0copy = delta0->clone();
|
||||
}
|
||||
Expression * add3Operands[3] = {b->clone(), ccopy, new Division(delta0copy, ccopy->clone(), false)};
|
||||
solutions[k] = new Multiplication(new Division(new Rational(-1), new Multiplication(new Rational(3), a->clone(), false), false), new Addition(add3Operands, 3, false), false);
|
||||
}
|
||||
sol = 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
{
|
||||
assert(false);
|
||||
sol = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < sol; i++) {
|
||||
Simplify(&solutions[i], context, angleUnit);
|
||||
}
|
||||
return sol;
|
||||
}
|
||||
|
||||
ExpressionLayout * Equal::privateCreateLayout(PrintFloat::Mode floatDisplayMode, ComplexFormat complexFormat) const {
|
||||
assert(floatDisplayMode != PrintFloat::Mode::Default);
|
||||
assert(complexFormat != ComplexFormat::Default);
|
||||
|
||||
@@ -157,6 +157,10 @@ bool Expression::hasAncestor(const Expression * e) const {
|
||||
|
||||
/* Properties */
|
||||
|
||||
bool Expression::isRationalZero() const {
|
||||
return type() == Type::Rational && static_cast<const Rational*>(this)->isZero();
|
||||
}
|
||||
|
||||
bool Expression::recursivelyMatches(ExpressionTest test, Context & context) const {
|
||||
if (test(this, context)) {
|
||||
return true;
|
||||
@@ -219,7 +223,58 @@ int Expression::getVariables(char * variables) const {
|
||||
return numberOfVariables;
|
||||
}
|
||||
|
||||
int Expression::getPolynomialCoefficients(char symbolName, Expression ** coefficients) const {
|
||||
bool Expression::getLinearCoefficients(char * variables, Expression * coefficients[], Expression ** constant, Context & context) const {
|
||||
char * x = variables;
|
||||
while (*x != 0) {
|
||||
int degree = polynomialDegree(*x);
|
||||
if (degree > 1 || degree < 0) {
|
||||
return false;
|
||||
}
|
||||
x++;
|
||||
}
|
||||
Expression * equation = clone();
|
||||
x = variables;
|
||||
int index = 0;
|
||||
Expression * polynomialCoefficients[k_maxNumberOfPolynomialCoefficients];
|
||||
while (*x != 0) {
|
||||
int degree = equation->getPolynomialCoefficients(*x, polynomialCoefficients);
|
||||
if (degree == 1) {
|
||||
coefficients[index] = polynomialCoefficients[1];
|
||||
} else {
|
||||
assert(degree == 0);
|
||||
coefficients[index] = new Rational(0);
|
||||
}
|
||||
delete equation;
|
||||
equation = polynomialCoefficients[0];
|
||||
x++;
|
||||
index++;
|
||||
}
|
||||
*constant = equation;
|
||||
// xy = 0?
|
||||
bool isMultivariablePolynomial = (*constant)->recursivelyMatches([](const Expression * e, Context & context) {
|
||||
return e->type() == Type::Symbol && static_cast<const Symbol *>(e)->isVariableSymbol();
|
||||
}, context);
|
||||
for (int i = 0; i < index; i++) {
|
||||
if (isMultivariablePolynomial) {
|
||||
break;
|
||||
}
|
||||
isMultivariablePolynomial |= coefficients[i]->recursivelyMatches([](const Expression * e, Context & context) {
|
||||
return e->type() == Type::Symbol && static_cast<const Symbol *>(e)->isVariableSymbol();
|
||||
}, context);
|
||||
}
|
||||
if (isMultivariablePolynomial) {
|
||||
for (int i = 0; i < index; i++) {
|
||||
delete coefficients[i];
|
||||
coefficients[i] = nullptr;
|
||||
}
|
||||
delete *constant;
|
||||
*constant = nullptr;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int Expression::getPolynomialCoefficients(char symbolName, Expression * coefficients[]) const {
|
||||
int deg = polynomialDegree(symbolName);
|
||||
if (deg == 0) {
|
||||
coefficients[0] = clone();
|
||||
|
||||
@@ -43,7 +43,7 @@ int Multiplication::polynomialDegree(char symbolName) const {
|
||||
return degree;
|
||||
}
|
||||
|
||||
int Multiplication::getPolynomialCoefficients(char symbolName, Expression ** coefficients) const {
|
||||
int Multiplication::getPolynomialCoefficients(char symbolName, Expression * coefficients[]) const {
|
||||
int deg = polynomialDegree(symbolName);
|
||||
if (deg < 0 || deg + 1 > k_maxNumberOfPolynomialCoefficients) {
|
||||
return -1;
|
||||
|
||||
@@ -79,7 +79,7 @@ int Power::polynomialDegree(char symbolName) const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Power::getPolynomialCoefficients(char symbolName, Expression ** coefficients) const {
|
||||
int Power::getPolynomialCoefficients(char symbolName, Expression * coefficients[]) const {
|
||||
int deg = polynomialDegree(symbolName);
|
||||
if (deg <= 0) {
|
||||
return deg;
|
||||
|
||||
@@ -115,7 +115,7 @@ int Symbol::polynomialDegree(char symbol) const {
|
||||
|
||||
int Symbol::getVariables(char * variables) const {
|
||||
size_t variablesLength = strlen(variables);
|
||||
if (m_name >= 'a' && m_name <= 'z') {
|
||||
if (isVariableSymbol()) {
|
||||
char * currentChar = variables;
|
||||
while (*currentChar != 0) {
|
||||
if (*currentChar == m_name) {
|
||||
@@ -133,7 +133,7 @@ int Symbol::getVariables(char * variables) const {
|
||||
return variablesLength;
|
||||
}
|
||||
|
||||
int Symbol::getPolynomialCoefficients(char symbolName, Expression ** coefficients) const {
|
||||
int Symbol::getPolynomialCoefficients(char symbolName, Expression * coefficients[]) const {
|
||||
if (m_name == symbolName) {
|
||||
coefficients[0] = new Rational(0);
|
||||
coefficients[1] = new Rational(1);
|
||||
@@ -300,6 +300,13 @@ bool Symbol::isScalarSymbol() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Symbol::isVariableSymbol() const {
|
||||
if (m_name >= 'a' && m_name <= 'z') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int Symbol::simplificationOrderSameType(const Expression * e, bool canBeInterrupted) const {
|
||||
assert(e->type() == Expression::Type::Symbol);
|
||||
if ((uint8_t)m_name == ((uint8_t)static_cast<const Symbol *>(e)->name())) {
|
||||
|
||||
Reference in New Issue
Block a user