使用Python(德拜模型)将具有参数限制的积分函数拟合到数据

Fit an integral function with parametric limit to data with Python (Debye Model)

我正在尝试将电阻率与温度数据拟合到 Bloch-G运行金属电阻率的艾森公式:

function

如您所见,有一个具有参数限制的积分函数。我不知道如何实现 运行 最小二乘拟合的算法。 我想到了:

import matplotlib.pyplot as plt
import numpy as np
import pylab as pl
import scipy as sp
from scipy.optimize import leastsq

#retrieve data from file
data = pl.loadtxt('salita.txt')
Temp = data[:, 0]
Res = data[:, 2]

def debye_func(p, T, r):
    rho0, AD, TD = p
    coeff = AD*np.power(T, 5)/np.power(TD, 4)
    f = np.power(x^5)/np.power(np.sinh(x), 2) #function to integrate
    err_debye = r - rho0 - coeff * #integral???
    return err_debye

p0 = sp.array([0.0001 , 0.00001, 50])

plsq = leastsq(debye_func, p0, args=(Temp, Res))

print plsq

关于如何编写它的想法?

编辑:我的代码变成了:

import matplotlib.pyplot as plt
import numpy as np
import pylab as pl
import scipy as sp
from scipy.optimize import leastsq
from scipy.integrate import quad

#retrieve data from file
data = pl.loadtxt('salita.txt')
Temp = data[:, 0]
Res = data[:, 2]

def debye_integrand(x):
    return np.power(x, 5)/np.power(np.sinh(x), 2)

def debye_func(p, T, r):
    rho0, AD, TD = p
    coeff = AD*np.power(T, 5)/np.power(TD, 4)
    err_debye = r - rho0 - coeff * quad(debye_integrand, 0, TD/(2*T))
    return err_debye

p0 = sp.array([0.0001 , 0.00001, 50])

plsq = leastsq(debye_func, p0, args=(Temp, Res))

print plsq

现在我得到一个 ValueError:

Traceback (most recent call last):
  File "debye.py", line 24, in <module>
    plsq = leastsq(debye_func, p0, args=(Temp, Res))
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/scipy/optimize/minpack.py", line 348, in leastsq
    m = _check_func('leastsq', 'func', func, x0, args, n)[0]
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/scipy/optimize/minpack.py", line 14, in _check_func
    res = atleast_1d(thefunc(*((x0[:numinputs],) + args)))
  File "debye.py", line 19, in debye_func
    err_debye = r - rho0 - coeff * quad(debye_integrand, 0, TD/(2*T))
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/scipy/integrate/quadpack.py", line 247, in quad
    retval = _quad(func,a,b,args,full_output,epsabs,epsrel,limit,points)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/scipy/integrate/quadpack.py", line 296, in _quad
    if (b != Inf and a != -Inf):
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

我认为这意味着我正在为 leastsq 提供一个它不能接受的参数,但我不知道如何修改我的代码。

EDIT2:我用 Maxima 分析求解了我的方程,我得到了

import matplotlib.pyplot as plt
import numpy as np
import pylab as pl
import scipy as sp
from scipy.optimize import leastsq
from scipy.integrate import quad
from scipy.special import zetac
from mpmath import polylog

#retrieve data from file
data = pl.loadtxt('salita.txt')
Temp = data[:, 0]
Res = data[:, 2]

def debye_integrand(x):
    return np.power(x, 5)/np.power(np.sinh(x), 2)

def debye_func(p, T, r, integral):
    rho0, AD, TD = p
    coeff = AD*np.power(T, 5)/np.power(TD, 4)
    den = np.exp(TD/T) -1
    m1 = 5*((TD/(2*T))**4)*np.log(np.exp(TD/(2*T)+1)*(np.exp(TD/T)-1)+120*polylog(5, np.exp(TD/(T))*(1-np.exp(TD/(2*T)))
    m2 = 120*(TD/(2*T))*polylog(4, np.exp(TD/(2*T)))*(np.exp(np.exp(TD/T))-1)+60*((TD/(2*T))**2)*polylog(3, np.exp(TD/(2*T))*(1-np.exp((TD/(2*T)))
    m3 = 20*((TD/(2*T))**3)*polylog(2, np.exp(TD/(2*T))*(np.exp(TD/T)-1)+120**polylog(5, -np.exp(TD/(2*T)))*(1-np.exp(TD/T))
    m4 = 120*(TD/(2*T))*polylog(4, -np.exp(TD/(2*T)))*(np.exp(TD/T)-1)+60*((TD/(2*T))**2)*polylog(3, -np.exp(TD/(2*T)))*(1-np.exp(TD/T))
    m5 = 20*((TD/(2*T))**3)*polylog(2, -np.exp(TD/(2*T)))*(np.exp(TD/T)-1) -2*((TD/(2*T))**5)*np.exp(TD/T)
    m6 = 5*((TD/(2*T))**4)*np.log(1-np.exp(TD/(2*T))*(np.exp(TD/T)-1)
    zeta = 15.0*zetac(5)/2

    integral = (m1+m2+m3+m4+m5+m6)/den +zeta

    err_debye = r - rho0 - coeff * integral
    return err_debye

#initizalied with Einstein model fit
p0 = sp.array([0.00001 , 0.0000001, 70.0])  

plsq = leastsq(debye_func, p0, args=(Temp, Res))

print plsq

m2 表示 SyntaxError: invalid syntax。我试着用数字的方式用循环来做,但我没有成功。

我的.txt文件是here,如果你想试试。第一列是温度,第三列是电阻率。

例如,您可以单独定义被积函数,

def debye_integrand(x,  n):
        return x**n/((np.exp(x) - 1)*(1 - np.exp(-x)))

然后使用scipy.integrate.quad进行数值积分,

from scipy.integrate import quad 

def debye_func(p, T, r):
    # [...] the rest of your code from above here
    err_debye = r - rho0 - coeff * quad(debye_integrand, 0, T/TD, args=(n,))
    return np.sum(err_debye**2)

这是总体思路,可能需要根据您的代码进一步调整。一个理想的解决方案是找到该积分的解析解,或者用 scipy.special 中的经典积分函数重写它,但它可能并不简单(见下文)。

此外,您应该使用更通用的 scipy.opitimize.minimize 函数而不是最小二乘拟合,因为它提供的算法对于非平滑优化更有效和稳健。默认优化方法 BFGS 是一个好的开始。

编辑:实际上,这个积分(对于n=5)有一个解析解,例如,您可以使用 Maxima,

>> integrate(x**5/((exp(x) - 1)*(1 - exp(-x))), x, 0, a)

其中 a 是积分极限,li_k k 阶的多对数函数(参见 mpmath.polylog),ζ 是黎曼 Zeta 函数(参见 scipy.special.zetac)。

不过,根据您的需要,使用数值积分(或预先计算的 table 查找)可能会更快,而不是将所有这些放在一起,然后将其转换为 python.

编辑2:这里是积分解析计算的最终解,

import numpy as np
import mpmath as mp
from scipy.optimize import minimize
from scipy.integrate import quad
import matplotlib.pyplot as plt

def debye_integral_sym_scalar(x):
     """
     Calculate the Debye integral for a scalar using multi precision math,
     as otherwise it overflows with 64bit floats
     """
     exp_x = mp.exp(x)

     m1 = -120*mp.polylog(5, exp_x)
     m2 = 120*x*mp.polylog(4, exp_x)
     m3 = -60*x**2*mp.polylog(3, exp_x)
     m4 = 20*x**3*mp.polylog(2, exp_x)
     m5 = 5*x**4*mp.log(1 - exp_x)
     m6 = - x**5*exp_x

     return m1 + m2 + m3 + m4 + m5 + m6/(exp_x - 1) + 120*mp.zeta(5)

 # this is the actual function that we can use

def debye_integral_sym(x):
     f =  np.vectorize(debye_integral_sym_scalar, otypes=[np.complex])
     return f(x).real

def debye_integrand(x,  n):
     return x**n/((np.exp(x) - 1)*(1 - np.exp(-x)))

# test that debye_integral_sym returns the same result as quad
a = 10.0 
res0 =  quad(debye_integrand, 0, a, args=(5,))[0]
res1 = debye_integral_sym(a)
np.testing.assert_allclose(res0, res1)

def resistivity_fit(p, T):
    rho0, AD, TD = p
    coeff = AD*np.power(T, 5)/np.power(TD, 4)
    return rho0 + coeff * debye_integral_sym(TD/(2*T))


def debye_err_func(p, T, r):
    return  np.sum((r - resistivity_fit(p, T))**2)

# wget "http://pastebin.com/raw.php?i=tvzcdxYA" -O salita.txt
data = np.loadtxt('salita.txt')

temp_exp = data[:, 0]
res_exp = data[:, 2]

p0 = np.array([0.0001 , 0.00001, 50])
p_opt = minimize(debye_err_func, p0, args=(temp_exp, res_exp))

print p_opt

temp = np.linspace(temp_exp.min(), temp_exp.max(), 100)

plt.plot(temp_exp, res_exp, '.', label='Experimental data')
plt.plot(temp, resistivity_fit(p_opt.x, temp), 'r', label='Bloch-Gruneisen fit')
plt.legend(loc='best')
plt.xlabel('Temperature [K]')
plt.ylabel('Resistivity')
plt.show()

有了优化函数的输出,

   status: 0
   success: True
   njev: 5
   nfev: 25
   hess_inv: array([[  7.32764243e-01,  -4.89555962e-01,  -1.93879729e-08],
   [ -4.89555962e-01,   3.27690582e-01,  -2.09510086e-08],
   [ -1.93879729e-08,  -2.09510086e-08,   1.00000000e+00]])
   fun: 1.784420370873494e-11
   x: array([  9.96468440e-06,   7.40349389e-06,   5.00000000e+01])
   message: 'Optimization terminated successfully.'
   jac: array([ -1.11880569e-06,   1.28115957e-06,   2.31303410e-12])

以及由此产生的情节,