sympy replace subexpression with symbol - python

Here's an example of my problem. I define all the variables, except x which is automatic in the notebook session. Then I try to substitute an expression of previously defined variable omega_P.
In [2]: omega, omega_P, omega_C = symbols('omega omega_P omega_C')
R = 1 - omega_P**2/(omega*(omega+omega_C))
pprint(R)
2
ω_P
1 - ───────────
ω⋅(ω + ω_C)
In [3]: x = omega_P**2/omega**2
pprint(x)
2
ω_P
────
2
ω
In [4]: pprint(R.subs(omega_P**2, x*omega**2))
2
ω_P
1 - ───────────
ω⋅(ω + ω_C)
omega_P was not changed by subs().

Related

How to substitute partial powers?

I have an expression which contains terms like expr = a**m * b**n where a,b are symbols and m,n are integers. I want to use expr.subs(a**i * b**j, 0) to set the expression to zero if a**i * b**j is a factor of a**m * b**n (here i,j are integers too). This works in some cases, but not when only one of the symbols is used for the substitution:
>>> from sympy import symbols
>>> a, b = symbols('a b')
>>> (a**2 * b**3).subs(a**2 * b**2, 0) # (1) works
0
>>> (a**2 * b**3).subs(a * b**2, 0) # (2) works
0
>>> (a**2 * b**3).subs(b**2, 0) # (3) does not work
a**2*b**3
>>> (a**3).subs(a**2, 0) # (4) does not work
a**3
>>> (a**4).subs(a**2, 0) # (5) works
0
I would like this substitution to work in all of the above cases and logically it should (since for example a**2 * b**3 is equivalent to a**2 * b * (b**2) and thus the substitution (b**2, 0) should yield 0). However, if using a single symbol in the substitution, it seems to work only for those cases where the expression contains the subs-term raised to a power; for example:
>>> (a**2 * b**4).subs(b**2, 0) # (6) works
0
>>> (a**2 * b**4).subs(b**3, 0) # (7) does not work
a**2*b**4
This behavior seems a little odd since it works for b**1 and also when artificially augmenting both, the actual expression and the subs-term, with some other symbol:
>>> (a**2 * b**4).subs(b, 0) # (8) works
0
>>> c = symbols('c')
>>> (c * (a**2 * b**4)).subs(c * (b**3), 0) # (9) works
0
While I could use this last observation (augmenting expressions) as a workaround, it doesn't seem neither clear nor efficient.
Hence, is there another way to make the substitution work for all cases where the subs-term is contained in the actual expression?
>>> sympy.__version__
'1.10.1'
To check if something is a factor of a product you can use extract_multiplicatively:
>>> expr = a**3*b**3*c
>>> 0 if expr.extract_multiplicatively(a*b**2) else expr
0
You could also use the rem function to see if the remainder is 0:
>>> rem(expr, a*b**2)
0
If your expressions occur within a larger expression, then these could be incorporated into a replace call, expr.replace(...). If you are trying to ignore high-order terms in an expression you can just make a function to add the order of all symbol-based powers in a term:
>>> expr = expand((x + 1)**3*(y + 1)**2)
>>> def order(m):
... return sum([degree(m, x) for x in m.free_symbols])
...
>>> expr.replace(lambda x: not x.is_Add,
... lambda x: 0 if order(x)>2 else x)
3*x**2 + 6*x*y + 3*x + y**2 + 2*y + 1
works, because a2 = 0 is 0 * b3 what leads to 0
a = 0 means a**2 = 0 -> 0
b2 = 0 but b2 is not in b**3, so it does not work
same issue as 3
a4 is in a2 so 0
same as 5 but with b´s
same issue as 3
b is in b**4
c = c so it comes to 0 because only symbols get replaced
Everything works like intended

Updating variables with sympy

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

Simplification of expression for factorial by SymPy

Can you please tell me if there is a command in SymPy to simplify the factorial? For example, Maxima has such a function, but I can't find it in SymPy.
n!/(n+1)! = 1/(n+1)
There is a function gammasimp for this:
https://docs.sympy.org/latest/tutorial/simplification.html#gammasimp
This is used internaly by simplify so you can also use that (using gammsimp directly is faster):
In [1]: simplify(factorial(n)/factorial(n + 1))
Out[1]:
1
─────
n + 1
In [2]: gammasimp(factorial(n)/factorial(n + 1))
Out[2]:
1
─────
n + 1

Solving KKT equations in SymPy

I am trying to solve KKT equations using sympy. All of the equations are symbolic and contain constants that are not given as numbers but as symbols. Alongside with the equations, there are also inequality constraints.
Is it possible to do this in sympy? If not, are there any alternatives?
An example would be:
Doing your question by hand, we get the first Lagrangian to be L = x**2 - bx + 1 - lambda(x - a).
Differentiating with respect to x and lambda and setting to 0, we solve to get x = a, lambda = 2a - b.
So if lambda < 0, we repeat with the new Lagrangian, L = x**2 - bx + 1.
This gives us 2 cases: If 2a - b < 0, then x = b/2, otherwise, x = a.
The following code reproduces this logic. Copy paste this into a file called lagrangian.py and see example 4 for your specific query.
"""
KKT Conditions:
Goal:
To minimise/maximise f(x_) subject to gi(x_) >= 0 for all i and hi(x_) == 0 for all i
where x_ refers to a vector x.
Variables with a `*` after them are optimal quantities.
1. gi(x*) and hi(x*) is feasible (that is, they are satisfied)
2. (df/dxj)(x*) - sum(lambdai* * (dgi/dxj)(x*)) - sum(mui* * (dhi/dxj)(x*)) = 0 for all j
3. lambdai* * gi(x*) = 0 for all i
4. lambdai >= 0 for all i
"""
from sympy import *
from typing import Iterable
def kkt(f, g=None, h=None, x=None):
"""
Finds the optimal values of `x` for `f` given the equalities `g[i] >= 0` for all i
and `h[i] == 0` for all i.
TODO: Remove the private variables _lambda and _mu from the output.
TODO: Make the output more user friendly.
Examples:
>>> from sympy import *
>>> from lagrangian import kkt
>>> x = list(symbols("x:3"))
Example 0 the most basic
>>> kkt(x[0]**2)
([x0], FiniteSet((0,)))
Example 1 from References
>>> kkt(2 * x[0] ** 2 + x[1] ** 2 + 4 * x[2]**2,
... h=[x[0] + 2*x[1] - x[2] - 6, 2 * x[0] - 2 * x[1] + 3 * x[2] - 12])
([_mu0, _mu1, x0, x1, x2], FiniteSet((504/67, 424/67, 338/67, 80/67, 96/67)))
Example 2 from References
>>> kkt(x[0] ** 2 + 2 * x[1] ** 2 + 3 * x[2] ** 2,
... [5 * x[0] - x[1] - 3 * x[2] - 3,
... 2 * x[0] + x[1] + 2 * x[2] - 6])
([_lambda0, x0, x1, x2], FiniteSet((72/35, 72/35, 18/35, 24/35)))
Example 3 from References
>>> kkt(4 * x[0] ** 2 + 2 * x[1] ** 2,
... [-2 * x[0] - 4 * x[1] + 15],
... [3 * x[0] + x[1] - 8])
([_mu0, x0, x1], FiniteSet((64/11, 24/11, 16/11)))
Example 4 for general KKT
>>> t, a, b = symbols("x a b")
>>> kkt(t ** 2 - b * t + 1, t - a, x=t)
Piecewise((([x], FiniteSet((b/2,))), 2*a - b < 0), (([_lambda0, x], FiniteSet((2*a - b, a))), True))
Warnings:
This function uses recursion and if queries are such as example 4 with many inequality
conditions, one will experience heavy performance issues.
References:
.. [1] http://apmonitor.com/me575/index.php/Main/KuhnTucker
Disadvantages:
- Does not allow for arbitrary number of inequalities and equalities.
- Does not work for functions of f that have an infinite
number of turning points and no equality constraints (I think)
"""
# begin sanity checks
if not g:
g = []
if not h:
h = []
if not x:
x = list(f.free_symbols)
if not isinstance(g, Iterable):
g = [g]
if not isinstance(h, Iterable):
h = [h]
if not isinstance(x, Iterable):
x = [x]
# end sanity checks
def grad(func):
"""Returns the grad of an expression or function `func`"""
grad_f = Matrix(len(x), 1, derive_by_array(func, x))
return grad_f
# define our dummy variables for the inequalities
if g:
_lambdas = list(symbols(f"_lambda:{len(g)}", real=True))
sum_g = sum([_lambdas[i] * g[i] for i in range(len(g))])
else:
_lambdas = []
sum_g = 0
# define our dummy variables for the equalities
if h:
_mus = list(symbols(f"_mu:{len(h)}", real=True))
sum_h = sum([_mus[i] * h[i] for i in range(len(h))])
else:
_mus = []
sum_h = 0
# define the lagrangian
lagrangian = f - sum_g - sum_h
# find grad of lagrangian
grad_l = grad(lagrangian)
# 1. feasibility conditions
feasible = Matrix(len(g) + len(h), 1, g + h)
# 2. combine everything into a vector equation
eq = grad_l.row_insert(0, feasible)
solution = solve(eq, x + _lambdas + _mus, set=True)
# 4. remove all non-binding inequality constraints
# for each _lambda solution, add a new solution with that inequality removed
# in the case that it is false.
pieces = []
for i, lamb in enumerate(_lambdas):
new_g = g[:i] + g[i + 1:]
lamb_sol_index = [i for i, s in enumerate(solution[0]) if s == lamb][0]
for solution_piece in solution[1]:
lamb_sol = solution_piece[lamb_sol_index]
try:
if lamb_sol >= 0: # we dont need to check the next kkt if the value is known.
continue
except TypeError: # error when inequality cannot be identified
pass
pieces.append((kkt(f, new_g, h, x), lamb_sol < 0))
pieces.append((solution, True))
return Piecewise(*pieces)

How to multiply arrays in pandas?

I have two arrays
x = [a,b,c]
y = [5,6,7]
I want to calculate the product such that the result of x * y is
x[0]* 5 + x[1] * 6 + x[2] * 7
Actually this is part of constraints equation that I have to form for optimization using scipy and pandas.
Also,
I have many numpy arrays that I created after reading a csv file. And I want to create my objective function on run time.
Here is the hard coded form of the objective function
def objFunc(x,sign=1.0) :
"""return sign*(sum(coeff[0:] *(wf[0:] + wv[0:] * decisionVars[0:])**power.values[0:]))"""
return sign* (( coeff.values[0]*(wf.values[0]+ wv.values[0] *x[0])**power.values[0] ) +
(coeff.values[1]*(wf.values[1]+ wv.values[1] *x[0])**power.values[1])+
(coeff.values[2]*(wf.values[2]+ wv.values[2] *x[0])**power.values[2]) +
(coeff.values[3]*(wf.values[3]+ wv.values[3] *x[0])**power.values[3]) +
(coeff.values[4]*(wf.values[4]+ wv.values[4] *x[0])**power.values[4] )+
(coeff.values[5]*(wf.values[5]+ wv.values[5] *x[0])**power.values[5]) +
(coeff.values[6]*(wf.values[6]+ wv.values[6] *x[1])**power.values[6]) +
(coeff.values[7]*(wf.values[7]+ wv.values[7] *x[1])**power.values[7]) +
(coeff.values[8]*(wf.values[8]+ wv.values[8] *x[1])**power.values[8]) +
(coeff.values[9]*(wf.values[9]+ wv.values[9] *x[2])**power.values[9]) +
(coeff.values[10]*(wf.values[10]+ wv.values[10] *x[2])**power.values[10]) +
(coeff.values[11]*(wf.values[11]+ wv.values[11] *x[2])**power.values[11]))
I used various ways to calculate it but to no avail.
df = pd.DataFrame.from_csv('C:\Users\prashant.mudgal\Downloads\T1 - Copy.csv')
df2 = pd.DataFrame.from_csv('C:\Users\prashant.mudgal\Downloads\T2.csv')
decisionVars= df2['DV']
coeff = df2['coef']
"""subset for power"""
power = df2['p']
wf = df2['weight_f']
wv = df2['weight_v']
def objFunc(x,sign=1.0) :
return sign*(sum(coeff[0:] *(wf[0:] + wv[0:] * decisionVars[0:])**power.values[0:]))
This works out of the box:
In [9]: df = pd.DataFrame([[1,5],[2,6],[3,7]], columns=list('ab'))
In [10]: df
Out[10]:
a b
0 1 5
1 2 6
2 3 7
In [11]: df.a * df.b
Out[11]:
0 5
1 12
2 21
dtype: int64

Categories

Resources