【问题标题】:Scipy minimize successfully terminates, but doesn’t fulfill inequality constraintsScipy 最小化成功终止,但不满足不等式约束
【发布时间】:2019-06-04 13:18:25
【问题描述】:

我试图最小化函数 0.5*(x^2+y^2) 服从一系列 (N=20) x a1+y a2+a3 z >= 1 形式的不等式约束。解应该在 x=0.50251, y=-0.5846, z=0.36787 左右。例程以消息“优化已成功终止”终止,但超过一半的约束未得到遵守。我也尝试了不同的求解器,结果相同。

缩放目标函数会改变解,但不会收敛到预期的解。

from scipy.optimize import minimize
import numpy as np


Pct=np.array([[-0.664,  3.179],[ 0.231, -2.044],[-2.493,  3.25 ],[ 0.497, -0.654],[-1.27,   1.248],[-1.185,  1.814],[-1.843,  4.386],[-1.616,  1.401],[ 0.052, -1.232],[-3.145,  0.404],[ 0.672, -1.655],[ 2.202, -1.888],[ 4.084, -1.067],[ 1.006, -1.671],[-2.255,  1.51 ],[-1.264,  1.663],[ 1.897, -2.217],[ 1.843, -1.276],[-1.693,  1.623],[ 2.297, -1.709]])
Sid=np.array([-1,  1, -1,  1, -1, -1, -1, -1,  1, -1,  1,  1,  1,  1, -1, -1,  1,  1, -1,  1])

# func to be minimized 
def OptFunc(x):
  return 0.5*(x[0]**2+x[1]**2)
def JacOptFunc(x):
  return np.array([x[0],x[1],0.0])  

# Constraints
c=[]
for i in range(len(Sid)):
  c+=[{'type': 'ineq', 'fun': lambda x:  Sid[i]*(x[0]*Pct[i,0]+x[1]*Pct[i,1]+x[2])-1 }]
cons=tuple(c)    

# start optimization
res = minimize(OptFunc,(0.3,-0.2,0.1),constraints=cons,method='SLSQP',jac=JacOptFunc)

#expected solution should be around
# [0.5025062702615434, -0.584685257866671, 0.36787016514022236]
print("-->",res.message)
print("solution ",res.x,flush=True)

print("Check Constraints")
cons=list(cons)
for i in range(len(cons)):
  lokfun=c[i]['fun']
  print("Constraint # ",i," value: ",lokfun(res.x))

预期的结果是 x=0.50251, y=-0.5846, z=0.36787 但我得到以下输出:

--> Optimization terminated successfully.
solution  [-1.14580677e-04 -1.16285140e-04  1.00006446e+00]

Check Constraints
Constraint #  0  value:  -1.9997708716077622
Constraint #  1  value:  0.0002756791862408292
Constraint #  2  value:  -1.999972183420499
Constraint #  3  value:  8.356438220613605e-05
Constraint #  4  value:  -2.0000648541023893
Constraint #  5  value:  -1.9999892973558606
Constraint #  6  value:  -1.9997656060620763
Constraint #  7  value:  -2.000086707390163
Constraint #  8  value:  0.00020176559401496874
Constraint #  9  value:  -2.0003778375289833
Constraint #  10  value:  0.00017991418852214558
Constraint #  11  value:  3.1700190727068644e-05
Constraint #  12  value:  -0.0002794107423930159
Constraint #  13  value:  0.00014350480474445426
Constraint #  14  value:  -2.000147249362345
Constraint #  15  value:  -2.0000159082853974
Constraint #  16  value:  0.00010490510804150865
Constraint #  17  value:  1.6681482228886324e-06
Constraint #  18  value:  -2.0000697148012767
Constraint #  19  value:  -1.354516498963676e-11

【问题讨论】:

    标签: python scipy-optimize-minimize


    【解决方案1】:

    我对@9​​87654322@知之甚少,但我可以发现问题

    for i in range(len(Sid)):
      c+=[{'type': 'ineq', 'fun': lambda x:  Sid[i]*(x[0]*Pct[i,0]+x[1]*Pct[i,1]+x[2])-1 }]
    

    问题在于 Python 闭包是后期绑定的,这意味着每个约束中 i 的值实际上是在循环完成后计算的。实际上,您实际上是在施加相同的(最后一个)约束 20 次。见https://docs.python-guide.org/writing/gotchas/#late-binding-closures

    一个可能的解决方法:

    from scipy.optimize import minimize
    import numpy as np
    
    
    Pct=np.array([[-0.664,  3.179],[ 0.231, -2.044],[-2.493,  3.25 ],[ 0.497, -0.654],[-1.27,   1.248],[-1.185,  1.814],[-1.843,  4.386],[-1.616,  1.401],[ 0.052, -1.232],[-3.145,  0.404],[ 0.672, -1.655],[ 2.202, -1.888],[ 4.084, -1.067],[ 1.006, -1.671],[-2.255,  1.51 ],[-1.264,  1.663],[ 1.897, -2.217],[ 1.843, -1.276],[-1.693,  1.623],[ 2.297, -1.709]])
    Sid=np.array([-1,  1, -1,  1, -1, -1, -1, -1,  1, -1,  1,  1,  1,  1, -1, -1,  1,  1, -1,  1])
    
    # func to be minimized 
    def OptFunc(x):
        return 0.5*(x[0]**2+x[1]**2)
    def JacOptFunc(x):
        return np.array([x[0],x[1],0.0])  
    
    # Constraints
    def constraint_maker(i=0):  # i MUST be an optional keyword argument, else it will not work
        def constraint(x):
            return Sid[i]*(x[0]*Pct[i,0]+x[1]*Pct[i,1]+x[2])-1 
        return constraint
    
    c=[]
    for i in range(len(Sid)):
        c+=[{'type': 'ineq', 'fun': constraint_maker(i)}]
    cons=tuple(c)
    
    # start optimization
    res = minimize(OptFunc,(0.3,-0.2,0.1),constraints=cons,method='SLSQP',jac=JacOptFunc)
    
    #expected solution should be around
    # [0.5025062702615434, -0.584685257866671, 0.36787016514022236]
    print("-->",res.message)
    print("solution ",res.x)
    
    print("Check Constraints")
    cons=list(cons)
    for i in range(len(cons)):
        lokfun=c[i]['fun']
        print("Constraint # ",i," value: ",lokfun(res.x))
    

    结果

    --> Optimization terminated successfully.
    solution  [ 0.52374351 -0.56495542  0.37021863]
    Check Constraints
    Constraint #  0  value:  0.7735403550593944
    Constraint #  1  value:  0.6459722649608017
    Constraint #  2  value:  1.7715790719554194
    Constraint #  3  value:  8.137268636687622e-11
    Constraint #  4  value:  -2.2235047136831554e-10
    Constraint #  5  value:  0.27524657110337936
    Constraint #  6  value:  2.0729351509689136
    Constraint #  7  value:  0.2676534344356165
    Constraint #  8  value:  0.09347837249122604
    Constraint #  9  value:  0.5051967055706261
    Constraint #  10  value:  0.6571754935710583
    Constraint #  11  value:  1.5901376792721638
    Constraint #  12  value:  2.1119945643862095
    Constraint #  13  value:  0.8411451130595076
    Constraint #  14  value:  0.6639056792092357
    Constraint #  15  value:  0.23131403951409935
    Constraint #  16  value:  1.6162662427554526
    Constraint #  17  value:  1.0563610395273058
    Constraint #  18  value:  0.43340178883510116
    Constraint #  19  value:  1.5387662919992176
    

    【讨论】:

    • 非常感谢。你知道为什么它没有在检查约束输出中弹出(我引用了同一个元组)。这些值不同,所以 i 值似乎在那里发生了变化,但在最小化函数调用中没有变化?
    • @Emi 也许是由于 lambda 评估恶作剧(即如果 lambda 绑定发生多次)?这只是一个粗略的猜测,我没有真正的想法。
    • @Leporello 再次感谢。
    • @Emi 如果您的问题得到解决,请通过接受此答案将问题标记为已回答:)
    猜你喜欢
    • 1970-01-01
    • 2017-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-01
    • 1970-01-01
    • 2013-12-03
    • 1970-01-01
    相关资源
    最近更新 更多