0

YourHomicidalApe asked a question which is essentially this:

Given two strings that represent equations that are converted to SymPy Eq instances, how would you tell if they represent the same equations? (The "same" meaning that algebraic manipulation of each equation could be done to obtain equivalent expressions, perhaps in some canonical form.)

For example, the following equations should compare the same. What tools can be used to do this? It is understood that the == cannot be used since that tests for structural equality and the equals method apparently doesn't work with instances of Eq unless they are trivially the same:

>>> from sympy.parsing.sympy_parser import T
>>> from sympy.abc import x, y
>>> Eq(y, x).equals(Eq(x, y))
True
>>> Eq(2*y, x).equals(Eq(x/2, y))
False

What suggestions do you have for dealing with testing the mathematical equivalence of equations like the ones shown below?

>>> from sympy.parsing import *
>>> a = parse_expr('y=x^2+.5', transformations=T[:])
>>> b = parse_expr('2*y=2x^2+1', transformations=T[:])
>>> a==b
False
>>> a.equals(b)
False
>>> e1, e2 = [i.rewrite(Add) for i in (a, b)]
>>> e1.equals(e2)
False

Does anyone else deal with such expressions, perhaps in the context of getting input from beginning algebra students that are to be tested against a known answer?

smichr
  • 16,948
  • 2
  • 27
  • 34

1 Answers1

2

As is often the case in symbolic computing a seemingly innocent question like "is this expression equivalent to that one" will in full generality be unsolvable. Even determining that two distinct equations have the same left hand sides and the same right hand sides is undecidable due to Richardson's theorem:

https://en.wikipedia.org/wiki/Richardson%27s_theorem

Of course there are many cases where this problem is decidable but as is usually the case in symbolic computing there needs to be some restriction on the problem space. In particular we need either or both of:

  • We need to define some class of possible equations or expressions that we want to be able to handle.
  • We need to define what sort of manipulation is allowable to transform one equation into an another.

In your two examples both equations are polynomial equations involving two variables with rational or float coefficients so we could take that as the class of allowable equations. The floats are a potential obstacle but I will presume that converting them to the most likely rational number with nsimplify will be acceptable in your use case. In that case our equations are polynomial equations involving multiple variables but with rational coefficients.

Now what kind of manipulation is allowed? Only certain manipulations of a polynomial equation would necessarily give another polynomial equation:

  • Adding or subtracting polynomials from both sides.
  • Multiplying both sides by either a rational number or a polynomial.
  • Raising both sides to an integer power.

An important equation her arises:

Would you consider (x - 1)**2 = 0 to be equivalent to (x - 1) = 0?

In some contexts it is important to distinguish the multiplicity of factors/roots of polynomials but in others it is not.

Another consideration is whether you want the equations to be equivalent modulo some assumptions for example we might say that (x - 1)**2 = y**2 is equivalent to x - 1 = y if say y was constrained to be nonnegative. There are many possible assumptions that you could impose so I won't try to consider those.

Create the equations (by parsing strings or anything else) and convert the floats to rational:

In [3]: a = Eq(y, x**2 + 0.5)

In [4]: b = Eq(2*y, 2*x**2 + 1)

In [5]: a
Out[5]: 
     2      
y = x  + 0.5

In [6]: b
Out[6]: 
         2    
2⋅y = 2⋅x  + 1

In [7]: a = nsimplify(a) # float to rational

In [8]: a
Out[8]: 
     2   1
y = x  + ─
         2

Now convert to expressions by subtracting the sides:

In [9]: a = a.lhs - a.rhs

In [10]: b = b.lhs - b.rhs

In [11]: a
Out[11]: 
   2       1
- x  + y - ─
           2

In [12]: b
Out[12]: 
     2          
- 2⋅x  + 2⋅y - 1

This last step assumed that you didn't mind if both equations had say a + 1 on each side that would have cancelled. Maybe you do care about that or maybe you don't: it depends on the situation. Here is an example of when you might care (if this equation was allowable in your problem space):

In [19]: eq = Eq(x + 1/x, 1/x)

In [20]: eq
Out[20]: 
    1   1
x + ─ = ─
    x   x

In [21]: solve(eq)
Out[21]: []

In [22]: eq.lhs - eq.rhs
Out[22]: x

In [23]: solve(eq.lhs - eq.rhs)
Out[23]: [0]

At this stage it seems that for the equations to be equivalent you would allow only that one is a multiple of the other but what are we allowed to multiply by? I'm going to presume that the only allowable difference here is a factor that is an explicit rational number. In that case we can make both polynomials canonical by making them monic:

In [16]: monic(a)
Out[16]: 
 2       1
x  - y + ─
         2

In [17]: monic(b)
Out[17]: 
 2       1
x  - y + ─
         2

In [18]: monic(a) == monic(b)
Out[18]: True

More complex notions of equivalence could be defined than this but it is sufficient for the examples given. You can generalise the problem by generalising the constraints on the problem but there can be no algorithmic answer to the problem without some constraints.

Oscar Benjamin
  • 12,649
  • 1
  • 12
  • 14