mathjs icon indicating copy to clipboard operation
mathjs copied to clipboard

Nested derivatives fail

Open khanybaba opened this issue 4 months ago • 3 comments

Describe the bug mathJs evaluates this derivative correctly: derivative('x * e^x', "x") // returns "e ^ x * (x + 1)"

however, if the expression to be differentiated is involves another derivative mathJs returns an incorrect answer: derivative('x * derivative("e^x", "x")', "x") // returns "e^x"

To Reproduce enter expression in mathJs demo window: derivative('x * derivative("e^x", "x")', "x")

khanybaba avatar Sep 08 '25 21:09 khanybaba

That is an interesting issue.

The function derivative has code in place to check whether a part of the expression is a constant or a function/operator with constant arguments that will resolve in a constant, like 2 + 3 or concat("hello", "world"). See:

https://github.com/josdejong/mathjs/blob/d8a88a1d6958a740c0fe74194923e802eaec819c/src/function/algebra/derivative.js#L190-L192

The nested derivative("e^x", "x") is marked as constant because it is a function with two string arguments, similar to concat("hello", "world").

So, the problem here is that this isConst function does not check whether the outcome actually is a constant value and not a node tree (or maybe even a lambda returned by a function). I'm not yet sure what a good solution for this will be. Some ideas:

  1. Create a list with functions known to not return a constant, like derivative, parse, simplify, and mark them as not constant. Problem is that this solution is not 100% safe: there can be custom functions that return dynamic stuff.
  2. Same as (1) but the other way around: define a list with functions that are known to return a constant, like add, sqrt, sin, etc. This will be quite a long list, but the good thing is that this is a safe solution that cannot have false positives. A downside is that custom defined functions cannot be handled by derivative by default.
  3. When having a function with constant arguments, evaluate it and check whether the result is a node tree. This will give problems with partial derivatives though, because they use undefined variables that cannot be evaluated. For example derivative('x + log(y)*y', 'x').

Any thoughts?

josdejong avatar Sep 10 '25 14:09 josdejong

The derivative of y with respect to x is zero, same as the derivative of 0 with respect to x. So therefore I think it is safe to evaluate function calls in a context in which all variables other than the variable of differentiation evaluate to 0. So I think modifying option 3 with such an evaluation strategy will work in all cases (if I am not mistaken, I didn't think about this deeply).

gwhitney avatar Sep 11 '25 21:09 gwhitney

Yes indeed, using zero will work in most cases I think. There are edge cases like y/y though that can give an issue when evaluated with 0, we have to think that through.

josdejong avatar Sep 17 '25 07:09 josdejong