Nested derivatives fail
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")
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:
- 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. - 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 byderivativeby default. - 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?
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).
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.