用二次多项式拟合函数 f(x,y,z)

Fitting a function f(x,y,z) with a quadratic polynomial

我正在尝试用以下二次多项式拟合函数 f(x,y,z):

3d polynomial

在三个维度上有些扭曲的球面。该问题与固体物理中有效质量的计算有关。

这是一张数据图片,显示它确实在所有方向上呈抛物线下降,即使 z 方向的曲率相当低: 3d parabolas

我对对应于有效质量的系数感兴趣。我有一个 xyz 坐标数组,它是规则的并且以最大值为中心:

[[ 0.          0.          0.        ]
 [ 0.          0.          0.01282017]
 [ 0.          0.          0.02564034]
 ...
 [-0.05026321 -0.05026321 -0.03846052]
 [-0.05026321 -0.05026321 -0.02564034]
 [-0.05026321 -0.05026321 -0.01282017]]

以及相应的一维标量值数组,每个点一个。此最大值附近的数据点数量范围为 100 到 1000。

这是我目前尝试用于拟合的代码:

def func(data, mxx, mxy, mxz, myy, myz, mzz):
    x = data[:, 0]
    y = data[:, 1]
    z = data[:, 2]
    return (
        (1 / (2 * mxx)) * (x ** 2)
        + (1 / (1 * mxy)) * (x * y)
        + (1 / (1 * mxz)) * (x * z)
        + (1 / (2 * myy)) * (y ** 2)
        + (1 / (1 * myz)) * (y * z)
        + (1 / (2 * mzz)) * (z ** 2)
    ) + f(0, 0, 0)

energy = data[:, 3]
guess = (mxx, mxy, mxz, myy, myz, mzz)
params, pcov = scipy.optimize.curve_fit(
    func, data, energy, p0=guess, method="trf"
)

其中 f(0,0,0) 是函数在 (0, 0, 0) 处的值,我使用 scipy.interpolate.griddata 函数检索它。

对于这个问题,粗略地说,质量应该是负数,其值介于-0.2 和-2 之间。我正在通过有限差分微分创建猜测值。

但是,我没有从 scipy.interpolate.curve_fit 中得到任何有意义的结果——通常系数以巨大的数字结尾(比如 1e9)。在这一点上我完全迷路了。

我做错了什么:(?

其中一个问题是您适合 1/m。虽然从物理学的角度来看这是正确的,但从算法的角度来看却很糟糕。如果拟合算法需要更改接近零的 m 值的符号,则系数会发散。因此,最好拟合 mI = 1/m 并在以后进行相应的错误级数。这里我使用 leastsq,这需要对协方差矩阵进行一些额外的计算(因为它是 returns 的简化形式)。我用 g() 和反质量进行拟合,但是您可以在引入 f() 并直接拟合 m 时立即重现您的问题。

第二点是数据有一个偏移量,即如果x = y = z = 0数据是v= -0.0195,这需要引入到模型中。

最后,我要说的是,您的数据中已经存在非抛物线行为。

尽管如此,它是这样的:

import matplotlib.pyplot as plt
import numpy as np
np.set_printoptions(linewidth=300)

from scipy.optimize import leastsq
from scipy.optimize import curve_fit

data = np.loadtxt( "silicon.csv", delimiter=',' )

def f( x, y, z, mxx, mxy, mxz, myy, myz, mzz, offI ):
    out = 1./(2 * mxx) * x * x
    out += 1./( mxy ) * x * y
    out += 1./( mxz ) * x * z
    out += 1./( 2 * myy ) * y * y
    out += 1./( myz ) * y * z
    out += 1./( 2 * mzz ) * z * z
    out += 1./offI
    return out

def g( x, y, z, mxxI, mxyI, mxzI, myyI, myzI, mzzI, off ):
    out = mxxI / 2 * x * x
    out += mxyI  * x * y
    out += mxzI * x * z
    out += myyI / 2 * y * y
    out += myzI  * y * z
    out += mzzI / 2  * z * z
    out += off
    return out


def residuals( params, indata ):
    out = list()
    for x, y, z, v in indata:
        out.append( v - g( x,y, z, *params ) )
    return out

sol, cov, info, msg, ier = leastsq( residuals,  7*[0], args=( data, ), full_output=True)

s_sq = sum( [x**2 for x in residuals( sol, data) ] )/ (len( data ) - len( sol ) )
print "solution"
print sol

masses = [1/x for x in sol]
print "masses:"
print masses

print "covariance matrix:"
covMX = cov * s_sq
print covMX

print "sum of residuals"
print sum( residuals( sol, data) )

### plotting the cuts

fig = plt.figure('cuts')
ax = dict()
for i in range( 1, 10 ):
    ax[i] = fig.add_subplot( 3, 3, i )

dl = np.linspace( -.2, .2, 25)

#### xx
xdata = [ [ x, v ] for x,y,z,v in data if ( abs(y)<1e-3 and abs(z) < 1e-3 ) ]
vl = np.fromiter( ( f( x, 0, 0, *masses ) for x in dl ), np.float )
ax[1].plot( *zip(*sorted( xdata ) ), ls='', marker='o')
ax[1].plot( dl, vl )

#### xy
xydata = [ [ x, v ] for x, y, z, v in data if ( abs( x - y )<1e-2 and abs(z) < 1e-3 ) ]
vl = np.fromiter( ( f( xy, xy, 0, *masses ) for xy in dl ), np.float )
ax[2].plot( *zip(*sorted( xydata ) ), ls='', marker='o')
ax[2].plot( dl, vl )

#### xz
xzdata = [ [ x, v ] for x, y, z, v in data if ( abs( x - z )<1e-2 and abs(y) < 1e-3 ) ]
vl = np.fromiter( ( f( xz, 0, xz, *masses ) for xz in dl ), np.float )
ax[3].plot( *zip(*sorted( xzdata ) ), ls='', marker='o')
ax[3].plot( dl, vl )

#### yy
ydata = [ [ y, v ] for x, y, z, v in data if ( abs(x)<1e-3 and abs(z) < 1e-3 ) ]
vl = np.fromiter( ( f( 0, y, 0, *masses ) for y in dl ), np.float )
ax[5].plot( *zip(*sorted( ydata ) ), ls='', marker='o' )
ax[5].plot( dl, vl )

#### yz
yzdata = [ [ y, v ] for x, y, z, v in data if ( abs( y - z )<1e-2 and abs(x) < 1e-3 ) ]
vl = np.fromiter( ( f( 0, yz, yz, *masses ) for yz in dl ), np.float )
ax[6].plot( *zip(*sorted( yzdata ) ), ls='', marker='o')
ax[6].plot( dl, vl )

#### zz
zdata = [ [ z, v ] for x, y, z, v in data if ( abs(x)<1e-3 and abs(y) < 1e-3 ) ]
vl = np.fromiter( ( f( 0, 0, z, *masses ) for z in dl ), np.float )
ax[9].plot( *zip(*sorted( zdata ) ), ls='', marker='o' )
ax[9].plot( dl, vl )

#### some diag
ddata = [ [ z, v ] for x, y, z, v in data if ( abs(x - y)<1e-3 and abs(x - z) < 1e-3 ) ]
vl = np.fromiter( ( f( d, d, d, *masses ) for d in dl ), np.float )
ax[7].plot( *zip(*sorted( ddata ) ), ls='', marker='o' )
ax[7].plot( dl, vl )

#### some other diag
ddata = [ [ z, v ] for x, y, z, v in data if ( abs(x - y)<1e-3 and abs(x + z) < 1e-3 ) ]
vl = np.fromiter( ( f( d, d, -d, *masses ) for d in dl ), np.float )
ax[8].plot( *zip(*sorted( ddata ) ), ls='', marker='o' )
ax[8].plot( dl, vl )

plt.show()

这给出了以下输出:

solution
[-1.46528595  0.25090717  0.25090717 -1.46528595  0.25090717 -1.46528595 -0.01993436]
masses:
[-0.6824606499739905, 3.985537743156507, 3.9855376943660676, -0.6824606473928339, 3.9855377322848344, -0.6824606467055248, -50.16463861555409]
covariance matrix:
[
[ 4.76417852e-03 -1.46907683e-12 -8.57639600e-12 -2.21281938e-12 -2.38444957e-12  8.42981521e-12 -2.70034183e-05]
[-1.46907683e-12  9.17104397e-04 -7.10573582e-13  1.32125214e-11  7.44553140e-12  1.29909935e-11 -1.11259046e-13]
[-8.57639600e-12 -7.10573582e-13  9.17104389e-04 -8.60004172e-12 -6.14797647e-12  8.27070243e-12  3.11127064e-14]
[-2.21281914e-12  1.32125214e-11 -8.60004172e-12  4.76417860e-03 -4.20477032e-12  9.20893224e-12 -2.70034186e-05]
[-2.38444957e-12  7.44553140e-12 -6.14797647e-12 -4.20477032e-12  9.17104395e-04  1.50963408e-11 -7.28889534e-14]
[ 8.42981530e-12  1.29909935e-11  8.27070243e-12  9.20893175e-12  1.50963408e-11  4.76417849e-03 -2.70034182e-05]
[-2.70034183e-05 -1.11259046e-13  3.11127064e-14 -2.70034186e-05 -7.28889534e-14 -2.70034182e-05  5.77019926e-07]
]
sum of residuals
4.352727352163743e-09

...这里的一些 1d 切割显示出与抛物线行为的一些显着偏差,如果一个不在其中一个主轴上的话。