Measured on master at 5211ccd6. Not about constants — this is an ordinary variable.
var x = MathS.Var("x");
"x ^ 3".ToEntity().Differentiate(x, 2) // (0 * x ^ 2 + 2 * x ^ 1 * 1 * 3) * 1 + 0 * 3 * x ^ 2
"x ^ 3".ToEntity().Differentiate(x).Differentiate(x) // 2 * x * 3
Same value, and one of them is an answer.
Why
The two overloads take different routes:
public Entity Differentiate(Variable variable)
=> Transformation.Differentiation(variable).ApplyOrKeep(this);
internal Entity DifferentiateOnce(Variable variable)
=> InnerDifferentiate(variable).InnerSimplified; // <- the simplification
public Entity Differentiate(Variable x, int power)
{
...
ent = ent.InnerDifferentiate(x); // <- straight to the raw form
...
}
Differentiate(Variable) goes through the transformation, which ends at DifferentiateOnce and calls InnerSimplified. Differentiate(Variable, int) calls InnerDifferentiate directly in its loop, so nothing is ever simplified — and because each iteration differentiates the unsimplified result of the last, the expression compounds: every 0 * and * 1 the chain rule produces is still there to be differentiated again on the next pass.
That is also why it is worse than a cosmetic difference. At power = 3 the input to the third differentiation is already the unreduced output of two, so the cost grows with the mess rather than with the derivative.
Not the first-power case
Differentiate(x, 1) is fine — one raw pass is what InnerDifferentiate is for, and there is nothing accumulated yet. It is power >= 2 that diverges, and power = 0 (returns the input) and power < 0 (integrates) are unaffected.
Fix
Use DifferentiateOnce in the loop, which is the method that exists for exactly this and is what the other overload reaches anyway.
Found while measuring #993, where "pi ^ 3".Differentiate(MathS.pi, 2) shows the same shape — but that one is about the constant, and this reproduces with any variable, so it is separate.
Measured on
masterat5211ccd6. Not about constants — this is an ordinary variable.Same value, and one of them is an answer.
Why
The two overloads take different routes:
Differentiate(Variable)goes through the transformation, which ends atDifferentiateOnceand callsInnerSimplified.Differentiate(Variable, int)callsInnerDifferentiatedirectly in its loop, so nothing is ever simplified — and because each iteration differentiates the unsimplified result of the last, the expression compounds: every0 *and* 1the chain rule produces is still there to be differentiated again on the next pass.That is also why it is worse than a cosmetic difference. At
power = 3the input to the third differentiation is already the unreduced output of two, so the cost grows with the mess rather than with the derivative.Not the first-power case
Differentiate(x, 1)is fine — one raw pass is whatInnerDifferentiateis for, and there is nothing accumulated yet. It ispower >= 2that diverges, andpower = 0(returns the input) andpower < 0(integrates) are unaffected.Fix
Use
DifferentiateOncein the loop, which is the method that exists for exactly this and is what the other overload reaches anyway.Found while measuring #993, where
"pi ^ 3".Differentiate(MathS.pi, 2)shows the same shape — but that one is about the constant, and this reproduces with any variable, so it is separate.