mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 00:37:25 +01:00
[poincare] Create a a flag on Expression that is set when the
approximation encouters a complex value All approximation methods take the complex format into account.
This commit is contained in:
@@ -19,7 +19,8 @@ double Model::levelSet(double * modelCoefficients, double xMin, double step, dou
|
||||
Expression yExpression = Number::DecimalNumber(y);
|
||||
PoincareHelpers::Simplify(&yExpression, *context);
|
||||
Expression modelExpression = simplifiedExpression(modelCoefficients, context);
|
||||
double result = modelExpression.nextIntersection("x", xMin, step, xMax, *context, Preferences::sharedPreferences()->angleUnit(), yExpression).abscissa;
|
||||
Preferences * preferences = Preferences::sharedPreferences();
|
||||
double result = modelExpression.nextIntersection("x", xMin, step, xMax, *context, preferences->complexFormat(), preferences->angleUnit(), yExpression).abscissa;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -251,13 +251,12 @@ T Sequence::approximateToNextRank(int n, SequenceContext * sqctx) const {
|
||||
Poincare::Symbol vn1Symbol("v(n+1)", 6);
|
||||
Poincare::Symbol unSymbol("u(n)", 4);
|
||||
Poincare::Symbol un1Symbol("u(n+1)", 6);
|
||||
Preferences * preferences = Poincare::Preferences::sharedPreferences();
|
||||
switch (m_type) {
|
||||
case Type::Explicit:
|
||||
{
|
||||
ctx.setValueForSymbol(un, unSymbol);
|
||||
ctx.setValueForSymbol(vn, vnSymbol);
|
||||
return expression(sqctx).approximateWithValueForSymbol(symbol(), (T)n, ctx, preferences->angleUnit());
|
||||
return PoincareHelpers::ApproximateWithValueForSymbol(expression(sqctx), symbol(), (T)n, ctx);
|
||||
}
|
||||
case Type::SingleRecurrence:
|
||||
{
|
||||
@@ -268,7 +267,7 @@ T Sequence::approximateToNextRank(int n, SequenceContext * sqctx) const {
|
||||
ctx.setValueForSymbol(unm1, unSymbol);
|
||||
ctx.setValueForSymbol(vn, vn1Symbol);
|
||||
ctx.setValueForSymbol(vnm1, vnSymbol);
|
||||
return expression(sqctx).approximateWithValueForSymbol(symbol(), (T)(n-1), ctx, preferences->angleUnit());
|
||||
return PoincareHelpers::ApproximateWithValueForSymbol(expression(sqctx), symbol(), (T)(n-1), ctx);
|
||||
}
|
||||
default:
|
||||
{
|
||||
@@ -282,7 +281,7 @@ T Sequence::approximateToNextRank(int n, SequenceContext * sqctx) const {
|
||||
ctx.setValueForSymbol(unm2, unSymbol);
|
||||
ctx.setValueForSymbol(vnm1, vn1Symbol);
|
||||
ctx.setValueForSymbol(vnm2, vnSymbol);
|
||||
return expression(sqctx).approximateWithValueForSymbol(symbol(), (T)(n-2), ctx, preferences->angleUnit());
|
||||
return PoincareHelpers::ApproximateWithValueForSymbol(expression(sqctx), symbol(), (T)(n-2), ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "function.h"
|
||||
#include "poincare_helpers.h"
|
||||
#include <string.h>
|
||||
#include <cmath>
|
||||
#include <assert.h>
|
||||
@@ -41,7 +42,7 @@ void Function::setActive(bool active) {
|
||||
|
||||
template<typename T>
|
||||
T Function::templatedApproximateAtAbscissa(T x, Poincare::Context * context) const {
|
||||
return expression(context).approximateWithValueForSymbol(symbol(), x, *context, Preferences::sharedPreferences()->angleUnit());
|
||||
return PoincareHelpers::ApproximateWithValueForSymbol(expression(context), symbol(), x, *context);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@ namespace Shared {
|
||||
namespace PoincareHelpers {
|
||||
|
||||
inline Poincare::Layout CreateLayout(const Poincare::Expression e) {
|
||||
return e.createLayout(Poincare::Preferences::sharedPreferences()->displayMode(), Poincare::Preferences::sharedPreferences()->numberOfSignificantDigits());
|
||||
Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences();
|
||||
return e.createLayout(preferences->displayMode(), preferences->numberOfSignificantDigits());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
@@ -24,25 +25,39 @@ inline int Serialize(const Poincare::Expression e, char * buffer, int bufferSize
|
||||
|
||||
template <class T>
|
||||
inline Poincare::Expression Approximate(const Poincare::Expression e, Poincare::Context & context) {
|
||||
Poincare::Preferences::ComplexFormat complexFormat = Poincare::Preferences::sharedPreferences()->complexFormat();
|
||||
Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences();
|
||||
Poincare::Preferences::ComplexFormat complexFormat = preferences->complexFormat();
|
||||
complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(complexFormat, e, context);
|
||||
return e.approximate<T>(context, complexFormat, Poincare::Preferences::sharedPreferences()->angleUnit());
|
||||
return e.approximate<T>(context, complexFormat, preferences->angleUnit());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T ApproximateToScalar(const Poincare::Expression e, Poincare::Context & context) {
|
||||
return e.approximateToScalar<T>(context, Poincare::Preferences::sharedPreferences()->angleUnit());
|
||||
Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences();
|
||||
Poincare::Preferences::ComplexFormat complexFormat = preferences->complexFormat();
|
||||
complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(complexFormat, e, context);
|
||||
return e.approximateToScalar<T>(context, complexFormat, preferences->angleUnit());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T ApproximateWithValueForSymbol(const Poincare::Expression e, const char * symbol, T x, Poincare::Context & context) {
|
||||
Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences();
|
||||
Poincare::Preferences::ComplexFormat complexFormat = preferences->complexFormat();
|
||||
complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(complexFormat, e, context);
|
||||
return e.approximateWithValueForSymbol<T>(symbol, x, context, complexFormat, preferences->angleUnit());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T ApproximateToScalar(const char * text, Poincare::Context & context) {
|
||||
Poincare::Preferences::ComplexFormat complexFormat = Poincare::Preferences::sharedPreferences()->complexFormat();
|
||||
Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences();
|
||||
Poincare::Preferences::ComplexFormat complexFormat = preferences->complexFormat();
|
||||
complexFormat = Poincare::Expression::UpdatedComplexFormatWithTextInput(complexFormat, text);
|
||||
return Poincare::Expression::approximateToScalar<T>(text, context, complexFormat, Poincare::Preferences::sharedPreferences()->angleUnit());
|
||||
return Poincare::Expression::approximateToScalar<T>(text, context, complexFormat, preferences->angleUnit());
|
||||
}
|
||||
|
||||
inline Poincare::Expression ParseAndSimplify(const char * text, Poincare::Context & context) {
|
||||
return Poincare::Expression::ParseAndSimplify(text, context, Poincare::Preferences::sharedPreferences()->complexFormat(), Poincare::Preferences::sharedPreferences()->angleUnit());
|
||||
Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences();
|
||||
return Poincare::Expression::ParseAndSimplify(text, context, preferences->complexFormat(), preferences->angleUnit());
|
||||
}
|
||||
|
||||
inline void Simplify(Poincare::Expression * e, Poincare::Context & context, Poincare::Preferences::ComplexFormat complexFormat = Poincare::Preferences::sharedPreferences()->complexFormat()) {
|
||||
@@ -51,7 +66,8 @@ inline void Simplify(Poincare::Expression * e, Poincare::Context & context, Poin
|
||||
}
|
||||
|
||||
inline void ParseAndSimplifyAndApproximate(const char * text, Poincare::Expression * simplifiedExpression, Poincare::Expression * approximateExpression, Poincare::Context & context) {
|
||||
Poincare::Expression::ParseAndSimplifyAndApproximate(text, simplifiedExpression, approximateExpression, context, Poincare::Preferences::sharedPreferences()->complexFormat(), Poincare::Preferences::sharedPreferences()->angleUnit());
|
||||
Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences();
|
||||
Poincare::Expression::ParseAndSimplifyAndApproximate(text, simplifiedExpression, approximateExpression, context, preferences->complexFormat(), preferences->angleUnit());
|
||||
}
|
||||
|
||||
inline void SimplifyAndApproximate(Poincare::Expression * e, Poincare::Expression * approximate, Poincare::Context & context, Poincare::Preferences::ComplexFormat complexFormat = Poincare::Preferences::sharedPreferences()->complexFormat()) {
|
||||
|
||||
@@ -103,22 +103,26 @@ double StorageCartesianFunction::sumBetweenBounds(double start, double end, Poin
|
||||
|
||||
Expression::Coordinate2D StorageCartesianFunction::nextMinimumFrom(double start, double step, double max, Context * context) const {
|
||||
const char unknownX[2] = {Poincare::Symbol::UnknownX, 0};
|
||||
return expressionReduced(context).nextMinimum(unknownX, start, step, max, *context, Preferences::sharedPreferences()->angleUnit());
|
||||
Preferences * preferences = Preferences::sharedPreferences();
|
||||
return expressionReduced(context).nextMinimum(unknownX, start, step, max, *context, preferences->complexFormat(), preferences->angleUnit());
|
||||
}
|
||||
|
||||
Expression::Coordinate2D StorageCartesianFunction::nextMaximumFrom(double start, double step, double max, Context * context) const {
|
||||
const char unknownX[2] = {Poincare::Symbol::UnknownX, 0};
|
||||
return expressionReduced(context).nextMaximum(unknownX, start, step, max, *context, Preferences::sharedPreferences()->angleUnit());
|
||||
Preferences * preferences = Preferences::sharedPreferences();
|
||||
return expressionReduced(context).nextMaximum(unknownX, start, step, max, *context, preferences->complexFormat(), preferences->angleUnit());
|
||||
}
|
||||
|
||||
double StorageCartesianFunction::nextRootFrom(double start, double step, double max, Context * context) const {
|
||||
const char unknownX[2] = {Poincare::Symbol::UnknownX, 0};
|
||||
return expressionReduced(context).nextRoot(unknownX, start, step, max, *context, Preferences::sharedPreferences()->angleUnit());
|
||||
Preferences * preferences = Preferences::sharedPreferences();
|
||||
return expressionReduced(context).nextRoot(unknownX, start, step, max, *context, preferences->complexFormat(), preferences->angleUnit());
|
||||
}
|
||||
|
||||
Expression::Coordinate2D StorageCartesianFunction::nextIntersectionFrom(double start, double step, double max, Poincare::Context * context, Expression e) const {
|
||||
const char unknownX[2] = {Poincare::Symbol::UnknownX, 0};
|
||||
return expressionReduced(context).nextIntersection(unknownX, start, step, max, *context, Preferences::sharedPreferences()->angleUnit(), e);
|
||||
Preferences * preferences = Preferences::sharedPreferences();
|
||||
return expressionReduced(context).nextIntersection(unknownX, start, step, max, *context, preferences->complexFormat(), preferences->angleUnit(), e);
|
||||
}
|
||||
|
||||
StorageCartesianFunction::CartesianFunctionRecordData * StorageCartesianFunction::recordData() const {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "storage_function.h"
|
||||
#include "poincare_helpers.h"
|
||||
#include <poincare/symbol.h>
|
||||
#include "poincare/src/parsing/parser.h"
|
||||
#include <string.h>
|
||||
@@ -76,7 +77,7 @@ T StorageFunction::templatedApproximateAtAbscissa(T x, Poincare::Context * conte
|
||||
return NAN;
|
||||
}
|
||||
const char unknownX[2] = {Poincare::Symbol::UnknownX, 0};
|
||||
return expressionReduced(context).approximateWithValueForSymbol(unknownX, x, *context, Preferences::sharedPreferences()->angleUnit());
|
||||
return PoincareHelpers::ApproximateWithValueForSymbol(expressionReduced(context), unknownX, x, *context);
|
||||
}
|
||||
|
||||
StorageFunction::FunctionRecordData * StorageFunction::recordData() const {
|
||||
|
||||
@@ -78,7 +78,8 @@ bool EquationStore::haveMoreApproximationSolutions(Context * context) {
|
||||
return false;
|
||||
}
|
||||
double step = (m_intervalApproximateSolutions[1]-m_intervalApproximateSolutions[0])*k_precision;
|
||||
return !std::isnan(definedModelAtIndex(0)->standardForm(context).nextRoot(m_variables[0], m_approximateSolutions[m_numberOfSolutions-1], step, m_intervalApproximateSolutions[1], *context, Preferences::sharedPreferences()->angleUnit()));
|
||||
Preferences * preferences = Preferences::sharedPreferences();
|
||||
return !std::isnan(definedModelAtIndex(0)->standardForm(context).nextRoot(m_variables[0], m_approximateSolutions[m_numberOfSolutions-1], step, m_intervalApproximateSolutions[1], *context, preferences->complexFormat(), preferences->angleUnit()));
|
||||
}
|
||||
|
||||
void EquationStore::approximateSolve(Poincare::Context * context) {
|
||||
@@ -87,8 +88,9 @@ void EquationStore::approximateSolve(Poincare::Context * context) {
|
||||
m_numberOfSolutions = 0;
|
||||
double start = m_intervalApproximateSolutions[0];
|
||||
double step = (m_intervalApproximateSolutions[1]-m_intervalApproximateSolutions[0])*k_precision;
|
||||
Preferences * preferences = Preferences::sharedPreferences();
|
||||
for (int i = 0; i < k_maxNumberOfApproximateSolutions; i++) {
|
||||
m_approximateSolutions[i] = definedModelAtIndex(0)->standardForm(context).nextRoot(m_variables[0], start, step, m_intervalApproximateSolutions[1], *context, Preferences::sharedPreferences()->angleUnit());
|
||||
m_approximateSolutions[i] = definedModelAtIndex(0)->standardForm(context).nextRoot(m_variables[0], start, step, m_intervalApproximateSolutions[1], *context, preferences->complexFormat(), preferences->angleUnit());
|
||||
if (std::isnan(m_approximateSolutions[i])) {
|
||||
break;
|
||||
} else {
|
||||
|
||||
@@ -167,6 +167,7 @@ public:
|
||||
Expression defaultReplaceUnknown(const Symbol & symbol);
|
||||
|
||||
/* Complex */
|
||||
static void SetEncounterComplex(bool encounterComplex);
|
||||
static Preferences::ComplexFormat UpdatedComplexFormatWithTextInput(Preferences::ComplexFormat complexFormat, const char * textInput);
|
||||
static Preferences::ComplexFormat UpdatedComplexFormatWithExpressionInput(Preferences::ComplexFormat complexFormat, const Expression & e, Context & context);
|
||||
bool isReal(Context & context) const { return node()->isReal(context); }
|
||||
@@ -193,20 +194,21 @@ public:
|
||||
Expression degreeToRadian();
|
||||
|
||||
/* Approximation Helper */
|
||||
// These methods reset the sApproximationEncounterComplex flag. They should not be use to implement node approximation
|
||||
template<typename U> static U epsilon();
|
||||
template<typename U> Expression approximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
template<typename U> U approximateToScalar(Context& context, Preferences::AngleUnit angleUnit) const;
|
||||
template<typename U> U approximateToScalar(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
template<typename U> static U approximateToScalar(const char * text, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit);
|
||||
template<typename U> U approximateWithValueForSymbol(const char * symbol, U x, Context & context, Preferences::AngleUnit angleUnit) const;
|
||||
template<typename U> U approximateWithValueForSymbol(const char * symbol, U x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
/* Expression roots/extrema solver */
|
||||
struct Coordinate2D {
|
||||
double abscissa;
|
||||
double value;
|
||||
};
|
||||
Coordinate2D nextMinimum(const char * symbol, double start, double step, double max, Context & context, Preferences::AngleUnit angleUnit) const;
|
||||
Coordinate2D nextMaximum(const char * symbol, double start, double step, double max, Context & context, Preferences::AngleUnit angleUnit) const;
|
||||
double nextRoot(const char * symbol, double start, double step, double max, Context & context, Preferences::AngleUnit angleUnit) const;
|
||||
Coordinate2D nextIntersection(const char * symbol, double start, double step, double max, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const;
|
||||
Coordinate2D nextMinimum(const char * symbol, double start, double step, double max, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
Coordinate2D nextMaximum(const char * symbol, double start, double step, double max, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
double nextRoot(const char * symbol, double start, double step, double max, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
Coordinate2D nextIntersection(const char * symbol, double start, double step, double max, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const;
|
||||
|
||||
/* This class is meant to contain data about named functions (e.g. sin, tan...)
|
||||
* in one place: their name, their number of children and a pointer to a builder.
|
||||
@@ -298,7 +300,7 @@ private:
|
||||
Expression defaultShallowBeautify() { return *this; }
|
||||
|
||||
/* Approximation */
|
||||
template<typename U> Evaluation<U> approximateToEvaluation(Context& context, Preferences::AngleUnit angleUnit) const;
|
||||
template<typename U> Evaluation<U> approximateToEvaluation(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
|
||||
/* Properties */
|
||||
Expression defaultReplaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression expression);
|
||||
@@ -315,13 +317,13 @@ private:
|
||||
constexpr static double k_sqrtEps = 1.4901161193847656E-8; // sqrt(DBL_EPSILON)
|
||||
constexpr static double k_goldenRatio = 0.381966011250105151795413165634361882279690820194237137864; // (3-sqrt(5))/2
|
||||
constexpr static double k_maxFloat = 1e100;
|
||||
typedef double (*EvaluationAtAbscissa)(const char * symbol, double abscissa, Context & context, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1);
|
||||
Coordinate2D nextMinimumOfExpression(const char * symbol, double start, double step, double max, EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression = Expression(), bool lookForRootMinimum = false) const;
|
||||
void bracketMinimum(const char * symbol, double start, double step, double max, double result[3], EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression = Expression()) const;
|
||||
Coordinate2D brentMinimum(const char * symbol, double ax, double bx, EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression = Expression()) const;
|
||||
double nextIntersectionWithExpression(const char * symbol, double start, double step, double max, EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const;
|
||||
void bracketRoot(const char * symbol, double start, double step, double max, double result[2], EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const;
|
||||
double brentRoot(const char * symbol, double ax, double bx, double precision, EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const;
|
||||
typedef double (*EvaluationAtAbscissa)(const char * symbol, double abscissa, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1);
|
||||
Coordinate2D nextMinimumOfExpression(const char * symbol, double start, double step, double max, EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression = Expression(), bool lookForRootMinimum = false) const;
|
||||
void bracketMinimum(const char * symbol, double start, double step, double max, double result[3], EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression = Expression()) const;
|
||||
Coordinate2D brentMinimum(const char * symbol, double ax, double bx, EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression = Expression()) const;
|
||||
double nextIntersectionWithExpression(const char * symbol, double start, double step, double max, EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const;
|
||||
void bracketRoot(const char * symbol, double start, double step, double max, double result[2], EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const;
|
||||
double brentRoot(const char * symbol, double ax, double bx, double precision, EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ Expression AbsoluteValue::shallowReduce(Context & context, Preferences::ComplexF
|
||||
#endif
|
||||
Expression c = childAtIndex(0);
|
||||
if (c.isReal(context)) {
|
||||
float app = c.approximateToScalar<float>(context, angleUnit);
|
||||
float app = c.node()->approximate(float(), context, angleUnit).toScalar();
|
||||
if (!std::isnan(app) && app >= Expression::epsilon<float>()) {
|
||||
// abs(a) = a with a > 0
|
||||
replaceWithInPlace(c);
|
||||
|
||||
@@ -22,6 +22,9 @@ namespace Poincare {
|
||||
|
||||
template<typename T>
|
||||
void ComplexNode<T>::setComplex(std::complex<T> c) {
|
||||
if (c.imag() != 0.0) {
|
||||
Expression::SetEncounterComplex(true);
|
||||
}
|
||||
this->real(c.real());
|
||||
this->imag(c.imag());
|
||||
if (this->real() == -0) {
|
||||
|
||||
@@ -48,7 +48,7 @@ Expression ComplexArgument::shallowReduce(Context & context, Preferences::Comple
|
||||
#endif
|
||||
bool real = c.isReal(context);
|
||||
if (real) {
|
||||
float app = c.approximateToScalar<float>(context, angleUnit);
|
||||
float app = c.node()->approximate(float(), context, angleUnit).toScalar();
|
||||
if (!std::isnan(app) && app >= Expression::epsilon<float>()) {
|
||||
// arg(x) = 0 if x > 0
|
||||
Expression result = Rational(0);
|
||||
|
||||
@@ -76,7 +76,10 @@ Evaluation<T> DerivativeNode::templatedApproximate(Context& context, Preferences
|
||||
template<typename T>
|
||||
T DerivativeNode::approximateWithArgument(T x, Context & context, Preferences::AngleUnit angleUnit) const {
|
||||
assert(childAtIndex(1)->type() == Type::Symbol);
|
||||
return Expression(childAtIndex(0)).approximateWithValueForSymbol(static_cast<SymbolNode *>(childAtIndex(1))->name(), x, context, angleUnit);
|
||||
VariableContext variableContext = VariableContext(static_cast<SymbolNode *>(childAtIndex(1))->name(), &context);
|
||||
variableContext.setApproximationForVariable<T>(x);
|
||||
// Here we cannot use Expression::approximateWithValueForSymbol which would reset the sApproximationEncounterComplex flag
|
||||
return childAtIndex(0)->approximate(T(), variableContext, angleUnit).toScalar();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
namespace Poincare {
|
||||
|
||||
bool Expression::sSymbolReplacementsCountLock = false;
|
||||
static bool sApproximationEncounterComplex = false;
|
||||
|
||||
/* Constructor & Destructor */
|
||||
|
||||
@@ -269,10 +270,15 @@ Expression Expression::makePositiveAnyNegativeNumeralFactor(Context & context, P
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
Evaluation<U> Expression::approximateToEvaluation(Context& context, Preferences::AngleUnit angleUnit) const {
|
||||
Evaluation<U> Expression::approximateToEvaluation(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
|
||||
sApproximationEncounterComplex = false;
|
||||
// Reset interrupting flag because some evaluation methods use it
|
||||
sSimplificationHasBeenInterrupted = false;
|
||||
return node()->approximate(U(), context, angleUnit);
|
||||
Evaluation<U> e = node()->approximate(U(), context, angleUnit);
|
||||
if (complexFormat == Preferences::ComplexFormat::Real && sApproximationEncounterComplex) {
|
||||
e = Complex<U>::Undefined();
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
Expression Expression::defaultReplaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression expression) {
|
||||
@@ -314,6 +320,10 @@ Expression Expression::defaultReplaceUnknown(const Symbol & symbol) {
|
||||
|
||||
/* Complex */
|
||||
|
||||
void Expression::SetEncounterComplex(bool encounterComplex) {
|
||||
sApproximationEncounterComplex = encounterComplex;
|
||||
}
|
||||
|
||||
Preferences::ComplexFormat Expression::UpdatedComplexFormatWithTextInput(Preferences::ComplexFormat complexFormat, const char * textInput) {
|
||||
if (complexFormat == Preferences::ComplexFormat::Real && strchr(textInput, Ion::Charset::IComplex) != nullptr) {
|
||||
return Preferences::ComplexFormat::Cartesian;
|
||||
@@ -538,27 +548,27 @@ Expression Expression::setSign(ExpressionNode::Sign s, Context * context, Prefer
|
||||
|
||||
template<typename U>
|
||||
Expression Expression::approximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
|
||||
return isUninitialized() ? Undefined() : approximateToEvaluation<U>(context, angleUnit).complexToExpression(complexFormat);
|
||||
return isUninitialized() ? Undefined() : approximateToEvaluation<U>(context, complexFormat, angleUnit).complexToExpression(complexFormat);
|
||||
}
|
||||
|
||||
|
||||
template<typename U>
|
||||
U Expression::approximateToScalar(Context& context, Preferences::AngleUnit angleUnit) const {
|
||||
return approximateToEvaluation<U>(context, angleUnit).toScalar();
|
||||
U Expression::approximateToScalar(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
|
||||
return approximateToEvaluation<U>(context, complexFormat, angleUnit).toScalar();
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
U Expression::approximateToScalar(const char * text, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) {
|
||||
Expression exp = ParseAndSimplify(text, context, UpdatedComplexFormatWithTextInput(complexFormat, text), angleUnit);
|
||||
assert(!exp.isUninitialized());
|
||||
return exp.approximateToScalar<U>(context, angleUnit);
|
||||
return exp.approximateToScalar<U>(context, complexFormat, angleUnit);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
U Expression::approximateWithValueForSymbol(const char * symbol, U x, Context & context, Preferences::AngleUnit angleUnit) const {
|
||||
U Expression::approximateWithValueForSymbol(const char * symbol, U x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
|
||||
VariableContext variableContext = VariableContext(symbol, &context);
|
||||
variableContext.setApproximationForVariable<U>(x);
|
||||
return approximateToScalar<U>(variableContext, angleUnit);
|
||||
return approximateToScalar<U>(variableContext, complexFormat, angleUnit);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
@@ -580,7 +590,9 @@ bool Expression::isMinusOne(const Expression e) {
|
||||
}
|
||||
|
||||
Expression Expression::CreateComplexExpression(Expression ra, Expression tb, Preferences::ComplexFormat complexFormat, bool undefined, bool isZeroRa, bool isOneRa, bool isZeroTb, bool isOneTb, bool isNegativeRa, bool isNegativeTb) {
|
||||
if (undefined) {
|
||||
if (complexFormat == Preferences::ComplexFormat::Real && sApproximationEncounterComplex) {
|
||||
return Unreal();
|
||||
} else if (undefined) {
|
||||
return Undefined();
|
||||
}
|
||||
switch (complexFormat) {
|
||||
@@ -651,37 +663,42 @@ Expression Expression::CreateComplexExpression(Expression ra, Expression tb, Pre
|
||||
|
||||
/* Expression roots/extrema solver*/
|
||||
|
||||
typename Expression::Coordinate2D Expression::nextMinimum(const char * symbol, double start, double step, double max, Context & context, Preferences::AngleUnit angleUnit) const {
|
||||
return nextMinimumOfExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1 = Expression()) {
|
||||
return expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit);
|
||||
}, context, angleUnit);
|
||||
typename Expression::Coordinate2D Expression::nextMinimum(const char * symbol, double start, double step, double max, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
|
||||
complexFormat = UpdatedComplexFormatWithExpressionInput(complexFormat, *this, context);
|
||||
return nextMinimumOfExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1 = Expression()) {
|
||||
return expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit);
|
||||
}, context, complexFormat, angleUnit);
|
||||
}
|
||||
|
||||
typename Expression::Coordinate2D Expression::nextMaximum(const char * symbol, double start, double step, double max, Context & context, Preferences::AngleUnit angleUnit) const {
|
||||
Coordinate2D minimumOfOpposite = nextMinimumOfExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1 = Expression()) {
|
||||
return -expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit);
|
||||
}, context, angleUnit);
|
||||
typename Expression::Coordinate2D Expression::nextMaximum(const char * symbol, double start, double step, double max, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
|
||||
complexFormat = UpdatedComplexFormatWithExpressionInput(complexFormat, *this, context);
|
||||
Coordinate2D minimumOfOpposite = nextMinimumOfExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1 = Expression()) {
|
||||
return -expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit);
|
||||
}, context, complexFormat, angleUnit);
|
||||
return {.abscissa = minimumOfOpposite.abscissa, .value = -minimumOfOpposite.value};
|
||||
}
|
||||
|
||||
double Expression::nextRoot(const char * symbol, double start, double step, double max, Context & context, Preferences::AngleUnit angleUnit) const {
|
||||
return nextIntersectionWithExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1 = Expression()) {
|
||||
return expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit);
|
||||
}, context, angleUnit, nullptr);
|
||||
double Expression::nextRoot(const char * symbol, double start, double step, double max, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
|
||||
complexFormat = UpdatedComplexFormatWithExpressionInput(complexFormat, *this, context);
|
||||
return nextIntersectionWithExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1 = Expression()) {
|
||||
return expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit);
|
||||
}, context, complexFormat, angleUnit, nullptr);
|
||||
}
|
||||
|
||||
typename Expression::Coordinate2D Expression::nextIntersection(const char * symbol, double start, double step, double max, Poincare::Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const {
|
||||
double resultAbscissa = nextIntersectionWithExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1) {
|
||||
return expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit)-expression1.approximateWithValueForSymbol(symbol, x, context, angleUnit);
|
||||
}, context, angleUnit, expression);
|
||||
typename Expression::Coordinate2D result = {.abscissa = resultAbscissa, .value = approximateWithValueForSymbol(symbol, resultAbscissa, context, angleUnit)};
|
||||
typename Expression::Coordinate2D Expression::nextIntersection(const char * symbol, double start, double step, double max, Poincare::Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const {
|
||||
complexFormat = UpdatedComplexFormatWithExpressionInput(complexFormat, *this, context);
|
||||
complexFormat = UpdatedComplexFormatWithExpressionInput(complexFormat, expression, context);
|
||||
double resultAbscissa = nextIntersectionWithExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1) {
|
||||
return expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit)-expression1.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit);
|
||||
}, context, complexFormat, angleUnit, expression);
|
||||
typename Expression::Coordinate2D result = {.abscissa = resultAbscissa, .value = approximateWithValueForSymbol(symbol, resultAbscissa, context, complexFormat, angleUnit)};
|
||||
if (std::fabs(result.value) < step*k_solverPrecision) {
|
||||
result.value = 0.0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
typename Expression::Coordinate2D Expression::nextMinimumOfExpression(const char * symbol, double start, double step, double max, EvaluationAtAbscissa evaluate, Context & context, Preferences::AngleUnit angleUnit, const Expression expression, bool lookForRootMinimum) const {
|
||||
typename Expression::Coordinate2D Expression::nextMinimumOfExpression(const char * symbol, double start, double step, double max, EvaluationAtAbscissa evaluate, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression, bool lookForRootMinimum) const {
|
||||
Coordinate2D result = {.abscissa = NAN, .value = NAN};
|
||||
if (start == max || step == 0.0) {
|
||||
return result;
|
||||
@@ -690,13 +707,13 @@ typename Expression::Coordinate2D Expression::nextMinimumOfExpression(const char
|
||||
double x = start;
|
||||
bool endCondition = false;
|
||||
do {
|
||||
bracketMinimum(symbol, x, step, max, bracket, evaluate, context, angleUnit, expression);
|
||||
result = brentMinimum(symbol, bracket[0], bracket[2], evaluate, context, angleUnit, expression);
|
||||
bracketMinimum(symbol, x, step, max, bracket, evaluate, context, complexFormat, angleUnit, expression);
|
||||
result = brentMinimum(symbol, bracket[0], bracket[2], evaluate, context, complexFormat, angleUnit, expression);
|
||||
x = bracket[1];
|
||||
// Because of float approximation, exact zero is never reached
|
||||
if (std::fabs(result.abscissa) < std::fabs(step)*k_solverPrecision) {
|
||||
result.abscissa = 0;
|
||||
result.value = evaluate(symbol, 0, context, angleUnit, *this, expression);
|
||||
result.value = evaluate(symbol, 0, context, complexFormat, angleUnit, *this, expression);
|
||||
}
|
||||
/* Ignore extremum whose value is undefined or too big because they are
|
||||
* really unlikely to be local extremum. */
|
||||
@@ -718,13 +735,13 @@ typename Expression::Coordinate2D Expression::nextMinimumOfExpression(const char
|
||||
return result;
|
||||
}
|
||||
|
||||
void Expression::bracketMinimum(const char * symbol, double start, double step, double max, double result[3], EvaluationAtAbscissa evaluate, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const {
|
||||
void Expression::bracketMinimum(const char * symbol, double start, double step, double max, double result[3], EvaluationAtAbscissa evaluate, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const {
|
||||
Coordinate2D p[3];
|
||||
p[0] = {.abscissa = start, .value = evaluate(symbol, start, context, angleUnit, *this, expression)};
|
||||
p[1] = {.abscissa = start+step, .value = evaluate(symbol, start+step, context, angleUnit, *this, expression)};
|
||||
p[0] = {.abscissa = start, .value = evaluate(symbol, start, context, complexFormat, angleUnit, *this, expression)};
|
||||
p[1] = {.abscissa = start+step, .value = evaluate(symbol, start+step, context, complexFormat, angleUnit, *this, expression)};
|
||||
double x = start+2.0*step;
|
||||
while (step > 0.0 ? x <= max : x >= max) {
|
||||
p[2] = {.abscissa = x, .value = evaluate(symbol, x, context, angleUnit, *this, expression)};
|
||||
p[2] = {.abscissa = x, .value = evaluate(symbol, x, context, complexFormat, angleUnit, *this, expression)};
|
||||
if ((p[0].value > p[1].value || std::isnan(p[0].value)) && (p[2].value > p[1].value || std::isnan(p[2].value)) && (!std::isnan(p[0].value) || !std::isnan(p[2].value))) {
|
||||
result[0] = p[0].abscissa;
|
||||
result[1] = p[1].abscissa;
|
||||
@@ -743,11 +760,11 @@ void Expression::bracketMinimum(const char * symbol, double start, double step,
|
||||
result[2] = NAN;
|
||||
}
|
||||
|
||||
typename Expression::Coordinate2D Expression::brentMinimum(const char * symbol, double ax, double bx, EvaluationAtAbscissa evaluate, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const {
|
||||
typename Expression::Coordinate2D Expression::brentMinimum(const char * symbol, double ax, double bx, EvaluationAtAbscissa evaluate, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const {
|
||||
/* Bibliography: R. P. Brent, Algorithms for finding zeros and extrema of
|
||||
* functions without calculating derivatives */
|
||||
if (ax > bx) {
|
||||
return brentMinimum(symbol, bx, ax, evaluate, context, angleUnit, expression);
|
||||
return brentMinimum(symbol, bx, ax, evaluate, context, complexFormat, angleUnit, expression);
|
||||
}
|
||||
double e = 0.0;
|
||||
double a = ax;
|
||||
@@ -755,7 +772,7 @@ typename Expression::Coordinate2D Expression::brentMinimum(const char * symbol,
|
||||
double x = a+k_goldenRatio*(b-a);
|
||||
double v = x;
|
||||
double w = x;
|
||||
double fx = evaluate(symbol, x, context, angleUnit, *this, expression);
|
||||
double fx = evaluate(symbol, x, context, complexFormat, angleUnit, *this, expression);
|
||||
double fw = fx;
|
||||
double fv = fw;
|
||||
|
||||
@@ -767,10 +784,10 @@ typename Expression::Coordinate2D Expression::brentMinimum(const char * symbol,
|
||||
double tol1 = k_sqrtEps*std::fabs(x)+1E-10;
|
||||
double tol2 = 2.0*tol1;
|
||||
if (std::fabs(x-m) <= tol2-0.5*(b-a)) {
|
||||
double middleFax = evaluate(symbol, (x+a)/2.0, context, angleUnit, *this, expression);
|
||||
double middleFbx = evaluate(symbol, (x+b)/2.0, context, angleUnit, *this, expression);
|
||||
double fa = evaluate(symbol, a, context, angleUnit, *this, expression);
|
||||
double fb = evaluate(symbol, b, context, angleUnit, *this, expression);
|
||||
double middleFax = evaluate(symbol, (x+a)/2.0, context, complexFormat, angleUnit, *this, expression);
|
||||
double middleFbx = evaluate(symbol, (x+b)/2.0, context, complexFormat, angleUnit, *this, expression);
|
||||
double fa = evaluate(symbol, a, context, complexFormat, angleUnit, *this, expression);
|
||||
double fb = evaluate(symbol, b, context, complexFormat, angleUnit, *this, expression);
|
||||
if (middleFax-fa <= k_sqrtEps && fx-middleFax <= k_sqrtEps && fx-middleFbx <= k_sqrtEps && middleFbx-fb <= k_sqrtEps) {
|
||||
Coordinate2D result = {.abscissa = x, .value = fx};
|
||||
return result;
|
||||
@@ -803,7 +820,7 @@ typename Expression::Coordinate2D Expression::brentMinimum(const char * symbol,
|
||||
d = k_goldenRatio*e;
|
||||
}
|
||||
u = x + (std::fabs(d) >= tol1 ? d : (d>0 ? tol1 : -tol1));
|
||||
fu = evaluate(symbol, u, context, angleUnit, *this, expression);
|
||||
fu = evaluate(symbol, u, context, complexFormat, angleUnit, *this, expression);
|
||||
if (fu <= fx) {
|
||||
if (u<x) {
|
||||
b = x;
|
||||
@@ -837,7 +854,7 @@ typename Expression::Coordinate2D Expression::brentMinimum(const char * symbol,
|
||||
return result;
|
||||
}
|
||||
|
||||
double Expression::nextIntersectionWithExpression(const char * symbol, double start, double step, double max, EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const {
|
||||
double Expression::nextIntersectionWithExpression(const char * symbol, double start, double step, double max, EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const {
|
||||
if (start == max || step == 0.0) {
|
||||
return NAN;
|
||||
}
|
||||
@@ -846,27 +863,27 @@ double Expression::nextIntersectionWithExpression(const char * symbol, double st
|
||||
static double precisionByGradUnit = 1E6;
|
||||
double x = start+step;
|
||||
do {
|
||||
bracketRoot(symbol, x, step, max, bracket, evaluation, context, angleUnit, expression);
|
||||
result = brentRoot(symbol, bracket[0], bracket[1], std::fabs(step/precisionByGradUnit), evaluation, context, angleUnit, expression);
|
||||
bracketRoot(symbol, x, step, max, bracket, evaluation, context, complexFormat, angleUnit, expression);
|
||||
result = brentRoot(symbol, bracket[0], bracket[1], std::fabs(step/precisionByGradUnit), evaluation, context, complexFormat, angleUnit, expression);
|
||||
x = bracket[1];
|
||||
} while (std::isnan(result) && (step > 0.0 ? x <= max : x >= max));
|
||||
|
||||
double extremumMax = std::isnan(result) ? max : result;
|
||||
Coordinate2D resultExtremum[2] = {
|
||||
nextMinimumOfExpression(symbol, start, step, extremumMax, [](const char * symbol, double x, Context & context, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1) {
|
||||
nextMinimumOfExpression(symbol, start, step, extremumMax, [](const char * symbol, double x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1) {
|
||||
if (expression1.isUninitialized()) {
|
||||
return expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit);
|
||||
return expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit);
|
||||
} else {
|
||||
return expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit)-expression1.approximateWithValueForSymbol(symbol, x, context, angleUnit);
|
||||
return expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit)-expression1.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit);
|
||||
}
|
||||
}, context, angleUnit, expression, true),
|
||||
nextMinimumOfExpression(symbol, start, step, extremumMax, [](const char * symbol, double x, Context & context, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1) {
|
||||
}, context, complexFormat, angleUnit, expression, true),
|
||||
nextMinimumOfExpression(symbol, start, step, extremumMax, [](const char * symbol, double x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1) {
|
||||
if (expression1.isUninitialized()) {
|
||||
return -expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit);
|
||||
return -expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit);
|
||||
} else {
|
||||
return expression1.approximateWithValueForSymbol(symbol, x, context, angleUnit)-expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit);
|
||||
return expression1.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit)-expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit);
|
||||
}
|
||||
}, context, angleUnit, expression, true)};
|
||||
}, context, complexFormat, angleUnit, expression, true)};
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (!std::isnan(resultExtremum[i].abscissa) && (std::isnan(result) || std::fabs(result - start) > std::fabs(resultExtremum[i].abscissa - start))) {
|
||||
result = resultExtremum[i].abscissa;
|
||||
@@ -878,12 +895,12 @@ double Expression::nextIntersectionWithExpression(const char * symbol, double st
|
||||
return result;
|
||||
}
|
||||
|
||||
void Expression::bracketRoot(const char * symbol, double start, double step, double max, double result[2], EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const {
|
||||
void Expression::bracketRoot(const char * symbol, double start, double step, double max, double result[2], EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const {
|
||||
double a = start;
|
||||
double b = start+step;
|
||||
while (step > 0.0 ? b <= max : b >= max) {
|
||||
double fa = evaluation(symbol, a, context, angleUnit, *this, expression);
|
||||
double fb = evaluation(symbol, b, context, angleUnit,* this, expression);
|
||||
double fa = evaluation(symbol, a, context, complexFormat, angleUnit, *this, expression);
|
||||
double fb = evaluation(symbol, b, context, complexFormat, angleUnit,* this, expression);
|
||||
if (fa*fb <= 0) {
|
||||
result[0] = a;
|
||||
result[1] = b;
|
||||
@@ -896,17 +913,17 @@ void Expression::bracketRoot(const char * symbol, double start, double step, dou
|
||||
result[1] = NAN;
|
||||
}
|
||||
|
||||
double Expression::brentRoot(const char * symbol, double ax, double bx, double precision, EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const {
|
||||
double Expression::brentRoot(const char * symbol, double ax, double bx, double precision, EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const {
|
||||
if (ax > bx) {
|
||||
return brentRoot(symbol, bx, ax, precision, evaluation, context, angleUnit, expression);
|
||||
return brentRoot(symbol, bx, ax, precision, evaluation, context, complexFormat, angleUnit, expression);
|
||||
}
|
||||
double a = ax;
|
||||
double b = bx;
|
||||
double c = bx;
|
||||
double d = b-a;
|
||||
double e = b-a;
|
||||
double fa = evaluation(symbol, a, context, angleUnit, *this, expression);
|
||||
double fb = evaluation(symbol, b, context, angleUnit, *this, expression);
|
||||
double fa = evaluation(symbol, a, context, complexFormat, angleUnit, *this, expression);
|
||||
double fb = evaluation(symbol, b, context, complexFormat, angleUnit, *this, expression);
|
||||
double fc = fb;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
if ((fb > 0.0 && fc > 0.0) || (fb < 0.0 && fc < 0.0)) {
|
||||
@@ -926,7 +943,7 @@ double Expression::brentRoot(const char * symbol, double ax, double bx, double p
|
||||
double tol1 = 2.0*DBL_EPSILON*std::fabs(b)+0.5*precision;
|
||||
double xm = 0.5*(c-b);
|
||||
if (std::fabs(xm) <= tol1 || fb == 0.0) {
|
||||
double fbcMiddle = evaluation(symbol, 0.5*(b+c), context, angleUnit, *this, expression);
|
||||
double fbcMiddle = evaluation(symbol, 0.5*(b+c), context, complexFormat, angleUnit, *this, expression);
|
||||
double isContinuous = (fb <= fbcMiddle && fbcMiddle <= fc) || (fc <= fbcMiddle && fbcMiddle <= fb);
|
||||
if (isContinuous) {
|
||||
return b;
|
||||
@@ -964,7 +981,7 @@ double Expression::brentRoot(const char * symbol, double ax, double bx, double p
|
||||
} else {
|
||||
b += xm > 0.0 ? tol1 : tol1;
|
||||
}
|
||||
fb = evaluation(symbol, b, context, angleUnit, *this, expression);
|
||||
fb = evaluation(symbol, b, context, complexFormat, angleUnit, *this, expression);
|
||||
}
|
||||
return NAN;
|
||||
}
|
||||
@@ -975,16 +992,16 @@ template double Expression::epsilon<double>();
|
||||
template Expression Expression::approximate<float>(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
template Expression Expression::approximate<double>(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
|
||||
template float Expression::approximateToScalar(Context& context, Preferences::AngleUnit angleUnit) const;
|
||||
template double Expression::approximateToScalar(Context& context, Preferences::AngleUnit angleUnit) const;
|
||||
template float Expression::approximateToScalar(Context& context, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
template double Expression::approximateToScalar(Context& context, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
|
||||
template float Expression::approximateToScalar<float>(const char * text, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit);
|
||||
template double Expression::approximateToScalar<double>(const char * text, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit);
|
||||
|
||||
template Evaluation<float> Expression::approximateToEvaluation(Context& context, Preferences::AngleUnit angleUnit) const;
|
||||
template Evaluation<double> Expression::approximateToEvaluation(Context& context, Preferences::AngleUnit angleUnit) const;
|
||||
template Evaluation<float> Expression::approximateToEvaluation(Context& context, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
template Evaluation<double> Expression::approximateToEvaluation(Context& context, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
|
||||
template float Expression::approximateWithValueForSymbol(const char * symbol, float x, Context & context, Preferences::AngleUnit angleUnit) const;
|
||||
template double Expression::approximateWithValueForSymbol(const char * symbol, double x, Context & context, Preferences::AngleUnit angleUnit) const;
|
||||
template float Expression::approximateWithValueForSymbol(const char * symbol, float x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
template double Expression::approximateWithValueForSymbol(const char * symbol, double x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ Evaluation<T> FunctionNode::templatedApproximate(Context& context, Preferences::
|
||||
if (e.isUninitialized()) {
|
||||
return Complex<T>::Undefined();
|
||||
}
|
||||
return e.approximateToEvaluation<T>(context, angleUnit);
|
||||
return e.node()->approximate(T(), context, angleUnit);
|
||||
}
|
||||
|
||||
Function::Function(const char * name, size_t length) :
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <poincare/serialization_helper.h>
|
||||
#include <poincare/symbol.h>
|
||||
#include <poincare/undefined.h>
|
||||
#include <poincare/variable_context.h>
|
||||
#include <cmath>
|
||||
#include <float.h>
|
||||
#include <stdlib.h>
|
||||
@@ -66,7 +67,11 @@ Evaluation<T> IntegralNode::templatedApproximate(Context & context, Preferences:
|
||||
|
||||
template<typename T>
|
||||
T IntegralNode::functionValueAtAbscissa(T x, Context & context, Preferences::AngleUnit angleUnit) const {
|
||||
return Expression(childAtIndex(0)).approximateWithValueForSymbol(static_cast<SymbolNode *>(childAtIndex(1))->name(), x, context, angleUnit);
|
||||
// Here we cannot use Expression::approximateWithValueForSymbol which would reset the sApproximationEncounterComplex flag
|
||||
assert(childAtIndex(1)->type() == Type::Symbol);
|
||||
VariableContext variableContext = VariableContext(static_cast<SymbolNode *>(childAtIndex(1))->name(), &context);
|
||||
variableContext.setApproximationForVariable<T>(x);
|
||||
return childAtIndex(0)->approximate(T(), variableContext, angleUnit).toScalar();
|
||||
}
|
||||
|
||||
#ifdef LAGRANGE_METHOD
|
||||
|
||||
@@ -47,7 +47,7 @@ Evaluation<T> StoreNode::templatedApproximate(Context& context, Preferences::Ang
|
||||
if (e.isUninitialized()) {
|
||||
return Complex<T>::Undefined();
|
||||
}
|
||||
return e.approximateToEvaluation<T>(context, angleUnit);
|
||||
return e.node()->approximate(T(), context, angleUnit);
|
||||
}
|
||||
|
||||
Expression Store::shallowReduce(Context & context) {
|
||||
|
||||
@@ -132,7 +132,7 @@ Evaluation<T> SymbolNode::templatedApproximate(Context& context, Preferences::An
|
||||
if (e.isUninitialized()) {
|
||||
return Complex<T>::Undefined();
|
||||
}
|
||||
return e.approximateToEvaluation<T>(context, angleUnit);
|
||||
return e.node()->approximate(T(), context, angleUnit);
|
||||
}
|
||||
|
||||
Symbol::Symbol(const char * name, int length) : SymbolAbstract(TreePool::sharedPool()->createTreeNode<SymbolNode>(SymbolAbstract::AlignedNodeSize(length, sizeof(SymbolNode)))) {
|
||||
|
||||
@@ -36,7 +36,7 @@ float Trigonometry::characteristicXRange(const Expression & e, Context & context
|
||||
/* To compute a, the slope of the expression child(0), we compute the
|
||||
* derivative of child(0) for any x value. */
|
||||
Poincare::Derivative derivative = Poincare::Derivative::Builder(e.childAtIndex(0).clone(), Symbol(x, 1), Float<float>(1.0f));
|
||||
float a = derivative.approximateToScalar<float>(context, angleUnit);
|
||||
float a = derivative.node()->approximate(float(), context, angleUnit).toScalar();
|
||||
float pi = angleUnit == Preferences::AngleUnit::Radian ? M_PI : 180.0f;
|
||||
return 2.0f*pi/std::fabs(a);
|
||||
}
|
||||
@@ -248,7 +248,7 @@ Expression Trigonometry::shallowReduceInverseFunction(Expression & e, Context& c
|
||||
|
||||
// Step 1. Look for an expression of type "arccos(cos(x))", return x
|
||||
if (AreInverseFunctions(e.childAtIndex(0), e)) {
|
||||
float trigoOp = e.childAtIndex(0).childAtIndex(0).approximateToScalar<float>(context, angleUnit);
|
||||
float trigoOp = e.childAtIndex(0).childAtIndex(0).node()->approximate(float(), context, angleUnit).toScalar();
|
||||
if ((e.type() == ExpressionNode::Type::ArcCosine && trigoOp >= 0.0f && trigoOp <= pi) ||
|
||||
(e.type() == ExpressionNode::Type::ArcSine && trigoOp >= -pi/2.0f && trigoOp <= pi/2.0f) ||
|
||||
(e.type() == ExpressionNode::Type::ArcTangent && trigoOp >= -pi/2.0f && trigoOp <= pi/2.0f)) {
|
||||
@@ -260,7 +260,7 @@ Expression Trigonometry::shallowReduceInverseFunction(Expression & e, Context& c
|
||||
|
||||
// Step 2. Special case for arctan(sin(x)/cos(x))
|
||||
if (e.type() == ExpressionNode::Type::ArcTangent && ExpressionIsEquivalentToTangent(e.childAtIndex(0))) {
|
||||
float trigoOp = e.childAtIndex(0).childAtIndex(1).childAtIndex(0).approximateToScalar<float>(context, angleUnit);
|
||||
float trigoOp = e.childAtIndex(0).childAtIndex(1).childAtIndex(0).node()->approximate(float(), context, angleUnit).toScalar();
|
||||
if (trigoOp >= -pi/2.0f && trigoOp <= pi/2.0f) {
|
||||
Expression result = e.childAtIndex(0).childAtIndex(1).childAtIndex(0);
|
||||
e.replaceWithInPlace(result);
|
||||
@@ -449,7 +449,7 @@ Expression Trigonometry::table(const Expression e, ExpressionNode::Type type, Co
|
||||
return Expression();
|
||||
}
|
||||
// We approximate the given expression to quickly compare it to the cheat table entries.
|
||||
float eValue = e.approximateToScalar<float>(context, angleUnit);
|
||||
float eValue = e.node()->approximate(float(), context, angleUnit).toScalar();
|
||||
if (std::isnan(eValue) || std::isinf(eValue)) {
|
||||
return Expression();
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ using namespace Poincare;
|
||||
|
||||
static inline void assert_approximation_equals(const Expression i, float f) {
|
||||
Shared::GlobalContext c;
|
||||
quiz_assert(i.approximateToScalar<float>(c, Preferences::AngleUnit::Degree) == f);
|
||||
quiz_assert(i.approximateToScalar<float>(c, Cartesian, Degree) == f);
|
||||
}
|
||||
|
||||
static inline void assert_parsed_expression_is_equal_to(const char * exp, Expression e) {
|
||||
|
||||
@@ -11,7 +11,7 @@ using namespace Poincare;
|
||||
template<typename T>
|
||||
void assert_exp_is_bounded(Expression exp, T lowBound, T upBound, bool upBoundIncluded = false) {
|
||||
Shared::GlobalContext globalContext;
|
||||
T result = exp.approximateToScalar<T>(globalContext, Radian);
|
||||
T result = exp.approximateToScalar<T>(globalContext, Cartesian, Radian);
|
||||
quiz_assert(result >= lowBound);
|
||||
quiz_assert(result < upBound || (result == upBound && upBoundIncluded));
|
||||
}
|
||||
|
||||
@@ -159,10 +159,10 @@ void assert_parsed_expression_evaluates_to(const char * expression, const char *
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void assert_parsed_expression_approximates_with_value_for_symbol(Expression expression, const char * symbol, T value, T approximation, Poincare::Preferences::AngleUnit angleUnit) {
|
||||
void assert_parsed_expression_approximates_with_value_for_symbol(Expression expression, const char * symbol, T value, T approximation, Poincare::Preferences::ComplexFormat complexFormat, Poincare::Preferences::AngleUnit angleUnit) {
|
||||
Shared::GlobalContext globalContext;
|
||||
T result = expression.approximateWithValueForSymbol(symbol, value, globalContext, angleUnit);
|
||||
quiz_assert(std::fabs(result - approximation) < 10.0*Expression::epsilon<T>());
|
||||
T result = expression.approximateWithValueForSymbol(symbol, value, globalContext, complexFormat, angleUnit);
|
||||
quiz_assert((std::isnan(result) && std::isnan(approximation)) || std::fabs(result - approximation) < 10.0*Expression::epsilon<T>());
|
||||
}
|
||||
|
||||
void assert_parsed_expression_simplify_to(const char * expression, const char * simplifiedExpression, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat) {
|
||||
@@ -219,5 +219,5 @@ void assert_expression_layout_serialize_to(Poincare::Layout layout, const char *
|
||||
|
||||
template void assert_parsed_expression_evaluates_to<float>(char const*, char const *, Poincare::Preferences::AngleUnit, Poincare::Preferences::ComplexFormat, int);
|
||||
template void assert_parsed_expression_evaluates_to<double>(char const*, char const *, Poincare::Preferences::AngleUnit, Poincare::Preferences::ComplexFormat, int);
|
||||
template void assert_parsed_expression_approximates_with_value_for_symbol(Poincare::Expression, const char *, float, float, Poincare::Preferences::AngleUnit);
|
||||
template void assert_parsed_expression_approximates_with_value_for_symbol(Poincare::Expression, const char *, double, double, Poincare::Preferences::AngleUnit);
|
||||
template void assert_parsed_expression_approximates_with_value_for_symbol(Poincare::Expression, const char *, float, float, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit);
|
||||
template void assert_parsed_expression_approximates_with_value_for_symbol(Poincare::Expression, const char *, double, double, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit);
|
||||
|
||||
@@ -30,7 +30,7 @@ template<typename T>
|
||||
void assert_parsed_expression_evaluates_to(const char * expression, const char * approximation, Poincare::Preferences::AngleUnit angleUnit = Degree, Poincare::Preferences::ComplexFormat complexFormat = Cartesian, int numberOfSignificantDigits = -1);
|
||||
|
||||
template<typename T>
|
||||
void assert_parsed_expression_approximates_with_value_for_symbol(Poincare::Expression expression, const char * symbol, T value, T approximation, Poincare::Preferences::AngleUnit angleUnit = Degree);
|
||||
void assert_parsed_expression_approximates_with_value_for_symbol(Poincare::Expression expression, const char * symbol, T value, T approximation, Poincare::Preferences::ComplexFormat complexFormat = Cartesian, Poincare::Preferences::AngleUnit angleUnit = Degree);
|
||||
|
||||
void assert_parsed_expression_simplify_to(const char * expression, const char * simplifiedExpression, Poincare::Preferences::AngleUnit angleUnit = Poincare::Preferences::AngleUnit::Radian, Poincare::Preferences::ComplexFormat complexFormat = Poincare::Preferences::ComplexFormat::Cartesian);
|
||||
void assert_parsed_expression_serialize_to(Poincare::Expression expression, const char * serializedExpression, Poincare::Preferences::PrintFloatMode mode = DecimalMode, int numberOfSignifiantDigits = 7);
|
||||
|
||||
@@ -163,6 +163,17 @@ QUIZ_CASE(poincare_user_variable_functions_with_context) {
|
||||
|
||||
// Clean the storage for other tests
|
||||
Ion::Storage::sharedStorage()->recordNamed("f.func").destroy();
|
||||
|
||||
// f: x->R(-1)
|
||||
assert_simplify("R(-1)*R(-1)>f(x)");
|
||||
// Approximate f(?) with ? = 5
|
||||
// Cartesian
|
||||
assert_parsed_expression_approximates_with_value_for_symbol(Function("f", 1, Symbol(Symbol::SpecialSymbols::UnknownX)), x, 1.0, -1.0);
|
||||
// Real
|
||||
assert_parsed_expression_approximates_with_value_for_symbol(Function("f", 1, Symbol(Symbol::SpecialSymbols::UnknownX)), x, 1.0, (double)NAN, Real);
|
||||
|
||||
// Clean the storage for other tests
|
||||
Ion::Storage::sharedStorage()->recordNamed("f.func").destroy();
|
||||
}
|
||||
|
||||
QUIZ_CASE(poincare_user_variable_properties) {
|
||||
|
||||
Reference in New Issue
Block a user