具有线性约束的 Scipy.optimize.minimize SLSQP 失败
Posted
技术标签:
【中文标题】具有线性约束的 Scipy.optimize.minimize SLSQP 失败【英文标题】:Scipy.optimize.minimize SLSQP with linear constraints fails 【发布时间】:2016-10-13 23:39:36 【问题描述】:考虑以下(凸)优化问题:
minimize 0.5 * y.T * y
s.t. A*x - b == y
其中优化(向量)变量为x
和y
和A
,b
分别是适当维度的矩阵和向量。
下面的代码使用 Scipy 的 SLSQP
方法很容易找到解决方案:
import numpy as np
from scipy.optimize import minimize
# problem dimensions:
n = 10 # arbitrary integer set by user
m = 2 * n
# generate parameters A, b:
np.random.seed(123) # for reproducibility of results
A = np.random.randn(m,n)
b = np.random.randn(m)
# objective function:
def obj(z):
vy = z[n:]
return 0.5 * vy.dot(vy)
# constraint function:
def cons(z):
vx = z[:n]
vy = z[n:]
return A.dot(vx) - b - vy
# constraints input for SLSQP:
cons = ('type': 'eq','fun': cons)
# generate a random initial estimate:
z0 = np.random.randn(n+m)
sol = minimize(obj, x0 = z0, constraints = cons, method = 'SLSQP', options='disp': True)
Optimization terminated successfully. (Exit mode 0) Current function value: 2.12236220865 Iterations: 6 Function evaluations: 192 Gradient evaluations: 6
请注意,约束函数是一个方便的“数组输出”函数。
现在,原则上可以使用一组等效的“标量输出”约束函数来代替约束的数组输出函数(实际上,scipy.optimize 文档仅讨论了这种类型的约束函数作为输入minimize
)。
这是等价的约束集,后面是minimize
的输出(与上面的列表相同的A
、b
和初始值):
# this is the i-th element of cons(z):
def cons_i(z, i):
vx = z[:n]
vy = z[n:]
return A[i].dot(vx) - b[i] - vy[i]
# listable of scalar-output constraints input for SLSQP:
cons_per_i = ['type':'eq', 'fun': lambda z: cons_i(z, i) for i in np.arange(m)]
sol2 = minimize(obj, x0 = z0, constraints = cons_per_i, method = 'SLSQP', options='disp': True)
Singular matrix C in LSQ subproblem (Exit mode 6) Current function value: 6.87999270692 Iterations: 1 Function evaluations: 32 Gradient evaluations: 1
显然,算法失败了(返回的目标值实际上是给定初始化的目标值),我觉得这有点奇怪。请注意,运行[cons_per_i[i]['fun'](sol.x) for i in np.arange(m)]
表明使用数组输出约束公式获得的sol.x
符合预期的cons_per_i
的所有标量输出约束(在数值公差范围内)。
如果有人对此问题有任何解释,我将不胜感激。
【问题讨论】:
【参考方案1】:您遇到了"late binding closures" gotcha。所有对cons_i
的调用都使用等于19 的第二个参数进行。
解决方法是在定义约束的字典中使用 args
字典元素而不是 lambda 函数闭包:
cons_per_i = ['type':'eq', 'fun': cons_i, 'args': (i,) for i in np.arange(m)]
这样,最小化工作:
In [417]: sol2 = minimize(obj, x0 = z0, constraints = cons_per_i, method = 'SLSQP', options='disp': True)
Optimization terminated successfully. (Exit mode 0)
Current function value: 2.1223622086
Iterations: 6
Function evaluations: 192
Gradient evaluations: 6
您也可以使用链接文章中的建议,即使用带有第二个参数的 lambda 表达式,该参数具有所需的默认值:
cons_per_i = ['type':'eq', 'fun': lambda z, i=i: cons_i(z, i) for i in np.arange(m)]
【讨论】:
以上是关于具有线性约束的 Scipy.optimize.minimize SLSQP 失败的主要内容,如果未能解决你的问题,请参考以下文章