scipy.optimize.basinhopping

scipy.optimize.basinhopping(func, x0, niter=100, T=1.0, stepsize=0.5, minimizer_kwargs=None, take_step=None, accept_test=None, callback=None, interval=50, disp=False, niter_success=None, seed=None)[源代码]

使用盆地跳跃算法找出函数的全局最小值。

盆地跳跃是一种将全局步长算法与每一步的局部最小化相结合的两阶段方法。它的设计是为了模拟原子簇能量最小化的自然过程,它很好地解决了“漏斗状,但崎岖的”能量景观的类似问题。 [5].

由于步进、步骤接受和最小化方法都是可定制的,因此该功能还可以用于实现其他两阶段方法。

参数
func :可调用 f(x, *args)可调用

功能需要优化。 args 可以在dict中作为可选项传递 minimizer_kwargs

x0array_like

初步猜测是这样的。

niter整数,可选

盆地跳跃迭代次数。总共会有 niter + 1 运行本地最小化程序。

T浮动,可选

接受或拒绝标准的“温度”参数。较高的“温度”意味着函数值的较大跳跃将被接受。为了获得最佳效果 T 应该与局部极小值之间的间隔(在函数值中)相当。

stepsize浮动,可选

随机置换中使用的最大步长。

minimizer_kwargsDICT,可选

要传递给本地最小化程序的额外关键字参数 scipy.optimize.minimize() 一些重要选项可能包括:

方法应力

最小化方法(例如 "L-BFGS-B" )

参数元组

传递给目标函数的额外参数 (func )及其导数(雅可比、黑森)。

take_step :可调用 take_step(x) ,可选可调用

用此例程替换默认的步进例程。默认的步进例程是坐标的随机位移,但其他步进算法可能更适合某些系统。 take_step 可以选择将属性 take_step.stepsize 。如果此属性存在,则 basinhopping 将进行调整 take_step.stepsize 以试图优化全局最小搜索。

accept_test :可调用, accept_test(f_new=f_new, x_new=x_new, f_old=fold, x_old=x_old) ,可选可调用的,

定义将用于判断是否接受该步骤的测试。这将用于基于“温度”的Metropolis测试之外 T 。可接受的返回值为True、False或 "force accept" 。如果任何测试返回FALSE,则拒绝该步骤。如果是后者,则这将覆盖任何其他测试以接受该步骤。例如,这可以用来强制脱离局部最小值,该局部最小值 basinhopping 被困在。

回调 :可调用, callback(x, f, accept) ,可选可调用的,

将为找到的所有最小值调用的回调函数。 xf 是试验最小值的坐标和函数值,并且 accept 这个最低标准是否被接受。例如,这可以用来保存找到的最低N个最小值。另外, callback 可用于指定用户定义的停止条件,方法是选择性地返回True以停止 basinhopping 例行公事。

interval整数,可选

的更新频率的间隔。 stepsize

disp布尔值,可选

设置为True可打印状态消息

niter_success整数,可选

如果全局最小候选项在此迭代次数内保持相同,则停止运行。

seed :{无,整型, numpy.random.Generator{无,整型,

如果 seed 为无(或 np.random )、 numpy.random.RandomState 使用的是Singleton。如果 seed 是一个整型、一个新的 RandomState 实例,其种子设定为 seed 。如果 seed 已经是一个 GeneratorRandomState 实例,则使用该实例。指定 seed 用于可重复最小化。使用此种子生成的随机数仅影响默认的Metropolis accept_test 和默认设置 take_step 。如果你自己提供的话 take_stepaccept_test ,并且这些函数使用随机数生成,则这些函数负责其随机数生成器的状态。

退货
resOptimizeResult

优化结果表示为 OptimizeResult 对象。重要属性包括: x 解决方案阵列, fun 函数在解处的值,以及 message 它描述了终止的原因。这个 OptimizeResult 由选定的最小化程序返回的最低限度的对象也包含在此对象中,并且可以通过 lowest_optimization_result 属性。看见 OptimizeResult 有关其他属性的说明,请参见。

参见

minimize

局部最小化函数为每个盆地跳跃步骤调用一次。 minimizer_kwargs 传递给此例程。

注意事项

盆地跳跃是一种随机算法,它试图找到一个或多个变量的光滑标量函数的全局最小值 [1] [2] [3] [4] 。目前形式的算法由David Wales和Jonathan Doye描述 [2] http://www-wales.ch.cam.ac.uk/.

该算法是迭代的,每个循环由以下特征组成

  1. 坐标的随机摄动

  2. 局部极小化

  3. 根据最小化函数值接受或拒绝新坐标

这里使用的验收测试是标准蒙特卡罗算法的Metropolis标准,尽管还有许多其他可能性 [3].

这种全局极小化方法已被证明对物理和化学中的各种问题都是极其有效的。当函数有许多由大障碍分隔的最小值时,它特别有用。有关分子系统的数据库,请参阅剑桥集群数据库http://www-wales.ch.cam.ac.uk/CCD.html,这些数据库主要是使用盆地跳跃进行优化的。该数据库包括超过300个自由度的最小化问题。

有关盆地跳跃的FORTRAN实现,请参阅自由软件程序gmin(http://www-wales.ch.cam.ac.uk/GMIN))。该实现具有上述过程的许多不同变体,包括更高级的步进算法和交替的接受标准。

对于随机全局优化,无法确定是否确实找到了真正的全局最小值。取而代之的是,作为一致性检查,该算法可以从多个不同的随机起点运行,以确保在每个示例中找到的最低最小值已经收敛到全局最小值。因此, basinhopping 将在默认情况下仅针对迭代次数运行 niter 并返回找到的最低最小值。这留给用户来确保这实际上是全局最小值。

选择 stepsize :这是中的一个重要参数 basinhopping 这取决于问题的解决。在每个维度中,在从X0-Step Size到X0+Step Size的区域内均匀地选择步长。理想情况下,它应该与正在优化的函数的局部最小值之间的典型间隔(在参数值中)相当。 basinhopping 将在默认情况下调整 stepsize 找到最佳值,但这可能需要多次迭代。如果为设置合理的初始值,您将获得更快的结果 stepsize

选择 T :参数 T 是大都会标准中使用的“温度”。如果满足以下条件,则始终接受Basin跳跃步骤 func(xnew) < func(xold) 。否则,它们被接受的概率为:

exp( -(func(xnew) - func(xold)) / T )

所以,为了达到最好的效果, T 应与局部极小值之间的典型差异(函数值)相当。(局部最小值之间的“墙”的高度无关紧要。)

如果 T 为0时,算法变为单调跳盆,其中所有增加能量的步骤都被拒绝。

0.12.0 新版功能.

参考文献

1

“能源景观”,剑桥大学出版社,剑桥,英国。

2(1,2)

Wales,D J,和Doye J P K,盆地跳跃的全球优化和包含多达110个原子的Lennard-Jones星系团的最低能量结构。物理化学学报A,1997,101,5111。

3(1,2)

李增和谢拉加,H.A.,蛋白质折叠过程中多重极小问题的蒙特卡罗极小化方法。纳特尔。阿卡德。SCI。美国,1987,84,6611。

4

“团簇、晶体和生物分子的全局优化,科学”,1999,285,1368。

5

Olson,B.,Hashmi,I.,Molloy,K.和Shehu1,A.,盆地跳跃作为表征生物大分子的通用和通用优化框架,人工智能进展,卷2012(2012),文章ID 674832, DOI:10.1155/2012/674832

示例

下面的例子是一个一维极小化问题,许多局部极小值叠加在一条抛物线上。

>>> from scipy.optimize import basinhopping
>>> func = lambda x: np.cos(14.5 * x - 0.3) + (x + 0.2) * x
>>> x0=[1.]

Basinhop在内部使用局部最小化算法。我们将使用参数 minimizer_kwargs 告诉BasinHopping使用哪种算法以及如何设置极小化程序。此参数将传递给 scipy.optimize.minimize()

>>> minimizer_kwargs = {"method": "BFGS"}
>>> ret = basinhopping(func, x0, minimizer_kwargs=minimizer_kwargs,
...                    niter=200)
>>> print("global minimum: x = %.4f, f(x0) = %.4f" % (ret.x, ret.fun))
global minimum: x = -0.1951, f(x0) = -1.0009

接下来,考虑一个二维最小化问题。另外,这一次,我们将使用梯度信息来显著加快搜索速度。

>>> def func2d(x):
...     f = np.cos(14.5 * x[0] - 0.3) + (x[1] + 0.2) * x[1] + (x[0] +
...                                                            0.2) * x[0]
...     df = np.zeros(2)
...     df[0] = -14.5 * np.sin(14.5 * x[0] - 0.3) + 2. * x[0] + 0.2
...     df[1] = 2. * x[1] + 0.2
...     return f, df

我们还将使用不同的局部最小化算法。另外,我们必须告诉极小器,我们的函数返回能量和梯度(雅可比)。

>>> minimizer_kwargs = {"method":"L-BFGS-B", "jac":True}
>>> x0 = [1.0, 1.0]
>>> ret = basinhopping(func2d, x0, minimizer_kwargs=minimizer_kwargs,
...                    niter=200)
>>> print("global minimum: x = [%.4f, %.4f], f(x0) = %.4f" % (ret.x[0],
...                                                           ret.x[1],
...                                                           ret.fun))
global minimum: x = [-0.1951, -0.1000], f(x0) = -1.0109

以下是使用自定义分步例程的示例。假设您希望第一个坐标的步长比坐标的睡觉大。这可以按如下方式实现:

>>> class MyTakeStep:
...    def __init__(self, stepsize=0.5):
...        self.stepsize = stepsize
...        self.rng = np.random.default_rng()
...    def __call__(self, x):
...        s = self.stepsize
...        x[0] += self.rng.uniform(-2.*s, 2.*s)
...        x[1:] += self.rng.uniform(-s, s, x[1:].shape)
...        return x

因为 MyTakeStep.stepsize 现有的盆地跳跃将调整 stepsize 来优化搜索。我们将使用与前面相同的2-D函数

>>> mytakestep = MyTakeStep()
>>> ret = basinhopping(func2d, x0, minimizer_kwargs=minimizer_kwargs,
...                    niter=200, take_step=mytakestep)
>>> print("global minimum: x = [%.4f, %.4f], f(x0) = %.4f" % (ret.x[0],
...                                                           ret.x[1],
...                                                           ret.fun))
global minimum: x = [-0.1951, -0.1000], f(x0) = -1.0109

现在,让我们使用一个自定义回调函数来执行一个示例,该函数打印找到的每个最小值的值

>>> def print_fun(x, f, accepted):
...         print("at minimum %.4f accepted %d" % (f, int(accepted)))

这次我们只跑10步跳篮。

>>> rng = np.random.default_rng()
>>> ret = basinhopping(func2d, x0, minimizer_kwargs=minimizer_kwargs,
...                    niter=10, callback=print_fun, seed=rng)
at minimum 0.4159 accepted 1
at minimum -0.4317 accepted 1
at minimum -1.0109 accepted 1
at minimum -0.9073 accepted 1
at minimum -0.4317 accepted 0
at minimum -0.1021 accepted 1
at minimum -0.7425 accepted 1
at minimum -0.9073 accepted 1
at minimum -0.4317 accepted 0
at minimum -0.7425 accepted 1
at minimum -0.9073 accepted 1

-1.0109处的最小值实际上是全局最小值,已经在第8次迭代中找到。

现在,让我们使用自定义的 accept_test

>>> class MyBounds:
...     def __init__(self, xmax=[1.1,1.1], xmin=[-1.1,-1.1] ):
...         self.xmax = np.array(xmax)
...         self.xmin = np.array(xmin)
...     def __call__(self, **kwargs):
...         x = kwargs["x_new"]
...         tmax = bool(np.all(x <= self.xmax))
...         tmin = bool(np.all(x >= self.xmin))
...         return tmax and tmin
>>> mybounds = MyBounds()
>>> ret = basinhopping(func2d, x0, minimizer_kwargs=minimizer_kwargs,
...                    niter=10, accept_test=mybounds)