python - 具有线性约束的 Scipy.optimize.minimize SLSQP 失败

标签 python numpy optimization scipy

考虑以下(凸)优化问题:

minimize 0.5 * y.T * y
s.t.     A*x - b == y

其中优化(向量)变量是xyAb是一个矩阵和向量,分别具有适当的维度。

下面的代码使用 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 的输出(与上面的列表相同的 Ab 和初始值) :

# 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]['f​​un'](sol.x) for i in np.arange(m)] 显示 sol.x,使用数组输出约束公式满足 cons_per_i 的所有标量输出约束(在数值公差范围内)。

如果有人对此问题有一些解释,我将不胜感激。

最佳答案

您遇到了 "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)]

关于python - 具有线性约束的 Scipy.optimize.minimize SLSQP 失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37791680/

相关文章:

python - 精确的计算和绘图

c# - 通过设置垃圾收集 bool 值来提高 150% 的速度?

algorithm - 更好的算法 - 下一个半素数

algorithm - 查询n次时如何改进Dijkstra算法?

python - 不明白为什么我的图像在 pygame 中不会移动

python - 如何匹配此模式和字符串中的最后一个空格?

python - 在 Django 中结合 ~Q 和 F?

python - 脚本执行后启动 ipython qtconsole 作为交互式解释器

python - Numpy longdouble 算术似乎不在 long double with conversion

python - matplotlib 轮廓可以匹配像素边缘吗?