scipy.最小化似乎不遵守约束



>scipy.最小化似乎不遵守约束。 下面是一个简单的示例,其中约束用于防止对数中的负参数,但最小函数不遵守:

import math
from scipy.optimize import minimize
def obj(x,b):
print "obj x",x
return math.log(x-b)
def constr(x,b):
print "constr x",x
return x-b
x=3.1
b=3
a=minimize(obj,x,args=(b),constraints={'type': 'ineq', 'fun':constr,'args':[b]})

输出为:

constr x [ 3.1]
obj x [ 3.1]
constr x [ 3.1]
obj x [ 3.1]
obj x [ 3.10000001]
constr x [ 3.1]
constr x [ 3.10000001]
obj x [ 3.]
Traceback (most recent call last):
File "scipy_minimize_constraints.py", line 19, in 
a=minimize(obj,x,args=(b),constraints={'type': 'ineq', 'fun':constr,'args':[b]})
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/scipy/optimize/_minimize.py", line 495, in minimize
constraints, callback=callback, **options)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/scipy/optimize/slsqp.py", line 378, in _minimize_slsqp
fx = func(x)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/scipy/optimize/optimize.py", line 292, in function_wrapper
return function(*(wrapper_args + args))
File "scipy_minimize_constraints.py", line 9, in obj
return math.log(x-b)
ValueError: math domain error

蟒蛇2.7 西皮版本 1.0.0

我做错了什么吗?

约束

def constr(x,b):
return x-b

结果为x-b >= 0(非阴性(,但允许x-b = 0。那么log(x-b)是未定义的,如果x-b = 0.您需要引入一些 epsilon,例如:

eps = 1e-12
def constr(x,b):
return x-b-eps

这不会抛出您的错误。

但可能还有一个更重要的问题:我真的不认为求解器(这里的 SLSQP(保证它的迭代是可行的!在某些情况下,这可能会有问题。在您的简单示例中,要走的方法是将约束转换为边界。这当然不那么富有表现力(并不总是可能的;但对于你的小例子来说确实如此(,但这些界限在迭代中得到了尊重!

inferred_lb = b + eps
a = minimize(obj, x, args=(b,), bounds=[(inferred_lb, None)])

(当然:不要使用最小化进行一维优化。有minimize_scalar。

我不知道 scipy 的最小化是如何工作的,但我认为你是对的,它不遵循约束。

如果在 obj(x,b( 中,我尝试打印 x - b,则在抛出错误时,最后一个循环的输出为负数。

不过,这并不完全令人惊讶。 您最小化的函数本质上是约束 z> 0 的 log(z(。 这不会收敛得很慢... :p

编辑:将关键字参数"tol"(容错(设置为最小化器的0.1左右可以消除错误(如minimize(..., tol=0.1)(。我仍然不知道为什么它首先如此重要。

编辑(因为我仍然无法评论(: @sascha,这是有道理的,但为什么该函数甚至计算 x - b 的负值?最小化函数是否在其约束之外"测试"只是为了更好地优化其中的解决方案? (如果我应该把这个问题作为一个新问题来问,请告诉我。

最新更新