Solving a system of differential equations with SymPy - python

I'm trying to solve the system of differential equations one gets from a tuned mass damper:
https://en.wikipedia.org/wiki/Tuned_mass_damper.
I first initialize all of the constants, variables, and functions that I'll need.
from sympy import *
m1, m2, c1, c2, k1, k2, F0, t, x10, x20 = symbols('m1 m2 c1 c2 k1 k2 F0 t x10 x20')
x1, x2 = symbols('x1 x2', cls=Function)
x1_ = x1(t).diff(t)
x1__ = x1_.diff(t)
x2_ = x2(t).diff(t)
x2__ = x2_.diff(t)
eq1 = Eq(m1*x1__ + c1*x1_ + k1*x1(t) - m2*x2__ - c2*x2_ - k2*x2(t) - F0)
eq2 = Eq(m2*x2__ + c2*x2_ + k1*x2(t))
pprint(eq1)
pprint(eq2)
which prints:
2
d d d
-F₀ + c₁⋅──(x₁(t)) - c₂⋅──(x₂(t)) + k₁⋅x₁(t) - k₂⋅x₂(t) + m₁⋅───(x₁(t)) - m₂⋅─
dt dt 2
dt
2
d
── (x₂(t)) = 0
2
dt
2
d d
c₂⋅──(x₂(t)) + k₁⋅x₂(t) + m₂⋅───(x₂(t)) = 0
dt 2
dt
Then I try passing the two equations to the dsolve sympy function with the appropriate initial conditions:
system = [eq1, eq2]
ics = {x1(0): x10, x2(0): x20}
solution = dsolve(system, (x1(t), x2(t)), ics=ics)
pprint(solution)
However, I'm met with an error:
Traceback (most recent call last):
File "/Users/wyatt_blair/...", line 22, in <module>
solution = dsolve(system, (x1(t), x2(t)), ics=ics)
File "/Users/wyatt_blair/anaconda3/lib/python3.6/site-packages/sympy/solvers/ode.py", line 584, in dsolve
match = classify_sysode(eq, func)
File "/Users/wyatt_blair/anaconda3/lib/python3.6/site-packages/sympy/solvers/ode.py", line 1458, in classify_sysode
type_of_equation = check_linear_2eq_order2(eq, funcs, func_coef)
File "/Users/wyatt_blair/anaconda3/lib/python3.6/site-packages/sympy/solvers/ode.py", line 1568, in check_linear_2eq_order2
x = func[0].func
AttributeError: 'list' object has no attribute 'func'
I tried printing the variable func in ode.py and found the it was: [[x1(t), x2(t)]].
It seems that ode.py was expecting func to look like this: [x1(t), x2(t)]. If I change the two problematic lines in ode.py that were failing to:
x = func[0][0].func
y = func[0][1].func
I get an ImplementationError, which is strange because this equation seems like something SymPy could handle as it isn't too crazy.
Am I passing in x1(t) and x2(t) incorrectly?

Related

How do you convert a system of symbolic sympy ODEs to matrix form?

I am trying to (ultimately) simulate an inverted pendulum having a PID controller.
I am employing the Euler-Lagrange method to derive the equations of motion using SymPy.
I can compute the Euler-Lagrange equations in SymPy just fine.
Now that I have my symbolic differential equations, I would like to write them in matrix form.
It can be done by hand, but I would like to know if SymPy can separate the 2nd time-derivates out, put them in a vector, and put the rest into a matrix.
Here is the code I have so far:
#Import Libraries
import math
import numpy as np
import vpython as vp
import sympy as sp
from sympy import *
from numpy import *
from scipy.integrate import odeint
import matplotlib.pyplot as plt
#Allow display of symbols
sp.init_printing()
#Set up symbolic equations
####Symbolic Variables####
m1, m2 = sp.symbols('m1 m2') #masses of rad & cart
l = sp.symbols('l') #Length of rod
J = sp.symbols('J') #Rotational moment of inertia
t, g, F, b = sp.symbols('t g F b') #Gravitational acceleration, time, forces
z, theta1 = sp.symbols('z, theta1', cls = sp.Function)
x, y =sp.symbols('x y')
#Rotational Inertia
J = (m1*(l/2)**2)/12
####Time Derivatives####
theta1 = theta1(t)
z = z(t)
z_d = sp.diff(z, t)
theta1_d = sp.diff(theta1, t)
z_dd = sp.diff(z_d, t)
theta1_dd = sp.diff(theta1_d, t)
####Coordinates of component centers of mass####
x1 = l/2*sp.sin(theta1) + z
y1 = l/2*sp.cos(theta1)
x2 = z
y2 = 0
v1_sq = sp.diff(x1,t)**2 + sp.diff(y1,t)**2
v1 = sp.sqrt(v1_sq)
v2 = sp.diff(x2,t)
#Kinetic Energy
K1 = (1/2)*m1*(v1**2) + (1/2)*J*(sp.diff(theta1,t)**2)
K2 = (1/2)*m2*(sp.diff(z,t)**2)
K = K1 + K2
#Potential Energy
P = m1*g*l/2*sp.cos(theta1)
#Lagrangian
L = K - P
#Compute the Euler Lagrange Equation
#Compute dL/dq
dL_dz = sp.diff(L, z).simplify()
dL_dtheta1 = sp.diff(L, theta1).simplify()
#Compute dL/dq_dt
dL_dz_dt = sp.diff(L, z_d).simplify()
dL_dtheta1_dt = sp.diff(L, theta1_d).simplify()
#Compute d/dt(dL/dq_dt)
d_dt_dL_dz_dt = sp.diff(dL_dz_dt, t).simplify()
d_dt_dL_dtheta1_dt = sp.diff(dL_dtheta1_dt, t).simplify()
#Compute d/dt(dL/dq_dt) - dL/dq
LE1 = d_dt_dL_dz_dt.simplify() - dL_dz.simplify()
LE2 = d_dt_dL_dtheta1_dt.simplify() - dL_dtheta1.simplify()
LE = LE1 + LE2
I have tried using sympy.linear_eq_to_matrix(equations, *symbols), but the 'symbols' portion is a differentiated variable in my case, causing SymPy to issue this error:
ValueError Traceback (most recent call last)
<ipython-input-8-eab6a82bce2c> in <module>
88
89 b = sp.linsolve([LE1, LE2], (z_dd, theta1_dd))
---> 90 sp.linear_eq_to_matrix([LE1, LE2], *[z_dd, theta1_dd])
~\Anaconda3\lib\site-packages\sympy\solvers\solveset.py in linear_eq_to_matrix(equations, *symbols)
2555 for i in symbols:
2556 if not isinstance(i, Symbol):
-> 2557 raise ValueError(filldedent('''
2558 Expecting a Symbol but got %s
2559 ''' % i))
ValueError:
Expecting a Symbol but got Derivative(theta1(t), (t, 2))
Any help would be appreciated.
Thanks.

Sympy returns "Key Error" when solving a system of ODEs

I am trying to solve the geodesic equation using the initial values:
Where the above is a system of two ODEs. In the code, x1 = T(s) and x2 = P(s) and Γabc = Ga(a,b,c).
My code is as shown below:
from gravipy import *
from sympy import *
def distance(t1,p1,t2,p2):
init_printing()
R = 1737
dimensions = 2
t,p = symbols("t p")
x = Coordinates("x",[t,p])
Metric = diag(R**2, R**2*sin(t)**2)
g = MetricTensor("g", x, Metric)
Ga = Christoffel("Ga", g)
T,P = symbols("T P", cls=Function)
s = symbols("s")
ics = {T(1):t1, T(2):t2, P(1):p1, P(2):p2}
system=[]
coords = [T(s),P(s)]
for a in range(dimensions):
eq = coords[a].diff(s,s)
for b in range(dimensions):
for c in range(dimensions):
christ = Ga(a+1,b+1,c+1).replace("t","T(s)")
eq += christ * coords[b].diff(s) * coords[c].diff(s)
system.append(Eq(eq,0))
print(system)
T,P = dsolve(system, [T(s), P(s)], ics=ics)
print(T,P)
coords=[T(s),P(s)]
integral = 0
for mu in range(dimensions):
for nu in range(dimensions):
integral += g(mu,nu).replace("t","T(s)") * coords[mu].diff(s) * coords[nu].diff(s)
print(integrate(sqrt(integral), (s, 1, 2)))
distance(1,1,2,2)
However, when I run the program, it generates the system of equations:
[Eq(-3017169*sin(2*T(s))*Derivative(P(s), s)**2/2 + Derivative(T(s), s, s), 0), Eq(3017169*sin(2*T(s))*Derivative(P(s), s)*Derivative(T(s), s) + Derivative(P(s), s, s), 0)]
Or, more readably:
but then fails when running dsolve(), with the error:
File "C:\Users\user\AppData\Local\Programs\Python\Python36\lib\site-packages\sympy\solvers\ode.py", line 584, in dsolve
match = classify_sysode(eq, func)
File "C:\Users\user\AppData\Local\Programs\Python\Python36\lib\site-packages\sympy\solvers\ode.py", line 1377, in classify_sysode
if not order[func]:
KeyError: P(s)
Where it alternates between P(s) and T(s). It appears that the error occurs when classifying the ODE, so does this mean that sympy is unable to solve the equations?
Please check the docs, but I believe dsolve is for solving systems of equations using linear algebra and LU decomposition.
I think you want a Runge Kutta 5th order integration method - look into ode.
You have four coupled, first order ODEs:
dM/ds = d^2T/ds^2 = your first equation
dN/ds = d^2P/ds^2 = your second equation
dT/ds = M
dP/ds = N

Sympy with Cython produces singular matrix when attempting to find roots

I am trying to solve a set of nonlinear equations using Sympy. This is my code with a few numbers changed and only one input value (the real code runs through 170,000 lines of data):
import sympy as sp
K0 = 2.00*10**-4
x2, y2 = sp.symbols('x2, y2')
x0 = 500
y0 = 500
R1 = ((x2 - x0)**2 + (y2 - y0)**2)**0.5
R2 = K0 * R1
H2 = sp.atan(R2 * (x2 - x0)/R1)
V2 = sp.atan(R2 * (y2 - y0)/R1)
x, y = sp.symbols('x, y')
x0 = 1.0
y0 = 2.0
x = R1 * H2
y = R1 * V2
dat = sp.nsolve([x - x0, y - y0], [x2, y2], [512, 512]) # This line is the problem
print "dat = %f, %f" % (dat[0], dat[1])
Purely with Python, the code runs fine and produces a good output - but it's very slow. To speed up the process, I used Cython to compile a .pyx file with the exact same code (in a definition, def test()), yet during run-time I'm met with:
File "test.py", line 3, in <module>
demo.test()
File "demo.pyx", line 17, in demo.test
dat = sp.nsolve([x - x0, y - y0], [x2, y2], [512, 512])
File "C:\...\site-packages\sympy\utilities\decorator.py", line 91, in func_wrapper
return func(*args, **kwargs)
File "C:\...\site-packages\sympy\solvers\solvers.py", line 2847, in nsolve
x = findroot(f, x0, J=J, **kwargs)
File "C:\...\site-packages\mpmath\calculus\optimization.py", line 960, in findroot
for x, error in iterations:
File "C:\...\site-packages\mpmath\calculus\optimization.py", line 658, in __iter__
s = self.ctx.lu_solve(Jx, fxn)
File "C:\...\site-packages\mpmath\matrices\linalg.py", line 227, in lu_solve
A, p = ctx.LU_decomp(A)
File "C:\...\site-packages\mpmath\matrices\linalg.py", line 137, in LU_decomp
raise ZeroDivisionError('matrix is numerically singular')
ZeroDivisionError: matrix is numerically singular
I have narrowed down the problem to the x - x0 and y - y0 part. For some reason, the compiled code cannot handle finding the roots when they're not equal to 0. Can nsolve simply not be converted to C using Cython? Is there something I'm missing that I have to do with Cython?
You can use sympy.lambdify together with e.g. SciPy's solvers. If that is not fast enough you could use symengine.Lambdify.
Getting the function signatures right, and deriving the Jacobian, requires you to jump through hoops. If you want to use a library for that I have written pyneqsys:
>>> from pyneqsys.symbolic import SymbolicSys
>>> neqsys = SymbolicSys([x2, y2], [x - x0, y - y0])
>>> neqsys.solve([512, 512])
Out[4]:
(array([ 547.28609349, 594.58064617]),
fjac: array([[ 0.91320338, 0.4075041 ],
[-0.4075041 , 0.91320338]])
fun: array([ -1.37667655e-13, 1.52011737e-12])
message: 'The solution converged.'
nfev: 17
njev: 2
qtf: array([ 1.55620322e-10, 4.63225371e-10])
r: array([ 0.02751454, 0.023682 , 0.03261983])
status: 1
success: True
x: array([ 547.28609349, 594.58064617]))
If those 170 000 solves involves gradually changing parameters pyneqsys can exploit that (by propagating the solution as guess between solves). It can also use symengine.Lambdify automatically by setting the environment variable SYM_BACKEND=sympysymengine

Implementing Newtons method

I'm trying to use Newtons method to solve a Satellite Navigation problem, also I'm fairly new to programming.
I keep getting the following error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\Ninjasoup\Anaconda3\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 714, in runfile
execfile(filename, namespace)
File "C:\Users\Ninjasoup\Anaconda3\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 89, in execfile
exec(compile(f.read(), filename, 'exec'), namespace)
File "C:/Users/Ninjasoup/Desktop/MSc Space Science/SatNav AssignmentCode/SatNavCode1.3.py", line 72, in <module>
fA = np.sqrt((x-xA)**2 + (y-yA)**2 + (z-zA)**2) - (dA-b)
TypeError: unsupported operand type(s) for -: 'type' and 'float'
I've tried changing the declaration of the unkown variables to different types but I keep getting the same error.
Any help would be greatly appreciated.
import math
import numpy as np
from sympy import diff
#Constants
R = 6371
SD = 20200
c = 299792458
#Given Data
latA = 47.074834081442773
lonA = 18.487157448324282
latB = 17.949919573189003
lonB = 17.786195009535710
latC = 48.196896294687626
lonC = -67.929788607990332
latD = 77.374761092966111
lonD = -25.681600844602748
tofA = 0.070745909570054
tofB = 0.075407082536252
tofC = 0.074696101874954
tofD = 0.071921760657713
#Pseudo Range error
dA = c*tofA
dB = c*tofB
dC = c*tofC
dD = c*tofD
#Unknown Variables
x =float
y =float
z =float
b =float
#Coversion of Shperical to Cartesian Co-ordinates
xA = (R+SD) * math.cos(math.radians(latA)) * math.cos(math.radians(lonA))
yA = (R+SD) * math.cos(math.radians(latA)) * math.sin(math.radians(lonA))
zA = (R+SD) *math.sin(math.radians(latA))
xB = (R+SD) * math.cos(math.radians(latB)) * math.cos(math.radians(lonB))
yB = (R+SD) * math.cos(math.radians(latB)) * math.sin(math.radians(lonB))
zB = (R+SD) *math.sin(math.radians(latB))
xC = (R+SD) * math.cos(math.radians(latC)) * math.cos(math.radians(lonC))
yC = (R+SD) * math.cos(math.radians(latC)) * math.sin(math.radians(lonC))
zC = (R+SD) *math.sin(math.radians(latC))
xD = (R+SD) * math.cos(math.radians(latD)) * math.cos(math.radians(lonD))
yD = (R+SD) * math.cos(math.radians(latD)) * math.sin(math.radians(lonD))
zD = (R+SD) *math.sin(math.radians(latD))
#P1 = np.array([xA,yA,zA])
#P2 = np.array([xB,yB,zB])
#P3 = np.array([xC,yC,zC])
#P4 = np.array([xD,yD,zD])
#print(P1,P2,P3,P4)
fA = np.sqrt((x-xA)**2 + (y-yA)**2 + (z-zA)**2) - (dA-b)
dfA1 = diff(fA, x)
dfA2 = diff(fA, y)
dfA3 = diff(fA, z)
dfA4 = diff(fA, b)
fB = np.sqrt((x-xB)**2 + (y-yB)**2 + (z-zB)**2) - (dB-b)
dfB1 = diff(fB, x)
dfB2 = diff(fB, y)
dfB3 = diff(fB, z)
dfB4 = diff(fB, b)
fC = np.sqrt((x-xC)**2 + (y-yC)**2 + (z-zC)**2) - (dC-b)
dfC1 = diff(fC, x)
dfC2 = diff(fC, y)
dfC3 = diff(fC, z)
dfC4 = diff(fC, b)
fD = np.sqrt((x-xD)**2 + (y-yD)**2 + (z-zD)**2) - (dD-b)
dfD1 = diff(fD, x)
dfD2 = diff(fD, y)
dfD3 = diff(fD, z)
dfD4 = diff(fD, b)
#Matrix of Partial derivatives (Jacobian)
J = [[dfA1,dfA2,dfA3,dfA4],
[dfB1,dfB2,dfB3,dfB4],
[dfC1,dfC2,dfC3,dfC4],
[dfD1,dfD2,dfD3,dfD4]]
print(J)
#Matrix of functions
F = [[fA],
[fB],
[fC],
[fD]]
print(F)
#Guess Values
U = [[1],
[1],
[1],
[1]]
#Evaluated values
x,y,z,b = U - np.linalg.solve(J,F)
#Iteration 2..will do more iterations later.
U1 = [[x],
[y],
[z],
[b]]
x1,y1,z1,b1 = U1 - np.linalg.solve(J,F)
#Convert x,y,z back to spherical constants once code is working
From the x=float lines, it looks like you want to do symbolic calculation. Having "unknown variables" isn't possible with the basic python syntax (and in a large portion of progamming languages afaik) but the sympy package you're already using is meant just for that (you should probably check out the tutorial page here).
Here is how you would create symbolic variables (aka "unknow variables") :
from sympy import symbols
x, y, z, b = symbols('x y z b')
But once that's is done you will notice that your code break a bit further down the road when you try to use np.linalg.solve. The numpy module is only meant to operate on special objects called numpy arrays ,they are essentially N-dimentionnal matrices of numbers aka "not symbolic expressions".
But sympy also has a solution to that problem : you can create matrices containing symbolic expressions and solve matricial equations too with ot . I'll just link you to the tutorial so you can see how to properly define said matrices and how to use them.
You need to initialize x, y, z, and b to actual floating values like 0.0, not to float.
>>> 0.0 # a floating-point number
0.0
>>> float # an object of class 'type' named 'float'
<class 'float'>
>>> x = float # initialize to an object of class 'type'
>>> y = 0.0 # initialize to a floating number
>>> x
<class 'float'>
>>> y
0.0
>>> type(x)
<class 'type'>
>>> type(y)
<class 'float'>

How do I specify the constraints in PuLP Python for multiplying two variables?

I am trying to model the following Integer Programming model using Python PuLP
I have written the following code :
from pulp import *
#knapsack problem
def knapsolve(item):
prob = LpProblem('BinPacking', LpMinimize)
ys = [LpVariable("y{0}".format(i+1), cat="Binary") for i in range(item.bins)]
xs = [LpVariable("x{0}{1}".format(i+1, j+1), cat="Binary")
for i in range(item.items) for j in range(item.bins)]
#minimize objective
nbins = sum(ys)
prob += nbins
print(nbins)
#constraints
t = nbins >= 1
print(t)
prob += t
for i in range(item.items):
con1 = sum(xs[(i + j*item.bins)]* ys[j] for j in range(item.bins))
t = con1 == 1
prob += t
print(t)
status = prob.solve()
print(LpStatus[status])
print("Objective value:", value(prob.objective))
print ('\nThe values of the variables : \n')
for v in prob.variables():
print(v.name, "=", v.varValue)
return
class Probelm:
#bins, binweight, items, weight, itemheight, binheight
bins = 5
items = 5
binweight = [2,3,2,5,3]
itemweight = [1,2,2,1,3]
itemheight = [2,1,4,5,3]
binheight = [4,9,10,8,10]
item = Problem()
knapsolve(item)
At runtime this provides the error :
y1 + y2 + y3 + y4 + y5
y1 + y2 + y3 + y4 + y5 >= 1
Traceback (most recent call last):
File "C:\Python34\BinPacking6.py", line 58, in <module>
knapsolve(item)
File "C:\Python34\BinPacking6.py", line 27, in knapsolve
con1 = sum(xs[(i + j*item.bins)]* ys[j] for j in range(item.bins))
File "C:\Python34\BinPacking6.py", line 27, in <genexpr>
con1 = sum(xs[(i + j*item.bins)]* ys[j] for j in range(item.bins))
File "C:\Python34\lib\site-packages\pulp-1.6.1-py3.4.egg\pulp\pulp.py", line 209, in __mul__
return LpAffineExpression(self) * other
File "C:\Python34\lib\site-packages\pulp-1.6.1-py3.4.egg\pulp\pulp.py", line 786, in __mul__
return self * LpAffineExpression(other)
File "C:\Python34\lib\site-packages\pulp-1.6.1-py3.4.egg\pulp\pulp.py", line 774, in __mul__
raise TypeError("Non-constant expressions cannot be multiplied")
TypeError: Non-constant expressions cannot be multiplied
As far as I can identify, the problem lies in con1 = sum(xs[(i + j*item.bins)]* ys[j] for j in range(item.bins)). How do I specify the constraints?
The message from PuLP is correct. You are trying to pass a non-linear construct (multiplication of two variables) to PuLP while PuLP is designed for linear models only. Fortunately, the simple bin-packing model you want to solve can be formulated as a linear MIP model, see here. Before starting typing some Python code it often helps me to first write down the mathematical model more precise than what you posted (which is too general).

Categories

Resources