为什么scipy.optimize.root调用了四次初始值的回调函数?
Why does scipy.optimize.root call the callback function with the initial value four times?
我正在研究 scipy.optimize.root
,我正在尝试了解它的作用和工作原理。
我的示例代码如下:
import numpy as np
from scipy.optimize import root
def func(x):
""" test function x + 2 * np.cos(x) = 0 """
f = x + 2 * np.cos(x)
print x,f
return f
def main():
sol = root(func, 0.3)
if __name__ == "__main__":
main()
使用 func 中的 print 语句,我得到以下输出:
[ 0.3] [ 2.21067298]
[ 0.3] [ 2.21067298]
[ 0.3] [ 2.21067298]
[ 0.3] [ 2.21067298]
[-5.10560121] [-4.33928627]
[-1.52444136] [-1.43176461]
[-0.80729233] [ 0.57562174]
[-1.01293614] [ 0.0458079]
[-1.03071618] [-0.0023067]
[-1.02986377] [ 7.49624786e-06]
[-1.02986653] [ 1.20746968e-09]
[-1.02986653] [ -6.66133815e-16]
到目前为止一切顺利。我现在想知道为什么它用初始值调用四次?非常感谢你。
它们实际上是不一样的。一种潜在的查看方式是更改 print options.
if __name__ == "__main__":
np.set_printoptions(precision=15)
main()
此后输出
[0.3] [2.210672978251212]
[0.3] [2.210672978251212]
[0.3] [2.210672978251212]
[0.300000004470348] [2.210672980079404]
...
在这种情况下,我们很幸运:epsilonic 变化可能仍然低于我们的机器精度,我们什么也看不到。
编辑
答案在 fortran source code 中。
在源码中搜索“call fcn(n,x”,看起来函数是:
- 首先在起点评估
- 然后在
nprint>0
请求时调用以启用迭代打印。
- 最后在终止前调用。
因此一共看到 3 幅画。
如果现在迭代的打印是 "turned on",那么 Jacobian 的数值(和打印)估计就开始了。
您没有提供 jacobian 信息,所以使用 numerical-differentiation。因此 func
被调用的次数高于内部迭代次数。
这也意味着,第一个 x 值不相同,但它们接近 机器精度。将 numpy 的 printoptions 更改为 precision=15
不足以观察到这一点!
默认优化器是 this one 并且文档说:
eps : float
A suitable step length for the forward-difference approximation of the Jacobian (for fprime=None). If eps is less than the machine precision, it is assumed that the relative errors in the functions are of the order of the machine precision.
编辑:看来我错了!
print(x,f)
print('hash x: ', hash(x[0].item()))
输出:
[0.3] [2.21067298]
hash x: 691752902764108160
[0.3] [2.21067298]
hash x: 691752902764108160
[0.3] [2.21067298]
hash x: 691752902764108160
[0.3] [2.21067298]
hash x: 691752913072029696
那些似乎确实是相同的数字(如果hash
中没有隐藏魔法)!如果需要一些设置内容而不是缓存(scipy.optimize缓存x参数的某些部分),可能需要查看内部结构。
寻根例程首先会调用一个input sanitizer,它会调用以初始值传入的函数。 (第一次评估)
然后默认Powell root-finder (which is the one you're using) will call its internal MINPACK routine hybrd
, which will evaluate once at the beginning (2nd evaluation at the initial value). Then hybrd
calls fdjac1
在此位置求近似雅可比。这需要两次评估,一次评估值本身(第 3 次!),另一个评估前一步,这是第四次调用,参数略有不同,如 Kanak 的回答中所述。
编辑:当调用函数的成本很高时,重复的回调评估可能是非常不受欢迎的。如果是这种情况,可以 memoize 函数以避免对相同输入进行重复计算,而无需打开数值例程的黑框。记忆化在纯函数(即没有副作用的函数)上工作得很好,当将数值函数传递给求根或最小化例程时,情况通常如此。
我正在研究 scipy.optimize.root
,我正在尝试了解它的作用和工作原理。
我的示例代码如下:
import numpy as np
from scipy.optimize import root
def func(x):
""" test function x + 2 * np.cos(x) = 0 """
f = x + 2 * np.cos(x)
print x,f
return f
def main():
sol = root(func, 0.3)
if __name__ == "__main__":
main()
使用 func 中的 print 语句,我得到以下输出:
[ 0.3] [ 2.21067298]
[ 0.3] [ 2.21067298]
[ 0.3] [ 2.21067298]
[ 0.3] [ 2.21067298]
[-5.10560121] [-4.33928627]
[-1.52444136] [-1.43176461]
[-0.80729233] [ 0.57562174]
[-1.01293614] [ 0.0458079]
[-1.03071618] [-0.0023067]
[-1.02986377] [ 7.49624786e-06]
[-1.02986653] [ 1.20746968e-09]
[-1.02986653] [ -6.66133815e-16]
到目前为止一切顺利。我现在想知道为什么它用初始值调用四次?非常感谢你。
它们实际上是不一样的。一种潜在的查看方式是更改 print options.
if __name__ == "__main__":
np.set_printoptions(precision=15)
main()
此后输出
[0.3] [2.210672978251212]
[0.3] [2.210672978251212]
[0.3] [2.210672978251212]
[0.300000004470348] [2.210672980079404]
...
在这种情况下,我们很幸运:epsilonic 变化可能仍然低于我们的机器精度,我们什么也看不到。
编辑
答案在 fortran source code 中。 在源码中搜索“call fcn(n,x”,看起来函数是:
- 首先在起点评估
- 然后在
nprint>0
请求时调用以启用迭代打印。 - 最后在终止前调用。
因此一共看到 3 幅画。
如果现在迭代的打印是 "turned on",那么 Jacobian 的数值(和打印)估计就开始了。
您没有提供 jacobian 信息,所以使用 numerical-differentiation。因此 func
被调用的次数高于内部迭代次数。
这也意味着,第一个 x 值不相同,但它们接近 机器精度。将 numpy 的 printoptions 更改为 precision=15
不足以观察到这一点!
默认优化器是 this one 并且文档说:
eps : float
A suitable step length for the forward-difference approximation of the Jacobian (for fprime=None). If eps is less than the machine precision, it is assumed that the relative errors in the functions are of the order of the machine precision.
编辑:看来我错了!
print(x,f)
print('hash x: ', hash(x[0].item()))
输出:
[0.3] [2.21067298]
hash x: 691752902764108160
[0.3] [2.21067298]
hash x: 691752902764108160
[0.3] [2.21067298]
hash x: 691752902764108160
[0.3] [2.21067298]
hash x: 691752913072029696
那些似乎确实是相同的数字(如果hash
中没有隐藏魔法)!如果需要一些设置内容而不是缓存(scipy.optimize缓存x参数的某些部分),可能需要查看内部结构。
寻根例程首先会调用一个input sanitizer,它会调用以初始值传入的函数。 (第一次评估)
然后默认Powell root-finder (which is the one you're using) will call its internal MINPACK routine hybrd
, which will evaluate once at the beginning (2nd evaluation at the initial value). Then hybrd
calls fdjac1
在此位置求近似雅可比。这需要两次评估,一次评估值本身(第 3 次!),另一个评估前一步,这是第四次调用,参数略有不同,如 Kanak 的回答中所述。
编辑:当调用函数的成本很高时,重复的回调评估可能是非常不受欢迎的。如果是这种情况,可以 memoize 函数以避免对相同输入进行重复计算,而无需打开数值例程的黑框。记忆化在纯函数(即没有副作用的函数)上工作得很好,当将数值函数传递给求根或最小化例程时,情况通常如此。