I have searched a lot through the questions and, honestly, couldn't find something that applies to my case.
I am working with the SymPy library in order to perform large calculations and it has been working tremendously well. However, my recent results have became too convoluted. So, I began to implement more definitions and symbols in order to perform several substitutions and narrow down the expression to a more feasible form. I have been able to do this, 'manually', for a lot of terms, which are too much particular to allow any kind of automation. But, there is a specific family of contributions in my expression that could be greatly simplified if I could run this simple substitution rule.
Let A be my complete expression. In this image, the dots represent already simplified terms and the summation represents the general setup behind my query. My goal is to rewrite it like this.
The T_k are just denoting generic terms; in fact, they are composed by several literal Symbols
. In my code, the rho letter is associated to a single variable, Symbol
-type. So, with that said, my problem could be exemplified as follows:
# sympy symbols: rho, a, b, c, d
# sympy functions: f, g, h
A = (a * b) * f(rho) + (c * d) * g(rho) + (a * c * d * b) * h(rho)
# apply some clever substitution method ???
A.subs(???)
>>> f(a * b) * rho + g(c * d) * rho + h(a * c * d * b) * rho
Of course, in the above example, I could do it "by hand". The problem lies when there is a lot of terms like that. I tried to achieve my goal using lambda
expressions, but couldn't make it work. The idea was: for any generic lambda t
, I could try to input the map {t * f(rho) : f(t) * rho}
, for each symbolic function (# f, g, h
), but not caring about the particular companion terms.
I tried defining, for instance, genSub_f = lambda t: A.subs(t * f(rho), f(t) * rho)
. However, in order to make the substitution, I would have to call genSub_f(a * b)
, which works, but it's not practicable, since one would have to detain prior knowledge about the combinations of symbols that are going along with f(rho)
. How to proceed?
You could use Wild
symbols for this however there are a few ways to obtain your desired output.
One complication is it seems you don't want to apply the substitution
f(a*b)*rho -> f(a*b*rho)
. There are a few ways to achieve this, however first I'll give an answer that does result in this substitution:
from sympy import *
rho, a, b, c, d = symbols("rho, a, b, c, d")
f, g, h = symbols("f, g, h", cls=Function)
A = (a * b) * f(rho) + (c * d) * g(rho) + (a * c * d * b) * h(rho)
w1, w2 = symbols("w1, w2", cls=Wild)
for ff in [f, g, h]:
A = A.replace(w1*ff(w2), w2*ff(w1))
print(A) # f(a*b*rho) + g(c*d*rho) + h(a*b*c*d*rho)
So what about excluding the substitution rho*f(a*b) -> f(a*b*rho)
?
One approach is to exclude rho
from the Wild
matches by defining your Wild
symbol w1
like so (we also have to exclude the substitution 1*f(...) -> ...*f(1)
). This results in your desired output:
A = (a * b) * f(rho) + (c * d) * g(rho) + (a * c * d * b) * h(rho)
w1 = Wild("w1", exclude=[rho, 1]) # exclude unwanted substitutions
w2 = Wild("w2")
for ff in [f, g, h]:
A = A.replace(w1*ff(w2), w2*ff(w1))
print(A) # rho*f(a*b) + rho*g(c*d) + rho*h(a*b*c*d)
However, another way to achieve your desired output is to use match
instead of replace
. This way you won't have to explicitly exclude rho
(with this approach the replacement is done manually so there's no need to worry about avoiding 'double' application of the replacement rules):
A = (a * b) * f(rho) + (c * d) * g(rho) + (a * c * d * b) * h(rho)
w1, w2, w3 = symbols("w1, w2, w3", cls=Wild)
for ff in [f, g, h]:
m = A.match(w1*ff(w2) + w3)
if m:
A = m[w2]*ff(m[w1]) + m[w3] # manual replacement
print(A) # rho*f(a*b) + rho*g(c*d) + rho*h(a*b*c*d)