Minimizing a function using python for data fitting - python

I have a function as the following
q = 1 / sqrt( ((1+z)**2 * (1+0.01*o_m*z) - z*(2+z)*(1-o_m)) )
h = 5 * log10( (1+z)*q ) + 43.1601
I have experimental answers of above equation and once I must to put some data into above function and solve equation below
chi=(q_exp-q_theo)**2/err**2 # this function is a sigma, sigma chi from z=0 to z=1.4 (in the data file)
z, err and q_exp are in the data file(2.txt). Now I have to choose a range for o_m (0.2 to 0.4) and find in what o_m, the chi function will be minimized.
my code is:
from math import *
from scipy.integrate import quad
min = None
l = None
a = None
b = None
c = 0
def ant(z,om,od):
return 1/sqrt( (1+z)**2 * (1+0.01*o_m*z) - z*(2+z)*o_d )
for o_m in range(20,40,1):
o_d=1-0.01*o_m
with open('2.txt') as fp:
for line in fp:
n = list( map(float, line.split()) )
q = quad(ant,n[0],n[1],args=(o_m,o_d))[0]
h = 5.0 * log10( (1+n[1])*q ) + 43.1601
chi = (n[2]-h)**2 / n[3]**2
c = c + chi
if min is None or min>c:
min = c
l = o_m
print('chi=',q,'o_m=',0.01*l)
n[1],n[2],n[3],n[4] are z1, z2, q_exp and err, respectively in the data file. and z1 and z2 are the integration range.
I need your help and I appreciate your time and your attention.
Please do not rate a negative value. I need your answers.

Here is my understanding of the problem.
First I generate some data by the following code
import numpy as np
from scipy.integrate import quad
from random import random
def boxmuller(x0,sigma):
u1=random()
u2=random()
ll=np.sqrt(-2*np.log(u1))
z0=ll*np.cos(2*np.pi*u2)
z1=ll*np.cos(2*np.pi*u2)
return sigma*z0+x0, sigma*z1+x0
def q_func(z, oM, oD):
den= np.sqrt( (1.0 + z)**2 * (1+0.01 * oM * z) - z * (2+z) * (1-oD) )
return 1.0/den
def h_func(z,q):
out = 5 * np.log10( (1.0 + z) * q ) + .25#43.1601
return out
def q_Int(z1,z2,oM,oD):
out=quad(q_func, z1,z2,args=(oM,oD))
return out
ooMM=0.3
ooDD=1.0-ooMM
dataList=[]
for z in np.linspace(.3,20,60):
z1=.1+.1*z*.01*z**2
z2=z1+3.0+.08+z**2
q=q_Int(z1,z2,ooMM,ooDD)[0]
h=h_func(z,q)
sigma=np.fabs(.01*h)
h=boxmuller(h,sigma)[0]
dataList+=[[z,z1,z2,h,sigma]]
dataList=np.array(dataList)
np.savetxt("data.txt",dataList)
which I would then fit in the following way
import matplotlib
matplotlib.use('Qt5Agg')
from matplotlib import pyplot as plt
import numpy as np
from scipy.integrate import quad
from scipy.optimize import leastsq
def q_func(z, oM, oD):
den= np.sqrt( (1.0 + z)**2 * (1+0.01 * oM * z) - z * (2+z) * (1-oD) )
return 1.0/den
def h_func(z,q):
out = 5 * np.log10( (1.0 + z) * q ) + .25#43.1601
return out
def q_Int(z1,z2,oM,oD):
out=quad(q_func, z1,z2,args=(oM,oD))
return out
def residuals(parameters,data):
om,od=parameters
zList=data[:,0]
yList=data[:,3]
errList=data[:,4]
qList=np.fromiter( (q_Int(z1,z2, om,od)[0] for z1,z2 in data[ :,[1,2] ]), np.float)
hList=np.fromiter( (h_func(z,q) for z,q in zip(zList,qList)), np.float)
diffList=np.fromiter( ( (y-h)/e for y,h,e in zip(yList,hList,errList) ), np.float)
return diffList
dataList=np.loadtxt("data.txt")
###fitting
startGuess=[.4,.8]
bestFitValues, cov,info,mesg, ier = leastsq(residuals, startGuess , args=( dataList,),full_output=1)
print bestFitValues,cov
fig=plt.figure()
ax=fig.add_subplot(1,1,1)
ax.plot(dataList[:,0],dataList[:,3],marker='x')
###fitresult
fqList=[q_Int(z1,z2,bestFitValues[0], bestFitValues[1])[0] for z1,z2 in zip(dataList[:,1],dataList[:,2])]
fhList=[h_func(z,q) for z,q in zip(dataList[:,0],fqList)]
ax.plot(dataList[:,0],fhList,marker='+')
plt.show()
giving output
>>[ 0.31703574 0.69572673]
>>[[ 1.38135263e-03 -2.06088258e-04]
>> [ -2.06088258e-04 7.33485166e-05]]
and the graph
Note that for leastsq the covariance matrix is in reduced form and needs to be rescaled.

Unconcsiosely, this question overlap my other question. The correct answer is:
from math import *
import numpy as np
from scipy.integrate import quad
min=l=a=b=chi=None
c=0
z,mo,err=np.genfromtxt('Union2.1_z_dm_err.txt',unpack=True)
def ant(z,o_m): #0.01*o_m is steps of o_m
return 1/sqrt(((1+z)**2*(1+0.01*o_m*z)-z*(2+z)*(1-0.01*o_m)))
for o_m in range(20,40):
c=0
for i in range(len(z)):
q=quad(ant,0,z[i],args=(o_m,))[0] #Integration o to z
h=5*log10((1+z[i])*(299000/70)*q)+25 #function of dL
chi=(mo[i]-h)**2/err[i]**2 #chi^2 test function
c=c+chi
l=o_m
print('chi^2=',c,'Om=',0.01*l,'OD=',1-0.01*l)

Related

Python: how to integrate functions with two unknown parameters numerically

Now I have two functions respectively are
rho(u) = np.exp( (-2.0 / 0.2) * (u**0.2-1.0) )
psi( w(x-u) ) = (1/(4.0 * math.sqrt(np.pi))) * np.exp(- ((w * (x-u))**2) / 4.0) * (2.0 - (w * (x-u))**2)
And then I want to integrate 'rho(u) * psi( w(x-u) )' with respect to 'u'. So that the integral result can be one function with respect to 'w' and 'x'.
Here's my Python code snippet as I try to solve this integral.
import numpy as np
import math
import matplotlib.pyplot as plt
from scipy import integrate
x = np.linspace(0,10,1000)
w = np.linspace(0,10,500)
u = np.linspace(0,10,1000)
rho = np.exp((-2.0/0.2)*(u**0.2-1.0))
value = np.zeros((500,1000),dtype="float32")
# Integrate the products of rho with
# (1/(4.0*math.sqrt(np.pi)))*np.exp(- ((w[i]*(x[j]-u))**2) / 4.0)*(2.0 - (w[i]*(x[j]-u))**2)
for i in range(len(w)):
for j in range(len(x)):
value[i,j] =value[i,j]+ integrate.simps(rho*(1/(4.0*math.sqrt(np.pi)))*np.exp(- ((w[i]*(x[j]-u))**2) / 4.0)*(2.0 - (w[i]*(x[j]-u))**2),u)
plt.imshow(value,origin='lower')
plt.colorbar()
As illustrated above, when I do the integration, I used nesting for loops. We all know that such a way is inefficient.
So I want to ask whether there are methods not using for loop.
Here is a possibility using scipy.integrate.quad_vec. It executes in 6 seconds on my machine, which I believe is acceptable. It is true, however, that I have used a step of 0.1 only for both x and w, but such a resolution seems to be a good compromise on a single core.
from functools import partial
import matplotlib.pyplot as plt
from numpy import empty, exp, linspace, pi, sqrt
from scipy.integrate import quad_vec
from time import perf_counter
def func(u, x):
rho = exp(-10 * (u ** 0.2 - 1))
var = w * (x - u)
psi = exp(-var ** 2 / 4) * (2 - var ** 2) / 4 / sqrt(pi)
return rho * psi
begin = perf_counter()
x = linspace(0, 10, 101)
w = linspace(0, 10, 101)
res = empty((x.size, w.size))
for i, xVal in enumerate(x):
res[i], err = quad_vec(partial(func, x=xVal), 0, 10)
print(f'{perf_counter() - begin} s')
plt.contourf(w, x, res)
plt.colorbar()
plt.xlabel('w')
plt.ylabel('x')
plt.show()
UPDATE
I had not realised, but one can also work with a multi-dimensional array in quad_vec. The updated approach below enables to increase the resolution by a factor of 2 for both x and w, and keep an execution time of around 7 seconds. Additionally, no more visible for loops.
import matplotlib.pyplot as plt
from numpy import exp, mgrid, pi, sqrt
from scipy.integrate import quad_vec
from time import perf_counter
def func(u):
rho = exp(-10 * (u ** 0.2 - 1))
var = w * (x - u)
psi = exp(-var ** 2 / 4) * (2 - var ** 2) / 4 / sqrt(pi)
return rho * psi
begin = perf_counter()
x, w = mgrid[0:10:201j, 0:10:201j]
res, err = quad_vec(func, 0, 10)
print(f'{perf_counter() - begin} s')
plt.contourf(w, x, res)
plt.colorbar()
plt.xlabel('w')
plt.ylabel('x')
plt.show()
Addressing the comment
Just add the following lines before plt.show() to have both axes scale logarithmically.
plt.gca().set_xlim(0.05, 10)
plt.gca().set_ylim(0.05, 10)
plt.gca().set_xscale('log')
plt.gca().set_yscale('log')

python quad: integrating over one variable while treating other variable as constant?

I am doing simple integration, only thing is that I want to keep 'n' as a variable. How can I do this while still integrating over t?
import numpy as np
import matplotlib as mpl
from matplotlib import pyplot as plt
import scipy.integrate as integrate
from scipy.integrate import quad
import math as m
y = lambda t: 3*t
T = 4 #period
n = 1
w = 2*np.pi*n/T
#for odd functions
def integrand(t):
#return y*(2/T)*np.sin(w*t)
return y(t)*np.sin(n*w*t)
Bn = (2/T)*quad(integrand,-T/2,T/2)[0]
print(Bn)
Using quad, you cannot. Perhaps you're looking for symbolic integration like you would do with pen and paper; sympy can help you here:
import sympy
x = sympy.Symbol("x")
t = sympy.Symbol("t")
T = sympy.Symbol("T")
n = sympy.Symbol("n", positive=True)
w = 2 * sympy.pi * n / T
y = 3 * t
out = 2 / T * sympy.integrate(y * sympy.sin(n * w * t), (t, -T/2, T/2))
print(out)
2*(-3*T**2*cos(pi*n**2)/(2*pi*n**2) + 3*T**2*sin(pi*n**2)/(2*pi**2*n**4))/T
If you want to evaluate the integral for many n, quadpy can help:
import numpy as np
from quadpy import quad
y = lambda t: 3 * t
T = 4
n = np.linspace(0.5, 4.5, 20)
w = 2 * np.pi * n / T
# for odd functions
def integrand(t):
return y(t) * np.sin(np.multiply.outer(n * w, t))
Bn = (2 / T) * quad(integrand, -T / 2, T / 2)[0]
print(Bn)
[ 2.95202424 4.88513496 4.77595051 1.32599514 -1.93954768 -0.23784853
1.11558278 -0.95397681 0.63709387 -0.4752673 0.45818227 -0.4740128
0.35943759 -0.01510463 -0.30348511 0.09861289 0.25428048 0.10030723
-0.06099483 -0.13128359]

Solving a complex BVP problem with scipy solve_bvp

I have a complex BVP problem (almost Shrodinger equation). My code works but the solution is completely wrong or just equal to zero. What am I doing wrong?
I also have obtained right solution by Maple, there was no problem. Also I don't understand why there is no problem with plot when it has to be complex-valued function.
import numpy as np
from scipy.integrate import odeint
from scipy.integrate import solve_bvp as bvp
import matplotlib.pyplot as plt
mp = 938.2720813
mn = 939.5654133
mu = (mn + mp)/4
h2m = hbar**2/(2*mu)
V0 = 20
Rv = 1.5
Q0 = 1.5
Rq = 4.5
EIm = 0.3
ERe = 1
V = lambda x : -V0*np.exp(-x/Rv)
Q = lambda x : -Q0*np.exp(-x/Rq)
def fun(x, y):
return np.vstack((y[1], -( Q(x)/ h2m ) - ((ERe + 1j * EIm) *y[0]/ h2m ) + V(x)*y[0]/h2m - (2/y[0])* y[1]))
def bc(ya, yb):
return np.array([ya[0], yb[0]])
x = np.linspace(0, 1000, 10000)
y_a = np.zeros((2, x.size), dtype=np.complex)
# print(x.size)
i = 0
while i < x.size - 1:
i = i + 1
y_a[0, i] = 1000* 1j
y_a[1, i] = 1j
from scipy.integrate import solve_bvp
res_a = solve_bvp(fun, bc, x, y_a)
x_plot = np.linspace(0, 1000, 10000)
y_plot_a = res_a.sol(x_plot)[0]
import matplotlib.pyplot as plt
plt.plot(x_plot, y_plot_a, label='y_a')
plt.legend()
plt.xlabel("x")
plt.ylabel("y")
plt.show()
Upd: I fixed a mistake in the equation.
Result is still wrong. But there is another error - division by zero. How to avoid it? If I choose x = np.linspace(0.1, 1000, 10000) for example it doesn't help.

Specific heat fit using Debye+Einstein model in matplotlib

I am trying to find a fit to a specific heat data using gammaT+mDebye_model+(1-m)*Einstein model as given below.
Cel+ph(T ) = γ T + [αCDebye(T ) + (1 − α)CEinstein(T )]
where the Debye and Einstein models are given by eq. 3 and 4 in the attachment.
I have tried the following code in jupyter notebook following some examples on the web but i have no idea how can i combine these functions together to carry out the fit.
The data is linked https://www.dropbox.com/s/u0r2m3zwl8w77at/HC_ScPtBi.dat?dl=0
Column 1 is Temperature and Column 3 is Y data of interest.
Model is in https://www.dropbox.com/s/9452fq7eydajr5o/Debye.pdf?dl=0
Code is in https://www.dropbox.com/s/hk9b1t0agvt36zn/Untitled2.ipynb?dl=0
from matplotlib import pyplot
import numpy as np
from scipy import integrate
from scipy.optimize import curve_fit
from scipy.integrate import quad
data=np.genfromtxt('HC_ScPtBi.dat', skip_header=1)
R=8.314
n=3
M=1
T=data[10:290,0]
c=data[10:290,2]
def plot_data():
pyplot.scatter(T, c)
pyplot.xlabel('$T [K]$')
pyplot.ylabel('$C$')
plot_data()
def c_einstein(T, T_E):
x = T_E / T
return 3 *n*R*x**2 * np.exp(x) / (np.exp(x) - 1)**2
popt0, pcov0 = curve_fit(c_einstein, T, c, 250)
T_E = popt0[0]
delta_T_E = np.sqrt(pcov0[0, 0])
print(f"T_E = {T_E:.5} ± {delta_T_E:.3} K")
print(popt0)
plot_data()
#temps = np.linspace(10, T[-1], 100)
pyplot.plot(T, c_einstein(T, *popt0));
def integrand(y):
return y**4 * np.exp(y) / (np.exp(y) - 1)**2
#np.vectorize
def c_debye(T, T_D):
x = T / T_D
return 9 *n*R*x**3 * quad(integrand, 0, 1/x)[0]
popt1, pcov1 = curve_fit(c_debye, T, c, 150)
T_D = popt1[0]
delta_T_D = np.sqrt(pcov1[0, 0])
print(f"T_D = {T_D:.5} ± {delta_T_D:.3} K")
print(popt1)
plot_data()
pyplot.plot(T, c_einstein(T, *popt0), label='Einstein')
pyplot.plot(T, c_debye(T, *popt1), label='Debye')
pyplot.legend();
If it might be of any use, I obtained an excellent fit to a modified Weibull peak equation, with R-squared = 0.99999 and RMSE = 0.06878.
def Peak_WeibullPeak_Modified_model(x): # from zunzun.com
a = 6.4654735487019195E+01
b = 3.4517137038577323E+02
c = -1.5940608784806631E+00
d = 2.7331145870203617E+00
return = a * numpy.exp(-0.5 * numpy.power(numpy.log(x/b) / c, d))
You need to combine the Einstein and Debye equations into a single function, which should look something like this:
def func(T, alpha,gamma,T_e,T_d):
fn = lambda y: y**4 * np.exp(y) / (np.exp(y) - 1)**2
einst = (1-alpha)*3*n*R*T_e**2/T**2 * np.exp(T_e/T) / (np.exp(T_e/T) - 1)**2
debye_int = np.array([integrate.quad(fn, 0, T_d/t)[0] for t in T])
debye = alpha*9*n*R*T**3/T_d**3*debye_int
return einst+debye+gamma*T
You can then use that function in the curve fitting
coefs = curve_fit(func, T, c)[0]
plt.plot(T, func(T, *coefs))

python - different array length along interpolation axis?

I am trying to use the Python interpolation function to get the value y for a given x but I am getting the error "raise ValueError("x and y arrays must be equal in length along along interpolation axis" even though my arrays have both equal size and shape (according to what I get when I use .shape in my code). I am quite new to programming so I don't know how to check what else could be different in my arrays. Here is my code:
s = []
def slowroll(y, t):
phi, dphi, a = y
h = np.sqrt(1/3. * (1/2. * dphi**2 + 1/2.*phi**2))
da = h*a
ddphi = -3.*h*dphi - phi
return [dphi,ddphi,da]
phi_ini = 18.
dphi_ini = -0.1
init_y = [phi_ini,dphi_ini,1.]
h_ini =np.sqrt(1/3. * (1/2. * dphi_ini**2. + 1/2.*phi_ini**2.))
t=np.linspace(0.,20.,100.)
from scipy.integrate import odeint
sol = odeint(slowroll, init_y, t)
phi = sol[:,0]
dphi = sol[:,1]
a=sol[:,2]
n=np.log(a)
h = np.sqrt(1/3. * (1/2. * dphi**2 + 1/2.*phi**2))
s.extend(a*h)
x = np.asarray(s)
y = np.asarray(t)
F = interp1d(y, x, kind='cubic')
print F(7.34858263)
After adding in the required imports, I've been unable to duplicate your error with version 2.7.12. What python version are you running?
import numpy as np
from scipy.interpolate import interp1d
s = []
def slowroll(y, t):
phi, dphi, a = y
h = np.sqrt(1/3. * (1/2. * dphi**2 + 1/2.*phi**2))
da = h*a
ddphi = -3.*h*dphi - phi
return [dphi,ddphi,da]
phi_ini = 18.
dphi_ini = -0.1
init_y = [phi_ini,dphi_ini,1.]
h_ini =np.sqrt(1/3. * (1/2. * dphi_ini**2. + 1/2.*phi_ini**2.))
t=np.linspace(0.,20.,100.)
from scipy.integrate import odeint
sol = odeint(slowroll, init_y, t)
phi = sol[:,0]
dphi = sol[:,1]
a=sol[:,2]
n=np.log(a)
h = np.sqrt(1/3. * (1/2. * dphi**2 + 1/2.*phi**2))
s.extend(a*h)
x = np.asarray(s)
y = np.asarray(t)
F = interp1d(y, x, kind='cubic')
print F(7.34858263)
Output:
2.11688518961e+20

Categories