【问题标题】:LinearConstraint in scipy.optimizescipy.optimize 中的线性约束
【发布时间】:2018-08-24 09:55:12
【问题描述】:

我想使用 scipy.optimize 在大量线性不等式上最小化一个函数(最终是非线性的)。作为热身,我试图最小化框 0

新的代码部分(当前相关):

import numpy as np
from scipy.optimize import minimize



#Create initial point.

x0=[.1,.1]

#Create function to be minimized

def obj(x):
    return x[0]+x[1]


#Create linear constraints  lbnd<= A*(x,y)^T<= upbnd

A=np.array([[1,0],[0,1]])

b1=np.array([0,0])

b2=np.array([1,1])

cons=[{"type": "ineq", "fun": lambda x: np.matmul(A[i, :],x) -b1[i]} for i in range(A.shape[0])]

cons2=[{"type": "ineq", "fun": lambda x: b2[i]-np.matmul(A[i, :], x) } for i in range(A.shape[0])]

cons.extend(cons2)

sol=minimize(obj,x0,constraints=cons)

print(sol)

问题的原始版本:

我想使用 LinearConstraint 对象 在 scipy.optimize 中,如教程中所述: "Defining linear constraints"

我试着做一个更简单的例子,答案很明显:在正方形 0

编辑 1:该示例故意过于简单。最终,我想在大量线性约束下最小化非线性函数。我知道我可以使用字典理解将我的约束矩阵转换为字典列表,但我想知道“LinearConstraints”是否可以用作将矩阵转换为约束的现成方法。

编辑 2:正如 Johnny Drama 所指出的,LinearConstraint 是针对特定方法的。因此,在上面,我尝试改用他对 dict-comprehension 的建议来产生线性约束,但仍然没有得到预期的答案。

原始代码部分(现在无关):

from scipy.optimize import minimize
from scipy.optimize import LinearConstraint


#Create initial point.

x0=[.1,.1]

#Create function to be minimized

def obj(x):
    return x[0]+x[1]


#Create linear constraints  lbnd<= A* 
#(x,y)^T<= upbnd

A=[[1,0],[0,1]]

lbnd=[0,0]

upbnd=[0,0]

lin_cons=LinearConstraint(A,lbnd,upbnd)

sol=minimize(obj,x0,constraints=lin_cons)

print(sol)

【问题讨论】:

    标签: python scipy


    【解决方案1】:

    正如新手已经说过的,如果您想求解 LP(线性规划),请使用 scipy.optimize.linprog,即您的目标函数您的约束是线性的。如果目标或其中一个约束不是线性的,我们将面临 NLP(非线性优化问题),可以通过scipy.optimize.minimize 解决:

    minimize(obj_fun, x0=xinit, bounds=bnds, constraints=cons)
    

    其中obj_fun 是您的目标函数,xinit 是初始点,bnds 是变量边界的元组列表,cons 是约束字典列表。


    这是一个例子。假设我们要解决以下 NLP:

    由于所有约束都是线性的,我们可以用仿射线性函数A*x-b 来表达它们,这样我们就有不等式A*x &gt;= b。这里A 是一个 3x2 矩阵,b 是 3x1 右侧向量:

    import numpy as np
    from scipy.optimize import minimize
    
    obj_fun = lambda x: (x[0] - 1)**2 + (x[1] - 2.5)**2
    A = np.array([[1, -2], [-1, -2], [-1, 2]])
    b = np.array([-2, -6, -2])
    bnds = [(0, None) for i in range(A.shape[1])]  # x_1 >= 0, x_2 >= 0
    xinit = [0, 0] 
    

    现在剩下要做的就是定义约束,每个约束都必须是表单的字典

    {"type": "ineq", "fun": constr_fun}
    

    其中constr_fun 是一个可调用函数,例如constr_fun &gt;= 0。因此,我们可以定义每个约束

    cons = [{'type': 'ineq', 'fun': lambda x:  x[0] - 2 * x[1] + 2},
            {'type': 'ineq', 'fun': lambda x: -x[0] - 2 * x[1] + 6},
            {'type': 'ineq', 'fun': lambda x: -x[0] + 2 * x[1] + 2}]
    

    我们就完成了。然而,事实上,这对于许多约束来说是相当麻烦的。相反,我们可以通过以下方式直接传递所有约束:

    cons = [{"type": "ineq", "fun": lambda x: A @ x - b}]
    

    其中@ 表示matrix multiplication operator。把所有东西放在一起

    res = minimize(obj_fun, x0=xinit, bounds=bnds, constraints=cons)
    print(res)
    

    产量

         fun: 0.799999999999998
         jac: array([ 0.79999999, -1.59999999])
     message: 'Optimization terminated successfully.'
        nfev: 16
         nit: 4
        njev: 4
      status: 0
     success: True
           x: array([1.39999999, 1.69999999])
    

    同样,您可以使用LinearConstraint 对象:

    from scipy.optimize import LinearConstraint
    
    # lb <= A <= ub. In our case: lb = b, ub = inf
    lincon = LinearConstraint(A, b, np.inf*np.ones(3))
    
    # rest as above
    res = minimize(obj_fun, x0=xinit, bounds=bnds, constraints=(lincon,))
    

    编辑:回答您的新问题:

    # b1    <= A * x   <==>   -b1 >= -A*x        <==>   A*x - b1 >= 0
    # A * x <= b2      <==>    A*x - b2 <= 0     <==>  -Ax + b2 >= 0
    cons = [{"type": "ineq", "fun": lambda x: A @ x - b1}, {"type": "ineq", "fun": lambda x: -A @ x + b2}]
    sol=minimize(obj,x0,constraints=cons)
    print(sol)
    

    【讨论】:

    • 谢谢。为了澄清,我给出的例子故意过于简单。在实践中,我有一个非线性目标函数和大量线性约束,我已经将它们打包在一个 numpy 数组中。是的,我可以使用字典理解“手动”完成。但是,“LinearConstraint”有什么用呢?它不应该以某种方式从矩阵中为您生成这些约束吗?
    • See here: 方法'trust-constr'要求将约束定义为对象序列LinearConstraintNonlinearConstraint。另一方面,方法“SLSQP”和“COBLYA”要求将约束定义为字典序列,键为typefunjac 所以LinearConstraint 对象是仅当您想使用受信任区域约束的方法 ('trust-constr') 时才需要。
    • 好的,这有帮助。看来你也必须提供雅可比和黑森。现在,我会试试你的 dict-comprehension。
    • 再次感谢。我已经完成了您在 dict-comprehesion 方面的建议,但似乎得到了错误的答案。似乎没有正确读取约束。我在原帖中添加了一个示例。
    • 顺便说一句,根据docs.scipy.org/doc/scipy/reference/optimize.html,“不等式意味着它是非负数”,而在您的帖子中您说“其中 constr_fun 是一个可调用函数,因此 constr_fun
    【解决方案2】:

    错误在于你调用minimize函数的方式

    sol= minimize(obj, x0, constraints=lin_cons)
    

    确实,约束需要一个字典或字典列表,请参阅http://scipy.optimize.minimize

    对于您的特定 LP,我会写如下内容:

    from scipy.optimize import linprog
    import numpy as np
    
    c = np.array([1, 1])
    
    res = linprog(c, bounds=(0, 1))
    
    print('Optimal value: {}'.format( res.fun))
    print('Values: {}'.format(res.x))
    

    哪个输出

    Optimal value: -0.0
    Values: [ 0.  0.]
    

    因为没有限制。

    假设您要添加一个约束x + y &gt;= 0.5(相当于-x - y &lt;= -0.5)。那么你的 Lp 就变成了:

    c = np.array([1, 1])
    
    A_ub = np.array([[-1,-1]])
    
    b_ub = np.array([-0.5])
    
    res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=(0, 1))
    
    print('Optimal value: {}'.format( res.fun))
    print('Values: {}'.format(res.x))
    

    现在输出:

    Optimal value: 0.5
    Values: [ 0.5  0. ]
    

    【讨论】:

    • 谢谢,但我并不是特别想做线性规划,因为我最终会想要一个非线性目标函数。你说得对,我需要某种字典来进行约束,但是从我链接的教程中并不清楚,我还没有看到如何将 LinearConstraint 放入字典中。
    【解决方案3】:

    @乔尼:

    b1 -b1 >= -Ax Ax - b1 >= 0

    A * x A*x - b2 -Ax + b2 >= 0

    cons = [{"type": "ineq", "fun": lambda x: A @ x - b1}, {"type": "ineq", "fun": lambda x: -A @ x + b2}]

    sol=minimize(obj,x0,constraints=cons) 打印(溶胶)

    这是一个非常有趣的解决方案,对我来说效果很好。我正在尝试做同样的事情,但同时具有平等和不平等约束:

    所以我有: Aeq @ x - b =0 并且它工作正常,但是当我添加 A_ineq @ x - Lb 和 Ub - A_ineq @ x 它似乎不起作用,因为 Aeq 和 AIneq 不是相同的尺寸:

    def DefineLinearConstraint(Aeq, b, Aineq, Lb, Ub): 约束 = [{"type": "eq", "fun": lambda x: Aeq @ x - b, {"type": "ineq", "fun": lambda x: Aineq @ x - Lb}, {"类型”:“ineq”,“有趣”:lambda x:-Aineq @ x + Ub}]

    我有以下变量: Aeq = 数组([1, 1, 1, 1, 1], dtype=int64) x0 = 数组([[0.2], [0.2], [0.2],[0.2], [0.2]], dtype=object) 和 x 与 x0 的维度相同, b = 数组([1], dtype=object)

    对于不等式约束: Aineq = array([[1, 1, 1, 0, 0], [0, 0, 0, 1, 1]], dtype=int64)

    Lb = 数组([[0], [0]], dtype=object) 和 ub = array([[1], [1]], dtype=object)

    这个想法是添加组约束,在我的示例中,我有 2 个组约束我希望能够修改。

    【讨论】:

    • 请尝试改进答案的格式。
    • 你好 Xbel,抱歉格式化,我在我的办公室电脑上编码,由于代理禁令,我不能从我的电脑上写,必须从我的手机上写,这使得很难做得更好格式。抱歉给您带来不便...
    • 应该是格式化回答
    • 不用担心。只是它更难阅读......
    猜你喜欢
    • 1970-01-01
    • 2021-09-27
    • 1970-01-01
    • 1970-01-01
    • 2015-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多