mathjs
mathjs copied to clipboard
Implement symbols for CAS computations
Advanced CAS systems let users define their own symbols and perform symbolic operations with them. Example in Sympy. Since all math.js functions can operate on complex numbers (that means they have to call add and mul methods instead of JavaScript + and * operators), it should be really straightforward to implement something like ~~SymbolicExpression~~ (EDIT: already implemented as Node) which behaves like a number but remembers the symbols involved.
Example usage:
let [x, y] = math.symbols('x y')
math.evaluate('det( [[ 1, x ],[ y, 0 ]] )', {x, y})
// - x * y
Thanks for your input Michal. So far we already have SymbolNode, which is for example used for symbolic computations in the functions simplify and derivative. Is that what you mean?
See docs: https://mathjs.org/docs/expressions/algebra.html
Well, SymbolNode is an internal API and I don't see how it can be used by users right now. If I modify my example to use SymbolNode, it doesn't work:
let x = new math.SymbolNode('x');
let y = new math.SymbolNode('y');
math.evaluate('det( [[ 1, x ],[ y, 0 ]] )', {x, y});
// "Unexpected type of argument in function multiplyScalar (expected: number or Complex or BigNumber or Fraction or Unit or string or boolean, actual: Node, index: 0)"
I know simplify and derivative allow for symbolic computation, but those are the only places where mathjs allows for symbolic computation. In a true CAS, symbols are first-class citizens that can be used anywhere you like.
Of course the implementation of symbols should leverage the existing functionality. After looking into the Algebra chapter of the docs, I think that allowing arithmetical operations on Node object would be enough for my example (and many other potential uses of symbols) to work.
So should I go ahead and try implementing arithmetics on Node?
In a true CAS, symbols are first-class citizens that can be used anywhere you like.
I understand what you mean. The SymbolNode is public API though. We could definitely extend all functions in mathjs to support these nodes, so math.add(new SymbolNode('x'), 2) would return an OperatorNode with the SymbolNode(x) and a ConstantNode(2). I think we discussed this somewhere before but I can't find it.
I'm not sure though if that is what you are really looking for. The example you give:
let [x, y] = math.symbols('x y') math.evaluate('det( [[ 1, x ],[ y, 0 ]] )', {x, y}) // - x * y
is solving an equation. It may be enough to implement a function solve(expr, variable), similar to the existing function derivative(expr, variable), without the need to implement support for Node in all functions. Would that address your needs?
Implementing a function solve is discussed here: #38
Thanks for your comment, I'll start working on this in a week!
Just to clarify: I didn't mean solving an equation, I really meant computing the determinant with the symbol x in the upper right corner and the symbol y in the left bottom corner. Determinant in 2×2 matrices is just A₁₁ A₂₂ − A₁₂ A₂₁ = 1·0−x·y = −xy. But that's just nitpicking, not especially important for the gist of this issue 😅️
Thanks for your comment, I'll start working on this in a week!
That sounds good :). I don't yet have a clear idea on what you're going to implement exactly and whether that's something generic that we can put in mathjs. "the determinant with the symbol x in the upper right corner and the symbol y in the left bottom corner" sounds very specific to me?
I started fiddling with the implementation, but I got stuck very early. I wanted to add this overload for the add function:
'Node, any': function(x, y) {
return new OperatorNode('+', 'add', [clone(x), clone(y)])
},
However, when I try to add OperatorNode as a dependency for add, tests fail with Uncaught Error: some dependencies are missing. After investigating little further, I found out that everything in /expression/node depends on mathWithTransform, which means it can only load after the whole core of mathjs is done, right? Isn't there a way to use OperatorNode in add without creating a deadlock? Or should I somehow move the code “downstream” to /expression/node?
In a reply to your previous comment: Determinants are used very often in antisymetrization problems, which are quite wide-spread across several disciplines (see Jacobian, Hessian, Wronskian, Slater, Vandermonde determinants), so imho it's one of the operations you'd want to perform on symbols quite often. But I'll make an actual showcase once I have a working prototype :)
Did you add OperatorNode as a new dependency on top of the add function?
Yes, that's exactly what I did. And this alone was enough to stop the project from ~~compiling~~ loading successfully via require.
@m93a I created a small proof of concept of being able to use function addScalar to add two Nodes. see this PR: https://github.com/josdejong/mathjs/pull/1747
Closing the POC of #1747 in favor of #2475
This is a very valuable idea, and has spurred multiple POCs (including one I did myself), but still is rather more of a Discussion than an issue. Hence, moving.