如何在SciPy.optimize.minimize中定义不连续的边界

时间:2018-05-15 14:58:59

标签: python optimization scipy boundary

我使用scipy.optimize.minimize' SLSQP'方法,根据文件:

  

bounds:sequence,optional

     

变量的界限(仅适用于L-BFGS-B,TNC和SLSQP)。 (min,max)对> x中的每个元素对,定义该参数的边界。当在该方向上没有绑定时,使用None作为min> gt或max之一。

我想知道是否有可能为变量x定义一个不连续的边界,如(0,15)& (30,50); (x介于0到15之间,介于30和50之间)

或者还有其他更好的方法来实现这个目标吗?

提前谢谢你们!

2 个答案:

答案 0 :(得分:2)

  

x介于0和15之间,介于30和50之间

这会使模型不可行。没有这样的x。你可能意味着:

x介于0和15之间 OR 介于30和50之间

这是非凸的,因此标准的局部求解器会遇到麻烦。它通常使用额外的二进制变量建模:

30 δ ≤ x ≤ 15(1-δ) + 50 δ  
δ ∈ {0,1}

当然,这假设您可以处理二进制变量(SLSQP不能)。具有二元变量和非线性约束(或目标函数)的模型称为MINLP模型(混合整数非线性编程)。这些类型的模型的求解器很容易获得。

其他可行的方法:

  • 解决问题两次。一次使用0 ≤ x ≤ 15,一次使用30 ≤ x ≤ 50。然后选择最佳解决方案。
  • 使用scipy.optimize.basinhopping全局求解器帮助您摆脱局部最优。这不是一个严格的算法(没有保证),但它可以帮助。

一些通常不起作用的方法:

  • 而不是二进制变量δ ∈ {0,1}使用连续变量δ ∈ [0,1]并添加约束δ(1-δ)=0。通常这会让你陷入困境。
  • 另一个答案中的多项式方法:这也是非凸的,并不适合SLSQP。你将陷入局部最佳状态。
  • 如果x ∈ [15,30],则为目标添加惩罚。这也不适用于本地解算器。

答案 1 :(得分:2)

这是尝试实施流域购物方法described by Erwin Kalvelagen

首先,构造一个根在0,15,30和50的多项式 在所需区域是正面的:

In [123]: x = np.linspace(-1, 51, 100)

In [124]: plt.plot(x,-(x-0)*(x-15)*(x-30)*(x-50))
Out[124]: [<matplotlib.lines.Line2D at 0x7fa01d65b748>]

In [125]: plt.axhline(color='red')
Out[145]: <matplotlib.lines.Line2D at 0x7fa01d6620b8>

In [146]: plt.show()

enter image description here

现在您可以将该多项式用作约束:

import numpy as np
import scipy.optimize as optimize

cons = (
    {'type': 'ineq', 'fun': lambda x: -(x-0)*(x-15)*(x-30)*(x-50)}, )

def f(x):
    return (x-20)**2

res = optimize.basinhopping(f, [40], minimizer_kwargs={'method':'SLSQP', 'constraints': cons}, niter=10, stepsize=20)
print(res)

产量

 message: 'Optimization terminated successfully.'
    nfev: 13
     nit: 4
    njev: 4
  status: 0
 success: True
       x: array([15.])
                    message: ['requested number of basinhopping iterations completed successfully']
      minimization_failures: 0
                       nfev: 178
                        nit: 10
                       njev: 56
                          x: array([15.])

请注意,初始猜测位于40,而optimize.basinhopping设法在x = 15的另一个非连续间隔中找到最小值。 使用两个间隔之间距离的步长非常重要,以便basinhopping有机会从两个区间进行采样。

如果没有使用购物盆,optimize.minimize使用具有非凸约束的SLSQP可能无法从所有允许的间隔中进行采样。例如,

import scipy.optimize as optimize

cons = ({'type': 'ineq', 'fun': lambda x: -(x-0)*(x-15)*(x-30)*(x-50)}, )

def f(x):
    return (x-20)**2

res = optimize.minimize(
    f, [40], method='SLSQP', constraints=cons)
print(res.x)
# [30.]

res = optimize.minimize(
    f, [5], method='SLSQP', constraints=cons)
print(res.x)
# [15.]

表示您必须在每个间隔中猜测两次optimize.minimize 为了找到真正的最小值。