how to nudge substitutions in sympy - python

i have the following where a, b, c, d are all sympy symbols
p = a + b + 2*c + d
q = a + b + c + d
i wanted to do this:
p = p.subs(a + b + c + d, q)
and wanted to get this:
p = q + c
but p remains unchanged.
what should i be doing to get to p = q + c?
the matching performed by subs() seems to be looking for strict ordering, and hence it didn't split up the '2*c' term.
should i be using replace() instead of subs().
EDIT:
code as follows:
import sympy
a, b, c, d = sympy.symbols('a,b,c,d')
p = a + b + 2*c + d
q = a + b + c + d
r = p.subs(a + b + c + d, q)
print r
EDIT #2
https://groups.google.com/forum/#!topic/sympy/b_Yv6s15Y0Q
my problem is similar to the groups.google link, just that in that case subs() do its job.

First a comment about your code: since you define q to be the sum a+b+c+d you will never see the change, even if it did work (but it doesn't). Something else that does work is the following:
>>> p = a + b + 2*c + d
>>> q = var('q')
>>> p.extract_additively(a+b+c+d) + q
c + q
There is also an extract_multiplicatively method.

Related

Solving system of nonlinear (and nonpolynomial) equations

I would like to use python/sympy to solve simple systems of equations coming from impedance calculations in electronics.
In such calculations, due to the "parallel impedance" formula, one often has to deal with expressions of the form:
par(x,y) := (x*y)/(x+y)
Now I have tried with the following code:
from sympy import *
def par(var1,var2):
return (var1 * var2)/(var1+var2)
A = Symbol('A')
B = Symbol('B')
C = Symbol('C')
D = Symbol('D')
E = Symbol('E')
eq1 = A + par(B+50 , (C+ par(D, (E+50)) )) - 50
eq2 = B + par(A+50 , (C+ par(D , (E+50)) )) - 50
eq3 = E + par(D+50, (C+ par(A+50, B+50)) ) - 50
thus defining a system of three equations in five variables {A,B,C,D,E}, but then running
solve([eq1,eq2,eq3], A, B,C,D,E)
the computations just does not terminate.
Do you have any suggestions on how I could approach these type of equations?
Basically polynomials with division by polynomials, with solutions in the complex numbers.
Taking the suggestion of Oscar and focussing on A, B and C you can get a solution for them in terms of D and E:
>>> solve((eq1, eq2, eq3), A, B, C)[0] # one solution
(
50*(D + E)*(D + E + 50)/(3*D**2 - 2*D*E + 150*D - 3*E**2 - 50*E + 5000),
50*(D + E)*(D + E + 50)/(3*D**2 - 2*D*E + 150*D - 3*E**2 - 50*E + 5000),
(-3*D**3*E + 50*D**3 + 2*D**2*E**2 - 500*D**2*E + 10000*D**2 + 3*D*E**3 + 50*D*E**2 - 25000*D*E + 500000*D + 200*E**3 - 5000*E**2 - 500000*E + 12500000)/(3*D**3 + D**2*E + 150*D**2 - 5*D*E**2 + 100*D*E + 5000*D - 3*E**3 - 50*E**2 + 5000*E))
Notice that the solution for A and B is the same (consistent with the symmetry of the first two equations wrt A and B).
>>> sol = Dict(*zip((A,B,C),_))
>>> sol[A] = sol[B]
True
In this form, you can directly substitute values for D and E:
>>> sol.subs({D:1, E:S.Half})
{A: 1030/1367, B: 1030/1367, C: 2265971/1367}
You can also see what relationships between D and E are forbidden by solving for when any of the denominators are 0:
>>> from sympy.solvers.solvers import denoms
>>> set([j.simplify() for i in denoms(sol) for j in solve(i,D)])
{-E, E/3 - sqrt(10*E**2 - 9375)/3 - 25, E/3 + sqrt(10*E**2 - 9375)/3 - 25}
You could also try the Z3 library:
from z3 import Reals, Solver, sat, set_option
def par(var1, var2):
return (var1 * var2) / (var1 + var2)
vars = Reals('A B C D E')
A, B, C, D, E = vars
set_option(rational_to_decimal=True, precision=30)
s = Solver()
s.add(A + par(B + 50, (C + par(D, (E + 50)))) == 50)
s.add(B + par(A + 50, (C + par(D, (E + 50)))) == 50)
s.add(E + par(D + 50, (C + par(A + 50, B + 50))) == 50)
if s.check() == sat:
m = s.model()
print(m)
I get following output:
[D = 0.502507819412956050111927878839?,
C = 0.125,
E = 50.188198722936229689352204927638?,
B = -50.625,
A = -50.625]
The question mark at the end of D's and E's values means they have been approximated.
If you then try the values for A, B and C into the original code, sympy gives two exact expressions:
[{A: -405/8,
B: -405/8,
C: 1/8,
D: -31857/1292 + 5*sqrt(676050154)/5168,
E: 443/1304 + 5*sqrt(676050154)/2608},
{A: -405/8,
B: -405/8,
C: 1/8,
D: -5*sqrt(676050154)/5168 - 31857/1292,
E: 443/1304 - 5*sqrt(676050154)/2608}]

Sympy Linsolve unexpected results

I am trying to solve a system of equations with Linsolve, but obviously must have misunderstood something, since I keep getting unexpected results. Say I want to solve the two following equations:
a + b = 0
a - b + c = 0
I would expect the result:
b = 0.5*c
Instead Sympy returns the empty set. With nonlinsolve I get (-a), which doesn't make much sense either:
>>> import sympy
>>> a, b, c = sympy.symbols('a b c')
>>> Eqns = [a + b, a - b + c]
>>>sympy.linsolve(Eqns, b)
()
>>>sympy.nonlinsolve(Eqns, b)
(-a)
I think I'm going insane, please help :)
You also need to pass the other variable. So pass as many variables as equations or it's unsolvable, just like by hand.
import sympy as sp
a, b, c = sp.symbols('a b c')
Eqns = [a + b, a - b + c]
sp.solve(Eqns, b, a)

how to represent a variable with other variables given a equation set in SymPy?

I tried to eliminate r and z from the equation set and get the expression of S without r and z:
var('xi R R_bfs k S z r')
solve(r**2 - 2*R*z + (k + 1)*z**2, S*cos(xi)+z-R_bfs, S*sin(xi)-r, S, r, z)
This returns an empty list for S, but I am sure there is solution for s. Is there any method or function to handle this problem?
When I run into problems like this I try to use the CAS to do the steps for me that lead to the solution that I want. With only 3 equations this is pretty straightforward.
We can eliminate S from the last 2 equations
>>> eqs = r**2 - 2*R*z + (k + 1)*z**2, S*cos(xi)+z-R_bfs, S*sin(xi)-r
>>> solve(eqs[1:],(r,z))
{r: S*sin(xi), z: R_bfs - S*cos(xi)}
This solution can be substituted into the first equation
>>> e1 = eqs[0].subs(_)
This results in a polynomial in S, of degree = 2, that does not contain r or z
>>> degree(e1, S)
2
>>> e1.has(r, z)
False
And the solutions of a general quadratic are
>>> q = solve(a*x**2 + b*x + c, x); q
[(-b + sqrt(-4*a*c + b**2))/(2*a), -(b + sqrt(-4*a*c + b**2))/(2*a)]
So all we need are the values of a, b and c from e1 and we should have our
solutions for S, free or r and z:
>>> A, B, C = Poly(e1, S).all_coeffs()
>>> solns = [i.subs({a: A, b: B, c: C}) for i in q]
Before we look at those, let's let cse remove common expressions
>>> reps, sols = cse(solns)
Here are the replacements that are identified
>>> for i in reps:
... print(i)
(x0, cos(xi))
(x1, x0**2)
(x2, k*x1 + x1 + sin(xi)**2)
(x3, 1/(2*x2))
(x4, 2*R)
(x5, x0*x4)
(x6, 2*R_bfs*x0)
(x7, k*x6)
(x8, x5 - x6 - x7)
(x9, R_bfs**2)
(x10, sqrt(-4*x2*(-R_bfs*x4 + k*x9 + x9) + x8**2))
And in terms of those, here are the solutions:
>>> for i in sols:
... print(i)
x3*(x10 - x5 + x6 + x7)
-x3*(x10 + x8)
If you prefer the non-cse form, you can look at that, too. Here is one solution:
>>> print(filldedent(solns[0]))
(-2*R*cos(xi) + 2*R_bfs*k*cos(xi) + 2*R_bfs*cos(xi) +
sqrt(-4*(-2*R*R_bfs + R_bfs**2*k + R_bfs**2)*(k*cos(xi)**2 +
sin(xi)**2 + cos(xi)**2) + (2*R*cos(xi) - 2*R_bfs*k*cos(xi) -
2*R_bfs*cos(xi))**2))/(2*(k*cos(xi)**2 + sin(xi)**2 + cos(xi)**2))
If your initial all-in-one-go solution fails, try to let SymPy be your Swiss Army Knife :-)

Remove mixed-variable terms in SymPy series expansion

Consider two functions of SymPy symbols e and i:
from sympy import Symbol, expand, Order
i = Symbol('i')
e = Symbol('e')
f = (i**3 + i**2 + i + 1)
g = (e**3 + e**2 + e + 1)
z = expand(f*g)
This will produce
z = e**3*i**3 + e**3*i**2 + e**3*i + e**3 + e**2*i**3 + e**2*i**2 + e**2*i + e**2 + e*i**3 + e*i**2 + e*i + e + i**3 + i**2 + i + 1
However, assume that e and i are both small and we can neglect both terms that are order three or higher. Using Sympy’s series tool or simply adding an O-notation Order class can handle this:
In : z = expand(f*g + Order(i**3) + Order(e**3))
Out: 1 + i + i**2 + e + e*i + e*i**2 + e**2 + e**2*i + e**2*i**2 + O(i**3) + O(e**3)
Looks great. However, I am still left with mixed terms e**2 * i**2. Individual variables in these terms are less than the desired cut-off so SymPy keeps them. However, mathematically small²·small² = small⁴. Likewise, e·i² = small·small² = small³.
At least for my purposes, I want these mixed terms dropped. Adding a mixed Order does not produce the desired result (it seems to ignore the first two orders).
In : expand(f*g + Order(i**3) + Order(e**3) + Order((i**2)*(e**2)))
Out: 1 + i + i**2 + i**3 + e + e*i + e*i**2 + e*i**3 + e**2 + e**2*i + e**3 + e**3*i + O(e**2*i**2, e, i)
Question: Does SymPy have an easy system to quickly remove the n-th order terms, as well as terms that are (e^a)·(i^b) where a+b > n?
Messy Solution: I have found a way to solve this, but it is messy and potentially not general.
z = expand(f*g + Order((e**2)*i) + Order(e*(i**2)))
zz = expand(z.removeO() + Order(e**3) + Order(i**3))
produces
zz = 1 + i + i**2 + e + e*i + e**2 + O(i**3) + O(e**3)
which is exactly what I want. So to specify my question: Is there a way to do this in one step that can be generalized to any n? Also, my solution loses the big-O notation that indicates mixed-terms were lost. This is not needed but would be nice.
As you have a dual limit, you must specify both infinitesimal variables (e and i) in all Order objects, even if they don’t appear in the first argument.
The reason for this is that Order(expr) only automatically chooses those symbols as infinitesimal that actually appear in the expr and thus, e.g., O(e) is only for the limit e→0.
Now, Order objects with different limits don’t mix well, e.g.:
O(e*i)+O(e) == O(e*i) != O(e)+O(e*i) == O(e) # True
This leads to a mess where results depend on the order of addition, which is a good indicator that this is something to avoid.
This can be avoided by explicitly specifying the infinitesimal symbols (as addition arguments of Order), e.g.:
O(e*i)+O(e,e,i) == O(e,e,i)+O(e*i) == O(e,e,i) # True
I haven’t found a way to avoid going through all combinations of e and i manually, but this can be done by a simple iteration:
orders = sum( Order(e**a*i**(n-a),e,i) for a in range(n+1) )
expand(f*g+orders)
# 1 + i + i**2 + e + e*i + e**2 + O(e**2*i, e, i) + O(e*i**2, e, i) + O(i**3, e, i) + O(e**3, e, i)
Without using Order you might try something simple like this:
>>> eq = expand(f*g) # as you defined
>>> def total_degree(e):
... x = Dummy()
... free = e.free_symbols
... if not free: return S.Zero
... for f in free:
... e = e.subs(f, x)
... return degree(e)
>>> eq.replace(lambda x: total_degree(x) > 2, lambda x: S.Zero)
e**2 + e*i + e + i**2 + i + 1
There is a way about it using Poly. I have made a function that keeps the O(...) term and another that does not (faster).
from sympy import Symbol, expand, Order, Poly
i = Symbol('i')
e = Symbol('e')
f = (i**3 + i**2 + i + 1)
g = (e**3 + e**2 + e + 1)
z = expand(f*g)
def neglect(expr, order=3):
z = Poly(expr)
# extract all terms and keep the lower order ones
d = z.as_dict()
d = {t: c for t,c in d.items() if sum(t) < order}
# Build resulting polynomial
return Poly(d, z.gens).as_expr()
def neglectO(expr, order=3):
# This one keeps O terms
z = Poly(expr)
# extract terms of higher "order"
d = z.as_dict()
large = {t: c for t,c in d.items() if sum(t) >= order}
for t in large: # Add each O(large monomial) to the expression
expr += Order(Poly({t:1},z.gens).as_expr(), *z.gens)
return expr
print(neglect(z))
print(neglectO(z))
This code prints the following:
e**2 + e*i + e + i**2 + i + 1
1 + i + i**2 + e + e*i + e**2 + O(e**2*i, e, i) + O(e*i**2, e, i) + O(i**3, e, i) + O(e**3, e, i)

sympy : expression simplification

I begin with sympy python lib.
If, I have this expression
from sympy.abc import a,b,c,p,q
e = p * ( a + b ) + q * ( a + c )
how I can use a,b,c as factor ? like
a(p+q) + b*p + c*q
from sympy.abc import a,b,c,p,q
from sympy import collect, expand
e = p * ( a + b ) + q * ( a + c )
print e
print expand(e)
print collect(expand(e),a)
collect is indeed the function you want. You can pass multiple symbols as the collection variable to collect them all. And as you noticed, collect will not expand your expression first, so if you want that, you have to do it yourself with expand.
In [15]: collect(e.expand(), [a, b, c])
Out[15]: a⋅(p + q) + b⋅p + c⋅q

Categories