checking convergence using python integrator - python

I am looking to integrate the difference between my numerical and exact solution to the heat equation though I am not sure what would be the best to way to tackle this. Is there a specific integrator that would allow me to do this ?
I hope to integrate it wrt to $x$.
I have the following code so far:
import numpy as np
from matplotlib import pyplot
from mpl_toolkits.mplot3d import Axes3D
from scipy import linalg
import matplotlib.pyplot as plt
import math
def initial_data(x):
y = np.zeros_like(x)
for i in range(len(x)):
if (x[i] < 0.25):
y[i] = 0.0
elif (x[i] < 0.5):
y[i] = 4.0 * (x[i] - 0.25)
elif (x[i] < 0.75):
y[i] = 4.0 * (0.75 - x[i])
else:
y[i] = 0.0
return y
def heat_exact(x, t, kmax = 150):
"""Exact solution from separation of variables"""
yexact = np.zeros_like(x)
for k in range(1, kmax):
d = -8.0*
(np.sin(k*np.pi/4.0)-2.0*np.sin(k*np.pi/2.0)+np.sin(3.0*k*np.pi/4.0))/((np.pi*k)**2)
yexact += d*np.exp(-(k*np.pi)**2*t)*np.sin(k*np.pi*x)
return yexact
def ftcs_heat(y, ynew, s):
ynew[1:-1] = (1 - 2.0 * s) * y[1:-1] + s * (y[2:] + y[:-2])
# Trivial boundary conditions
ynew[0] = 0.0
ynew[-1] = 0.0
Nx = 198
h = 1.0 / (Nx + 1.0)
t_end = 0.25
s = 1.0 / 3.0 # s = delta / h^2
delta = s * h**2
Nt = int(t_end / delta)+1
x = np.linspace(0.0, 1.0, Nx+2)
y = initial_data(x)
ynew = np.zeros_like(y)
for n in range(Nt):
ftcs_heat(y, ynew, s)
y = ynew
fig = plt.figure(figsize=(8,6))
ax = fig.add_subplot(111)
x_exact = np.linspace(0.0, 1.0, 200)
ax.plot(x, y, 'kx', label = 'FTCS')
ax.plot(x_exact, heat_exact(x_exact, t_end), 'b-', label='Exact solution')
ax.legend()
plt.show()
diff = (y - heat_exact(x_exact,t_end))**2 # squared difference between numerical and exact solutions
x1 = np.trapz(diff, x=x) #(works!)
import scipy.integrate as integrate
x1 = integrate.RK45(diff,diff[0],0,t_end) #(preferred but does not work)
What I am looking to integrate is the variable diff (the squared difference). Any suggestions are welcomed, thanks.
Edit: I would like to use RK45 method however I am not sure what should my y0 be, I have tried x1 = integrate.RK45(diff,diff[0],0,t_end) but get the following output error:
raise ValueError("`y0` must be 1-dimensional.")
ValueError: `y0` must be 1-dimensional.

By integration you mean you want to find the area between y and heat_exact? Or do you want to know if they are the same within a specific limit? The latter can be found with numpy.isclose. The former you can use several integration functions builtin numpy.
For example:
np.trapz(diff, x=x)
Oh, shouldn't the last line be diff = (y - heat_exact(x_exact,t_end))**2? My integration of this diff gave 8.32E-12, which looks right judging by the plots you gave me.
Check out also scipy.integrate

Related

Using Python DAE solver for coupled equations in time and space

I would like to solve two coupled equations in time and space, using the scikits.odes.dae solver.
My equations are as follows:
dy1/dt = dy1/dz + y2
y1 = 5 * y2
The code I have written is the following
import matplotlib.pyplot as plt
import numpy as np
from scikits.odes import dae
N = 51 #number os spacesteps
L = 1.0 #[m] length of sorbent bed, also a guess
dz = L/(N-1) #[m] length of space step
time = np.arange(0, 1.5, 0.1)
dydz = 1
y0 = [1, 0.2] #initial values y0[0] = y1 and y0[1] = y2
yp0 = [1, 1] #initial guess for \dot{y1} and \dot{y2}
def trial_space(t, y, ydot, result):
result[0] = ydot[0] - 6 * dydz + y[1]
result[1] = y[0] - 5 * y[1]
solver = dae('ida', trial_space)
solution = = solver.solve(time, y0, yp0)
Currently, I am feeding the solver a constant value of dydz, but actually I would like to use a central differencing scheme to obtain
dy/dz[i] = (y[i+1] - y[i-1])/(2*dz)
How do I integrate this into the solver?

stackoverflow error in 2d fipy PDE solver in python

I am trying to solve a system of three coupled PDEs and two ODEs (coupled) in 2D. The problem tries to solve for a case of a particle moving in a medium. The medium is given by the fields- velocity components vx, vy, and density m and there is one particle moving in this medium with position Rx, Ry.
It runs fine for a while but then throws up errors:
"Fatal Python error: Cannot recover from stack overflow.
Current thread 0x00007fe155915700 (most recent call first):"
Here is the code:
"""
"""
from fipy import (CellVariable, PeriodicGrid2D, Viewer, TransientTerm, DiffusionTerm,
UniformNoiseVariable, LinearLUSolver, numerix,
ImplicitSourceTerm, ExponentialConvectionTerm, VanLeerConvectionTerm,
PowerLawConvectionTerm, Variable)
import sys
import inspect
import matplotlib.pyplot as plt
import numpy as np
import scipy.ndimage
from scipy.optimize import curve_fit
from scipy.signal import correlate
from scipy.stats import kurtosis
from scipy.interpolate import interp1d
numerix.random.seed(2)
def run_simulation(f0, Total_time):
# Define mesh size and number of points
nx = 50
ny = nx
L = 50
dx = L / nx
dy = dx
mesh = PeriodicGrid2D(dx, dy, nx, ny)
# Variables to use
vx = CellVariable(name='vx', mesh=mesh, hasOld=1)
vy = CellVariable(name='vy', mesh=mesh, hasOld=1)
m = CellVariable(name='m', mesh=mesh, hasOld=1)
# Initial condition
m.setValue(UniformNoiseVariable(mesh=mesh, minimum=0.6215, maximum=0.6225))
vx.setValue(UniformNoiseVariable(mesh=mesh, minimum=0, maximum=0.00001))
vy.setValue(UniformNoiseVariable(mesh=mesh, minimum=0, maximum=0.00001))
#particle position
x0=10.0
y0=25.0
# create grids for grad function
xgrid=np.unique(mesh.x.value)+dx/2
ygrid=np.unique(mesh.y.value)+dy/2
# parameters ------------------------------
B=4.0
Gamma=1.0
gamma=1.0
Dm=0.005
C=100.0
## save the initial positions in Rx,Ry
Rx=Variable(value=x0)
Ry=Variable(value=y0)
theta=Variable(value=0.0) # n-hat = cos(theta) x-hat + sin(theta) y-hat
sigma = 1
dt = 0.05
#-----------------------------------------
x_hat = [1.0, 0.0]
y_hat = [0.0, 1.0]
#------------- dirac delta function --------------
# https://stackoverflow.com/questions/58041222/dirac-delta-source-term-in-fipy
def delta_func(x, y, epsilon):
return ((x < epsilon) & (x > -epsilon) & (y < epsilon) & (y > -epsilon)) * \
(1 + numerix.cos(numerix.pi * x / epsilon) * numerix.cos(numerix.pi * y / epsilon)) / 2 / epsilon
############## equations #############
# renormalized parameters by Gamma
# fields : velocity vector, density scalar
# Gamma * v = -B rho(grad(rho)) + f* n-cap* delta(r-R), B>0, f>0, Gamma>0
# dot(rho) + del.(v rho) = 0
# particle
# dot(R) = (f/gamma)*(n-cap) - (C/gamma) * rho(grad(rho)) C>0
# Gamma=gamma=1, B' = B/Gamma, C'=C/gamma, f'=f/Gamma
######################################
eq_m = (TransientTerm(var=m) + ExponentialConvectionTerm(coeff=x_hat * vx + y_hat * vy, var=m) == DiffusionTerm(coeff=Dm, var=m) )
eq_vx = (ImplicitSourceTerm(coeff=1., var=vx) == -(B/Gamma)*m.grad.dot(x_hat)*(m) + (f0/Gamma)*numerix.cos(theta)* delta_func(mesh.x-Rx,mesh.y-Ry,sigma) )
eq_vy = (ImplicitSourceTerm(coeff=1., var=vy) == -(B/Gamma)*m.grad.dot(y_hat)*(m) + (f0/Gamma)*numerix.sin(theta)* delta_func(mesh.x-Rx,mesh.y-Ry,sigma) )
eq = eq_m & eq_vx & eq_vy
viewer = Viewer(vars=(m))
elapsed = 0.0
ms = []
vxs = []
vys = []
xs = []
ys = []
while elapsed < Total_time:
# Old values are used for sweeping when solving nonlinear values
vx.updateOld()
vy.updateOld()
m.updateOld()
print(elapsed, Rx, Ry)
mgrid=np.reshape(m.value,(nx,ny))
# gradient cal, dydx[0][x,y], dydx[1][x,y] -> x derivative, y derivative at x,y
dydx=np.gradient(mgrid,dx,dy,edge_order=2)
# impose periodic boundary on gradient
dydx[0][nx-1,:]=(mgrid[0,:]-mgrid[nx-2,:])/(2.0*dx)
dydx[0][0,:]=(mgrid[1,:]-mgrid[nx-1,:])/(2.0*dx)
dydx[1][:,ny-1]=(mgrid[:,0]-mgrid[:,ny-2])/(2.0*dy)
dydx[1][:,0]=(mgrid[:,1]-mgrid[:,ny-1])/(2.0*dy)
# solve ode
idx = np.argmin(np.abs(xgrid - Rx))
idy = np.argmin(np.abs(ygrid - Ry))
x0=x0+ ((f0/gamma)*np.cos(theta) - C*mgrid[idx,idy]*dydx[0][idx,idy])*dt
y0=y0+ ((f0/gamma)*np.sin(theta) - C*mgrid[idx,idy]*dydx[1][idx,idy])*dt
if(x0>L):
x0=x0-L
if(x0<0):
x0=x0+L
if(y0>L):
y0=y0-L
if(y0<0):
y0=y0+L
Rx.setValue(x0) # element-wise assignment did not work
Ry.setValue(y0)
elapsed += dt
res = 1e5
old_res = res * 2
step = 0
while res > 1e-5 and step < 5 and old_res / res > 1.01:
old_res = res
res = eq.sweep(dt=dt)
step += 1
# The variable values are just numpy arrays so easy to use!
# save velocity & density
vxs.append(vx.value.copy())
vys.append(vy.value.copy())
ms.append(m.value.copy())
viewer.plot()
# save x and y positions
xs.append(mesh.x.value.copy())
ys.append(mesh.y.value.copy())
return ms, vxs, vys, xs, ys
if __name__ == '__main__':
path = 'result/'
Total_time= 50 #40
f0 = 2
ms, vxs, vys, xs, ys = run_simulation(f0,Total_time)
name = 'f0_{:.4f}'.format(f0)
y = np.array([ms, vxs, vys])
xx = np.reshape(xs,(50,50))
yy = np.reshape(ys,(50,50))
vx = np.reshape(vxs[800][:],(50,50))
vy = np.reshape(vys[800][:],(50,50))
print(np.shape(xx), np.shape(xs), np.shape(vx))
#np.save(path + name, y)
plt.imshow(np.reshape(ms[800][:],(50,50)), aspect='auto', interpolation='bicubic', cmap='jet', extent=[0, 50, 50, 0])
plt.colorbar(label='density m')
plt.xlabel(r'$x $')
plt.ylabel(r'$y $')
plt.gcf().savefig(path + 'rho_'+name+'.png', format='png', bbox_inches='tight')
plt.clf()
#---------------------------------------------
plt.imshow(np.reshape(vxs[800][:],(50,50)), aspect='auto', interpolation='bicubic', cmap='jet', extent=[0, 50, 50, 0])
plt.colorbar(label='velocity vx')
plt.xlabel(r'$x $')
plt.ylabel(r'$y $')
plt.gcf().savefig(path + 'vel_'+name+'.png', format='png', bbox_inches='tight')
plt.clf()
#---------------------------------------------
plt.quiver(xx,yy,vx,vy,scale=3)
plt.xlabel(r'$x $')
plt.ylabel(r'$y $')
plt.gcf().savefig(path + 'v_'+name+'.png', format='png', bbox_inches='tight')
plt.clf()
What can cause this error? I am not defining the equation inside the loop (this caused a similar problem before). Thank you in advance for your help.
UPDATE
I changed the function calling for x.mesh as
delta_func(xp,yp,sigma) where xp and yp are xp=Variable(value=mesh.x-Rx) and yp=Variable(value=mesh.y-Ry). Directly calling the x.mesh might have caused the problem according to an answer to my old question. But that did not help, I am still getting the overflow error. Here is the new version of the code:
"""
"""
from fipy import (CellVariable, PeriodicGrid2D, Viewer, TransientTerm, DiffusionTerm,
UniformNoiseVariable, LinearLUSolver, numerix,
ImplicitSourceTerm, ExponentialConvectionTerm, VanLeerConvectionTerm,
PowerLawConvectionTerm, Variable)
import sys
import inspect
import matplotlib.pyplot as plt
import numpy as np
import scipy.ndimage
from scipy.optimize import curve_fit
from scipy.signal import correlate
from scipy.stats import kurtosis
from scipy.interpolate import interp1d
numerix.random.seed(2)
def run_simulation(f0, Total_time):
# Define mesh size and number of points
nx = 50
ny = nx
L = 50
dx = L / nx
dy = dx
mesh = PeriodicGrid2D(dx, dy, nx, ny)
# Variables to use
vx = CellVariable(name='vx', mesh=mesh, hasOld=1)
vy = CellVariable(name='vy', mesh=mesh, hasOld=1)
m = CellVariable(name='m', mesh=mesh, hasOld=1)
# Initial condition
m.setValue(UniformNoiseVariable(mesh=mesh, minimum=0.6215, maximum=0.6225))
vx.setValue(UniformNoiseVariable(mesh=mesh, minimum=0, maximum=0.00001))
vy.setValue(UniformNoiseVariable(mesh=mesh, minimum=0, maximum=0.00001))
#particle position
x0=10.0
y0=25.0
# create grids for grad function
xgrid=np.unique(mesh.x.value)+dx/2
ygrid=np.unique(mesh.y.value)+dy/2
# parameters ------------------------------
B=4.0
Gamma=1.0
gamma=1.0
Dm=0.005
C=100.0
## save the initial positions in Rx,Ry
Rx=Variable(value=x0)
Ry=Variable(value=y0)
theta=Variable(value=0.0) # n-hat = cos(theta) x-hat + sin(theta) y-hat
sigma = 1
dt = 0.05
#-----------------------------------------
xp=Variable(value=mesh.x-Rx)
yp=Variable(value=mesh.y-Ry)
x_hat = [1.0, 0.0]
y_hat = [0.0, 1.0]
#------------- dirac delta function --------------
# https://stackoverflow.com/questions/58041222/dirac-delta-source-term-in-fipy
def delta_func(x, y, epsilon):
return ((x < epsilon) & (x > -epsilon) & (y < epsilon) & (y > -epsilon)) * \
(1 + numerix.cos(numerix.pi * x / epsilon) * numerix.cos(numerix.pi * y / epsilon)) / 2 / epsilon
############## equations #############
# renormalized parameters by Gamma
# fields : velocity vector, density scalar
# Gamma * v = -B rho(grad(rho)) + f* n-cap* delta(r-R), B>0, f>0, Gamma>0
# dot(rho) + del.(v rho) = 0
# particle
# dot(R) = (f/gamma)*(n-cap) - (C/gamma) * rho(grad(rho)) C>0
# Gamma=gamma=1, B' = B/Gamma, C'=C/gamma, f'=f/Gamma
######################################
eq_m = (TransientTerm(var=m) + ExponentialConvectionTerm(coeff=x_hat * vx + y_hat * vy, var=m) == DiffusionTerm(coeff=Dm, var=m) )
eq_vx = (ImplicitSourceTerm(coeff=1., var=vx) == -(B/Gamma)*m.grad.dot(x_hat)*(m) + (f0/Gamma)*numerix.cos(theta)* delta_func(xp,yp,sigma) )
eq_vy = (ImplicitSourceTerm(coeff=1., var=vy) == -(B/Gamma)*m.grad.dot(y_hat)*(m) + (f0/Gamma)*numerix.sin(theta)* delta_func(xp,yp,sigma) )
eq = eq_m & eq_vx & eq_vy
viewer = Viewer(vars=(m))
elapsed = 0.0
ms = []
vxs = []
vys = []
xs = []
ys = []
while elapsed < Total_time:
# Old values are used for sweeping when solving nonlinear values
vx.updateOld()
vy.updateOld()
m.updateOld()
print(elapsed, Rx, Ry)
mgrid=np.reshape(m.value,(nx,ny))
# gradient cal, dydx[0][x,y], dydx[1][x,y] -> x derivative, y derivative at x,y
dydx=np.gradient(mgrid,dx,dy,edge_order=2)
# impose periodic boundary on gradient
dydx[0][nx-1,:]=(mgrid[0,:]-mgrid[nx-2,:])/(2.0*dx)
dydx[0][0,:]=(mgrid[1,:]-mgrid[nx-1,:])/(2.0*dx)
dydx[1][:,ny-1]=(mgrid[:,0]-mgrid[:,ny-2])/(2.0*dy)
dydx[1][:,0]=(mgrid[:,1]-mgrid[:,ny-1])/(2.0*dy)
# solve ode
idx = np.argmin(np.abs(xgrid - Rx))
idy = np.argmin(np.abs(ygrid - Ry))
x0=x0+ ((f0/gamma)*np.cos(theta) - C*mgrid[idx,idy]*dydx[0][idx,idy])*dt
y0=y0+ ((f0/gamma)*np.sin(theta) - C*mgrid[idx,idy]*dydx[1][idx,idy])*dt
if(x0>L):
x0=x0-L
if(x0<0):
x0=x0+L
if(y0>L):
y0=y0-L
if(y0<0):
y0=y0+L
Rx.setValue(x0) # element-wise assignment did not work
Ry.setValue(y0)
elapsed += dt
res = 1e5
old_res = res * 2
step = 0
while res > 1e-5 and step < 5 and old_res / res > 1.01:
old_res = res
res = eq.sweep(dt=dt)
step += 1
# The variable values are just numpy arrays so easy to use!
# save velocity & density
vxs.append(vx.value.copy())
vys.append(vy.value.copy())
ms.append(m.value.copy())
viewer.plot()
# save x and y positions
xs.append(mesh.x.value.copy())
ys.append(mesh.y.value.copy())
return ms, vxs, vys, xs, ys
if __name__ == '__main__':
path = 'result/'
Total_time= 100 #40
f0 = 2
ms, vxs, vys, xs, ys = run_simulation(f0,Total_time)
name = 'f0_{:.4f}'.format(f0)
y = np.array([ms, vxs, vys])
xx = np.reshape(xs,(50,50))
yy = np.reshape(ys,(50,50))
vx = np.reshape(vxs[380][:],(50,50))
vy = np.reshape(vys[380][:],(50,50))
print(np.shape(xx), np.shape(xs), np.shape(vx))
#np.save(path + name, y)
plt.imshow(np.reshape(ms[380][:],(50,50)), aspect='auto', interpolation='bicubic', cmap='jet', extent=[0, 50, 50, 0])
plt.colorbar(label='density m')
plt.xlabel(r'$x $')
plt.ylabel(r'$y $')
plt.gcf().savefig(path + 'rho_'+name+'.png', format='png', bbox_inches='tight')
plt.clf()
#---------------------------------------------
plt.imshow(np.reshape(vxs[380][:],(50,50)), aspect='auto', interpolation='bicubic', cmap='jet', extent=[0, 50, 50, 0])
plt.colorbar(label='velocity vx')
plt.xlabel(r'$x $')
plt.ylabel(r'$y $')
plt.gcf().savefig(path + 'vel_'+name+'.png', format='png', bbox_inches='tight')
plt.clf()
#---------------------------------------------
plt.quiver(xx,yy,vx,vy,scale=3)
plt.xlabel(r'$x $')
plt.ylabel(r'$y $')
plt.gcf().savefig(path + 'v_'+name+'.png', format='png', bbox_inches='tight')
plt.clf()
I must confess that I ran out of patience before getting the stack overflow error, but I was able to identify the problem.
It's a similar (although more subtle) issue to what you reported before. Because theta is declared as a Variable,
x0=x0+ ((f0/gamma)*np.cos(theta) - C*mgrid[idx,idy]*dydx[0][idx,idy])*dt
y0=y0+ ((f0/gamma)*np.sin(theta) - C*mgrid[idx,idy]*dydx[1][idx,idy])*dt
result in longer and longer Variable expressions (and longer and longer step times). I.e., x0 = (((x0_0 + dx0_1) + dx0_2) + dx0_3) + dx0_4 + ...
Changing these to
x0=x0+ (((f0/gamma)*np.cos(theta) - C*mgrid[idx,idy]*dydx[0][idx,idy])*dt).value
y0=y0+ (((f0/gamma)*np.sin(theta) - C*mgrid[idx,idy]*dydx[1][idx,idy])*dt).value
resolves this issue.
Addressing the warning following the question edit
The warning in the comments about "...has been cast to a constant CellVariable" is due to:
xp=Variable(value=mesh.x-Rx)
yp=Variable(value=mesh.y-Ry)
This is definitely not recommended. This takes the MeshVariable objects mesh.x and mesh.y and then throws away the mesh. When the result is later associated with a mesh, e.g., by multiplying by another MeshVariable or using as a SourceTerm, FiPy warns because it looks like it could fit on the mesh, but it's not on a mesh. Just use
xp=mesh.x-Rx
yp=mesh.y-Ry

Scipy b spline basis functions

Is there a more efficient way in Scipy to generate the b-spline basis functions similar to the recursive code given in the example of the b-spline function:
scipy.interpolate.BSpline
def B(x, k, i, t):
if k == 0:
return 1.0 if t[i] <= x < t[i+1] else 0.0
if t[i+k] == t[i]:
c1 = 0.0
else:
c1 = (x - t[i])/(t[i+k] - t[i]) * B(x, k-1, i, t)
if t[i+k+1] == t[i+1]:
c2 = 0.0
else:
c2 = (t[i+k+1] - x)/(t[i+k+1] - t[i+1]) * B(x, k-1, i+1, t)
return c1 + c2
I tried to use scipy.interpolate.BSpline.basis_element but was unable to generate the same results as the function "B".
The way I have previously done this is by the following:
import scipy.interpolate as si
import numpy as np
import matplotlib.pyplot as plt
nt = 3 # the number of knots
x = np.linspace(0,24,10)
y = np.ones((x.shape)) # the function we wish to interpolate (very trivial in this case)
t = np.linspace(x[0],x[-1],nt)
t = t[1:-1]
tt = np.linspace(0.0, 24, 100)
y_rep = si.splrep(x, y, t=t, k=2)
y_i = si.splev(tt, y_rep)
spl = np.zeros((100,))
plt.figure()
for i in range(nt+1):
vec = np.zeros(nt+1)
vec[i] = 1.0
y_list = list(y_rep)
y_list[1] = vec.tolist()
y_i = si.splev(tt, y_list) # your basis spline function
spl = spl + y_i*y_rep[1][i] # the interpolated function
plt.plot(tt, y_i)
You can have a look at the two functions scipy.interpolate.splrep and scipy.interpolate.splev. They respectively make a B-spline representation and evaluate the B-splines. The above code generates the (in this case) four basis spline functions:
I hope this is what you wanted.

Why is tempered mcmc fit not convering well?

I am trying to fit a simple straight line y=mx+c type to some synthetic data using parallel-tempered mcmc. My goal is to just be able to understand how to use it, so that I can apply to some more complex models later. The example I am trying is the replica of what has already been done in a simple emcee code :
http://dfm.io/emcee/current/user/line/
but instead of using mcmc, I want to use parallel-tempered mcmc:
http://dfm.io/emcee/current/user/pt/
Here is a working code:
import numpy as np
from emcee import PTSampler
import emcee
# Choose the "true" parameters.
m_true = -0.9594
b_true = 4.294
f_true = 0.534
# Generate some synthetic data from the model.
N = 50
x = np.sort(10*np.random.rand(N))
yerr = 0.1+0.5*np.random.rand(N)
y = m_true*x+b_true
y += np.abs(f_true*y) * np.random.randn(N)
y += yerr * np.random.randn(N)
def lnlike(theta, x, y, yerr):
m, b, lnf = theta
model = m * x + b
inv_sigma2 = 1.0/(yerr**2 + model**2*np.exp(2*lnf))
return -0.5*(np.sum((y-model)**2*inv_sigma2 - np.log(inv_sigma2)))
def lnprior(theta):
m, b, lnf = theta
if -5.0 < m < 0.5 and 0.0 < b < 10.0 and -10.0 < lnf < 1.0:
return 0.0
return -np.inf
def lnprob(theta, x, y, yerr):
lp = lnprior(theta)
if not np.isfinite(lp):
return -np.inf
return lp + lnlike(theta, x, y, yerr)
import scipy.optimize as op
nll = lambda *args: -lnlike(*args)
result = op.minimize(nll, [m_true, b_true, np.log(f_true)], args=(x, y, yerr))
m_ml, b_ml, lnf_ml = result["x"]
init = [0.5, m_ml, b_ml, lnf_ml]
ntemps = 10
nwalkers = 100
ndim = 3
from multiprocessing import Pool
pos = np.random.uniform(low=-1, high=1, size=(ntemps, nwalkers, ndim))
for i in range(ntemps):
#initialize parameters near scipy optima
pos[i:,] = np.array([result["x"] + 1e-4*np.random.randn(ndim) for i in range(nwalkers)])
pool = Pool(processes=4)
sampler=PTSampler(ntemps,nwalkers, ndim, lnlike, lnprior, loglargs=(x, y, yerr), pool=pool)# args=(x, y, yerr))
#burn-in
sampler.run_mcmc(pos, 1000)
sampler.reset()
sampler.run_mcmc(pos, 10000, thin=10)
samples = sampler.chain.reshape((-1, ndim))
print('Number of posterior samples is {}'.format(samples.shape[0]))
#print best fit value together with errors
print(map(lambda v: (v[1], v[2]-v[1], v[1]-v[0]),
zip(*np.percentile(samples, [16, 50, 84],
axis=0))))
import corner
fig = corner.corner(samples, labels=["$m$", "$b$", "$\ln\,f$"],
truths=[m_true, b_true, np.log(f_true)])
fig.savefig("triangle.png")
The only problem when running this code is I get optimal parameters value which are way off the true values. And increasing the number of walkers or samples is not helping in any sense. Can anyone please advice why tempered-mcmc is not working here?
Update:
I found out a useful package called ptemcee (https://pypi.org/project/ptemcee/#description), although the documentation of this package is non-existent. It seems that this package might be useful, any help on how to implement the same linear fitting with this package would also be highly appreciated.
I have modified some lines
import time
import numpy as np
from emcee import PTSampler
import corner
import matplotlib.pyplot as plt
import scipy.optimize as op
t1 = time.time()
np.random.seed(6) # To reproduce results
# Choose the "true" parameters.
m_true = -0.9594
b_true = 4.294
f_true = 0.534
# Generate some synthetic data from the model.
N = 50
x = np.sort(10 * np.random.rand(N))
yerr = 0.1 + 0.5 * np.random.rand(N)
y_1 = m_true * x + b_true
y = np.abs(f_true * y_1) * np.random.randn(N) + y_1
y += yerr * np.random.randn(N)
plt.plot(x, y, 'o')
# With emcee
def lnlike(theta, x, y, yerr):
m, b, lnf = theta
model = m * x + b
inv_sigma2 = 1.0/(yerr**2 + model**2*np.exp(2*lnf))
return -0.5*(np.sum((y-model)**2*inv_sigma2 - np.log(inv_sigma2)))
def lnprior(theta):
m, b, lnf = theta
if -5.0 < m < 0.5 and 0.0 < b < 10.0 and -10.0 < lnf < 1.0:
return 0.0
return -np.inf
def lnprob(theta, x, y, yerr):
lp = lnprior(theta)
if not np.isfinite(lp):
return -np.inf
return lp + lnlike(theta, x, y, yerr)
nll = lambda *args: -lnlike(*args)
result = op.minimize(nll, [m_true, b_true, np.log(f_true)], args=(x, y, yerr))
m_ml, b_ml, lnf_ml = result["x"]
init = [0.5, m_ml, b_ml, lnf_ml]
ntemps = 10
nwalkers = 100
ndim = 3
pos = np.random.uniform(low=-1, high=1, size=(ntemps, nwalkers, ndim))
for i in range(ntemps):
pos[i:, :] = np.array([result["x"] + 1e-4*np.random.randn(ndim) for i in range(nwalkers)])
sampler = PTSampler(ntemps, nwalkers, ndim, lnlike, lnprior, loglargs=(x, y, yerr), threads=4) # args=(x, y, yerr))
#burn-in
print(pos.shape)
sampler.run_mcmc(pos, 100)
sampler.reset()
sampler.run_mcmc(pos, 5000, thin=10)
samples = sampler.chain.reshape((-1, ndim))
print('Number of posterior samples is {}'.format(samples.shape[0]))
#print best fit value together with errors
p1, p2, p3 = map(lambda v: (v[1], v[2]-v[1], v[1]-v[0]),
zip(*np.percentile(samples, [16, 50, 84],
axis=0)))
print(p1, '\n', p2, '\n', p3)
fig = corner.corner(samples, labels=["$m$", "$b$", "$\ln\,f$"],
truths=[m_true, b_true, np.log(f_true)])
t2 = time.time()
print('It took {:.3f} s'.format(t2 - t1))
plt.show()
The figure I get with corner is:
The important line is
sampler = PTSampler(ntemps, nwalkers, ndim, lnlike, lnprior, loglargs=(x, y, yerr), threads=4)
I have used threads=4 instead of Pool.
Look closely at this line print(p1, '\n', p2, '\n', p3), it prints the values of m_true, b_true and f_true you get:
(-1.277782877669762, 0.5745273177144817, 2.0813620981463297)
(4.800481378230051, 3.1747356851201163, 2.245189235990341)
(-0.9391847529845194, 1.1196053087321716, 3.6017609114364273)
For f, you need np.exp(-0.93918), which is 0.3909, which is close to 0.534. The values you get are close (-1.277 compared to -0.9594 and 4.8 compared to 4.294), although the errors are not bad (except for f). I mean, are you expecting to get the exact numbers? With this method, in my computer, it takes 111 s to complete, is that normal?
Let's try something different. Let's be clear: the problem is not easy when f_true is added. I will use pymc3 (you don't need to know how to use pymc3, I want to check the results found by emcee).
import time
import numpy as np
import corner
import matplotlib.pyplot as plt
import pymc3 as pm
t1 = time.time()
np.random.seed(6)
# Choose the "true" parameters.
m_true = -0.9594
b_true = 4.294
f_true = 0.534
# Generate some synthetic data from the model.
N = 50
x = np.sort(10 * np.random.rand(N))
yerr = 0.1 + 0.5 * np.random.rand(N)
y_1 = m_true * x + b_true
y = np.abs(f_true * y_1) * np.random.randn(N) + y_1
y += yerr * np.random.randn(N)
plt.plot(x, y, 'o')
with pm.Model() as model: # model specifications in PyMC3 are wrapped in a with-statement
# Define priors
f = pm.HalfCauchy('f', beta=5)
m = pm.Normal('m', 0, sd=20)
b = pm.Normal('b', 0, sd=20)
mu2 = b + m * x
sigma2 = yerr**2 + f**2 * (y_1)**2
post = pm.Normal('y', mu=mu2, sd=pm.math.sqrt(sigma2), observed=y)
with model:
trace = pm.sample(2000, tune=2000)
print(pm.summary(trace))
pm.traceplot(trace)
all_values = np.stack([trace.get_values('b'), trace.get_values('m'), trace.get_values('f')], axis=1)
fig2 = corner.corner(all_values, labels=["$b$", "$m$", "$f$"],
truths=[b_true, m_true, f_true])
t2 = time.time()
print('It took {:.3f} s'.format(t2 - t1))
plt.show()
The summary is
mean sd mc_error hpd_2.5 hpd_97.5 n_eff Rhat
m -0.995545 0.067818 0.001174 -1.123187 -0.857653 2685.610018 1.000121
b 4.398158 0.332526 0.005585 3.767336 5.057909 2746.736563 1.000201
f 0.425442 0.063884 0.000904 0.311037 0.554446 4195.591204 1.000309
The important part is the column mean, you see that the values found by pymc3 are close to the true values. The columns hpd_2.5 and hpd_97.5 are the errors for f, b and m. And it took 14 s.
The figure I get with corner is
You will say that the results of emcee are not quite good, but if you really want more accuracy, you have to modify this function:
def lnprior(theta):
m, b, lnf = theta
if -5.0 < m < 0.5 and 0.0 < b < 10.0 and -10.0 < lnf < 1.0:
return 0.0
return -np.inf
The famous prior. In this case, it is flat, and since there are a lot of priors...

Correct implementation of SI, SIS, SIR models (python)

I have created some very basic implementations of the mentioned models. However, although graphs seem to look right, the numbers don't add up to a constant. That is for the sum of susceptible/infected/recovered people in each compartment should add up to N (which is total number of people), but it doesn't, for some reason it adds up to some bizarre decimal numbers, and I really don't know how to fix it, after looking at it for 3 days now.
The SI Model:
import matplotlib.pyplot as plt
N = 1000000
S = N - 1
I = 1
beta = 0.6
sus = [] # infected compartment
inf = [] # susceptible compartment
prob = [] # probability of infection at time t
def infection(S, I, N):
t = 0
while (t < 100):
S = S - beta * ((S * I / N))
I = I + beta * ((S * I) / N)
p = beta * (I / N)
sus.append(S)
inf.append(I)
prob.append(p)
t = t + 1
infection(S, I, N)
figure = plt.figure()
figure.canvas.set_window_title('SI model')
figure.add_subplot(211)
inf_line, =plt.plot(inf, label='I(t)')
sus_line, = plt.plot(sus, label='S(t)')
plt.legend(handles=[inf_line, sus_line])
plt.ticklabel_format(style='sci', axis='y', scilimits=(0,0)) # use scientific notation
ax = figure.add_subplot(212)
prob_line = plt.plot(prob, label='p(t)')
plt.legend(handles=prob_line)
type(ax) # matplotlib.axes._subplots.AxesSubplot
# manipulate
vals = ax.get_yticks()
ax.set_yticklabels(['{:3.2f}%'.format(x*100) for x in vals])
plt.xlabel('T')
plt.ylabel('p')
plt.show()
SIS Model:
import matplotlib.pylab as plt
N = 1000000
S = N - 1
I = 1
beta = 0.3
gamma = 0.1
sus = \[\]
inf = \[\]
def infection(S, I, N):
for t in range (0, 1000):
S = S - (beta*S*I/N) + gamma * I
I = I + (beta*S*I/N) - gamma * I
sus.append(S)
inf.append(I)
infection(S, I, N)
figure = plt.figure()
figure.canvas.set_window_title('SIS model')
inf_line, =plt.plot(inf, label='I(t)')
sus_line, = plt.plot(sus, label='S(t)')
plt.legend(handles=\[inf_line, sus_line\])
plt.ticklabel_format(style='sci', axis='y', scilimits=(0,0))
plt.xlabel('T')
plt.ylabel('N')
plt.show()
SIR Model:
import matplotlib.pylab as plt
N = 1000000
S = N - 1
I = 1
R = 0
beta = 0.5
mu = 0.1
sus = []
inf = []
rec = []
def infection(S, I, R, N):
for t in range (1, 100):
S = S -(beta * S * I)/N
I = I + ((beta * S * I)/N) - R
R = mu * I
sus.append(S)
inf.append(I)
rec.append(R)
infection(S, I, R, N)
figure = plt.figure()
figure.canvas.set_window_title('SIR model')
inf_line, =plt.plot(inf, label='I(t)')
sus_line, = plt.plot(sus, label='S(t)')
rec_line, = plt.plot(rec, label='R(t)')
plt.legend(handles=[inf_line, sus_line, rec_line])
plt.ticklabel_format(style='sci', axis='y', scilimits=(0,0))
plt.xlabel('T')
plt.ylabel('N')
plt.show()
I'll look only at the SI model.
Your two key variables are S and I. (You may have reversed the meanings of these two variables, though that does not affect what I write here.) You initialize them so their sum is N which is the constant 1000000.
You update your two key variables in the lines
S = S - beta * ((S * I / N))
I = I + beta * ((S * I) / N)
You apparently intend to add to I and subtract from S the same value, so the sum of S and I is unchanged. However, you actually first change S then use that new value to change I, so the values added and subtracted are not actually the same, and the sum of the variables has not remained constant.
You can fix this by using Python's ability to update multiple variables in one line. Replace those two lines with
S, I = S - beta * ((S * I / N)), I + beta * ((S * I) / N)
This calculates both of the new values before updating the variables, so the same value actually added and subtracted from the two variables. (There are other ways to get the same effect, such as temporary variables for the updated values, or one temporary variable to store the amount to add and subtract, but since you use Python you may as well use its capabilities.)
When I now run the program, I get these graphs:
which I think is what you want.
So the solution above worked for the SIS model as well.
As for the SIR model I had to solve differential equations using odeint, here is a simple solution to the SIR model:
import matplotlib.pylab as plt
from scipy.integrate import odeint
import numpy as np
N = 1000
S = N - 1
I = 1
R = 0
beta = 0.6 # infection rate
gamma = 0.2 # recovery rate
# differential equatinons
def diff(sir, t):
# sir[0] - S, sir[1] - I, sir[2] - R
dsdt = - (beta * sir[0] * sir[1])/N
didt = (beta * sir[0] * sir[1])/N - gamma * sir[1]
drdt = gamma * sir[1]
print (dsdt + didt + drdt)
dsirdt = [dsdt, didt, drdt]
return dsirdt
# initial conditions
sir0 = (S, I, R)
# time points
t = np.linspace(0, 100)
# solve ODE
# the parameters are, the equations, initial conditions,
# and time steps (between 0 and 100)
sir = odeint(diff, sir0, t)
plt.plot(t, sir[:, 0], label='S(t)')
plt.plot(t, sir[:, 1], label='I(t)')
plt.plot(t, sir[:, 2], label='R(t)')
plt.legend()
plt.xlabel('T')
plt.ylabel('N')
# use scientific notation
plt.ticklabel_format(style='sci', axis='y', scilimits=(0,0))
plt.show()

Categories

Resources