为什么scipy.optimize.root使用初始值四次调用回调函数?

时间:2018-02-14 14:26:14

标签: python scipy equation-solving

我正在玩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]

到目前为止一切顺利。我现在想知道为什么它用初始值调用四次?非常感谢你。

3 个答案:

答案 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 ”, 看起来像 ,函数是:

  1. 首先在起点进行评估
  2. 然后在nprint>0请求时调用以启用迭代打印。
  3. 最后在终止前被叫。
  4. 因此可以看到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的某些部分)的设置内容,可能需要查看内部结构。