Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sympy subs vs replace vs xreplace

Tags:

python

sympy

What is the difference between subs, replace, and xreplace in sympy?

I have been using subs for substitution of symbolics. I was discussing with someone and they recommended using replace instead of subs for most cases. Then I also stumbled on xreplace.

For my application I am substituting an expression in for a variable. Is one of these recommended over the other for that? What are the main uses for each? Which one would generally be recommended?

like image 619
Boto Avatar asked May 02 '26 03:05

Boto


1 Answers

Comparison Table

Credit goes to the official docs for all quotes and basics of examples, but in an attempt to make a direct and thorough comparison

subs replace xreplace
signature subs(*args, **kwargs) replace(query, value, map=False, simultaneous=True, exact=None) xreplace(rule)
docstring Substitutes old for new in an expression after sympifying args. Replace matching subexpressions of self with value. Replace occurrences of objects within the expression.
"see also" link blurb substitution of subexpressions as defined by the objects themselves. replacement capable of doing wildcard-like matching, parsing of match, and conditional replacements exact node replacement in expr tree; also capable of using matching rules
replacement design logical replacement query or rule-matching pattern replacement
accepts mapping of replacements True
dict or new:old pairs
False
one at a time, though it can have complex logic
True
dict of patterns and replacements

Examples

For single expressions, subs replaces on real equivalence, extracting sub-expressions and replacing them, while replace and xreplace match on literal patterns - for example, see how they match x**2

>>> (x**2 + x**4).subs(x**2, y)         # logical/mathematical replacement
y**2 + y
>>> (x**2 + x**4).xreplace({x**2: y})   # pattern replacement (multiple)
x**4 + y
>>> (x**2 + x**4).replace(x**2, y)      # pattern replacement (single)
x**4 + y

However, while subs and xreplace have fairly simple acceptable arguments, replace has a very complex syntax, replacing via some types, pattern matching(see warnings!), or filters .. and further with the first argument modifying what format the second replacement argument may take, from a direct expression to a callable, which may additionally be passed either the match itself or the match's arguments!

replace by type

>>> (x**2 + x**4).replace(Pow, lambda a,b: Pow(a, b+1))  # unpacks Pow args
x**5 + x**3
>>> (x**2 + x**4).replace(Pow, lambda a,b: tan(a)**(b+1))
tan(x)**5 + tan(x)**3
>>> (x**2 + x**4).replace(Number, lambda: 10)  # make every number 10
2*x**10

replace by pattern

>>> (x**2 + x**4).replace(Wild('a', exclude=[Pow, 4]), lambda a: y)
y**4 + y**y

replace by callable filter

>>> (x**2 + x**4).replace(lambda expr: expr**2 == x**2, lambda a: y)
y**4 + y**2
>>> (x**2 + x**4).replace(lambda expr: expr%2==0, lambda a: Pow(y, a))
x**(y**2) + x**(y**4)

Similar Functions

match command match(pattern, old=False) (and matches helper)
Note unbounded Symbols are ignored and ordering matters (see Warnings below)

>>> (x**2 + x**4).match(Wild('n')**4 + (Wild('m')**2))
{n_: x, m_: x}

The subs docs makes special note that .evalf() supports substituting too, and when numerically evaluating after substituting, this takes the passed precision into account, potentially getting a more expected result

>>> (1/x).evalf(subs={x: 3.0}, n=21)
0.333333333333333333333
>>> (1/x).subs({x: 3.0}).evalf(21)
0.333333333333333314830

the rewrite command rewrite(*args, deep=True, **hints)

Rewrite self using a defined rule.

Rewriting transforms an expression to another, which is mathematically equivalent but structurally different. For example you can rewrite trigonometric functions as complex exponentials or combinatorial functions as gamma function.

>>> expr = cos(x) + I*sin(x)
>>> expr.rewrite(exp)
exp(I*x)
>>> expr.rewrite(sin, exp)
exp(I*x)/2 + cos(x) - exp(-I*x)/2

Warnings and Wild

Wild can match very unexpectedly if not properly constrained - check out my answer to What's the actual behavior of expr.replace() when using "exact=False"?!

Further beware of sensitivity to ordering and the real representation when doing pattern matches - this somewhat non-obvious behavior happens because replace treats the passed expression as a pattern, which may not be an exact match when additional Symbols become involved!

>>> (x**2 + x**4).match(Wild('n')**2 + (Wild('m')**4))
{n_: x**2, m_: sqrt(x)}
>>> (x**2 + x**4).match(Wild('n')**4 + (Wild('m')**2))
{n_: x, m_: x}
>>> srepr(x**2 + x**4)
"Add(Pow(Symbol('x'), Integer(4)), Pow(Symbol('x'), Integer(2)))"
>>> (x**2 + x**4 - y**3).replace(x**2 + x**4, y)  # fails
x**4 + x**2 - y**3
>>> (x**2 + x**4 - y**3).replace(x**4 + x**2, y)  # closer
x**4 + x**2 - y**3
>>> srepr(x**2 + x**4 - y**3)  # actually of the form Add(a, b, c)
"Add(Pow(Symbol('x'), Integer(4)), Pow(Symbol('x'), Integer(2)), Mul(Integer(-1), Pow(Symbol('y'), Integer(3))))"
like image 162
ti7 Avatar answered May 03 '26 19:05

ti7



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!