[poincare] Added framework to derivate unary functions, and implemented it for Sine

Added a method unaryFunctionDifferential to ExpressionNode and Expression, to be implemented by subclasses representing unary functions.
Added a function derivateUnaryFunction to Derivative, to factor (f°g)' -> g' * f'°g.

Change-Id: Id1780f1082ccd001f1282fe4ddfff2b7055d3a27
This commit is contained in:
Gabriel Ozouf
2020-05-20 10:54:12 +02:00
committed by Émilie Feral
parent 648cdbaa29
commit 5cf85368ea
8 changed files with 48 additions and 2 deletions

View File

@@ -52,6 +52,7 @@ public:
static Derivative Builder(Expression child0, Symbol child1, Expression child2) { return TreeHandle::FixedArityBuilder<Derivative, DerivativeNode>({child0, child1, child2}); }
static Expression UntypedBuilder(Expression children);
static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("diff", 3, &UntypedBuilder);
static void DerivateUnaryFunction(Expression function, Expression symbol, Expression symbolValue);
Expression shallowReduce(ExpressionNode::ReductionContext context);
};

View File

@@ -385,6 +385,7 @@ protected:
* It returns whether the instance is differentiable, and differentiates it if
* able. */
bool didDerivate(ExpressionNode::ReductionContext reductionContext, Expression symbol, Expression symbolValue) { return node()->didDerivate(reductionContext, symbol, symbolValue); }
Expression unaryFunctionDifferential() { return node()->unaryFunctionDifferential(); }
private:
static constexpr int k_maxSymbolReplacementsCount = 10;
@@ -414,6 +415,7 @@ private:
Expression shallowReduceUsingApproximation(ExpressionNode::ReductionContext reductionContext);
Expression defaultShallowBeautify() { return *this; }
bool defaultDidDerivate() { return false; }
Expression defaultUnaryFunctionDifferential() { return *this; }
/* Approximation */
template<typename U> Evaluation<U> approximateToEvaluation(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;

View File

@@ -224,6 +224,7 @@ public:
/*!*/ virtual Expression shallowReduce(ReductionContext reductionContext);
/*!*/ virtual Expression shallowBeautify(ReductionContext reductionContext);
/*!*/ virtual bool didDerivate(ReductionContext, Expression symbol, Expression symbolValue);
virtual Expression unaryFunctionDifferential();
/* Return a clone of the denominator part of the expression */
/*!*/ virtual Expression denominator(ExpressionNode::ReductionContext reductionContext) const;
/* LayoutShape is used to check if the multiplication sign can be omitted between two expressions. It depends on the "layout syle" of the on the right of the left expression */

View File

@@ -35,6 +35,10 @@ private:
LayoutShape leftLayoutShape() const override { return LayoutShape::MoreLetters; };
LayoutShape rightLayoutShape() const override { return LayoutShape::BoundaryPunctuation; }
// Derivation
bool didDerivate(ReductionContext reductionContext, Expression symbol, Expression symbolValue) override;
Expression unaryFunctionDifferential() override;
// Evaluation
Evaluation<float> approximate(SinglePrecision p, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override {
return ApproximationHelper::Map<float>(this, context, complexFormat, angleUnit,computeOnComplex<float>);
@@ -52,6 +56,9 @@ public:
static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("sin", 1, &UntypedBuilderOneChild<Sine>);
Expression shallowReduce(ExpressionNode::ReductionContext reductionContext);
bool didDerivate(ExpressionNode::ReductionContext reductionContext, Expression symbol, Expression symbolValue);
Expression unaryFunctionDifferential();
};
}

View File

@@ -1,8 +1,8 @@
#include <poincare/derivative.h>
#include <poincare/ieee754.h>
#include <poincare/layout_helper.h>
#include <poincare/multiplication.h>
#include <poincare/serialization_helper.h>
#include <poincare/symbol.h>
#include <poincare/undefined.h>
#include <cmath>
@@ -184,6 +184,12 @@ Expression Derivative::shallowReduce(ExpressionNode::ReductionContext reductionC
derivand = derivand.deepReduce(reductionContext);
replaceWithInPlace(derivand);
return derivand;
}
void Derivative::DerivateUnaryFunction(Expression function, Expression symbol, Expression symbolValue) {
Expression df = function.unaryFunctionDifferential();
Expression dg = Derivative::Builder(function.childAtIndex(0), symbol.clone().convert<Symbol>(), symbolValue.clone());
function.replaceWithInPlace(Multiplication::Builder(df, dg));
}

View File

@@ -131,6 +131,10 @@ bool ExpressionNode::didDerivate(ReductionContext reductionContext, Expression s
return Expression(this).defaultDidDerivate();
}
Expression ExpressionNode::unaryFunctionDifferential() {
return Expression(this).defaultUnaryFunctionDifferential();
}
bool ExpressionNode::isOfType(Type * types, int length) const {
for (int i = 0; i < length; i++) {
if (type() == types[i]) {

View File

@@ -1,5 +1,7 @@
#include <poincare/sine.h>
#include <poincare/complex.h>
#include <poincare/cosine.h>
#include <poincare/derivative.h>
#include <poincare/layout_helper.h>
#include <poincare/serialization_helper.h>
@@ -34,6 +36,13 @@ Expression SineNode::shallowReduce(ReductionContext reductionContext) {
return Sine(this).shallowReduce(reductionContext);
}
bool SineNode::didDerivate(ReductionContext reductionContext, Expression symbol, Expression symbolValue) {
return Sine(this).didDerivate(reductionContext, symbol, symbolValue);
}
Expression SineNode::unaryFunctionDifferential() {
return Sine(this).unaryFunctionDifferential();
}
Expression Sine::shallowReduce(ExpressionNode::ReductionContext reductionContext) {
{
@@ -46,4 +55,13 @@ Expression Sine::shallowReduce(ExpressionNode::ReductionContext reductionContext
return Trigonometry::shallowReduceDirectFunction(*this, reductionContext);
}
bool Sine::didDerivate(ExpressionNode::ReductionContext reductionContext, Expression symbol, Expression symbolValue) {
Derivative::DerivateUnaryFunction(*this, symbol, symbolValue);
return true;
}
Expression Sine::unaryFunctionDifferential() {
return Cosine::Builder(childAtIndex(0).clone());
}
}

View File

@@ -15,11 +15,12 @@ void assert_parses_and_reduces_as(const char * expression, const char * derivati
quiz_assert_print_if_failure(eReduced.isIdenticalTo(d), expression);
}
QUIZ_CASE(poincare_differential_addition) {
QUIZ_CASE(poincare_differential_operations) {
assert_parses_and_reduces_as("diff(1,x,1)", "0");
assert_parses_and_reduces_as("diff(x,x,1)", "1");
assert_parses_and_reduces_as("diff(1+2,x,1)", "0");
assert_parses_and_reduces_as("diff(a,x,1)", "0");
assert_parses_and_reduces_as("diff(diff(x^2,x,y),y,1)","2");
assert_parses_and_reduces_as("diff(1+x,x,1)", "1");
assert_parses_and_reduces_as("diff(undef,x,1)", "undef");
@@ -37,4 +38,10 @@ QUIZ_CASE(poincare_differential_addition) {
assert_parses_and_reduces_as("diff(x^2,x,x)", "2*x");
assert_parses_and_reduces_as("diff(a*x^2+b*x+c,x,x)", "2*a*x+b");
assert_parses_and_reduces_as("diff(1/x,x,1)", "-1");
}
QUIZ_CASE(poicare_differential_unary_functions) {
assert_parses_and_reduces_as("diff(sin(x),x,π)","-1");
assert_parses_and_reduces_as("diff(sin(2y),y,π/12)","√(3)");
assert_parses_and_reduces_as("diff(sin(2x)+sin(3x),x,π/6)","1");
}