sympy - substitute a specific power - python

I have this equality.
import sympy as sp
D, L, V = sp.symbols("D, L, V", real=True, positive=True)
Veq = sp.Eq(V, sp.pi * D**3 / 4 * (sp.Rational(2, 3) + L / D))
I would like to solve Veq for D**3. If I try a direct approach, sp.solve(Veq, D**3) the computation is going to take a while eventually giving me a tremendously long result (useless to me).
My attempt: trying to substitute D**3 with a new symbol, then solve for it. Unfortunately, the substitution is also going to replace the other D in the equality:
t = sp.symbols("t")
print(Veq.subs(D**3, t))
>>> Eq(V, pi*t*(L/t**(1/3) + 2/3)/4)
Note the term L/t**(1/3). I would like it to be L/D after the substitution. So far I've managed to manipulate the expression and reaching my goal with this code:
res = sp.Mul(*[a.subs(D**3, sp.symbols("t")) if a.has(D**3) else a for a in asd.args[1].args])
Veq = sp.Eq(V, res)
print(Veq)
>>> Eq(V, pi*t*(2/3 + L/D)/4)
I'm wondering, is there some flag for subs that I can use to reach my goal? Or some other method?

If you want the substitution to be exact you can use the exact flag:
>>> var('D V L')
(D, V, L)
>>> Veq = sp.Eq(V, sp.pi * D**3 / 4 * (sp.Rational(2, 3) + L / D))
>>> Veq.subs(D**3,y,exact=True)
Eq(V, pi*y*(2/3 + L/D)/4)
>>> solve(Veq.subs(D**3,y,exact=True),y)
[12*D*V/(pi*(2*D + 3*L))]
The exact flag appears to be ignore when assumptions are given:
>>> D, L, V = symbols("D, L, V", real=True, positive=True)
>>> (D**3+D).subs(D**3,y,exact=True)
y**(1/3) + y
>>> D, L, V = symbols("D, L, V")
>>> (D**3+D).subs(D**3,y,exact=True)
D + y
You can use replace for your situation:
>>> D, L, V = symbols("D, L, V", real=True, positive=True)
>>> (D**3+D).replace(D**3,y)
D + y
But since your expression is a Relational you have to use replace on the arguments, not the Relational (or else you will get an error):
>>> eq = Eq(D**3, D - 1)
>>> eq.func(*[a.replace(D**3,y) for a in eq.args])
Eq(y, D - 1)

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}]

performing more complex calculations on groupby objects

I am currently looking to do some calculations on a large dataset of options where I want first to split the data according to the strike price and expiry, then perform a set of calculations shown below onto each subgroup. I have been able to separate the data using groupby to get the split I want, I also wrote the calculation i want to do which works when tested on a subgroup. The only problem I have is to combine the two together.
Here is the code I used to group my data:
grouped =df.groupby(['Expiry','Strike'])
I had a read online and they mentioned the use of the apply function but the examples only included simple functions such as summation or averages.
Here is the calculation that I would like to perform on each subgroup data, where x,y,z,u,R are columns that in each subset that is the same for all subgroups:
def p(d, S, B, c):
return d * S + B - c
def b_t(r, b_old, S, d, d_old, t):
return np.exp(r * t) * b_old + S * (d_old - d)
def e_t(d_old, S, c, r, t, b_old):
return d_old * S - c + np.exp(r * t) * b_old
P_results = []
B_results = []
E_results = []
for i,(d,S,c,t,r) in enumerate(zip(x,y,z,u,R)):
B = b_t(r, b_old, S, d, d_old, t)
P = p(d, S, B, c)
E = e_t(d_old, S, c, r, t, b_old)
print('i={},P={},B={},E={}'.format(i,P,B,E))
B_results.append(B)
P_results.append(P)
E_results.append(E)
b_old = B
d_old = d
I thought maybe if I could save each subset as a new variable dataframe then maybe it could work but I haven't been able to do that.
I hope this is clear and I think posting some data would help but I am not sure how best to upload it here.
Much appreciate your help!
UPDATE 1: Found a solution that works
grouped =df.groupby(['Expiry','Strike'])
lg = list(grouped)
P_results = []
l_results =[]
B_results = []
E_results = []
for l in range(len(lg)):
df2=lg[l][1]
d_old = df2.iloc[0, 4]
S_old = df2.iloc[0, 8]
c_old = df2.iloc[0, 10]
b_old = c_old - d_old * S_old
x = df2.iloc[1:, 4]
y = df2.iloc[1:, 8]
z = df2.iloc[1:, 10]
u = df2.iloc[1:, 9]
R = df2.iloc[1:, 7]
for i, (d, S, c, t, r) in enumerate(zip(x, y, z, u, R)):
B = b_t(r, b_old, S, d, d_old, t)
P = p(d, S, B, c)
E = e_t(d_old, S, c, r, t, b_old)
print('i={},P={},B={},E={}'.format(i, P, B, E))
l_results.append(l)
B_results.append(B)
P_results.append(P)
E_results.append(E)
b_old = B
d_old = d
BB = pd.DataFrame(np.column_stack([l_results, P_results,
E_results,B_results]),columns=['l','P','E','B'])
All I did was to transform grouped into a callable list and then call each of the sections out using a for loop then use another for loop to perform the calculations. It is not the prettiest output, I put l_results there to show which group the calculations were referring to but seems to be sufficient for now. If there is any better way please let me know!

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 :-)

how to nudge substitutions in sympy

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.

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