scipy.optimize.

shgo#

scipy.optimize.shgo(func, bounds, args=(), constraints=None, n=100, iters=1, callback=None, minimizer_kwargs=None, options=None, sampling_method='simplicial', *, workers=1)[源代码][源代码]#

使用SHG优化找到函数的全局最小值。

SHGO 代表 “单纯同调全局优化”。

参数:
函数可调用

要最小化的目标函数。必须以 f(x, *args) 的形式,其中 x 是以 1-D 数组形式的参数,args 是任何额外固定参数的元组,这些参数是完整定义函数所需的。

bounds : 序列 或 Bounds序列或

变量的边界。有两种指定边界的方式:

  1. Bounds 类的实例。

  2. x 中每个元素的 (最小值, 最大值) 对序列。

参数tuple, 可选

完全指定目标函数所需的任何额外固定参数。

约束{约束, 字典} 或 {约束, 字典} 的列表, 可选

约束定义。仅适用于COBYLA、COBYQA、SLSQP和trust-constr。有关指定约束的更多详细信息,请参阅教程 [5]

备注

目前只有 COBYLA、COBYQA、SLSQP 和 trust-constr 局部最小化方法支持约束参数。如果在局部优化问题中使用的 constraints 序列未在 minimizer_kwargs 中定义,并且使用了约束方法,那么将使用全局的 constraints。(在 minimizer_kwargs 中定义 constraints 序列意味着 constraints 不会被添加,因此如果需要添加等式约束等,则需要将 constraints 中的不等式函数也添加到 minimizer_kwargs 中)。COBYLA 仅支持不等式约束。

在 1.11.0 版本发生变更: constraints 接受 NonlinearConstraint, LinearConstraint

nint, 可选

用于构建单纯复形的采样点数量。对于默认的 simplicial 采样方法,会生成 2**dim + 1 个采样点,而不是默认的 n=100。对于所有其他指定的值,会生成 n 个采样点。对于 sobolhalton 和其他任意 sampling_methods,会生成 n=100 或另一个指定的采样点数量。

迭代器int, 可选

用于构建单纯复形的迭代次数。默认值为1。

回调可调用,可选

在每次迭代后调用,形式为 callback(xk),其中 xk 是当前的参数向量。

minimizer_kwargsdict, 可选

传递给最小化器 scipy.optimize.minimize 的额外关键字参数。一些重要的选项可能是:

  • 方法str

    最小化方法。如果未指定,将根据问题是否有约束或边界,选择 BFGS、L-BFGS-B、SLSQP 中的一种。

  • 参数元组

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

  • 选项dict, 可选

    请注意,默认情况下,容差指定为 {ftol: 1e-12}

选项dict, 可选

求解器选项的字典。为全局例程指定的许多选项也会传递给 scipy.optimize.minimize 例程。同时传递给局部例程的选项标记为“(L)”。

停止准则,如果满足任何指定的准则,算法将终止。然而,默认算法不需要指定任何停止准则:

  • maxfevint (L)

    在可行域中函数评估的最大次数。(注意只有支持此选项的方法会在精确指定的值处终止程序。否则,该标准仅会在全局迭代期间终止)

  • f_min

    指定已知的最小目标函数值。

  • f_tol浮动

    停止准则中 f 值的精度目标。注意,如果全局例程中的采样点在此容差范围内,全局例程也将终止。

  • maxiter整数

    要执行的最大迭代次数。

  • 最大值整数

    要执行的最大采样评估次数(包括在不可行点中的搜索)。

  • maxtime浮动

    最大允许的处理运行时间

  • minhgrd整数

    最小同调群秩差分。在每次迭代中(近似地)计算目标函数的同调群。该群的秩与目标函数中局部凸子域的数量一一对应(在每个子域包含一个唯一全局最小值的情况下,经过充分采样点后)。如果在 maxhgrd 指定的迭代次数之间,hgr 的差值为 0,算法将终止。

目标函数知识:

  • 对称性列表或布尔值

    指定目标函数是否包含对称变量。在完全对称的情况下,搜索空间(因此性能)最多会减少 O(n!) 倍。如果指定 True,则所有变量将被设置为与第一个变量对称。默认设置为 False。

    例如,f(x) = (x_1 + x_2 + x_3) + (x_4)**2 + (x_5)**2 + (x_6)**2

    在这个方程中,x_2 和 x_3 相对于 x_1 对称,而 x_5 和 x_6 相对于 x_4 对称,这可以指定给求解器为:

    symmetry = [0, # 变量 1

    0, # 对称于变量 1 0, # 对称于变量 1 3, # 变量 4 3, # 对称于变量 4 3, # 对称于变量 4 ]

  • jac布尔值或可调用对象,可选

    目标函数的雅可比矩阵(梯度)。仅适用于 CG、BFGS、Newton-CG、L-BFGS-B、TNC、SLSQP、dogleg、trust-ncg。如果 jac 是一个布尔值且为 True,则假定 fun 返回目标函数及其梯度。如果为 False,则将通过数值方法估计梯度。jac 也可以是一个返回目标函数梯度的可调用对象。在这种情况下,它必须接受与 fun 相同的参数。(自动传递给 scipy.optimize.minimize

  • hess, hessp可调用,可选

    目标函数的 Hessian 矩阵(二阶导数矩阵)或目标函数的 Hessian 矩阵乘以任意向量 p。仅适用于 Newton-CG、dogleg、trust-ncg。只需要提供 hessphess 中的一个。如果提供了 hess,则 hessp 将被忽略。如果既没有提供 hess 也没有提供 hessp,则 Hessian 乘积将使用 jac 上的有限差分进行近似。hessp 必须计算 Hessian 乘以任意向量。(自动传递给 scipy.optimize.minimize

算法设置:

  • minimize_every_iter布尔

    如果为 True,则每次迭代时都会将有前途的全局采样点传递给局部最小化例程。如果为 True,则仅运行最终的最小化器池。默认为 True。

  • local_iter整数

    每轮迭代只评估最佳最小化池候选者中的几个。如果为 False,则所有潜在点都会传递给局部最小化例程。

  • infty_constraints布尔

    如果为 True,则生成的任何超出可行域的采样点将被保存,并赋予目标函数值 inf。如果为 False,则这些点将被丢弃。使用此功能可能会在找到全局最小值之前提高函数评估的性能,指定 False 将使用更少的内存,但会略微降低性能。默认为 True。

反馈:

  • dispbool (L)

    设置为 True 以打印收敛消息。

采样方法str 或 function,可选

当前内置的采样方法选项有 haltonsobolsimplicial。默认的 simplicial 提供了在有限时间内收敛到全局最小值的理论保证。haltonsobol 方法在采样点生成方面速度更快,但代价是失去了保证收敛性。对于大多数“较简单”的问题,其中收敛相对较快,这种方法更为合适。用户定义的采样函数必须接受两个参数,即每次调用时维度为 dimn 个采样点,并输出形状为 n x dim 的采样点数组。

工人int 或类似映射的可调用对象,可选

并行运行本地串行最小化。提供 -1 以使用所有可用的 CPU 核心,或提供一个整数以使用该数量的进程(使用 multiprocessing.Pool)。

或者提供一个类似映射的可调用对象,例如 multiprocessing.Pool.map 用于并行评估。此评估按 workers(func, iterable) 进行。要求 func 是可序列化的。

Added in version 1.11.0.

返回:
res优化结果

优化结果表示为一个 OptimizeResult 对象。重要属性包括:x 对应于全局最小值的解数组,fun 在全局解处的函数输出,xl 局部最小解的有序列表,funl 在相应局部解处的函数输出,success 指示优化器是否成功退出的布尔标志,message 描述终止原因,nfev 包括采样调用在内的目标函数评估总数,nlfev 所有局部搜索优化导致的总目标函数评估数,nit 全局例程执行的迭代次数。

注释

使用单纯同调全局优化进行全局优化 [1]。适用于解决一般目的的NLP和黑箱优化问题,以达到全局最优(低维问题)。

一般来说,优化问题具有以下形式:

minimize f(x) subject to

g_i(x) >= 0,  i = 1,...,m
h_j(x)  = 0,  j = 1,...,p

其中 x 是一个包含一个或多个变量的向量。f(x) 是目标函数 R^n -> Rg_i(x) 是不等式约束,h_j(x) 是等式约束。

可选地,可以使用 bounds 参数为 x 中的每个元素指定上下限。

虽然SHGO的大多数理论优势仅在 f(x) 是Lipschitz光滑函数时得到证明,但该算法在 f(x) 是非连续、非凸和非光滑的更一般情况下,如果使用默认采样方法,也能证明收敛到全局最优 [1]

本地搜索方法可以通过 minimizer_kwargs 参数指定,该参数传递给 scipy.optimize.minimize。默认情况下,使用 SLSQP 方法。通常建议,如果为问题定义了不等式约束,则使用 SLSQPCOBYLACOBYQA 本地最小化方法,因为其他方法不使用约束。

haltonsobol 方法点是通过 scipy.stats.qmc 生成的。任何其他 QMC 方法也可以使用。

参考文献

[1] (1,2)

Endres, SC, Sandrock, C, Focke, WW (2018) “一种用于Lipschitz优化的单纯同调算法”, 《全局优化杂志》。

[2]

Joe, SW 和 Kuo, FY (2008) “构建具有更好二维投影的Sobol’序列”, SIAM J. Sci. Comput. 30, 2635-2654.

[3]

Hock, W 和 Schittkowski, K (1981) “非线性规划代码的测试示例”, 经济与数学系统讲义, 187. Springer-Verlag, 纽约. http://www.ai7.uni-bayreuth.de/test_problem_coll.pdf

[4]

Wales, DJ (2015) “Perspective: Insight into reaction coordinates and dynamics from the potential energy landscape”, Journal of Chemical Physics, 142(13), 2015.

[5]

https://docs.scipy.org/doc/scipy/tutorial/optimize.html#多元标量函数的有约束最小化-minimize

示例

首先考虑最小化Rosenbrock函数,rosen

>>> from scipy.optimize import rosen, shgo
>>> bounds = [(0,2), (0, 2), (0, 2), (0, 2), (0, 2)]
>>> result = shgo(rosen, bounds)
>>> result.x, result.fun
(array([1., 1., 1., 1., 1.]), 2.920392374190081e-18)

请注意,边界决定了目标函数的维度,因此是必需的输入,不过你可以使用 None 或类似 np.inf 的对象来指定空边界,这些将被转换为大浮点数。

>>> bounds = [(None, None), ]*4
>>> result = shgo(rosen, bounds)
>>> result.x
array([0.99999851, 0.99999704, 0.99999411, 0.9999882 ])

接下来,我们考虑Eggholder函数,这是一个具有多个局部最小值和一个全局最小值的问题。我们将展示参数的使用和 shgo 的功能。(https://en.wikipedia.org/wiki/Test_functions_for_optimization)

>>> import numpy as np
>>> def eggholder(x):
...     return (-(x[1] + 47.0)
...             * np.sin(np.sqrt(abs(x[0]/2.0 + (x[1] + 47.0))))
...             - x[0] * np.sin(np.sqrt(abs(x[0] - (x[1] + 47.0))))
...             )
...
>>> bounds = [(-512, 512), (-512, 512)]

shgo 内置了低差异采样序列。首先,我们将输入 Sobol’ 序列的 64 个初始采样点:

>>> result = shgo(eggholder, bounds, n=64, sampling_method='sobol')
>>> result.x, result.fun
(array([512.        , 404.23180824]), -959.6406627208397)

shgo 还返回找到的任何其他局部最小值,这些可以通过以下方式调用:

>>> result.xl
array([[ 512.        ,  404.23180824],
       [ 283.0759062 , -487.12565635],
       [-294.66820039, -462.01964031],
       [-105.87688911,  423.15323845],
       [-242.97926   ,  274.38030925],
       [-506.25823477,    6.3131022 ],
       [-408.71980731, -156.10116949],
       [ 150.23207937,  301.31376595],
       [  91.00920901, -391.283763  ],
       [ 202.89662724, -269.38043241],
       [ 361.66623976, -106.96493868],
       [-219.40612786, -244.06020508]])
>>> result.funl
array([-959.64066272, -718.16745962, -704.80659592, -565.99778097,
       -559.78685655, -557.36868733, -507.87385942, -493.9605115 ,
       -426.48799655, -421.15571437, -419.31194957, -410.98477763])

这些结果在有许多全局最小值且需要其他全局最小值的值的应用中是有用的,或者在局部最小值可以提供系统洞察的情况下(例如物理化学中的形态学 [4])。

如果我们想找到更多的局部最小值,我们可以增加采样点的数量或迭代次数。我们将采样点的数量增加到64,并将迭代次数从默认的1增加到3。使用 simplicial 这将给我们64 x 3 = 192个初始采样点。

>>> result_2 = shgo(eggholder,
...                 bounds, n=64, iters=3, sampling_method='sobol')
>>> len(result.xl), len(result_2.xl)
(12, 23)

注意例如 n=192, iters=1n=64, iters=3 之间的区别。在第一种情况下,最小化池中的有前途的点只处理一次。在后者情况下,每64个采样点处理一次,总共处理3次。

为了演示如何解决具有非线性约束的问题,考虑以下来自 Hock 和 Schittkowski 问题 73(饲料)[Rb2e152d227b3-3]_ 的例子:

minimize: f = 24.55 * x_1 + 26.75 * x_2 + 39 * x_3 + 40.50 * x_4

subject to: 2.3 * x_1 + 5.6 * x_2 + 11.1 * x_3 + 1.3 * x_4 - 5    >= 0,

            12 * x_1 + 11.9 * x_2 + 41.8 * x_3 + 52.1 * x_4 - 21
                -1.645 * sqrt(0.28 * x_1**2 + 0.19 * x_2**2 +
                              20.5 * x_3**2 + 0.62 * x_4**2)      >= 0,

            x_1 + x_2 + x_3 + x_4 - 1                             == 0,

            1 >= x_i >= 0 for all i

[3] 中给出的近似答案是:

f([0.6355216, -0.12e-11, 0.3127019, 0.05177655]) = 29.894378
>>> def f(x):  # (cattle-feed)
...     return 24.55*x[0] + 26.75*x[1] + 39*x[2] + 40.50*x[3]
...
>>> def g1(x):
...     return 2.3*x[0] + 5.6*x[1] + 11.1*x[2] + 1.3*x[3] - 5  # >=0
...
>>> def g2(x):
...     return (12*x[0] + 11.9*x[1] +41.8*x[2] + 52.1*x[3] - 21
...             - 1.645 * np.sqrt(0.28*x[0]**2 + 0.19*x[1]**2
...                             + 20.5*x[2]**2 + 0.62*x[3]**2)
...             ) # >=0
...
>>> def h1(x):
...     return x[0] + x[1] + x[2] + x[3] - 1  # == 0
...
>>> cons = ({'type': 'ineq', 'fun': g1},
...         {'type': 'ineq', 'fun': g2},
...         {'type': 'eq', 'fun': h1})
>>> bounds = [(0, 1.0),]*4
>>> res = shgo(f, bounds, n=150, constraints=cons)
>>> res
 message: Optimization terminated successfully.
 success: True
     fun: 29.894378159142136
    funl: [ 2.989e+01]
       x: [ 6.355e-01  1.137e-13  3.127e-01  5.178e-02] # may vary
      xl: [[ 6.355e-01  1.137e-13  3.127e-01  5.178e-02]] # may vary
     nit: 1
    nfev: 142 # may vary
   nlfev: 35 # may vary
   nljev: 5
   nlhev: 0
>>> g1(res.x), g2(res.x), h1(res.x)
(-5.062616992290714e-14, -2.9594104944408173e-12, 0.0)