我正在玩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]
到目前为止一切顺利。我现在想知道为什么它用初始值调用四次?非常感谢你。
答案 0 :(得分:2)
它们实际上并不相同。一种可能的方法是更改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变化可能仍然低于我们的机器精度,我们什么都看不到。
<强> 修改 强>
答案在堡垒source code。 通过在源代码中搜索“调用fcn(n,x ”, 看起来像 ,函数是:
nprint>0
请求时调用以启用迭代打印。因此可以看到3张照片。
如果遵循迭代的打印现在“打开”,雅各比的数字(和打印)估计开始。
答案 1 :(得分:2)
根查找例程首先调用input sanitizer,它将调用以初始值传入的函数。 (第一次评估)
然后默认的Powell root-finder(你正在使用的那个)将调用其内部MINPACK例程hybrd
,它将在开始时评估一次(初始值的第二次评估) 。然后hybrd
调用fdjac1
在此位置找到近似雅可比行列式。这需要两次评估,一次是值本身(第三次!),另一次是前进,这是第四次调用,参数略有不同,正如卡纳克的答案中所解释的那样。
编辑:当调用函数的成本很高时,重复的回调评估可能是非常不受欢迎的。如果是这种情况,则可以memoize该函数,以便可以避免使用相同输入的重复评估,而无需打开数字例程的黑盒子。使用纯函数(即没有副作用的函数),Memoization工作正常,这通常是将数值函数传递给根查找或最小化例程时的情况。
答案 2 :(得分:1)
您没有提供雅可比信息,因此使用了numerical-differentiation。因此,调用func
的次数高于内部迭代次数。
这也意味着,第一个x值不相同,但它们接近机器精度。将numpy的printoptions更改为precision=15
还不足以观察到这一点!
默认优化工具为this one,文档说:
eps:float
雅可比行列式的前向差分近似的合适步长(对于fprime = None)。如果eps小于机器精度,则假定函数中的相对误差是机器精度的顺序。
编辑:好像我错了!
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 cache x-arguments的某些部分)的设置内容,可能需要查看内部结构。