I have a problem with sympy where it won't update my values. So I'm trying to do an iterative method for solving $\sigma_x$ and when I try to put numerical values into my expression it won't update.
I have imported sympy as sy
So I first have this code:
q,b,E,t,nu,L,z,x,y = sy.symbols("q,b,E,t,nu,L,z,x,y")
D = (E*t**3)/(12*(1-nu**2))
q_0 = 4*q/(sy.pi*b) * sy.sin(sy.pi/2)*(1-sy.cos(sy.pi))
D2 = (sy.pi**2 / L**2) + (sy.pi**2 / b**2)
w = q_0/(D* D2**2) * sy.sin(sy.pi*x/L) * sy.sin(sy.pi * y / b)
M = 4
N = 4
w_iterert = 0
for m in range(1,M+1):
for n in range(N+1):
q_iterert = 4*q/(sy.pi*b*m)*sy.sin(sy.pi*n/2)*(1-sy.cos(sy.pi*m))
w_mn = q_iterert/(D*((sy.pi**2*m**2 / L**2) + (sy.pi**2 * n**2 / b**2))**2)
w_iterert += w_mn*sy.sin(m*pi*x/L)*sy.sin(n*pi*y/b)
Then I plot the analytical expression:
w_iterert
And now I use formulas to find my sigma_x:
w_xx_iter = sy.diff(w_iterert,x,2)
w_yy_iter = sy.diff(w_iterert,y,2)
sigma_x_iter = - z*E/(1-nu**2)*(w_xx_iter+nu*w_yy_iter)
Here is where I get it wrong. now I do this:
E = 210000
pi = sy.pi
q = 20
nu = 0.3
L = 4000
b = 1000
t = 10
x = 2
z = t/2
y = b/2
sigma_x_iter
And I would expect this to update the values and give me the numerical value. Instead I just get the same analytical expression. How can I update my values?
I tried everything, I just wrote and tried to copy the text into another notebook which obviously worked. But then I can't change the M N values and get a different result unless I do it automatically.
I had to edit your code several times to get something that I could copy-n-paste and run.
What I meant by subs is:
In [38]: sigma_x_iter.subs({E:210000, q:20,nu:0.3,L:4000,b:1000,t:10,x:2,z:10/2,
...: y:1000/2})
Out[38]:
⎛ ⎛ π ⎞ ⎛3⋅π ⎞⎞
⎜654766080000000000⋅sin⎜────⎟ 9844326400000000⋅sin⎜────⎟⎟
⎜ ⎝2000⎠ ⎝2000⎠⎟
2.0e-10⋅⎜──────────────────────────── + ──────────────────────────⎟ 9.6e-10⋅
⎝ 243049 2601 ⎠
─────────────────────────────────────────────────────────────────── + ────────
3
π
⎛ ⎛3⋅π ⎞ ⎛ π ⎞⎞
⎜1321369600000000⋅sin⎜────⎟ 725790720000000000⋅sin⎜────⎟⎟
⎜ ⎝2000⎠ ⎝2000⎠⎟
⎜────────────────────────── + ────────────────────────────⎟
⎝ 2601 243049 ⎠
───────────────────────────────────────────────────────────
3
π
In [39]: _38.n()
Out[39]: 0.361692509661739
In sympy you need to keep a clear distinction between a symbol, and the variable that references it. sympy works within the Python environment, without changing syntax. You may need to study the documented gotachas some more.
E as defined is a symbol:
In [42]: type(E)
Out[42]: sympy.core.symbol.Symbol
This assignment assigns an int to E, breaking any connection it had with the symbol. The symbol still exists in the various expressions, but you can no longer reference it with the variable E. This assignment does not touch any of the expressions.
In [43]: E = 210000
In [44]: type(E)
Out[44]: int
With Matlab, I can easily give the prefix of symbolic matrix elements :
For example, like this :
Mat4x4_SYM = sym('sp_', [4,4]);
tril(Mat2x2_SYM.') + triu(Mat2x2_SYM,1)
which gives :
Mat4x4_SYM =
[ sp_1_1, sp_1_2, sp_1_3, sp_1_4]
[ sp_1_2, sp_2_2, sp_2_3, sp_2_4]
[ sp_1_3, sp_2_3, sp_3_3, sp_3_4]
[ sp_1_4, sp_2_4, sp_3_4, sp_4_4]
How to find a way to do the same with Python Sympy ?
I tried with :
from sympy import symbols, Matrix
def f(i, j):
if i == 0 and j == 0:
return 'sp_1_1'
elif i == 0 and j == 1:
return 'sp_1_2'
elif i == 1 and j == 0:
return 'sp_1_2'
...
Mat4x4_SYM = Matrix(4, 4, lambda i,j: f(i,j))
As you can see, I want to force symmetry for this matrix in the symbolic variable names.
Is there anyone who has already met the same kind of issue to define easily like in Matlab the prefix for symbolic variable names ?
and if yes, is there a quick way to perform this ?
Using a list comprehension and f-strings:
In [9]: Matrix([[Symbol(f'sp_{min(i,j)}{max(i,j)}') for i in range(1, 4+1)] for j in range(1, 4+1)])
Out[9]:
⎡sp₁₁ sp₁₂ sp₁₃ sp₁₄⎤
⎢ ⎥
⎢sp₁₂ sp₂₂ sp₂₃ sp₂₄⎥
⎢ ⎥
⎢sp₁₃ sp₂₃ sp₃₃ sp₃₄⎥
⎢ ⎥
⎣sp₁₄ sp₂₄ sp₃₄ sp₄₄⎦
I'm using SymPy as a code generator for functions that contain many exponentials. Hence, it is important for numerical stability that the arguments of the exponentials are not evaluated. I want to prevent this:
>>> import sympy as sp
>>> x, y = sp.symbols('x y')
>>> expr = sp.exp(5.*x - 10.)
>>> print(expr)
4.53999297624849e-5*exp(5.0*x)
As it can lead to numerically inaccurate results.
I can prevent the evaluation of the exponentials as follows:
>>> expr = sp.exp(5.*x - 10., evaluate=False)
>>> print(expr)
exp(5.0*x - 10.0)
However, when I perform operations like a substitution or differentiation on the expression, the exponential is evaluated again:
>>> expr = sp.exp(5.*x - 10., evaluate=False)
>>> expr.subs(x, y)
4.53999297624849e-5*exp(5.0*y)
>>> expr.diff(x, 1)
5.0*(4.53999297624849e-5*exp(5.0*x))
What is the correct way in SymPy to prevent the evaluation of the exponential under such operations?
The most obvious point is that you are using float for integer values e.g.:
In [8]: exp(5*x - 10)
Out[8]:
5⋅x - 10
ℯ
In [9]: exp(5.*x - 10.)
Out[9]:
5.0⋅x
4.53999297624849e-5⋅ℯ
Maybe in your real problem you want to work with non-integers. Again rationals should be used for exact calculations:
In [10]: exp(Rational(1, 3)*x - S(3)/2)
Out[10]:
x 3
─ - ─
3 2
ℯ
Perhaps your input numbers are not really rational and you have them as Python floats but you want to keep them from evaluating. You can use symbols and then only substitute for them when evaluating:
In [13]: exp(a*x + b).evalf(subs={a:5.0, b:10.})
Out[13]:
a⋅x + b
ℯ
In [14]: exp(a*x + b).evalf(subs={x:1, a:5.0, b:10.})
Out[14]: 3269017.37247211
In [15]: exp(a*x + b).subs({a:5.0, b:10.})
Out[15]:
5.0⋅x
22026.4657948067⋅ℯ
If all of these seems awkward and you really do just want to stuff floats in and prevent evaluation then you can use UnevaluatedExpr:
In [21]: e = exp(UnevaluatedExpr(5.0)*x - UnevaluatedExpr(10.))
In [22]: e
Out[22]:
x⋅5.0 - 10.0
ℯ
In [23]: e.doit() # doit triggers evaluation
Out[23]:
5.0⋅x
4.53999297624849e-5⋅ℯ
I am trying to solve a set of equations using the following python code (using SymPy of course):
def Solve(kp1, kp2):
a, b, d, e, f = S('a b d e f'.split())
equations = [
Eq(a+b, 2.6),
Eq(2*a + b + d + 2*f, 7),
Eq(d + e, 2),
Eq(a*e,kp2*b*d),
Eq( ((b * (f**0.5))/a)*((a+b+d+e+f+13.16)**-0.5), kp1)
]
return solve(equations)
The code solves the equations successfully but after approximately 35 seconds. The Solve() function is used inside an iterative block(about 2000 iterations) in another file so speed really matters to me.
Is there any way to speed up the solver? If not can you recommend another way to solve system of equations using python?
You only need to solve the equation one time. After that you will have equations of the form:
a = f_1(kp1, kp2)
b = f_2(kp1, kp2)
...
so you can simply compute a, ..., e in dependency of kp1 and kp2. For example solving the first, third and fourth equations gives:
b: -a + 2.6
e: 2.0*kp2*(5.0*a - 13.0)/(5.0*a*kp2 - 5.0*a - 13.0*kp2),
d: 10.0*a/(-5.0*a*kp2 + 5.0*a + 13.0*kp2)
Solving all five equations on my pc is too slow, but if it gives you an expression you just have to plug in (substitute) the values for kp1 and kp2, you don't have to solve the equations again. For substitution take a look at the sympy documentation.
So your loop should look like:
solutions = sympy.solve(eqs, exclude=[kp1, kp2])
for data_kp1, data_kp2 in data:
for key, eq in solutions:
solution = eq.subs([(kp1, data_kp1), (kp2, data_kp2)])
final_solutions.append("{key}={solution}".format(key=key, solution=solution))
Solve the 1st 4 linear equations for a,b,d,e. This generates two solutions. Substitute each of these into the 5th equation (in this form: Eq(b**2*f, d**2*kp1**2*(a + b + 2*d + f + 13.16))) to give two equations (e51 and e52). These two equations are nonlinear in f and, if you use unrad on them you will end up with 2 sixth-order polynomials in f -- which is likely why you get no solution since only quartics are exactly solvable in general. Call those two equations e51u, and e52u.
If you are interested in the real roots, you can use real_roots to give the roots to these polynomials after substituting in the desired values for kp1 and kp2 to leave only f as the unknown. For example,
>>> ans = solve(equations[:-1],a,b,d,e)
>>> l=equations[-1] # modified as suggested
>>> l=l.lhs-l.rhs
>>> l
b**2*f - d**2*kp1**2*(a + b + 2*d + f + 13.16)
>>> e51=l.subs(ans[0]); e51u=unrad(e51)[0]
>>> e52=l.subs(ans[1]); e52u=unrad(e52)[0]
>>> import random
>>> for r1,r2 in [[random.random() for i in range(2)] for j in range(3)]:
... print [i.n(2) for i in real_roots(e51u.subs(dict(kp1=r1,kp2=r2)))]
... print [i.n(2) for i in real_roots(e52u.subs(dict(kp1=r1,kp2=r2)))]
... print '^_r1,r2=',r1,r2
...
[1.7, 2.9, 3.0, 8.2]
[1.7, 2.9, 3.0, 8.2]
^_r1,r2= 0.937748743197 0.134640776315
[1.3, 2.3, 4.7, 7.4]
[1.3, 2.3, 4.7, 7.4]
^_r1,r2= 0.490002815309 0.324553144174
[1.1, 2.1]
[1.1, 2.1]
^_r1,r2= 0.308803300429 0.595356213169
It appears that e51u and e52u both give the same solutions, so perhaps you only need to use one of them. And you should check the answers in the original equations to see which one(s) are true solutions:
>>> r1,r2
(0.30880330042869408, 0.59535621316941589)
>>> [e51.subs(dict(kp1=r1,kp2=r2,f=i)).n(2) for i in real_roots(e51u.subs(dict(kp1=r1,kp2=r2)))]
[1.0e-12, 13.]
So here you see that only the first solution (seen to be 1.1 from above) is actually a solution; 2.1 was a spurious solution.
Your system of equations can be transformed to a polynomial system in terms of kp1 and kp2 as symbolic parameters. In this case it is advantageous to convert it to a polynomial system so that it can be partially solved in preparation for numerical solving for particular values of the parameters.
The technique her involves computing Groebner bases for which you will want to not have any floats in your equations so let's first make sure that we use exact rational numbers (e.g. use sqrt rather than **0.5 and Rational('2.6') rather than 2.6):
In [20]: kp1, kp2 = symbols('kp1, kp2')
In [21]: a, b, d, e, f = symbols('a, b, d, e, f')
In [22]: equations = [
...: Eq(a+b, Rational('2.6')),
...: Eq(2*a + b + d + 2*f, 7),
...: Eq(d + e, 2),
...: Eq(a*e,kp2*b*d),
...: Eq( ((b * sqrt(f))/a)/sqrt((a+b+d+e+f+Rational('13.16'))), kp1)
...: ]
In [23]: for eq in equations:
...: pprint(eq)
...:
a + b = 13/5
2⋅a + b + d + 2⋅f = 7
d + e = 2
a⋅e = b⋅d⋅kp₂
b⋅√f
─────────────────────────────── = kp₁
_________________________
╱ 329
a⋅ ╱ a + b + d + e + f + ───
╲╱ 25
The final equation can be trivially transformed to a polynomial: multiply out the denominator and square both sides e.g.:
In [24]: den = denom(equations[-1].lhs)
In [25]: neweq = Eq((equations[-1].lhs*den)**2, (equations[-1].rhs*den)**2)
In [26]: neweq
Out[26]:
2 2 2 ⎛ 329⎞
b ⋅f = a ⋅kp₁ ⋅⎜a + b + d + e + f + ───⎟
⎝ 25⎠
In [27]: equations[-1] = neweq
In [28]: for eq in equations:
...: pprint(eq)
...:
a + b = 13/5
2⋅a + b + d + 2⋅f = 7
d + e = 2
a⋅e = b⋅d⋅kp₂
2 2 2 ⎛ 329⎞
b ⋅f = a ⋅kp₁ ⋅⎜a + b + d + e + f + ───⎟
⎝ 25⎠
Now that we have a polynomial system we are ready to compute its Groebner basis. We will select a, b, d, e, f as the generators for the Groebner basis and leave kp1 and kp2 as part of the coefficient ring:
In [29]: basis = list(groebner(equations, [a, b, d, e, f]))
This now is a reduced system of equations for abdef with complicated looking coefficients that depend on kp1 and kp2. I'll show one of the equations as an example:
In [30]: basis[-1]
Out[30]:
⎛ 4 2 2 2 2 ⎞ ⎛
4 ⎛ 4 2 2 2 2 ⎞ 3 ⎜778⋅kp₁ ⋅kp₂ 399⋅kp₁ ⋅kp₂ 384⋅kp₁ 1⎟ 2 ⎜102481⋅
f ⋅⎝kp₁ ⋅kp₂ - kp₁ ⋅kp₂ - kp₁ + 1⎠ + f ⋅⎜───────────── - ───────────── - ──────── + ─⎟ + f ⋅⎜───────
⎝ 25 25 25 5⎠ ⎝ 6
4 2 2 2 2 2 ⎞ ⎛ 4 2 2 2
kp₁ ⋅kp₂ 15579⋅kp₁ ⋅kp₂ 13⋅kp₁ ⋅kp₂ 5148⋅kp₁ 1 ⎟ ⎜ 3799752⋅kp₁ ⋅kp₂ 8991⋅kp₁ ⋅kp₂
───────── + ─────────────── - ─────────── + ───────── + ───⎟ + f⋅⎜- ───────────────── - ──────────────
25 500 5 125 100⎠ ⎝ 3125 625
2 2⎞ 4 2
5772⋅kp₁ ⋅kp₂ 15984⋅kp₁ ⎟ 23853456⋅kp₁ ⋅kp₂
- ───────────── - ──────────⎟ + ──────────────────
125 625 ⎠ 15625
Although these equations might look complicated they have a particular structure which means that once you have substituted values for kp1 and kp2 the final equation depends only on f and all other equations give symbols linearly in terms of f. This means that you can substitute your values and then use either sympy's real_roots or numpys np.roots to get either exact or approximate roots for f, substitute those into your other equations and solve a simple system of linear equations for the remaining unknowns. These are the only steps that would need to be done in the final loop and we can use lambdify to make them happen quickly. The final result then is
from sympy import *
# First solve the system as much as possible in terms of symbolic kp1, kp2
kp1, kp2 = symbols('kp1, kp2')
a, b, d, e, f = symbols('a, b, d, e, f')
# Don't use any floats (yet)
equations = [
Eq(a+b, Rational('2.6')),
Eq(2*a + b + d + 2*f, 7),
Eq(d + e, 2),
Eq(a*e,kp2*b*d),
Eq( ((b * sqrt(f))/a)/sqrt((a+b+d+e+f+Rational('13.16'))), kp1)
]
# Convert to full polynomial system:
den = denom(equations[-1].lhs)
neweq = Eq((equations[-1].lhs*den)**2, (equations[-1].rhs*den)**2)
equations2 = equations[:-1] + [neweq]
# Compute the Groebner basis and split the linear equations for a, b, d, e
# from the polynomial equation for f
basis = list(groebner(equations2, [a, b, d, e, f]))
lineqs, eq_f = basis[:-1], basis[-1]
# Solve for a, b, d, e in terms of f, kp1 and kp2
[sol_abde] = linsolve(lineqs, [a, b, d, e])
# Use lambdify to be able to efficiently evaluate the solutions for a, b, d, e
sol_abde_lam = lambdify((f, kp1, kp2), sol_abde)
# Use lambdify to efficiently substitute kp1 and kp2 into the coefficients for eq_f
eq_f_lam = lambdify((kp1, kp2), Poly(eq_f, f).all_coeffs())
def solve_k(kp1val, kp2val):
eq_f_coeffs = eq_f_lam(kp1val, kp2val)
# Note that sympy's real_roots function is more accurate and does not need
# a threshold. It is however slower than np.roots:
#
# p_f = Poly(eq_f_coeffs, f)
# f_vals = real_roots(p_f) # exact (RootOf)
# f_vals = [r.n() for r in real_roots(p_f)] # approximate
#
f_vals = np.roots(eq_f_coeffs)
f_vals = f_vals.real[abs(f_vals.imag) < 1e-10] # arbitrary threshold
sols = []
for f_i in f_vals:
abde_vals = sol_abde_lam(f_i, kp1val, kp2val)
sol = {f: f_i}
sol.update(zip([a,b,d,e], abde_vals))
sols.append(sol)
return sols
Now you can generate the solutions for particular values of kp1 and kp2 in around 1 millisecond:
In [40]: %time solve_k(2.1, 3.8)
CPU times: user 1.59 ms, sys: 0 ns, total: 1.59 ms
Wall time: 1.51 ms
Out[40]:
[{a: 49.77432869943, b: -47.174328699430006, d: -0.7687860254920669, e: 2.768786025492067, f: -22.30277
1336968966}, {a: 3.4616794447024692, b: -0.8616794447024684, d: 36.964491584342106, e: -34.964491584342
11, f: -18.01308551452229}, {a: -0.5231222050642267, b: 3.1231222050642273, d: -0.0922228459726158, e:
2.092222845972616, f: 2.507672525518422}, {a: 0.34146680496125464, b: 2.258533195038746, d: 0.076528664
56901455, e: 1.9234713354309858, f: 1.9910022652348665}]
Caveats: the method above will work in most cases for a polynomial system like this although there are cases where the Groebner basis won't fully separate without the additional use of a splitting variable. Basically just introduce a new variable say t and a random equation like t = a + 2*b + 3*d + 4*e + 5*f. Then make t the last symbol for the Groebner basis and compute t instead of f with np.roots. Also in some cases computing the Groebner basis can be extremely slow but remember that it only ever needs to be computed once (you could save the result in a file if needed).
Another caveat is that squaring to transform into a polynomial system will introduce spurious solutions that don't satisfy the original radical equations. You can check by substituting into the original system. In this case only the last equation matters:
In [41]: sols = solve_k(2.1, 3.8)
In [42]: equations[-1].subs(sols[0])
Out[42]: -2.10000000000001 = kp₁
In [43]: equations[-1].subs(sols[1])
Out[43]: -2.10000000000006 = kp₁
In [44]: equations[-1].subs(sols[2])
Out[44]: -2.09999999999995 = kp₁
In [45]: equations[-1].subs(sols[3])
Out[45]: 2.09999999999993 = kp₁
So we see that of the 4 solutions returned only the last is correct because it (approximately) satisfies kp1 = 2.1 which was the value passed in. This means a bit of post-processing is needed to get the solutions that you actually want.
Sympy works with complex numbers, therefore there is possibility to solve equations like sin(z)=2. However, I cannot work it out. Anyone has an idea how to solve it in Sympy?
BTW, the solution has form like following:
z=\frac{\pi}{2}+\ln(2\pm\sqrt{3})i
I will add a very specialized method to solve this problem in Sympy, which can hardly be genralized:
from sympy import *
z=symbols('z')
r=re(sin(z)-2)
i=im(sin(z))
x,y=symbols('x,y',real=True)
eq1=r.subs({re(z):x,im(z):y})
eq2=i.subs({re(z):x,im(z):y})
solve((eq1,eq2),(x,y))
The output is [(pi/2, log(-sqrt(3) + 2)), (pi/2, log(sqrt(3) + 2))]. Anyone has a better solution?
If you prefer the log format, use .rewrite(log), like
In [4]: asin(2).rewrite(log)
Out[4]:
⎛ ___ ⎞
-ⅈ⋅log⎝╲╱ 3 ⋅ⅈ + 2⋅ⅈ⎠
Combining this with Games's answer, you can get:
In [3]: sols = solve(sin(z) - 2, z)
In [4]: sols
Out[4]: [π - asin(2), asin(2)]
In [5]: [i.rewrite(log) for i in sols]
Out[5]:
⎡ ⎛ ___ ⎞ ⎛ ___ ⎞⎤
⎣π + ⅈ⋅log⎝╲╱ 3 ⋅ⅈ + 2⋅ⅈ⎠, -ⅈ⋅log⎝╲╱ 3 ⋅ⅈ + 2⋅ⅈ⎠⎦
And by the way, there are really infinitely many solutions, because sin is 2*pi periodic. SymPy currently doesn't support giving all of them directly, but it's easy enough to get them by using sin(z + 2*pi*n) instead of sin(z):
In [8]: n = Symbol('n', integer=True)
In [9]: sols = solve(sin(z + 2*pi*n) - 2, z)
In [10]: sols
Out[10]: [-2⋅π⋅n + asin(2), -2⋅π⋅n + π - asin(2)]
In [11]: [i.rewrite(log) for i in sols]
Out[11]:
⎡ ⎛ ___ ⎞ ⎛ ___ ⎞⎤
⎣-2⋅π⋅n - ⅈ⋅log⎝╲╱ 3 ⋅ⅈ + 2⋅ⅈ⎠, -2⋅π⋅n + π + ⅈ⋅log⎝╲╱ 3 ⋅ⅈ + 2⋅ⅈ⎠⎦
Here n is any integer.
Well you need to set it like this
sin(z) - 2 = 0
So like this:
>>> from sympy.solvers import solve
>>> from sympy import *
>>> z = Symbol('z')
>>> solve(sin(z) - 2, z)
[pi - asin(2), asin(2)]
>>> asin(2).evalf()
1.5707963267949 - 1.31695789692482*I