I am trying to solve a linear ODE with a rectangular signal as second member, for the first and second order :
I define the second member with sympy.Piecewise. For The first order, Sympy gives the expecting result. But for the second order, the result is discontinuous. Sympy seems not to take y(5) as initial condition for the t>5 part.
How could I have a continuous solution ?
Here is my code :
python
import numpy as np
import matplotlib.pyplot as plt
import sympy as sp
t = sp.symbols('t')
y = sp.Function('y')
f = sp.Function('f')
#%% First order
t = sp.symbols('t')
y = sp.Function('y')
f = sp.Piecewise((1, t<5), (0, True))
eq = sp.Eq(y(t).diff(t) + y(t), f)
ics = {y(0): 0}
sol = sp.dsolve(eq, ics=ics)
# plot
yl = sp.lambdify(t, sol.rhs, modules='numpy')
fl = sp.lambdify(t, f, modules='numpy')
t_val = np.linspace(0,10,500)
plt.figure()
plt.plot(t_val, yl(t_val), label='y(t)')
plt.plot(t_val, fl(t_val), label='f(t)')
plt.title('first order', fontsize=30)
plt.legend(fontsize=20)
#%% Second order
t = sp.symbols('t')
y = sp.Function('y')
f = sp.Piecewise((1, t<5), (0, True))
eq = sp.Eq(sp.diff(y(t),t,t) + y(t) + sp.diff(y(t),t), f)
ics = {sp.diff(y(t),t).subs(t, 0): 0, y(0): 0}
sol = sp.dsolve(eq, ics=ics)
# plot
yl = sp.lambdify(t, sol.rhs, modules='numpy')
fl = sp.lambdify(t, f, modules='numpy')
t_val = np.linspace(0,10,500)
plt.figure()
plt.plot(t_val, yl(t_val), label='y(t)')
plt.plot(t_val, fl(t_val), label='f(t)')
plt.title('Second order', fontsize=30)
plt.legend(fontsize=20)
Related
I'm having some computational problems with the following code:
import numpy as np
from numpy import arange
from scipy.integrate import odeint
import matplotlib.pyplot as plt
from scipy.integrate import quad
import matplotlib as mpl
mpl.rcParams['agg.path.chunksize'] = 10000
# parameters
Ms = 100 #GeV Singlet Mass
Me = 0.511e-3 #Gev Electron Mass
Mp = 1.22e19 #GeV Planck Mass
gs = 106.75 # Entropy dof
H0 = 2.133*(0.7)*1e-42 # GeV Hubble parameter (unused)
gx = 2 # WIMP's dof
g = 100 # total dof
sigmav=[1e-25,1e-11,1e-12] # cross section's order of magnitude
xi=1e-2
xe=1e2
npts=int(1e5)
x = np.linspace(xi, xe, npts)
def fMB(p,x,m):
return np.exp(-x*np.sqrt(1+p*p/(m*m)))*p*p
def neq(x,m):
return (gx/(2*np.pi*np.pi))*quad(fMB, 0, np.inf, args=(x,m))[0]
def neq_nr(x,m):
return 2*(m**2/(2*np.pi*x))**(3/2)*np.exp(-x)
def stot(x):
return (2*np.pi*np.pi/45)*gs*Ms*Ms*Ms/(x*x*x)
def Yeq(x,m):
return neq(x,m)/stot(x)
Yeq2=np.vectorize(Yeq)
def Yeq_nr(x):
return 0.145*(gx/gs)*(x)**(3/2)*np.exp(-x)
def Yeq_r(x):
return 0.278*(3*gx/4)/gs
def Ytot(x):
if np.any(x<=1):
return Yeq_r(x)
else:
return Yeq_nr(x)
def eqd(yl,x,Ms,σv):
'''
Ms [GeV] : Singlet Mass
σv: [1/GeV^2] : ⟨σv⟩
'''
H = 1.67*g**(1/2)*Ms**2/Mp
dyl = -neq(x,Ms)*σv*(yl**2-Yeq(x,Ms)**2)/(x**(-2)*H*x*Yeq(x,Ms)) #occorre ancora dividere per Yeq_nr(x) oppure Yeq(x)
return dyl
y0=1e-15
yl0 = odeint( eqd, y0, x,args=(Ms,sigmav[0]), full_output=True)
yl1 = odeint( eqd, y0, x,args=(Ms,sigmav[1]), full_output=True)
yl2 = odeint( eqd, y0, x,args=(Ms,sigmav[2]), full_output=True)
fig = plt.figure(figsize=(11,8))
plt.loglog(x,yl0[0], label = r'$\langle σ v\rangle = %s {\rm GeV}^{-2}$'%(sigmav[0]))
plt.loglog(x,yl1[0], label = r'$\langle σ v\rangle = %s {\rm GeV}^{-2}$'%(sigmav[1]))
plt.loglog(x,yl2[0], label = r'$\langle σ v\rangle = %s {\rm GeV}^{-2}$'%(sigmav[2]))
plt.loglog(x,Yeq_nr(x), '--', label = '$Y_{EQ}^{nr}$')
plt.loglog(x,Yeq2(x,Ms), '--', label = '$Y_{EQ}$')
plt.ylim(ymax=0.1,ymin=y0)
plt.xlim(xmax=xe,xmin=xi)
plt.xlabel('$x = m_χ/T$', size= 15)
plt.ylabel('$Y$', size= 15)
plt.title('$m_χ = %s$ GeV'%(Ms), size= 15)
plt.legend(loc='best',fontsize=12)
plt.grid(True)
plt.savefig('abundance.jpg',bbox_inches='tight', dpi=150)
In particular, as soon as I use little values of sigmav (ranging from 10^-12 to 10^-25) the solution is well displayed, but making use of bigger values (starting from 10^-11) I obtain problems and I guess is a order of magnitudes problem, but I don't know how to handle it!
Thanks to everyone!
Edit 1:
I'm uploading a plot making use of three different values of sigmav and as you may see the bigger one (1e-10) is showing (I guess) precision problems plot_1
import sympy as sym
sym.init_printing()
import scipy as sp
import matplotlib as mpl
import matplotlib.pyplot as plt
#%%
x = sym.symbols('x')
f, g = sym.symbols('f g', cls=sym.int)
# f''+ 30000*f'+ 100000000*f = 0.001
# f(0) = 0 e f'(0) = 0
diffeq = sym.Eq(f(x).diff(x, x) + (30000)*f(x).diff(x) + (100000000)*f(x), sym.(0.001))
soln = sym.dsolve(diffeq,f(x))
constants = sym.solve([soln.rhs.subs(x,0) - 0, soln.rhs.diff(x,1).subs(x,0)- 0])
C1, C2 = sym.symbols('C1,C2')
soln = soln.subs(constants)
func = sym.lambdify(x,soln.rhs,'numpy')
#%%
#ploting the solution f(x)
xx = sp.arange(-1,1,.01)
y = func(xx)
plt.figure(1)
plt.plot(xx,y);
Hi! I want to solve this ODE of second order in python but it seems I'm doing something wrong.
f'' means the second derivate of f(x)
f' means the first derivate of f(x)
I have created a vector (v) and would like to perform the rotMatrix function on it. I cannot figure out how to call the function rotMatrix with a degree of 30 on the vector (v). I am also plotting the vectors.
Here is my code:
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style("white")
import math
def rotMatrix(angle):
return np.array([[np.cos(np.degrees(angle)), np.arcsin(np.degrees(angle))], [np.sin(np.degrees(angle)), np.cos(np.degrees(angle))]])
v = np.array([3,7])
v30 = rotMatrix(np.degrees(30)).dot(v)
plt.arrow(0,0,v[0],v[1], head_width=0.8, head_length=0.8)
plt.arrow(0,0,v30[0],v30[1],head_width=0.8, head_length=0.8)
plt.axis([-5,5,0,10])
plt.show()
In your rotMatrix function you have used the arcsin() function. You want to use -sin() You should also convert your degrees value to radians
return np.array([[np.cos(np.radians(angle)),
-np.sin(np.radians(angle))],
[np.sin(np.radians(angle)),
np.cos(np.radians(angle))]])
Or slightly improve efficiently and readability by
c = np.cos(np.radians(angle))
s = np.sin(np.radians(angle))
return np.array([[c, -s], [s, c]])
and the call with
rotMatrix(30).dot(v)
-sin and arcsin are very different.
When in doubt, play around with the calculations in an interactive Python/numpy session.
In [23]: 30/180*np.pi # do it yourself convsion - easy
Out[23]: 0.5235987755982988
In [24]: np.radians(30) # degrees to radians - samething
Out[24]: 0.52359877559829882
In [25]: np.sin(np.radians(30)) # sin(30deg)
Out[25]: 0.49999999999999994
enter image description here import numpy as np
import matplotlib.pyplot as plt
A = np.array([[3],[-3]])
n = np.linspace(0,2*np.pi,100)
def rotate_me(A,n):
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols = 2, figsize=(18, 16))
buf1 = []
buf11 = []
buf12 = []
buf13 = []
buf1p = []
buf2 = []
# t = []
for theta in n:
x=2
x1=3
x2=2
x3=3
# xp=3
# theta = 1/p
origin = [0],[0]
R = np.array([[x*np.cos(theta),-np.sin(theta)],
[np.sin(theta),np.cos(theta)]])
R1 = np.array([[np.cos(theta),-np.sin(theta)],
[np.sin(theta),np.cos(theta)*x1]])
R2 = np.array([[np.cos(theta),-np.sin(theta)],
[x2*np.sin(theta),np.cos(theta)]])
R3 = np.array([[np.cos(theta),-np.sin(theta)*x3],
[np.sin(theta),np.cos(theta)]])
Rp = np.array([[np.cos(theta),-np.sin(theta)],
[np.sin(theta),np.cos(theta)]])
V = R.dot(A)
V1 = R1.dot(A)
V2 = R2.dot(A)
V3 = R3.dot(A)
Vp = Rp.dot(A)
buf1.append(np.linalg.norm(V))
buf11.append(np.linalg.norm(V1))
buf12.append(np.linalg.norm(V2))
buf13.append(np.linalg.norm(V3))
buf1p.append(np.linalg.norm(Vp))
# buf2.append(np.linalg.norm(A))
ax1.quiver(*origin,V[0,:],V[1,:], scale=21,color ='r',label='cos')
ax1.quiver(*origin,V1[0,:],V1[1,:], scale=21,color ='g',label='cos')
ax1.quiver(*origin,V2[0,:],V2[1,:], scale=21,color ='m',label='sin')
ax1.quiver(*origin,V3[0,:],V3[1,:], scale=21,color ='b',label='-sin')
ax1.quiver(*origin,Vp[0,:],Vp[1,:], scale=21,color ='k',label='pure')
# print(theta)
# ax1.legend()
ax2.plot(n,buf1,color ='r')
ax2.plot(n,buf11,color ='g')
ax2.plot(n,buf12,color ='m')
ax2.plot(n,buf13,color ='b')
ax2.plot(n,buf1p,color ='k')
# ax2.plot(n,buf2,color ='b')
ax2.set_xlabel('angle')
ax2.set_ylabel('magnitude')
plt.show()
# return buf1,buf2
rotate_me(A,n)
So far I have managed to find the particular solution to this equation for any given mass and drag coefficient. I have not however found a way to plot the solution or even evaluate the solution for a specific point. I really want to find a way to plot the solution.
from sympy import *
m = float(raw_input('Mass:\n> '))
g = 9.8
k = float(raw_input('Drag Coefficient:\n> '))
f = Function('f')
f1 = g * m
t = Symbol('t')
v = Function('v')
equation = dsolve(f1 - k * v(t) - m * Derivative(v(t)), 0)
C1 = Symbol('C1')
C1_ic = solve(equation.rhs.subs({t:0}),C1)[0]
equation = equation.subs({C1:C1_ic})
For completeness, you may also use Sympy's plot, which is probably more convenient if you want a "quick and dirty" plot.
plot(equation.rhs,(t,0,10))
Import these libraries (seaborn just makes the plots pretty).
from matplotlib import pyplot as plt
import seaborn as sns
import numpy as np
Then tack this onto the end. This will plot time, t, against velocity, v(t).
# make a numpy-ready function from the sympy results
func = lambdify(t, equation.rhs,'numpy')
xvals = np.arange(0,10,.1)
yvals = func(xvals)
# make figure
fig, ax = plt.subplots(1,1,subplot_kw=dict(aspect='equal'))
ax.plot(xvals, yvals)
ax.set_xlabel('t')
ax.set_ylabel('v(t)')
plt.show()
I get a plot like this for a mass of 2 and a drag coefficient of 2.
If I've understood correctly, you want to represent the right hand side of your solution, here's one of the multiple ways to do it:
from sympy import *
import numpy as np
import matplotlib.pyplot as plt
m = float(raw_input('Mass:\n> '))
g = 9.8
k = float(raw_input('Drag Coefficient:\n> '))
f = Function('f')
f1 = g * m
t = Symbol('t')
v = Function('v')
equation = dsolve(f1 - k * v(t) - m * Derivative(v(t)), 0)
C1 = Symbol('C1')
C1_ic = solve(equation.rhs.subs({t: 0}), C1)[0]
equation = equation.subs({C1: C1_ic})
t1 = np.arange(0.0, 50.0, 0.1)
y1 = [equation.subs({t: tt}).rhs for tt in t1]
plt.figure(1)
plt.plot(t1, y1)
plt.show()
Python's curve_fit calculates the best-fit parameters for a function with a single independent variable, but is there a way, using curve_fit or something else, to fit for a function with multiple independent variables? For example:
def func(x, y, a, b, c):
return log(a) + b*log(x) + c*log(y)
where x and y are the independent variable and we would like to fit for a, b, and c.
You can pass curve_fit a multi-dimensional array for the independent variables, but then your func must accept the same thing. For example, calling this array X and unpacking it to x, y for clarity:
import numpy as np
from scipy.optimize import curve_fit
def func(X, a, b, c):
x,y = X
return np.log(a) + b*np.log(x) + c*np.log(y)
# some artificially noisy data to fit
x = np.linspace(0.1,1.1,101)
y = np.linspace(1.,2., 101)
a, b, c = 10., 4., 6.
z = func((x,y), a, b, c) * 1 + np.random.random(101) / 100
# initial guesses for a,b,c:
p0 = 8., 2., 7.
print(curve_fit(func, (x,y), z, p0))
Gives the fit:
(array([ 9.99933937, 3.99710083, 6.00875164]), array([[ 1.75295644e-03, 9.34724308e-05, -2.90150983e-04],
[ 9.34724308e-05, 5.09079478e-06, -1.53939905e-05],
[ -2.90150983e-04, -1.53939905e-05, 4.84935731e-05]]))
optimizing a function with multiple input dimensions and a variable number of parameters
This example shows how to fit a polynomial with a two dimensional input (R^2 -> R) by an increasing number of coefficients. The design is very flexible so that the callable f from curve_fit is defined once for any number of non-keyword arguments.
minimal reproducible example
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
def poly2d(xy, *coefficients):
x = xy[:, 0]
y = xy[:, 1]
proj = x + y
res = 0
for order, coef in enumerate(coefficients):
res += coef * proj ** order
return res
nx = 31
ny = 21
range_x = [-1.5, 1.5]
range_y = [-1, 1]
target_coefficients = (3, 0, -19, 7)
xs = np.linspace(*range_x, nx)
ys = np.linspace(*range_y, ny)
im_x, im_y = np.meshgrid(xs, ys)
xdata = np.c_[im_x.flatten(), im_y.flatten()]
im_target = poly2d(xdata, *target_coefficients).reshape(ny, nx)
fig, axs = plt.subplots(2, 3, figsize=(29.7, 21))
axs = axs.flatten()
ax = axs[0]
ax.set_title('Unknown polynomial P(x+y)\n[secret coefficients: ' + str(target_coefficients) + ']')
sm = ax.imshow(
im_target,
cmap = plt.get_cmap('coolwarm'),
origin='lower'
)
fig.colorbar(sm, ax=ax)
for order in range(5):
ydata=im_target.flatten()
popt, pcov = curve_fit(poly2d, xdata=xdata, ydata=ydata, p0=[0]*(order+1) )
im_fit = poly2d(xdata, *popt).reshape(ny, nx)
ax = axs[1+order]
title = 'Fit O({:d}):'.format(order)
for o, p in enumerate(popt):
if o%2 == 0:
title += '\n'
if o == 0:
title += ' {:=-{w}.1f} (x+y)^{:d}'.format(p, o, w=int(np.log10(max(abs(p), 1))) + 5)
else:
title += ' {:=+{w}.1f} (x+y)^{:d}'.format(p, o, w=int(np.log10(max(abs(p), 1))) + 5)
title += '\nrms: {:.1f}'.format( np.mean((im_fit-im_target)**2)**.5 )
ax.set_title(title)
sm = ax.imshow(
im_fit,
cmap = plt.get_cmap('coolwarm'),
origin='lower'
)
fig.colorbar(sm, ax=ax)
for ax in axs.flatten():
ax.set_xlabel('x')
ax.set_ylabel('y')
plt.show()
P.S. The concept of this answer is identical to my other answer here, but the code example is way more clear. At the time given, I will delete the other answer.
Fitting to an unknown numer of parameters
In this example, we try to reproduce some measured data measData.
In this example measData is generated by the function measuredData(x, a=.2, b=-2, c=-.8, d=.1). I practice, we might have measured measData in a way - so we have no idea, how it is described mathematically. Hence the fit.
We fit by a polynomial, which is described by the function polynomFit(inp, *args). As we want to try out different orders of polynomials, it is important to be flexible in the number of input parameters.
The independent variables (x and y in your case) are encoded in the 'columns'/second dimension of inp.
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
def measuredData(inp, a=.2, b=-2, c=-.8, d=.1):
x=inp[:,0]
y=inp[:,1]
return a+b*x+c*x**2+d*x**3 +y
def polynomFit(inp, *args):
x=inp[:,0]
y=inp[:,1]
res=0
for order in range(len(args)):
print(14,order,args[order],x)
res+=args[order] * x**order
return res +y
inpData=np.linspace(0,10,20).reshape(-1,2)
inpDataStr=['({:.1f},{:.1f})'.format(a,b) for a,b in inpData]
measData=measuredData(inpData)
fig, ax = plt.subplots()
ax.plot(np.arange(inpData.shape[0]), measData, label='measuered', marker='o', linestyle='none' )
for order in range(5):
print(27,inpData)
print(28,measData)
popt, pcov = curve_fit(polynomFit, xdata=inpData, ydata=measData, p0=[0]*(order+1) )
fitData=polynomFit(inpData,*popt)
ax.plot(np.arange(inpData.shape[0]), fitData, label='polyn. fit, order '+str(order), linestyle='--' )
ax.legend( loc='upper left', bbox_to_anchor=(1.05, 1))
print(order, popt)
ax.set_xticklabels(inpDataStr, rotation=90)
Result:
Yes. We can pass multiple variables for curve_fit. I have written a piece of code:
import numpy as np
x = np.random.randn(2,100)
w = np.array([1.5,0.5]).reshape(1,2)
esp = np.random.randn(1,100)
y = np.dot(w,x)+esp
y = y.reshape(100,)
In the above code I have generated x a 2D data set in shape of (2,100) i.e, there are two variables with 100 data points. I have fit the dependent variable y with independent variables x with some noise.
def model_func(x,w1,w2,b):
w = np.array([w1,w2]).reshape(1,2)
b = np.array([b]).reshape(1,1)
y_p = np.dot(w,x)+b
return y_p.reshape(100,)
We have defined a model function that establishes relation between y & x.
Note: The shape of output of the model function or predicted y should be (length of x,)
popt, pcov = curve_fit(model_func,x,y)
The popt is an 1D numpy array containing predicted parameters. In our case there are 3 parameters.
Yes, there is: simply give curve_fit a multi-dimensional array for xData.