我想计算Fadeeva
函数special.wofz
的二阶导数。 Fadeeva
函数与错误函数密切相关。因此,如果有人更熟悉erf,那么答案是值得赞赏的。
以下是查找wofz
的二阶导数的代码:
import numpy as np
import matplotlib.pyplot as plt
from scipy.special import wofz
def Z(x):
return wofz(x)
## first derivative of wofz (analytically)
def Zp(x):
return -2/1j/np.pi**0.5 - 2*x*Z(x)
##second derivative (analytically)
def Zpp(x):
return (Z(x)+x*Zp(x))*x
x = np.float64(np.linspace(1e4,14e4,1000))
plt.plot(x, Zpp(x).imag,"-")
Zpp_num=np.diff(Zp(x))/np.diff(x) ##calc numerically the second derivative
plt.plot(x[:-1],Zpp_num.imag)
代码生成下一个数字:
显然,分析计算存在严重问题。我一直在使用的公式是正确的。它必须是一些数字问题。
问:有人能告诉我这种行为的原因是什么吗?是否由于wofz功能的精确性?有谁知道计算wofz
的算法?可以产生可靠结果的论点有多大?我找不到任何关于它的信息。另外,我知道我可以使用wofz
的渐近近似来找到二阶导数,但如果可能的话我想使用scipy
。
答案 0 :(得分:4)
正如您所怀疑的那样,在计算衍生产品时问题是数字来源。正如@clwainwright已经在评论中指出的正确的二阶导数是
Zpp = -2*(Z(x) + x*Zp(x))
这两个术语的虚部表现如下:
显示您有两个几乎相等的小数量,并计算它们的差异。
进一步了解详情,
Zpp = -2*(1-2*x**2)*Z(x) - 4j/sqrt(pi)*x
其虚部是
Im(Zpp) = - 4*x/sqrt(pi) - 2*(1-2*x**2)*Im(Z)
和Im(Z)
与道森函数D
(scipy.special.dawsn
)成比例,
Im(Z) = 2/sqrt(pi) * D
问题在于你有
Im(Zpp(x)) = -4/sqrt(pi)*( x - 2*x**2*dawsn(x) ) - 4/sqrt(pi)*dawsn(x)
为什么这是一个问题是因为the asymptotic expansion of the Dawson function以
开头D(x) ~ 1/(2x) + ...
其首要条款由Im(Zpp(x))
的第一项占用,而小修正则赋予函数其值(实际上,前导项是1/(2x)
的最后一项Im(Zpp(x))
1}}。
因此问题是Zpp
的分析表达所固有的。您可以尝试重塑分析表达式以摆脱这个数值问题(特别是精度损失),但这并不容易。您也可以尝试使用sympy
。我现在已经尝试了一段时间,没有成功。它可能仍然有可能。
答案 1 :(得分:3)
跟进@Andras Deak的回答,您可以分析地找出高x扩展,然后使用一些简单的平滑在它和scipy函数之间进行插值。实际上有两个术语在高x扩展中取消,所以你必须要小心。
这是我得到的答案:
import numpy as np
import matplotlib.pyplot as plt
from scipy.special import wofz
def Z(x):
return wofz(x)
## first derivative of wofz (analytically)
def Zp(x):
return -2/1j/np.pi**0.5 - 2*x*Z(x)
def dawsn_expansion(x):
# Accurate to order x^-9, or, relative to the first term x^-8
# So when x > 100, this will be as accurate as you can get with
# double floating point precision.
y = 0.5 * x**-2
return 1/(2*x) * (1 + y * (1 + 3*y * (1 + 5*y * (1 + 7*y))))
def dawsn_expansion_drop_first(x):
y = 0.5 * x**-2
return 1/(2*x) * (0 + y * (1 + 3*y * (1 + 5*y * (1 + 7*y))))
def dawsn_expansion_drop_first_two(x):
y = 0.5 * x**-2
return 1/(2*x) * (0 + y * (0 + 3*y * (1 + 5*y * (1 + 7*y))))
def blend(x, a, b):
# Smoothly blend x from 0 at a to 1 at b
y = (x - a) / (b - a)
y *= (y > 0)
y = y * (y <= 1) + 1 * (y > 1)
return y*y * (3 - 2*y)
def g(x):
"""Calculate `x + (1-2x^2) D(x)`, where D(x) is the dawson function"""
# For x < 50, use dawsn from scipy
# For x > 100, use dawsn expansion
b = blend(x, 50, 100)
y1 = x + (1 - 2*x**2) * special.dawsn(x)
y2 = dawsn_expansion_drop_first(x) - dawsn_expansion_drop_first_two(x) * 2*x**2
return b*y2 + (1-b)*y1
def Zpp(x):
# only return the imaginary component
return -4j/np.pi**0.5 * g(x)
x = np.logspace(0, 5, 2000)
dx = 1e-3
plt.plot(x, (Zp(x+dx) - Zp(x-dx)).imag/(2*dx))
plt.plot(x, Zpp(x).imag)
ax = plt.gca()
ax.set_xscale('log')
ax.set_yscale('log')
蓝线是数值导数,绿线是使用扩展的导数。后者实际上具有更好的行为x。