Pillsy
Pillsy

Reputation: 9901

Identical expressions in SymPy are not ==

I have two structurally identical expressions in SymPy, created in different ways:

import sympy as sp

f = sp.Symbol('f')
x = sp.Symbol('x')

by_hand = sp.Function(f)(x)
from_string = sp.sympify('f(x)')

Despite being identical expressions by construction, the two are not ==:

by_hand == from_string
# False

Indeed, SymPy doesn't even know that they're equal symbolically:

sp.simplify(by_hand - from_string)
# 𝑓(𝑥)−𝑓(𝑥) 

The two have identical sreprs:

(sp.srepr(by_hand), sp.srepr(from_string))
# ("Function('f')(Symbol('x'))", "Function('f')(Symbol('x'))")

sp.srepr(by_hand) == sp.srepr(from_string)
# True

I know that round-tripping through strings isn't necessary reliable for maintaining identity in SymPy, but even using the locals argument as a fix doesn't resolve this issue:

by_hand == sp.sympify('f(x)', locals={'f': f}) == by_hand
# False

Having to use locals is already somewhat painful, because I am trying to do unit testing of a routine that constructs a bunch of symbolic expressions by constructing them independently.

Upvotes: 1

Views: 78

Answers (1)

aaron
aaron

Reputation: 43083

There are a few ways to fix that.

Fix 1

Pass locals={'f': sp.Function(f)} instead of locals={'f': f}.

by_hand = sp.Function(f)(x)
from_string = sp.sympify('f(x)')

# by_hand == sp.sympify('f(x)', locals={'f': f})                     # Change this
by_hand == sp.sympify('f(x)', locals={'f': sp.Function(f)})  # True  # to this

You defined by_hand = sp.Function(f)(x), so Function('f') in sp.srepr(by_hand) is sp.Function(f), or sp.Function(sp.Symbol('f')).

On the other hand, Function('f') in sp.srepr(from_string) is sp.Function('f').
(This leads us to Fix 2.)

The difference is that sp.Function(sp.Symbol('f')) inherits the default assumption {'commutative': True} of sp.Symbol('f').
(This leads us to Fix 3.)

Fix 2

This is the clean fix and may be what you intended.

# f = sp.Symbol('f')  # Change this
f = sp.Function('f')  # to this
x = sp.Symbol('x')

# by_hand = sp.Function(f)(x)  # Change this
by_hand = f(x)                 # to this
from_string = sp.sympify('f(x)')

by_hand == from_string  # True

Fix 3

This hack demonstrates the inherited assumption {'commutative': True} identified in Fix 1.

by_hand = sp.Function(f)(x)
del by_hand._kwargs['commutative']         # Add this
# del by_hand._assumptions['commutative']  # Add this, optional
from_string = sp.sympify('f(x)')

by_hand == from_string  # True

Upvotes: 2

Related Questions