(first part from Day 5)
import sympy as sp
t = sp.symbols('t')
h = sp.symbols('h')
(t+h)**2
sp.expand((t+h)**2)
sp.diff((t+h)**2, t)
t,h = sp.symbols('t,h')
sp.expand((t+h)**3)
alpha,Omega = sp.symbols('alpha,Omega')
alpha
Omega
u = sp.Function('u')
u(t)
sp.diff(u(t),t)
sp.diff(u(alpha,t),t,t)
where p and q depend on t
import sympy as sp
t,h =sp.symbols('t,h')
u = sp.Function('u')
v = sp.Function('v')
p = sp.Function('p')
q = sp.Function('q')
uprime = sp.diff(u(t),t) # shorthand for the first derivative
vprime = sp.diff(v(t),t)
de = {uprime:p(t)*v(t), vprime:-q(t)*u(t)} # allow replacement according to the DE
de
{Derivative(u(t), t): p(t)*v(t), Derivative(v(t), t): -q(t)*u(t)}
Continuing on Day 6:
# python dict: an associative array linking "keys" (indices) to "values"
d = {'apple':'round fruit','zebra':'striped animal'}
d
{'apple': 'round fruit', 'zebra': 'striped animal'}
d['zebra']
'striped animal'
# sympy substitution
expr = t**2+h
expr
expr.subs( {t:7} )
expr.subs( {t:7,h:10} )
mylist = [5,6,7]
mylist[0]
5
uders = [u,de[uprime]] # start with this, then append higher derivatives
vders = [v,de[vprime]]
uders
[u, p(t)*v(t)]
# next element in uders will be the second derivative u''
sp.diff(uders[-1])
# we also want to substitute for v' using the de
sp.diff(uders[-1]).subs(de)
uders = [u,de[uprime]] # start with this, then append higher derivatives
vders = [v,de[vprime]]
for k in range(3):
uders.append( sp.diff(uders[-1]).subs(de) )
vders.append( sp.diff(vders[-1]).subs(de) )
uders
[u, p(t)*v(t), -p(t)*q(t)*u(t) + v(t)*Derivative(p(t), t), -p(t)**2*q(t)*v(t) - p(t)*u(t)*Derivative(q(t), t) - 2*q(t)*u(t)*Derivative(p(t), t) + v(t)*Derivative(p(t), (t, 2)), p(t)**2*q(t)**2*u(t) - 2*p(t)**2*v(t)*Derivative(q(t), t) - 4*p(t)*q(t)*v(t)*Derivative(p(t), t) - p(t)*u(t)*Derivative(q(t), (t, 2)) - 3*q(t)*u(t)*Derivative(p(t), (t, 2)) - 3*u(t)*Derivative(p(t), t)*Derivative(q(t), t) + v(t)*Derivative(p(t), (t, 3))]
de = {uprime:sp.sin(t)*v(t), vprime:-t*u(t)} # allow replacement according to the DE
de
{Derivative(u(t), t): v(t)*sin(t), Derivative(v(t), t): -t*u(t)}
uders = [u(t),de[uprime]] # start with this, then append higher derivatives
vders = [v(t),de[vprime]]
for k in range(3): # 3 is how many higher-order derivatives I want to append (could be as large a number as you like)
uders.append( sp.diff(uders[-1]).subs(de) )
vders.append( sp.diff(vders[-1]).subs(de) )
uders # our list of [u,u',u'',u''', ...]
[u(t), v(t)*sin(t), -t*u(t)*sin(t) + v(t)*cos(t), -2*t*u(t)*cos(t) - t*v(t)*sin(t)**2 - u(t)*sin(t) - v(t)*sin(t), t**2*u(t)*sin(t)**2 + 3*t*u(t)*sin(t) - 4*t*v(t)*sin(t)*cos(t) - 3*u(t)*cos(t) - 2*v(t)*sin(t)**2 - v(t)*cos(t)]
# python sum
sum([1,2,3])
6
# "list comprehension" for building a list whose elements are parametrized by another list-like thing
[i**2 for i in range(4)]
[0, 1, 4, 9]
# now build the Taylor polynomial for u and for v
# Here are the first 5 terms:
[ h**i/sp.factorial(i)*uders[i] for i in range(len(uders))] # just need to sum these to get the Taylor polynomial
[u(t), h*v(t)*sin(t), h**2*(-t*u(t)*sin(t) + v(t)*cos(t))/2, h**3*(-2*t*u(t)*cos(t) - t*v(t)*sin(t)**2 - u(t)*sin(t) - v(t)*sin(t))/6, h**4*(t**2*u(t)*sin(t)**2 + 3*t*u(t)*sin(t) - 4*t*v(t)*sin(t)*cos(t) - 3*u(t)*cos(t) - 2*v(t)*sin(t)**2 - v(t)*cos(t))/24]
# Here are the Taylor polynomials for u(t+h), v(t+h):
u_poly = sum([ h**i/sp.factorial(i)*uders[i] for i in range(len(uders))] )
v_poly = sum([ h**i/sp.factorial(i)*vders[i] for i in range(len(vders))] )
u_poly
Could use sympy subs to evaluate these expressions at particular values of t,u,v,h:
u_poly.subs( {u(t):7.3, v(t):-5.1, h:.01, t:2.5} )
But this is very slow.
Question about range(len()):
len([1,2,3,4,5,5,5,5])
8
mylist = [1,2,3,4,50,50,50,50]
# iteration if we just need the values
for item in mylist:
print(item)
1 2 3 4 50 50 50 50
# iteration if we need the index as well as the values
for i in range(len(mylist)):
print(i,mylist[i])
0 1 1 2 2 3 3 4 4 50 5 50 6 50 7 50
We have Taylor polynomial approximations to u(t+h),v(t+h) given t,u(t),v(t),h
u_poly
v_poly
For speed we need to move from sympy to numpy
To do this, we can use a sympy function called lambdify which generates a numpy-friendly function from a sympy expression
expr = t**2 + h
expr
# arguments of lambdify are the argument(s) of the generated function,
# the expression(s) to be lambdified, and a keyword "numpy"
foo = sp.lambdify( (t,h), expr, 'numpy' )
foo(5.5, .01)
30.26
import numpy as np
foo(5.5, np.array([.1,.01,.001])) # function will work for numpy array inputs
array([30.35 , 30.26 , 30.251])
# Here we lambdify our Taylor polynomial expressions:
nextuv = sp.lambdify( (t,h,u(t),v(t)), (u_poly,v_poly), 'numpy' )
# Then we can evaluate them at a particular (t,h,u,v) as follows:
nextuv(2.5, .0001, 7.3, -4.2)
(7.2997486039181, -4.201825005076226)
# Now we just need to use it to generate our approximation to the solution of the differential equation:
t = 0
u = 2
v = 5
tfinal = 3
nsteps = 20
h = (tfinal-t)/nsteps
print(f'{t:5.3f} {u:13.10f} {v:13.10f}')
for i in range(nsteps):
u,v = nextuv(t,h,u,v)
t += h
print(f'{t:5.3f} {u:13.10f} {v:13.10f}')
0.000 2.0000000000 5.0000000000 0.150 2.0560179687 4.9771835938 0.300 2.2212440601 4.9049865562 0.450 2.4869270577 4.7724993379 0.600 2.8377303003 4.5626566302 0.750 3.2509683472 4.2540238877 0.900 3.6962148978 3.8234451495 1.050 4.1358288620 3.2495407997 1.200 4.5268908433 2.5168527675 1.350 4.8248172806 1.6201744028 1.500 4.9885238948 0.5683382293 1.650 4.9865081720 -0.6134356322 1.800 4.8027479626 -1.8835007241 1.950 4.4410489248 -3.1866334548 2.100 3.9265760138 -4.4598028624 2.250 3.3038320401 -5.6402091355 2.400 2.6312022288 -6.6743159579 2.550 1.9731212443 -7.5262621320 2.700 1.3916247937 -8.1841381152 2.850 0.9392698602 -8.6631704450 3.000 0.6550699786 -9.0057315860
20//4, 20/4
(5, 5.0)