The bugs/limitations
The simplify function currently exhibits limitations and in some scenarios 'bugs' when simplifying in either (or both) a non-commutative context over the operators of addition and multiplication (whilst notably, associativity still remains).
Broadly, the bug(s) can be narrowed down to unevaluated constant/numeric operations in expressions constituted by addition or multiplication (or their inverses of subtraction/division) for a number of expression configurations, where evaluation can be understood as mathematically valid (given the context) in these scenarios.
The limitations can be understood similarly as failure of matching standard/non-function rule application for these same operators in this context and for similar expression arrangements.
To illustrate:
(for the 'bugs')
// In a non-commutative context...
// 'constant * constant' or 'constant + constant' evaluates, e.g.:
'2 * 2 -> 4'
'3 + 3 -> 6'
// Join these expressions with the same operator with at least one leading non-constant node ('v') however..
'v * 2 * 2'
'v + 3 + 3'
//.. and the simplified expression remains the same as if unsimplified
// The same goes for trailing numeric expressions which also include inverse operators/operations
// Simplifying the following:
'v * 8 / 4 * 2 * 3 -> LHS'
'v - 3 + 2 - 4' -> LHS' // (well, with the first subtraction op. flattened, so truthfully 'v + -3 + 2 - 4'
// Additionally, these numeric sub-expressions remain non-simplified when 'wedged' between non-constant nodes
// Such that
'b*c*2*2*2*c*b'
'b+c+2+2+2+c+b''
// both remain unchanged
And with regards to the 'limitations':
/*
* Where rule
*
* 'v*v -> v^2'
*
* is expected to apply
*/
// exprs. with successful application for this rule
'a*a'
'a*a*2'
'a*a+2'
..
// and unsuccessful
'2*b*b' // e.g. expr. of form 'c * v * v'
'2*b*b*2' // expr. of form 'c * v * v * c'
'2*b*b*3*b*b' // expr. of form 'c * v * v * c' * v * v
/*
* Where rule
*
* 'v * ( v * n1 + n2)'
*
* is expected to apply
*/
// successful app.
'd*(d*2+3)'
'd*(d*e+f)'
'd*(d*e+f)*2'
'd*(d*e+f)*v'
'd*(d*e+f)*...'
// unsuccessful app.
'2*d*(d*e+f)'
'2*2*d*(d*e+f)'
'g*d*(d*e+f)'
'(n1*n2..*..nx)*d*(d*e+f)' // excluding expressions where '(n1*n2..*..nx)' is eliminated, such as evaluating to '1'
/*
* And rule 'n^n1 * n^n2 -> n^(n1+n2)'
*/
// successful
'a^2*a^2'
'a^2*a^2*b'
'a^2*a^2*2'
'a^2*a^2*...
// unsuccessful
'...*a^2*a^2*2'
/*
* The same pattern repeats for remaining 'collect like factor' rules.
*
* Further, the same applies to rules for the other commonly accepted commutative operator: addition.
* Namely those bannered under 'collect like term' rules, including (rules):
*
* 'n+n -> 2*n' (e.g. rule does not apply for 'constant + n + n')
* 'v*n + v -> v*(n+1)' (e.g. rule does not apply for 'constant + v*n + v')
* 'n3*n1 + n3*n2 -> n3*(n1+n2)' (similar again)
*
* **Note** that this absence of rule-application is still the case for (collect like term) rules marked
* with property 'assuming.add.commutative' set to 'false', such as for rules 'n1*n3 + n2*n3 -> (n1+n2)*n3'
* and 'c*n + c -> c*(n+1)'
*/
To Reproduce
Call the mathjs.simplify function on the expressions given in the examples, with the options argument including the following context property:
context: {
add: {
associative: true,
commutative: false,
},
multiply: {
associative: true,
commutative: false,
},
},
or, with only one of these properties set to have a non-commutative context.
Issues discussed in #2817
Originally posted by samueltlg October 17, 2022
Hello,
Firstly, I have entitled this discussion in a more general manner such as to keep the discussion open around the topic to which it pertains (I hope you don't mind).
I have been following the progression of this module for quite some time, particularly in the area of simplification/the 'simplify' function. (thank you by the way, to whomever concerned, for the ongoing developments that have occurred here). Consequently, I have also become familiar with the default simplify rule-list, particularly with regards to its ordering of rules.
I did have a question or two though, which I am anticipating may evoke simple answers.
When simplifying an expr. with addition and multiplication set to have a non-commutative context (but let's say associativity remains), the following simplify as expected (default ruleset assumed):
'a + a -> 2 * a'
'3*a + 3*a -> 6 * a'
// implicit form of above
'3a + 3a -> 6 * a'
// symbol multiplication
'a * a -> a^2'
However, there are a number of operations which possibly do not simplify as one would expect for this context:
// the following two equivalent examples are understandable, since it can be
// deduced that the parser does not treat 'constant * symbol' nodes, implicit
// or otherwise, as single 'units'
'2 * b * 3 * b -> LHS/unsimplified'
// implicit form
'2b * 3b -> LHS/unsimplified'
// However, the following few examples are not expected
//(an example like this being the instigator of this issue being raised
'(2b) * (3b) -> LHS/unsimplified' // (1)
// And a somewhat different example, but nevertheless related due to
// being present when in a non-commutative multiplication context
' b * 2 * 2 * 2 -> LHS' // (2)
// And the third (incomplete simplification)
`b * b * b -> b^2 * b' // (3)
For example (1), unless there is something I am missing with regards to the mathematical properties of the multiplication operator (distributivity, maybe?), it seems that this should be simplifying to 6b^2.
(I say this, because for the case of addition, similar operations simplify fine, such as 3*a + 3*a -> 6 * a as given in the opening expected-output examples)
It seems as if, for the addition op., constant-symbol node pairs are understood as single 'units', whilst in the case of multiplication, this is not so?
For reference, here is the debug-output for a call to simplify on (1) with default-context:
Working on: (2 b) * (3 b)
Applying log(e) -> 1 produced 2 * b * 3 * b
Applying n * n -> n ^ 2 produced (b ^ 2) * (2 * 3)
Applying n * n ^ n1 -> n ^ (n1 + 1) produced (b ^ 2) * 2 * 3
Applying simplifyConstant produced 6 * (b ^ 2)
Working on: 6 * (b ^ 2)
And with a non-commutative context for addition and multiplication:
Working on: (2 b) * (3 b)
Applying log(e) -> 1 produced 2 * b * 3 * b
Applying simplifyConstant produced ((2 * b) * 3) * b
Applying n + n -> 2 * n produced 2 * b * 3 * b
Applying simplifyConstant produced ((2 * b) * 3) * b
Applying -n * n1 -> -(n * n1) produced 2 * b * 3 * b
Working on: ((2 * b) * 3) * b
Applying log(e) -> 1 produced 2 * b * 3 * b
Applying simplifyConstant produced ((2 * b) * 3) * b
Applying n + n -> 2 * n produced 2 * b * 3 * b
Applying simplifyConstant produced ((2 * b) * 3) * b
Applying -n * n1 -> -(n * n1) produced 2 * b * 3 * b
Example (2) b * 2 * 2 * 2 -> LHS is a different 'issue'. I believe that this should simplify to b * 8. Commutativity is concerned with the ordering of operands, and considering that the context is still associative, the independent '2 * 2 * 2' expression should successfully evaluate?
^ Having spent quite a lot of time debugging and examining with the simplify function, I am aware here that this expr. remains un-simplified due to maths working with left-heavy binary node trees.., with that said, would the simplification procedure not benefit from an additional pass of the simplification-loop with the flatten-unflatten process flattening and unflattening to a right-heavy tree? (in the case that commutativity is disabled for at least one of its usual operators, but associativity remains).
And finally for example (3), similar to (1), I do not see a reason why this should not simplify further?
Best wishes, and hope to hear your thoughts!
The bugs/limitations
The
simplifyfunction currently exhibits limitations and in some scenarios 'bugs' when simplifying in either (or both) a non-commutative context over the operators of addition and multiplication (whilst notably, associativity still remains).Broadly, the bug(s) can be narrowed down to unevaluated constant/numeric operations in expressions constituted by addition or multiplication (or their inverses of subtraction/division) for a number of expression configurations, where evaluation can be understood as mathematically valid (given the context) in these scenarios.
The limitations can be understood similarly as failure of matching standard/non-function rule application for these same operators in this context and for similar expression arrangements.
To illustrate:
(for the 'bugs')
And with regards to the 'limitations':
To Reproduce
Call the
mathjs.simplifyfunction on the expressions given in the examples, with theoptionsargument including the following context property:or, with only one of these properties set to have a non-commutative context.
Issues discussed in #2817
Originally posted by samueltlg October 17, 2022
Hello,
Firstly, I have entitled this discussion in a more general manner such as to keep the discussion open around the topic to which it pertains (I hope you don't mind).
I have been following the progression of this module for quite some time, particularly in the area of simplification/the 'simplify' function. (thank you by the way, to whomever concerned, for the ongoing developments that have occurred here). Consequently, I have also become familiar with the default simplify rule-list, particularly with regards to its ordering of rules.
I did have a question or two though, which I am anticipating may evoke simple answers.
When simplifying an expr. with addition and multiplication set to have a non-commutative context (but let's say associativity remains), the following simplify as expected (default ruleset assumed):
However, there are a number of operations which possibly do not simplify as one would expect for this context:
For example (1), unless there is something I am missing with regards to the mathematical properties of the multiplication operator (distributivity, maybe?), it seems that this should be simplifying to
6b^2.(I say this, because for the case of addition, similar operations simplify fine, such as
3*a + 3*a -> 6 * aas given in the opening expected-output examples)It seems as if, for the addition op., constant-symbol node pairs are understood as single 'units', whilst in the case of multiplication, this is not so?
For reference, here is the debug-output for a call to simplify on (1) with default-context:
And with a non-commutative context for addition and multiplication:
Example (2)
b * 2 * 2 * 2 -> LHSis a different 'issue'. I believe that this should simplify tob * 8. Commutativity is concerned with theorderingof operands, and considering that the context is still associative, the independent '2 * 2 * 2' expression should successfully evaluate?^ Having spent quite a lot of time debugging and examining with the
simplifyfunction, I am aware here that this expr. remains un-simplified due to maths working with left-heavy binary node trees.., with that said, would the simplification procedure not benefit from an additional pass of the simplification-loop with the flatten-unflatten process flattening and unflattening to a right-heavy tree? (in the case that commutativity is disabled for at least one of its usual operators, but associativity remains).And finally for example (3), similar to (1), I do not see a reason why this should not simplify further?
Best wishes, and hope to hear your thoughts!