how to pattern match and change all occurrences of subexpression in expression? - python

Using sympy, I need to replace all occurrence of exp(C+anything) with C*exp(anything). Because exp(C) is constant, I just write at as C.
I can do this for one occurrence of exp in the expression. But do not how to do it if there are than one instance.
For example, for one instance, as in x+exp(C_0+3*x)+3*y, I need to change it to x+C_0*exp(3*x)+3*y
For one instance, this seems to work after some trial and error
from sympy import *
x,y,C_0 = symbols('x y C_0')
expr=x+exp(C_0+3*x)+3*y
#first check if exp is in the expression
if any([isinstance(a, exp) for a in preorder_traversal(expr)]):
p_1=Wild('p1');p_2=Wild('p_2');p_3=Wild('p_3')
r=(p_1+exp(C_0+p_2)+p_3).matches(expr)
expr.subs(exp(C_0+r[p_2]),C_0*exp(r[p_2]))
Which gives
C_0*exp(3*x) + x + 3*y
But what about something like x+exp(C_0+3*x)+3*y+exp(C_0+30*x+y) which I need to change to x+C_0*exp(3*x)+3*y+C_0*exp(30*x+y) I can't make special pattern match for each possible case. I need a way to change all occurrences
In Mathematica, I do the above as follows
expr = x + Exp[c + 3*x]*3*y + 3*y + Exp[c + 30*x + y]
expr /. Exp[c + any_] :> (c Exp[any])
Which gives
I actually prefer to tell Python just to change exp(C+anything) to C*exp(anything) without having to give pattern for the overall expression, since that can change in many way.
I am sure the above is also possible in python/sympy. Any hints how to do it?

I would look for function exp inside of the expression, check whether its argument is Add, and then whether C_0 is among the arguments of Add. Then build a thing to replace exp with. Consider the following:
from sympy import *
x, y, C_0 = symbols('x y C_0')
expr = x + exp(C_0+3*x) + 3*y + exp(y+C_0+30*x) - exp(x+y-C_0) + exp(x*y)
exp_sum = [(a, a.args[0].args) for a in preorder_traversal(expr) if a.func == exp and a.args[0].func == Add]
exp_sum = [p for p in exp_sum if C_0 in p[1]]
new_exp = [C_0*exp(Add(*[x for x in p[1] if x != C_0])) for p in exp_sum]
for (old, new) in zip(exp_sum, new_exp):
expr = expr.subs(old[0], new)
Initially, exp_sum contains all parts of the form exp(Add(...)). After that it's filtered down to sums containing C_0. New exponentials are formed by taking all summands that are not C_0, adding them, applying exp and multiplying by C_0. Then substitution happens.
To clarify the process, here is what exp_sum is in the above example: a list of tuples (exponential and the summands inside):
[(exp(C_0 + 3*x), (C_0, 3*x)), (exp(C_0 + 30*x + y), (C_0, y, 30*x))]
And this is new_exp
[C_0*exp(3*x), C_0*exp(30*x + y)]
Finally, expr at the end:
C_0*exp(3*x) + C_0*exp(30*x + y) + x + 3*y + exp(x*y) - exp(-C_0 + x + y)
Notice that exp(-C_0...) is not affected by the change; it's not a part of the pattern.

Related

How to use sympy to convert all standalone integers in an expression that aren't exponents to 1

Is there a way to use sympy to find/replace all standalone integers (that aren't exponents) to 1.
For example, converting the following:
F(x) = (2/x^2) + (3/x^3) + 4
To:
F(x) = (1/x^2) + (1/x^3) + 1
I've searched extensively on stackoverflow for sympy expression.match/replace/find solutions, and have tried using a Wildcard symbol to find and replace all numbers in the expression but I keep running into the issue of matching and replacing the exponents (2 and 3 in this example) as well as they are also considered numbers.
Is there a simple (pythonic) way to achieve the above?
Thanks!
setdefault used with replace is a nice way to go. The single expression below has 3 steps:
mask off powers and record
change Rationals to 1 (to handle integers in numer or denom)
restore powers
>>> from sympy.abc import x
>>> from sympy import Dummy
>>> eq = (2/x**2) + (3/x**3) + 4 + 1/x/8
>>> reps = {}
>>> eq = eq.replace(lambda x: x.is_Pow, lambda x: reps.setdefault(x, Dummy())
).replace(lambda x: x.is_Rational, lambda x: 1
).xreplace({v:k for k,v in reps.items()})
1 + 1/x + 1/x**2 + 1/x**3
You can write a function that will recurse into your expression. For any expression expr, expr.args will give you the components of that expression. expr.is_constant() will tell you if it's a constant. expr.is_Pow will tell you if it's an exponential expression, so you can choose not to drill down into these expressions.
import sympy
def get_constants(expr):
c = set()
for x in expr.args:
if x.is_constant(): c |= {x}
if not x.is_Pow:
c |= get_constants(x)
return c
Now, you can get all the constants in said expression, and replace each of these constants using expr.replace().
def replace_constants(expr, repl):
for const in get_constants(expr):
expr = expr.replace(const, repl)
return expr
With your example expression, we get:
x = sympy.symbols('x')
F = 2/x**2 + 3/x**3 + 4
G = replace_constants(F, 1)
print(F) # 4 + 2/x**2 + 3/x**3
print(G) # 1 + x**(-2) + x**(-3)

symbolic solution of linear equations using Sympy

Basically I have [5x5][5x1]=[0] and would like to have the symbolic expression of the solution.
Here is my code.
from sympy import symbols, solve
gm1, gm2, gm4 = symbols(['gm1', 'gm2', 'gm4'])
gds1, gds2, gds3, gds4, gds5 = symbols(['gds1', 'gds2', 'gds3', 'gds4', 'gds5'])
s = symbols(['s'])
Cd, CF , Cin, Ct = symbols(['Cd', 'CF', 'Cin', 'Ct'])
K = symbols(['K'])
vb, vc, ve, vout, iin = symbols(['vb', 'vc', 've', 'vout', 'iin'])
sol = solve([-(gds1+gds3+(s*Cd))*vb + (gm1+gds1)*ve + -gm1*vout, \
-gm4*vb + (gds4-gds2-(s*Cin)-(s*CF))*vc + (gds2+gm2)*ve + s*CF*vout + iin, \
gds1*vb + gds2*vc + (-(s*Ct)-gds5-gds1-gm1-gm2-gds2)*ve + gm1*vout, \
K*vc + vout], [vout])
print(sol)
but, I got this error
TypeError: can't multiply sequence by non-int of type 'Symbol'
From here, symbolic multiplication seems working just fine.
I am not sure whether I describe my problem in a way that does not comply with Sympy or something else.
What did I miss here?
The problem is in the assignment of the single symbols s and K. If instead you do:
s, K = symbols(['s', 'K'])
Or:
s = symbols('s')
K = symbols('K')
Whether you get the right answer or not is another matter :)
When you pass a list to symbols you get a list back. You can unpack that like [s] = symbols(['s']) or you can just pass a string of space or comma separated strings like x, y = symbols('x y') or x, y = symbols(','.join(['x', 'y']).
If you select manual=True you will get a solution vout=K*vc which sets the 4th equation to 0. But that was almost obvious, right? And you didn't need the other 3 equations to tell you that. So go ahead and pick up to 3 other variables for which you want to solve. There are lots of possibilities:
>>> from sympy.functions.combinatorial.numbers import nC
>>> allsym = Tuple(*eqs).free_symbols
>>> nfree = len(allsym) - 1 # always take vout
>>> print(nC(nfree, 3)) # want 3 others
816
For example, selecting (vout, gds4, gm1, gds5) gives a solution of
[{gds4: (CF*K*s*vc + CF*s*vc + Cin*s*vc + gds2*vc -
gds2*ve - gm2*ve + gm4*vb - iin)/vc,
gm1: (Cd*s*vb + gds1*vb - gds1*ve + gds3*vb)/(K*vc + ve),
gds5: -(Cd*s*vb + Ct*s*ve - gds2*vc + gds2*ve + gds3*vb + gm2*ve)/ve,
vout: -K*vc}]

How to order terms in polynomial expression (sympy, python) according to increasing degree (univariate case)?

In sympy (python) it seems that, by default, terms in univarate polynomials are ordered according to decreasing degrees: highest degree first, then second to highest, and so on. So, for example, a polynomial like
x + 1 + x^3 + 3x^6
will be printed out as 3x^6 + x^3 + x + 1.
I would like to reverse this order of polynomial terms in sympy to be increasing in the degrees. For the same example, the print-out should read 1 + x + x^3 + 3x^6. A solution that globally changes some parameter in program preamble is preferred but other options are also welcome.
Here is an MWE to play around with. It is different from the actual program I am working with. One part of the actual program (not the MWE) is printing out a list of recursively defined polynomials, e.g., P_n(x) = P_(n-1)(x) + a_n * x^n. It is easier for me to compare them when they are ordered by increasing degree. This is the motivation to change the order; doing it globally would probably just keep the code more readable (aesthetically pleasing). But the MWE is just for the same simple polynomial given in example above.
import sympy as sym
from sympy import *
x = sym.Symbol('x')
polynomial = x + 1 + x**3 + 3*x**6
print(polynomial)
Output of MWE:
>>> 3*x**6 + x**3 + x + 1
Desired output for MWE:
>>> 1 + x + x**3 + 3*x**6
You can get the leading term using sympy.polys.polytools.LT:
LT(3x ** 6 + x ** 3 + x + 1) == 3x**6
So at least you can churn out terms recursively and print it in your own way.
Unfortunately I’ve been trying to find some way to print the terms in some fix order for a long while and find no solution better than this
It's seems that there isn't an explicit way to do that and I found this approach to the problem:
to modify the print-representation of the object you can subclass its type and override the corresponding printing method (for LaTeX, MathML, ...) see documentation.
In this case _sympystr is used to "generates readable representations of SymPy expressions."
Here a basic implementation:
from sympy import Poly, symbols, latex
class UPoly(Poly):
"""Modified univariative polynomial"""
def _sympystr(self, printer) -> str:
"""increasing order of powers"""
if self.is_multivariate: # or: not self.is_univariate
raise Exception('Error, Polynomial is not univariative')
x = next(iter(expr.free_symbols))
poly_print = ""
for deg, coef in sorted(self.terms()):
term = coef * x**deg[0]
if coef.is_negative:
term = -term # fix sign
poly_print += " - "
else:
poly_print += " + "
poly_print += printer._print(term)
return poly_print.lstrip(" +-")
def _latex(self, printer):
return latex(self._sympystr(printer)) # keep the order
x = symbols('x')
expr = 2*x + 6 - x**5
up = UPoly(expr)
print(up)
#6 + 2*x - x**5
print(latex(up))
#6 + 2 x - x^{5}

How to reduce the size of the generator in sympy.Poly?

Given the formula, I want to convert it to a Poly object and minimize the size of the generator. For example, if my formula is x^2+x+sqrt(x), I would expect to have only sqrt(x) in the generator. But in most of my attempts, the generator contains both x and sqrt(x).
from sympy import *
print('first attempt:',Poly('x^2+x+sqrt(x)'))
x=Symbol('x', positive=True)
print('second attempt:',Poly(x**2+x+sqrt(x)))
print('third attempt:',str(Poly({1:1,2:1,4:1},gens=S('sqrt(x)'))))
first attempt: Poly(x**2 + x + (sqrt(x)), x, sqrt(x), domain='ZZ')
second attempt: Poly(x**2 + x + (sqrt(x)), x, sqrt(x), domain='ZZ')
third attempt: Poly((sqrt(x))**4 + (sqrt(x))**2 + (sqrt(x)), sqrt(x), domain='ZZ')
Only the third method was successful, but the code for it is ugly and hard to automatize (I want to convert it because I want to easily extract coefficients and powers, so it gives me nothing). Is there a nicer way to do it?
EDIT
Based on smichr response, I've created a function to reduce the size of the generator.
def _powr(formula):
if formula.func==Pow:
return formula.args
else:
return [formula,S('1')]
def reducegens(formula):
pol=Poly(formula)
newgens={}
ind={}
for gen in pol.gens:
base,pw=_powr(gen)
coef,_=pw.as_coeff_mul()
ml=pw/coef
if base**ml in newgens:
newgens[base**ml]=gcd(newgens[base**ml],coef)
else:
newgens[base**ml]=coef
ind[base**ml]=S('tmp'+str(len(ind)))
for gen in pol.gens:
base,pw=_powr(gen)
coef,_=pw.as_coeff_mul()
ml=pw/coef
pol=pol.replace(gen,ind[base**ml]**(coef/newgens[base**ml]))
newpol=Poly(pol.as_expr())
for gen in newgens:
newpol=newpol.replace(ind[gen],gen**newgens[gen])
return newpol
I've tried it on several examples and it's not bad, but there is room for improvements.
print(reducegens(S('x^2+x+sqrt(x)')))
print(reducegens(S('x**2+y**2+x**(1/2)+x**(1/3)')))
print(reducegens(S('sqrt(x)+sqrt(z)+sqrt(x)*sqrt(z)')))
print(reducegens(S('sqrt(2)+sqrt(3)+sqrt(6)')))
Poly((sqrt(x))**4 + (sqrt(x))**2 + (sqrt(x)), sqrt(x), domain='ZZ')
Poly((x**(1/6))**12 + (x**(1/6))**3 + (x**(1/6))**2 + y**2, x**(1/6), y, domain='ZZ')
Poly((sqrt(x))*(sqrt(z)) + (sqrt(x)) + (sqrt(z)), sqrt(x), sqrt(z), domain='ZZ')
Poly((sqrt(2)) + (sqrt(3)) + (sqrt(6)), sqrt(2), sqrt(3), sqrt(6), domain='ZZ')
There first three examples are OK, but it would be nice to represent sqrt(6) as sqrt(2)*sqrt(3) and not as another generator's element.
You can just replace sqrt(x) with y or (using symbol-trickery) replace it with a Symbol named sqrt(y) (or sqrt(y) which I do to show that it worked):
>>> from symyp.abc import x, y
>>> from sympy import Symbol
>>> (x**2+x+sqrt(x)).subs(sqrt(x), y)
y**4 + y**2 + y
>>> (x**2+x+sqrt(x)).subs(sqrt(x), Symbol('sqrt(y)'))
sqrt(y)**4 + sqrt(y)**2 + sqrt(y)
Either form now has a single symbol and will create a Poly with a single generator.

python: changing symbol variable and assign numerical value

In order to calculate derivatives and other expressions I used the sympy package and said that T = sy.Symbol('T') now that I have calculated the right expression:
E= -T**2*F_deriv_T(T,rho)
where
def F_deriv_rho(T,rho):
ret = 0
for n in range(5):
for m in range(4):
inner= c[n,m]*g_rho_deriv_rho_np*g_T_np
ret += inner
return ret
that looks like this:
F_deriv_rho: [0.0 7.76971e-5*T 0.0001553942*T**2*rho
T*(-5.14488e-5*log(rho) - 5.14488e-5)*log(T) + T*(1.22574e-5*log(rho)+1.22574e-5)*log(T) + T*(1.89488e-5*log(rho) + 1.89488e-5)*log(T) + T(2.29441e-5*log(rho) + 2.29441e-5)*log(T) + T*(7.49956e-5*log(rho) + 7.49956e-5)*log(T)
T**2*(-0.0001028976*rho*log(rho) - 5.14488e-5*rho)*log(T) + T**2*(2.45148e-5*rho*log(rho) + 1.22574e-5*rho)*log(T) + T**2*(3.78976e-5*rho*log(rho) + 1.89488e-5*rho)*log(T) + T**2*(4.58882e-5*rho*log(rho) + 2.29441e-5*rho)*log(T) + T**2*(0.0001499912*rho*log(rho) + 7.49956e 5*rho)*log(T)]
with python I would like to change T (and rho) as a symbol to a value. How could I do that?
So, I would like to create 10 numbers like T_def = np.arange(2000, 10000, 800)and exchange all my sy.symbol(T) by iterating through the 10 values I created in the array.
Thanks for your help
I have found the solution according to this post:
How to substitute multiple symbols in an expression in sympy?
by usings "subs":
>>> from sympy import Symbol
>>> x, y = Symbol('x y')
>>> f = x + y
>>> f.subs({x:10, y: 20})
>>> f
30
There's more for this kinda thing here: http://docs.sympy.org/latest/tutorial/basic_operations.html
EDIT: A faster way would be by using "lamdify" as suggested by #Bjoern Dahlgren

Categories