SciPy 的 trust-constr 忽略了我的约束
SciPy's trust-constr is ignoring my constraints
我正在做一些与统计相关的优化。我正在尝试使用 scipy 的最小化。但是,它给我的解决方案是不可行的。我的密码是
import numpy as np
from scipy.optimize import minimize, NonlinearConstraint
from math import e
def get_expectation(f,mu):
return f.dot(mu)
def get_neg_subgaus_coeff(f,mu):
return -(e**((f/10)**2)).dot(mu)
def grad_neg_subgaus_coeff(f,mu):
return -(2*e**((f/10)**2)*f)*mu
def my_mu(n):
return 1/(np.abs((np.arange(n)-n//2))+1)**2
def get_constraints(n):
constr_list = []
for i in range(n-1):
constr = lambda f: f[i]- f[i+1]
constr_list.append(NonlinearConstraint(constr,.1, .1))
return constr_list
def callback(x,_): # not important, only debug info
print(get_neg_subgaus_coeff(x, mu))
return False
def get_solution(mu,n):
constraints = get_constraints(n) + [NonlinearConstraint(lambda f: get_expectation(f,mu), 0, 0)]
return minimize(get_neg_subgaus_coeff, np.zeros(n)-1, args=(mu),jac=grad_neg_subgaus_coeff, method="trust-constr", bounds=[(-100, 100) for _ in range(n)], constraints=constraints, tol=1e-8, options={"maxiter":1000, "disp":True}, callback=callback)
if __name__ == "__main__":
n=10
mu = my_mu(n)
sol = get_solution(mu,n).x
print(sol)
现在,问题是当优化完成时(“满足xtol
终止条件。”和"constraint violation: 8.33e-17"),它给出了以下解决方案
[-0.93668183 -0.63135599 -0.35261352 -0.54613932 -0.52013279 0.21760397
1.25553898 -0.48837217 -2.04095876 -2.14095876]
这显然不满足 get_constraints
产生的条件,即数组的两个相邻元素最多相差 0.1。
当我删除雅可比行列式时,它的行为相同(只是运行速度较慢)。关于堆栈溢出有几个听起来很相似的问题,但那些是关于 SLSQP 的,似乎不是很相关。
知道为什么会这样吗?或者怎么解决?
我认为这与著名的 "feature" lambda 表达式有关。如果您查看:
flist = [lambda x: x+i for i in range(3)]
然后执行时:
[flist[i](1) for i in range(3)]
你可能会期待 [1+0,1+1,1+2] = [1,2,3]
。但是,您会看到:
[3, 3, 3]
基本上,此设置中只有一个 lambda(或者更确切地说:循环结束时的延迟绑定)。
一个可能的解决方法是:
from functools import partial
flist = [partial(lambda x,j: x+j, j=i) for i in range(3)]
[flist[i](1) for i in range(3)]
这将显示:[1,2,3]
。
将此技术应用于您的问题时,我们可以这样写:
def get_constraints(n):
flist = [partial(lambda f,j: f[j]-f[j+1], j=i) for i in range(n-1)]
return [NonlinearConstraint(flist[i],.1,.1) for i in range(n-1)]
现在的解决方案如下:
x: array([ 0.49289571, 0.39289571, 0.29289571, 0.19289571, 0.09289571,
-0.00710429, -0.10710429, -0.20710429, -0.30710429, -0.40710429])
结论:与求解器无关,而是对 lambda 的工作方式存在误解。
PS。准确地说,这是由于 "late binding closures"(不使用 lambda 函数也会发生同样的事情,但我通常会在其中看到这种行为)。参见 https://docs.python-guide.org/writing/gotchas/。
我正在做一些与统计相关的优化。我正在尝试使用 scipy 的最小化。但是,它给我的解决方案是不可行的。我的密码是
import numpy as np
from scipy.optimize import minimize, NonlinearConstraint
from math import e
def get_expectation(f,mu):
return f.dot(mu)
def get_neg_subgaus_coeff(f,mu):
return -(e**((f/10)**2)).dot(mu)
def grad_neg_subgaus_coeff(f,mu):
return -(2*e**((f/10)**2)*f)*mu
def my_mu(n):
return 1/(np.abs((np.arange(n)-n//2))+1)**2
def get_constraints(n):
constr_list = []
for i in range(n-1):
constr = lambda f: f[i]- f[i+1]
constr_list.append(NonlinearConstraint(constr,.1, .1))
return constr_list
def callback(x,_): # not important, only debug info
print(get_neg_subgaus_coeff(x, mu))
return False
def get_solution(mu,n):
constraints = get_constraints(n) + [NonlinearConstraint(lambda f: get_expectation(f,mu), 0, 0)]
return minimize(get_neg_subgaus_coeff, np.zeros(n)-1, args=(mu),jac=grad_neg_subgaus_coeff, method="trust-constr", bounds=[(-100, 100) for _ in range(n)], constraints=constraints, tol=1e-8, options={"maxiter":1000, "disp":True}, callback=callback)
if __name__ == "__main__":
n=10
mu = my_mu(n)
sol = get_solution(mu,n).x
print(sol)
现在,问题是当优化完成时(“满足xtol
终止条件。”和"constraint violation: 8.33e-17"),它给出了以下解决方案
[-0.93668183 -0.63135599 -0.35261352 -0.54613932 -0.52013279 0.21760397
1.25553898 -0.48837217 -2.04095876 -2.14095876]
这显然不满足 get_constraints
产生的条件,即数组的两个相邻元素最多相差 0.1。
当我删除雅可比行列式时,它的行为相同(只是运行速度较慢)。关于堆栈溢出有几个听起来很相似的问题,但那些是关于 SLSQP 的,似乎不是很相关。
知道为什么会这样吗?或者怎么解决?
我认为这与著名的 "feature" lambda 表达式有关。如果您查看:
flist = [lambda x: x+i for i in range(3)]
然后执行时:
[flist[i](1) for i in range(3)]
你可能会期待 [1+0,1+1,1+2] = [1,2,3]
。但是,您会看到:
[3, 3, 3]
基本上,此设置中只有一个 lambda(或者更确切地说:循环结束时的延迟绑定)。
一个可能的解决方法是:
from functools import partial
flist = [partial(lambda x,j: x+j, j=i) for i in range(3)]
[flist[i](1) for i in range(3)]
这将显示:[1,2,3]
。
将此技术应用于您的问题时,我们可以这样写:
def get_constraints(n):
flist = [partial(lambda f,j: f[j]-f[j+1], j=i) for i in range(n-1)]
return [NonlinearConstraint(flist[i],.1,.1) for i in range(n-1)]
现在的解决方案如下:
x: array([ 0.49289571, 0.39289571, 0.29289571, 0.19289571, 0.09289571,
-0.00710429, -0.10710429, -0.20710429, -0.30710429, -0.40710429])
结论:与求解器无关,而是对 lambda 的工作方式存在误解。
PS。准确地说,这是由于 "late binding closures"(不使用 lambda 函数也会发生同样的事情,但我通常会在其中看到这种行为)。参见 https://docs.python-guide.org/writing/gotchas/。