Non-sequential substitution in SymPy - python

I'm trying to use [SymPy][1] to substitute multiple terms in an expression at the same time. I tried the [subs function][2] with a dictionary as parameter, but found out that it substitutes sequentially.
In : a.subs({a:b, b:c})
Out: c
The problem is the first substitution resulted in a term that can be substituted by the second substitution, but it should not (for my cause).
Any idea on how to perform the substitutions simultaneously, without them interfering with each other?
Edit:
This is a real example
In [1]: I_x, I_y, I_z = Symbol("I_x"), Symbol("I_y"), Symbol("I_z")
In [2]: S_x, S_y, S_z = Symbol("S_x"), Symbol("S_y"), Symbol("S_z")
In [3]: J_is = Symbol("J_IS")
In [4]: t = Symbol("t")
In [5]: substitutions = (
(2 * I_x * S_z, 2 * I_x * S_z * cos(2 * pi * J_is * t) + I_y * sin(2 * pi * J_is * t)),
(I_x, I_x * cos(2 * pi * J_is * t) + 2 * I_x * S_z * sin(2 * pi * J_is * t)),
(I_y, I_y * cos(2 * pi * J_is * t) - 2 * I_x * S_z * sin(2 * pi * J_is * t))
)
In [6]: (2 * I_x * S_z).subs(substitutions)
Out[7]: (I_y*cos(2*pi*J_IS*t) - 2*I_x*S_z*sin(2*pi*J_IS*t))*sin(2*pi*J_IS*t) + 2*S_z*(I_x*cos(2*pi*J_IS*t) + 2*I_x*S_z*sin(2*pi*J_IS*t))*cos(2*pi*J_IS*t)
Only the appropriate substitution should happen, in this case only the first one. So the expected output should be the following:
In [6]: (2 * I_x * S_z).subs(substitutions)
Out[7]: I_y*sin(2*pi*J_IS*t) + 2*I_x*S_z*cos(2*pi*J_IS*t)

The current version of sympy provides the keyword simultaneous. The complicated operations in the previous answers are no more necessary:
In [1]: (x*sin(y)).subs([(x,y),(y,x)],simultaneous=True)
Out[1]: y⋅sin(x)

The subs(self,*args) method is defined (in part) this way:
In [11]: x.subs??
...
sequence = args[0]
if isinstance(sequence, dict):
return self._subs_dict(sequence)
elif isinstance(sequence, (list, tuple)):
return self._subs_list(sequence)
If you pass subs a dict, you lose control over the order of the substitutions.
While if you pass subs a list or tuple, you can control the order.
This doesn't allow you to do simultaneous substitutions. That would lead to difficulties if the user were to pass stuff like x.subs([(x,y),(y,x)]). So I doubt sympy has a method for doing simultaneous substitutions. Instead I believe all substutions are either unordered (if you pass a dict) or, at best, done by a 1-pass ordered substitution (if you pass a list or tuple):
In [17]: x.subs([(x,y),(y,z)])
Out[18]: z
In [19]: x.subs([(y,z),(x,y)])
Out[19]: y
PS. _subs_list(self, sequence) is defined (in part) like this:
In [14]: x._subs_list??
...
for old, new in sequence:
result = result.subs(old, new)
This nails down the order in which the subs are done.

Example for #~unutbu's answer:
>>> import ordereddict # collections.OrderedDict in Python 2.7+
>>> from sympy import *
>>> x,y,z = symbols('xyz')
>>> x.subs(ordereddict.OrderedDict([(x,y),(y,z)]))
y
>>> x.subs(ordereddict.OrderedDict([(y,z),(x,y)]))
z

Answering the edited question.
In your example you can use some temporary variables which will not be over-written be subsequent substitutions. Then, once all of the potentially overlapping substitutions have been made, you can replace the temporary variables with the real ones.
This example works for the question, if your full problem contains more complex substitutions, I think you should still be able to create temporary variables to avoid overlapping substitutions.
from sympy import Symbol, sin, cos, pi
I_x, I_y, I_z = Symbol("I_x"), Symbol("I_y"), Symbol("I_z")
S_x, S_y, S_z = Symbol("S_x"), Symbol("S_y"), Symbol("S_z")
J_is = Symbol("J_IS")
t = Symbol("t")
I_x_temp, I_y_temp, I_z_temp = Symbol("I_x_temp"), Symbol("I_y_temp"), Symbol("I_z_temp")
f = 2*I_x*S_z
answer = I_y*sin(2*pi*J_is*t) + 2*I_x*S_z*cos(2*pi*J_is*t)
subs1a = [
(2*I_x*S_z, 2*I_x_temp*S_z*cos(2*pi*J_is*t) + I_y_temp*sin(2*pi*J_is*t)),
(I_x, I_x_temp*cos(2* pi*J_is*t) + 2*I_x_temp*S_z*sin(2*pi*J_is*t)),
(I_y, I_y_temp*cos(2*pi*J_is*t) - 2*I_x_temp*S_z* sin(2*pi*J_is*t))
]
subs_temp = [(I_x_temp, I_x), (I_y_temp, I_y), (I_z_temp, I_z)]
print f
f = f.subs(subs1a)
print f
f = f.subs(subs_temp)
print f
print f == answer # True
Note, you can also perform two substitutions back to back:
f.subs(subs1a).subs(subs_temp) == answer

The Keyword simultaneous will do non-clashing subs regardless of the input (dict or sequence):
>>> x.subs([(x,y),(y,z)],simultaneous=1)
y
>>> x.subs([(y,z),(x,y)],simultaneous=1)
y

Related

Replacing Sympy indexed symbols with numeric values

I have a sympy expression I want to put numerical values in after differentiating it. The variables I want to replace are all the x[i], y[i] and R_abs[i] in the last expression and are numpy arrays a la
rx=np.array([-0.357, -0.742, -1.078, 0.206])
But trying subs or replace either doesn't do anything or raises the error that Symbols dont allow indexation for for example e1.subs(x[1],rx[0]). I pretty much went through every iteration I could think of to no avail.
import sympy as sp
r0,ge_x,ge_y,bx,by = sp.symbols('r0,ge_x,ge_y,bx,by', real=True) #Main symbols
i,x,y,R_abs = sp.symbols('i,x,y,R_abs', real=True) #Helper symbols
n=4
s2=sp.Sum((bx+r0*sp.Indexed('x',i)/sp.Indexed('R_abs',i)+ge_x*sp.Indexed('x',i)+ge_y*sp.Indexed('y',i)-sp.Indexed('x',i))**2+(by+r0*sp.Indexed('y',i)/sp.Indexed('R_abs',i)-ge_x*sp.Indexed('y',i)+ge_y*sp.Indexed('x',i)-sp.Indexed('y',i))**2,(i,1,n))
e1=sp.Eq(sp.diff(s2,bx).doit(),0)
With e1 then being
Eq(8*bx + 2*ge_x*x[1] + 2*ge_x*x[2] + 2*ge_x*x[3] + 2*ge_x*x[4] + 2*ge_y*y[1] + 2*ge_y*y[2] + 2*ge_y*y[3] + 2*ge_y*y[4] + 2*r0*x[4]/R_abs[4] + 2*r0*x[3]/R_abs[3] + 2*r0*x[2]/R_abs[2] + 2*r0*x[1]/R_abs[1] - 2*x[1] - 2*x[2] - 2*x[3] - 2*x[4], 0)
In here I would like to replace all the x, y, and R_abs with their numerical values.
I've always struggled with indexing in SymPy. Turns out, making Function instances are way easier than indexing instances of Symbol. It also makes notation simpler.
Also note that by using strings in your expression, I think SymPy makes its own symbols with those same string names but they can't be accessed with yours since your symbols are different. At least that's what happens sometimes to me.
Here is a working sample:
import sympy as sp
r0, ge_x, ge_y, bx, by = sp.symbols("r0 ge_x ge_y bx by", real=True) # main symbols
# define functions that will take the role of indexed symbols
x = sp.Function("x")
y = sp.Function("y")
R_abs = sp.Function("R_abs")
i = sp.Symbol("i", positive=True, integer=True)
n = 4
s2 = sp.Sum((bx + r0 * x(i) / R_abs(i) + ge_x * x(i) + ge_y * y(i) - x(i)) ** 2 +
(by + r0 * y(i) / R_abs(i) - ge_x * y(i) + ge_y * x(i) - y(i)) ** 2, (i, 1, n))
s2_prime = sp.diff(s2, bx).doit().simplify()
print(s2_prime)
# whatever lists you want. Can even be an instance of `np.ndarray`
# note that you summed from 1 to n so the 0th element will not be used
x_array = [0, 1, 2, 3, 4]
y_array = [4, 3, 2, 1, 0]
R_abs_array = [-10, 10, 5, 4, 3]
# define a function to access these array elements
x_function = lambda index: x_array[index]
y_function = lambda index: y_array[index]
R_abs_function = lambda index: R_abs_array[index]
# no idea why subs does not work and you MUST keep the same name for the variable.
# you can't have for example `evaluated_s2_prime = ...`.
# Probably something to do with forcing sp to remove references to `x`?
s2_prime = s2_prime.replace(x, x_function).replace(y, y_function).replace(R_abs, R_abs_function)
print(s2_prime)
Producing:
8*bx + 2*ge_x*x(1) + 2*ge_x*x(2) + 2*ge_x*x(3) + 2*ge_x*x(4) + 2*ge_y*y(1) + 2*ge_y*y(2) + 2*ge_y*y(3) + 2*ge_y*y(4) + 2*r0*x(4)/R_abs(4) + 2*r0*x(3)/R_abs(3) + 2*r0*x(2)/R_abs(2) + 2*r0*x(1)/R_abs(1) - 2*x(1) - 2*x(2) - 2*x(3) - 2*x(4)
8*bx + 20*ge_x + 12*ge_y + 31*r0/6 - 20

SymPy cannot evaluate dot product of *metamorphosed* vectors

I am working with SymPy vectors:
from sympy import *
from sympy.vector import *
N = CoordSys3D('N')
x = symbols('x')
v = x * N.i + x**2 * N.j
vf=factor(v)
vf1=vf.as_independent(Vector)[1]
type(vf1)
# sympy.core.add.Add
I need to calculate dot(vf1,vf1). But SymPy does not evaluate the dot product:
ss = dot(vf1,vf1)
ss
# 1 + 2*Dot(N.i, N.j*x) + Dot(N.j*x, N.j*x)
I suspect, this is because vf1 has been metamorphosed into another type, i.e. sympy.core.add.Add).
Is there a way to make SymPy evaluate ss? Is there a way to cast vf1 as a sympy.vector...?
EDIT
I have written a function that does the dot product. But I need to do this the SymPy way, so I don't have to re-implement my own version of every function in sympy.vector.
Yes, the as_independent does not respect the class of Add or Mul that it is dealing with and only uses Mul/Add (instead of VectorMul/VectorAdd in your case). This can be fixed with a transform:
>>> from sympy.core.rules import Transform
>>> T = Transform(lambda x: (VectorMul if x.is_Mul else VectorAdd)(*x.args),
... lambda x: x.is_Add or x.is_Mul and any(isinstance(i,BaseVector)
... for i in x.args))
>>> vf1.xreplace(T)
N.i + x*N.j
>>> dot(_,_)
x**2 + 1

Can I make this iteration easier by using a loop?

I'm relatively new to python but I have used loops in previous programs I have made but they were relatively simple.
I was wondering if there was a method that involved a loop or some other way to make this iteration more concise.
Basically is there a way for me to have a variable called 't' that holds values for t1,t2 and t3 in like a list which is then used in 'eq' and 'der' instead of copying and pasting the same piece of code?
I have already tried to look for an answer on here and other places but the solutions I have found so far don't seem to work with what I have got/I'm not python literate enough to understand them.
import numpy as np
U235_Decay_Constant = 9.72e-10
U238_Decay_Constant = 1.54e-10
t0 = 4.1e9
eq = ((np.exp(U238_Decay_Constant*t0)-1)-(0.0167*
((np.exp(U235_Decay_Constant*t0)-1)))-0.0094)
der = (U238_Decay_Constant*(np.exp(U238_Decay_Constant*t0)))-(0.0167*
(U235_Decay_Constant*(np.exp(U235_Decay_Constant*t0))))
t1 = t0 - (eq/der)
eq = ((np.exp(U238_Decay_Constant*t1)-1)-(0.0167*
((np.exp(U235_Decay_Constant*t1)-1)))-0.0094)
der = (U238_Decay_Constant*(np.exp(U238_Decay_Constant*t1)))-(0.0167*
(U235_Decay_Constant*(np.exp(U235_Decay_Constant*t1))))
t2 = t1 - (eq/der)
eq = ((np.exp(U238_Decay_Constant*t2)-1)-(0.0167*
((np.exp(U235_Decay_Constant*t2)-1)))-0.0094)
der = (U238_Decay_Constant*(np.exp(U238_Decay_Constant*t2)))-(0.0167*
(U235_Decay_Constant*(np.exp(U235_Decay_Constant*t2))))
t3 = t2 - (eq/der)
print(t3)
Might be a little easier to read as:
import numpy as np
U235_Decay_Constant = 9.72e-10
U238_Decay_Constant = 1.54e-10
t = [4.1e9, None, None, None]
t[0] = 4.1e9
for i in range(3):
eq = ((np.exp(U238_Decay_Constant*t[i])-1)-(0.0167*
((np.exp(U235_Decay_Constant*t[i])-1)))-0.0094)
der = (U238_Decay_Constant*(np.exp(U238_Decay_Constant*t[0])))-(0.0167*
(U235_Decay_Constant*(np.exp(U235_Decay_Constant*t[0]))))
t[i+1] = t[i] - (eq/der)
print(t[3])
Yes, iteration can help here. Add your values to a list, then t? is the last value in the list so far; replacing your t? references with t[-1] gives:
t = [4.1e9]
for _ in range(3):
eq = (
(np.exp(U238_Decay_Constant * t[-1]) - 1)
- (0.0167 * ((np.exp(U235_Decay_Constant * t[-1]) - 1)))
- 0.0094
)
der = (U238_Decay_Constant * (np.exp(U238_Decay_Constant * t[-1]))) - (
0.0167 * (U235_Decay_Constant * (np.exp(U235_Decay_Constant * t[-1])))
)
t.append(t[-1] - (eq / der))
The general principle is one of accumulation, where you produce the running output of a repeated application of a function. So the itertools.accumulate() function could help here too:
from itertools import accumulate, chain, repeat
def u238_decay(t, _):
eq = (
(np.exp(U238_Decay_Constant * t) - 1)
- (0.0167 * ((np.exp(U235_Decay_Constant * t) - 1)))
- 0.0094
)
der = (U238_Decay_Constant * (np.exp(U238_Decay_Constant * t))) - (
0.0167 * (U235_Decay_Constant * (np.exp(U235_Decay_Constant * t)))
)
return t - (eq / der)
series = accumulate(chain([4.1e9], repeat(None)), u238_decay)
The above produces an unending series of decay values:
>>> series = accumulate(chain([4.1e9], repeat(None)), u238_decay)
>>> next(series)
4100000000.0
>>> next(series)
4081406102.7439713
>>> next(series)
4081163259.5641546
>>> next(series)
4081163218.6509323
>>> next(series)
4081163218.650931
You could look into creating a numpy universal function so you can do the same with the numpy.ufunc.accumulate() method.
However, I suspect that your formula can be re-cast to not depend on the previous input, but only as a formula of starting amount and t as time, at which point you can use full numpy vectorised calculations.

Manipulate return values in python

I am trying to convert decimal degrees to degrees, minutes and seconds using the following code:
def dd2dms(deg):
d = int(deg)
md = abs(deg - d) * 60
m = int(md)
sd = (md - m) * 60
return [d, m, sd]
full = 500/60
print(full)
print(dd2dms(full))
But I get the following output:
8.333333333333334 [8, 20, 0]
I would like to get the output as:
8.333333333333334 8:20:0
How can I achieve this? I am taking baby steps in learning Python. :)
Here's a little class built around your angle that has a conversion function for dms and also a nicely formatted string representation:
class Angle(float):
def __init__(self, degrees):
self.degrees = degrees
def __repr__(self):
return "<Angle of {}° ({})>".format(self.degrees, self.dms)
#property
def dms(self):
deg = self.degrees
d = int(deg)
md = abs(deg - d) * 60
m = int(md)
sd = (md - m) * 60
return "{:g}:{:g}:{:g}".format(d, m, sd)
Demo:
In[1]: a = Angle(90.173)
In[2]: print(a)
90.173
In[3]: print(a.dms)
90:10:22.8
In[4]: a
Out[4]: <Angle of 90.173° (90:10:22.8)>
Since it inherits from float, you can even calculate with it. Note though that the return value will be a normal float (not an Angle) without further adaption of the class.
If it is just for display purposes something like:
print("{0}:{1}:{2}".format(*dd2dms(full)))
If dd2dms returns an array, you just need to extract the array elements, convert them to strings and then concatenate. If you're just beginning to learn Python, try something easy like this:
result = dd2dms(full)
print(str(result[0]) + ":" + str(result[1]) + ":" + str(result[2]))
where result[i] returns the i-th element of array result, str() constructs a string from its argument, and then you can just concatenate the strings with +.
Remove square brackets from return statement (to return a tuple instead a list):
return d, m, sd
and format output string:
"%s:%s:%s" % dd2dms(full)
Full snippet:
def dd2dms(deg):
d = int(deg)
md = abs(deg - d) * 60
m = int(md)
sd = (md - m) * 60
return d, m, sd
full = 500/60
print(full)
print("%s:%s:%s" % dd2dms(full))
In python 3.6 you can use use literal string interpolation https://www.python.org/dev/peps/pep-0498/ to do the following:
def dd2dms(deg):
d = int(deg)
md = abs(deg - d) * 60
m = int(md)
sd = (md - m) * 60
return f'{d}:{m}:{sd}'
You can even do computations in the strings
for instance, you could do :
def dd2dms(deg):
d = int(deg)
md = abs(deg - d) * 60
m = int(md)
return f'{d}:{m}:{(md - m) * 60}'
However, it's usually better to declare your variables explicitly.

set output in sympy for 2**3 * 2**4 = 2**7 instead of 128

I would like to set the output in sympy for expression 2**3 * 2**4 = to 2**7 instead of 128. Is there an easy way?
This 'works' , tho may not be what you really want
>>> from sympy import *
>>> x = Symbol('2')
>>> x
2
>>> x**3 * x**7
2**10
>>> z = x**3 * x**7
>>> z
2**10
>>> str(z)
'2**10'
>>> eval(str(z))
1024
added note (using suggestion by #if.... )
>>> two = Symbol('2', positive=True, integer=True)
>>> z = two**3 * two**7
>>> z
2**10
# a little cleaner perhaps than eval(str(z))
# but requiring you to remember the name `two`
>>> z.subs(two, 2)
1024
addition also 'works'
>>> two**3 + two**7
2**7 + 2**3
>>> ((two**3 + two**7)).subs(two,two)
2**7 + 2**3
>>> ((two**3 + two**7)).subs(two, 2)
136
Not really; you're asking to alter the default display of integers to be something outside the standard set of choices. Regardless of the implementation details, this will boil down to you writing a function that accepts an integer and produces the exponent form you want to see, as a character string.

Categories