如何在 SciPy.optimize.minimize 中定义不连续的边界
How to define discontinuous boundaries in SciPy.optimize.minimize
根据文档,我正在使用 scipy.optimize.minimize 'SLSQP' 方法:
bounds : sequence, optional
Bounds for variables (only for L-BFGS-B, TNC and SLSQP). (min, max) pairs for >each element in x, defining the bounds on that parameter. Use None for one of min >or max when there is no bound in that direction.
我想知道是否可以为变量 x 定义一个不连续的边界,例如 (0,15) & (30,50); (x介于0到15和30到50之间)
或者有没有其他更好的方法可以达到这个目的?
先谢谢大家了!
x is between 0 and 15 and between 30 and 50
这会使模型不可行。没有这样的x
。你的意思可能是:
x 在 0 到 15 之间 OR 在 30 到 50
之间
这是非凸的,因此标准的局部求解器对此有问题。它通常用一个额外的二进制变量建模:
30 δ ≤ x ≤ 15(1-δ) + 50 δ
δ ∈ {0,1}
当然,这是假设您可以处理二进制变量(SLSQP 不能)。具有二进制变量和非线性约束(或 objective 函数)的模型称为 MINLP 模型(混合整数非线性规划)。这些类型模型的求解器很容易获得。
其他一些可能有效的方法:
- 两次解决问题。一次
0 ≤ x ≤ 15
,一次 30 ≤ x ≤ 50
。然后选择最佳解决方案。
- 使用
scipy.optimize.basinhopping
全局求解器帮助您摆脱局部最优解。这不是一个严格的算法(没有保证),但它可以提供帮助。
一些通常不起作用的方法:
- 而不是二进制变量
δ ∈ {0,1}
使用连续变量 δ ∈ [0,1]
并添加约束 δ(1-δ)=0
。通常这会让你陷入困境。
- 另一个答案中的多项式方法:这也是非凸的,并不真正适合 SLSQP。你会陷入局部最优。
- 如果
x ∈ [15,30]
,则对 objective 添加惩罚。这也不适用于本地求解器。
这是实施跳盆方法的尝试 。
首先,构造一个根为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()
现在您可以使用该多项式作为约束:
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
有机会从两个间隔进行采样很重要。
如果没有 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
两次,并在每个间隔中进行猜测
为了找到真正的最小值。
根据文档,我正在使用 scipy.optimize.minimize 'SLSQP' 方法:
bounds : sequence, optional
Bounds for variables (only for L-BFGS-B, TNC and SLSQP). (min, max) pairs for >each element in x, defining the bounds on that parameter. Use None for one of min >or max when there is no bound in that direction.
我想知道是否可以为变量 x 定义一个不连续的边界,例如 (0,15) & (30,50); (x介于0到15和30到50之间)
或者有没有其他更好的方法可以达到这个目的?
先谢谢大家了!
x is between 0 and 15 and between 30 and 50
这会使模型不可行。没有这样的x
。你的意思可能是:
x 在 0 到 15 之间 OR 在 30 到 50
之间这是非凸的,因此标准的局部求解器对此有问题。它通常用一个额外的二进制变量建模:
30 δ ≤ x ≤ 15(1-δ) + 50 δ
δ ∈ {0,1}
当然,这是假设您可以处理二进制变量(SLSQP 不能)。具有二进制变量和非线性约束(或 objective 函数)的模型称为 MINLP 模型(混合整数非线性规划)。这些类型模型的求解器很容易获得。
其他一些可能有效的方法:
- 两次解决问题。一次
0 ≤ x ≤ 15
,一次30 ≤ x ≤ 50
。然后选择最佳解决方案。 - 使用
scipy.optimize.basinhopping
全局求解器帮助您摆脱局部最优解。这不是一个严格的算法(没有保证),但它可以提供帮助。
一些通常不起作用的方法:
- 而不是二进制变量
δ ∈ {0,1}
使用连续变量δ ∈ [0,1]
并添加约束δ(1-δ)=0
。通常这会让你陷入困境。 - 另一个答案中的多项式方法:这也是非凸的,并不真正适合 SLSQP。你会陷入局部最优。
- 如果
x ∈ [15,30]
,则对 objective 添加惩罚。这也不适用于本地求解器。
这是实施跳盆方法的尝试
首先,构造一个根为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()
现在您可以使用该多项式作为约束:
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
有机会从两个间隔进行采样很重要。
如果没有 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
两次,并在每个间隔中进行猜测
为了找到真正的最小值。