use*_*602 1 python math mathematical-optimization scipy
我正在做一些统计相关的优化。我正在尝试使用 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)
Run Code Online (Sandbox Code Playgroud)
现在的问题是,当优化完成时(“xtol满足终止条件”和“约束违规:8.33e-17”),它给出了以下解决方案
[-0.93668183 -0.63135599 -0.35261352 -0.54613932 -0.52013279 0.21760397
1.25553898 -0.48837217 -2.04095876 -2.14095876]
Run Code Online (Sandbox Code Playgroud)
这显然不满足get_constraints数组的两个相邻元素最多相差 0.1 的条件。
当我删除雅可比行列式时,它的行为是相同的(只是运行速度较慢)。关于堆栈溢出有几个听起来类似的问题,但这些问题都是关于 SLSQP 的,似乎不太相关。
有什么想法为什么会出现这种情况吗?或者如何解决?
我认为这与 lambda 的一个众所周知的“功能”有关。如果你看:
flist = [lambda x: x+i for i in range(3)]
Run Code Online (Sandbox Code Playgroud)
然后执行时:
[flist[i](1) for i in range(3)]
Run Code Online (Sandbox Code Playgroud)
你可能会期待[1+0,1+1,1+2] = [1,2,3]。然而,你会看到:
[3, 3, 3]
Run Code Online (Sandbox Code Playgroud)
基本上,此设置中只有一个 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)]
Run Code Online (Sandbox Code Playgroud)
这将显示:[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)]
Run Code Online (Sandbox Code Playgroud)
现在解决方案如下所示:
x: array([ 0.49289571, 0.39289571, 0.29289571, 0.19289571, 0.09289571,
-0.00710429, -0.10710429, -0.20710429, -0.30710429, -0.40710429])
Run Code Online (Sandbox Code Playgroud)
结论:与求解器无关,而是对 lambda 工作原理的误解。
附言。准确地说,这是由于“后期绑定闭包”(在不使用 lambda 函数的情况下也会发生同样的事情,但我通常会在其中看到这种行为)。请参阅https://docs.python-guide.org/writing/gotchas/。