Sympy – Comparing expressions

Question:

Is there a way to check if two expressions are mathematically equal? I expected
tg(x)cos(x) == sin(x) to output True, but it outputs False. Is there a way to make such comparisons with sympy? Another example is
(a+b)**2 == a**2 + 2*a*b + b**2 which surprisingly also outputs False.

I found some similar questions, but none covered this exact problem.

Asked By: Xico Sim

||

Answers:

From the SymPy documentation

== represents exact structural equality testing. “Exact” here means that two expressions will compare equal with == only if they are exactly equal structurally. Here, (x+1)^2 and x^2+2x+1 are not the same symbolically. One is the power of an addition of two terms, and the other is the addition of three terms.

It turns out that when using SymPy as a library, having == test for exact symbolic equality is far more useful than having it represent symbolic equality, or having it test for mathematical equality. However, as a new user, you will probably care more about the latter two. We have already seen an alternative to representing equalities symbolically, Eq. To test if two things are equal, it is best to recall the basic fact that if a=b, then a−b=0. Thus, the best way to check if a=b is to take a−b and simplify it, and see if it goes to 0. We will learn later that the function to do this is called simplify. This method is not infallible—in fact, it can be theoretically proven that it is impossible to determine if two symbolic expressions are identically equal in general—but for most common expressions, it works quite well.

As a demo for your particular question, we can use the subtraction of equivalent expressions and compare to 0 like so

>>> from sympy import simplify
>>> from sympy.abc import x,y
>>> vers1 = (x+y)**2
>>> vers2 = x**2 + 2*x*y + y**2
>>> simplify(vers1-vers2) == 0
True
>>> simplify(vers1+vers2) == 0
False
Answered By: miradulo

Alternatively you can use the .equals method to compare expressions:

from sympy import *
x = symbols('x')

expr1 = tan(x) * cos(x)
expr2 = sin(x)

expr1.equals(expr2)

True
Answered By: Prokhozhii

The solution with simplify was too slow for me (had to crosscheck multiple variables), so I wrote the following function, which does some simple checkes beforehand, to reduce computational time, to use simplify only in the last step.

import numpy as np
import sympy as sp

def check_equal(Expr1,Expr2):
    if Expr1==None or Expr2==None:
        return(False)
    if Expr1.free_symbols!=Expr2.free_symbols:
        return(False)
    vars = Expr1.free_symbols
    your_values=np.random.random(len(vars))
    Expr1_num=Expr1
    Expr2_num=Expr2
    for symbol,number in zip(vars, your_values):
        Expr1_num=Expr1_num.subs(symbol, sp.Float(number))
        Expr2_num=Expr2_num.subs(symbol, sp.Float(number))
    Expr1_num=float(Expr2_num)
    Expr2_num=float(Expr2_num)
    if not np.allclose(Expr1_num,Expr2_num):
        return(False)
    if (Expr1.equals(Expr2)):
        return(True)
    else:
        return(False)
Answered By: Okapi575

Check this out from the original sympy themselves.
https://github.com/sympy/sympy/wiki/Faq
enter image description here

Example of it working for me
enter image description here

Answered By: Mohammed

As previously stated, (expr1 - expr2).simplify() or expr1.equals(expr2) will sometimes fail to recognize equality for expressions that are complex to simplify. To deal with this, a numerical evaluation of the expressions with random numbers may constitute a relatively safe "brute force" test. I’ve adapted the excellent solution by @Okapi575 to:

  1. Test the numerical equality N-times with different random numbers each time for a more confident diagnostic
  2. Warn the user when a pair of expressions only passes the numeric test but not the symbolic equality test.

For example:

Example of test for equality

Hope it can prove useful:

import sympy as sp
import numpy as np

def check_equal(Expr1, Expr2, n=10, positive=False, strictly_positive=False):
    
    # Determine over what range to generate random numbers
    sample_min = -1
    sample_max = 1
    if positive:
        sample_min = 0
        sample_max = 1
    if strictly_positive:
        sample_min = 1
        sample_max = 2
    
    # Regroup all free symbols from both expressions
    free_symbols = set(Expr1.free_symbols) | set(Expr2.free_symbols)
    
    # Numeric (brute force) equality testing n-times
    for i in range(n):
        your_values=np.random.uniform(sample_min, sample_max, len(free_symbols))
        Expr1_num=Expr1
        Expr2_num=Expr2
        for symbol,number in zip(free_symbols, your_values):
            Expr1_num=Expr1_num.subs(symbol, sp.Float(number))
            Expr2_num=Expr2_num.subs(symbol, sp.Float(number))
        Expr1_num=float(Expr2_num)
        Expr2_num=float(Expr2_num)
        if not np.allclose(Expr1_num, Expr2_num):
            print("Fails numerical test")
            return(False)
        
    # If all goes well so far, check symbolic equality
    if (Expr1.equals(Expr2)):
        return(True)

    else:
        print("Passes the numerical test but not the symbolic test")
        # Still returns true though
        return(True)

EDIT: code updated (1) to compare expressions with differing numbers of free symbols (for example, after symbols got cancelled out during a simplification), and (2) to allow for the specification of a positive or strictly positive random number range.

Answered By: billjoie