避免多次评估具有相同输入的函数

Avoid evaluating the function with same input multiple times

我正在尝试利用 scipy.optimise.fsolve 来求解一个函数。我注意到该函数在迭代步骤的开始和结束时多次使用相同的值进行评估。例如,当评估以下代码时:

from scipy.optimize import fsolve

def yy(x):
    print(x)
    return x**2+9*x+20

y = fsolve(yy,22.)

print(y)

得到如下输出:

[ 22.]
[ 22.]
[ 22.]
[ 22.00000033]
[ 8.75471707]
[ 4.34171812]
[ 0.81508685]
[-1.16277103]
[-2.42105811]
[-3.17288066]
[-3.61657372]
[-3.85653348]
[-3.96397335]
[-3.99561793]
[-3.99984826]
[-3.99999934]
[-4.]
[-4.]
[-4.]

因此函数用 22. 计算了 3 次,这是不必要的。

当函数需要大量评估时间时,这尤其烦人。谁能解释一下并建议如何避免这个问题?

第一次评估只是为了检查函数输出的形状和数据类型。具体来说,fsolve 调用 _root_hybr 其中包含 the line

shape, dtype = _check_func('fsolve', 'func', func, x0, args, n, (n,))

当然,_check_func calls the function:

res = atleast_1d(thefunc(*((x0[:numinputs],) + args)))

由于此计算仅保留形状和数据类型,因此求解器将在实际求根过程开始时再次调用值为 x0 的函数。

以上占 一个 个无关调用(两个)。我没有追踪到另一个,但可以想象 FORTRAN 代码自己做了某种初步检查。当很久以前编写的算法被一遍又一遍地包装时,就会发生这种事情。

如果你真的想保存这两个昂贵的函数yy的评估,一种方法是单独计算值yy(x0)并存储它。例如:

def yy(x):
    if x == x0 and y0 is not None:
       return y0
    print(x)
    return x**2+9*x+20

x0 = 22.
y0 = None
y0 = yy(x0)
y = fsolve(yy, x0)

我意识到这个问题的一个重要原因是 fsolve 不适用于这样的问题:应该明智地选择求解器:)

multivariate: fmin, fmin_powell, fmin_cg, fmin_bfgs, fmin_ncg
nonlinear: leastsq
constrained: fmin_l_bfgs_b, fmin_tnc, fmin_cobyla
global: basinhopping, brute, differential_evolution
local: fminbound, brent, golden, bracket
n-dimensional: fsolve
one-dimensional: brenth, ridder, bisect, newton
scalar: fixed_point