josdejong / mathjs

An extensive math library for JavaScript and Node.js
https://mathjs.org
Apache License 2.0
14.31k stars 1.24k forks source link

derivative() does not support the full range of symbol characters #3253

Closed ShaydeNZ closed 1 week ago

ShaydeNZ commented 4 weeks ago

DESCRIBE THE BUG

The documentation lists the characters one can use in variable / symbol names, including a-z, A-Z, $, _, etc.

However, supplying a symbol in a string to derive against with anything but basic alphanumerics to derivative() fails.

Apologies in advance if this has already been reported; I looked through the Issues but didn't see an obvious similar report.

TO REPRODUCE

I'll use a variable with a '$' character in here for reference, but the same thing happened when using '_' in a variable name.

Problem is present in math.js 13.0.2 and 13.0.3.

To start, evaluate() works fine with these characters:

import * as math from "mathjs"
console.log(math.evaluate("1 + p$x", { p$x: 1 }))

"2"

But calling derivative() fails with the same symbol:

import * as math from "mathjs"
var d = math.derivative("1 + p$x", "p$x")
console.log(d.toString())

"TypeError: Unexpected type of argument in function derivative (expected: SymbolNode or identifier, actual: string, index: 1)"

Calling it with a boring symbol works, though:

import * as math from "mathjs"
var d = math.derivative("1 + px", "px")
console.log(d.toString())

"1"

We can get around this problem by parsing the symbol first:

import * as math from "mathjs"
var x = math.parse("p$x")
var d = math.derivative("1 + p$x", x)
console.log(d.toString())

"1"

For completeness, here is the failure using a variable with an underscore:

import * as math from "mathjs"
var d = math.derivative("1 + p_x", "p_x")
console.log(d.toString())

"TypeError: Unexpected type of argument in function derivative (expected: SymbolNode or identifier, actual: string, index: 1)"
josdejong commented 3 weeks ago

Thanks for reporting this bug.

I think the cause is in the logic for recognizing an identifier (I see that isString should be isString(s) BTW):

https://github.com/josdejong/mathjs/blob/88a4b35e9d7bbd4bc2e2bdaba5c20d3da702d870/src/core/function/typed.js#L127-L130

A pragmatic solution would be to improve this regex to better match the available characters. The most neat solution would be to replace the regular expression with using the isAlpha function used by parse:

https://github.com/josdejong/mathjs/blob/88a4b35e9d7bbd4bc2e2bdaba5c20d3da702d870/src/expression/parse.js#L501-L505

Anyone able to help implement a solution for this?