参考官网:Scipy.
有约束的多变量标量函数的最小化
对于有约束的最小化问题,Scipy提供的minimize这个包有三个:trust-constr, SLSQP'和COBYLA。它们要求使用稍微不同的结构来定义约束。
trust-constr需要要求约束被定义成一系列的LinearConstraint 和 NonlinearConstraint两种类型。
SLSQP'和COBYLA需要要求约束条件被定义为一连串的字典,其键为type、fun和jac。
考虑有约束的最小化2个变量的Rosenbrock函数

这个问题有唯一解:
[
x
0
,
x
1
]
=
[
0.4149
,
0.1701
]
[x_0,x_1]=[0.4149,0.1701]
[x0,x1]=[0.4149,0.1701]。
信赖域约束算法(method=‘trust-constr’)
信任域约束方法处理的是约束性最小化问题,其形式为:

除此之外,单边约束可以通过对np.inf设置上限或下限并加上适当的符号来指定。
定义边界约束
边界限制 0 ≤ x 0 ≤ 1 , − 0.5 ≤ x 1 ≤ 2.0 0≤x_0≤1,-0.5≤x_1≤2.0 0≤x0≤1,−0.5≤x1≤2.0被定义如下:
from scipy.optimize import Bounds
bounds = Bounds([0, -0.5], [1.0, 2.0])
定义线性约束
约束
x
0
+
2
x
1
≤
1
,
2
x
0
+
x
1
=
1
x_0+2x_1≤1,2x_0+x_1=1
x0+2x1≤1,2x0+x1=1可以写成如下形式:

用LinearConstraint去定义:
from scipy.optimize import LinearConstraint
linear_constraint = LinearConstraint([[1, 2], [2, 1]], [-np.inf, 1], [1, 1])
定义非线性约束
非线性约束为:

其雅可比矩阵(对每个变量求导)为:

黑塞矩阵的线性组合:

用NonlinearConstraint去定义:
#定义非线性约束
def cons_f(x):
return [x[0]**2 + x[1], x[0]**2 - x[1]]
#定义导数
def cons_J(x):
return [[2*x[0], 1], [2*x[0], -1]]
#定义二阶导数
def cons_H(x, v):
return v[0]*np.array([[2, 0], [0, 0]]) + v[1]*np.array([[2, 0], [0, 0]])
from scipy.optimize import NonlinearConstraint
nonlinear_constraint = NonlinearConstraint(cons_f, -np.inf, 1, jac=cons_J, hess=cons_H)
另外,也可以将Hessian定义为一个稀疏矩阵。
from scipy.sparse import csc_matrix
def cons_H_sparse(x, v):
return v[0]*csc_matrix([[2, 0], [0, 0]]) + v[1]*csc_matrix([[2, 0], [0, 0]])
nonlinear_constraint = NonlinearConstraint(cons_f, -np.inf, 1,
jac=cons_J, hess=cons_H_sparse)
当黑塞矩阵难以计算的时候,可以使用HessianUpdateStrategy,目前可用的策略有:BFGS和SR1。
from scipy.optimize import BFGS
nonlinear_constraint = NonlinearConstraint(cons_f, -np.inf, 1, jac=cons_J, hess=BFGS())
另外,Hessian可以用有限差分法进行近似。
nonlinear_constraint = NonlinearConstraint(cons_f, -np.inf, 1, jac=cons_J, hess='2-point')
雅可比矩阵也可以用有限差分法估计,然而,在这种情况下,黑塞不能用有限差分计算,需要由用户提供或用之前介绍的HessianUpdateStrategy定义:
nonlinear_constraint = NonlinearConstraint(cons_f, -np.inf, 1, jac='2-point', hess=BFGS())
求解
#设置初值
x0 = np.array([0.5, 0])
#这个需要结合之前无约束的算法来计算
res = minimize(rosen, x0, method='trust-constr', jac=rosen_der, hess=rosen_hess,
constraints=[linear_constraint, nonlinear_constraint],
options={'verbose': 1}, bounds=bounds)
print(res.x)
`gtol` termination condition is satisfied.
Number of iterations: 12, function evaluations: 8, CG iterations: 7, optimality: 2.99e-09, constraint violation: 0.00e+00, execution time: 0.014 s.
[0.41494531 0.17010937]
完整代码
import numpy as np
from scipy.optimize import minimize
#无约束
def rosen(x):
"""The Rosenbrock function"""
return sum(100.0*(x[1:]-x[:-1]**2.0)**2.0 + (1-x[:-1])**2.0)
def rosen_der(x):
xm = x[1:-1]
xm_m1 = x[:-2]
xm_p1 = x[2:]
der = np.zeros_like(x)
der[1:-1] = 200*(xm-xm_m1**2) - 400*(xm_p1 - xm**2)*xm - 2*(1-xm)
der[0] = -400*x[0]*(x[1]-x[0]**2) - 2*(1-x[0])
der[-1] = 200*(x[-1]-x[-2]**2)
return der
def rosen_hess(x):
x = np.asarray(x)
H = np.diag(-400*x[:-1],1) - np.diag(400*x[:-1],-1)
diagonal = np.zeros_like(x)
diagonal[0] = 1200*x[0]**2-400*x[1]+2
diagonal[-1] = 200
diagonal[1:-1] = 202 + 1200*x[1:-1]**2 - 400*x[2:]
H = H + np.diag(diagonal)
return H
#设置约束
from scipy.optimize import Bounds
bounds = Bounds([0, -0.5], [1.0, 2.0])
from scipy.optimize import LinearConstraint
linear_constraint = LinearConstraint([[1, 2], [2, 1]], [-np.inf, 1], [1, 1])
def cons_f(x):
return [x[0]**2 + x[1], x[0]**2 - x[1]]
#定义导数
def cons_J(x):
return [[2*x[0], 1], [2*x[0], -1]]
#定义二阶导数
def cons_H(x, v):
return v[0]*np.array([[2, 0], [0, 0]]) + v[1]*np.array([[2, 0], [0, 0]])
from scipy.optimize import NonlinearConstraint
nonlinear_constraint = NonlinearConstraint(cons_f, -np.inf, 1, jac=cons_J, hess=cons_H)
#求解
x0 = np.array([0.5, 0])
res = minimize(rosen, x0, method='trust-constr', jac=rosen_der, hess=rosen_hess,
constraints=[linear_constraint, nonlinear_constraint],
options={'verbose': 1}, bounds=bounds)
print(res.x)
另外也可以对目标函数的第一和第二导数进行近似,例如,黑塞矩阵可以用SR1准牛顿法近似,梯度可以用有限差分法近似:
from scipy.optimize import SR1
res = minimize(rosen, x0, method='trust-constr', jac="2-point", hess=SR1(),
constraints=[linear_constraint, nonlinear_constraint],
options={'verbose': 1}, bounds=bounds)
`gtol` termination condition is satisfied.
Number of iterations: 12, function evaluations: 24, CG iterations: 7, optimality: 4.48e-09, constraint violation: 0.00e+00, execution time: 0.02 s.
[0.41494531 0.17010937]
序列最小二乘(SLSQP) (method=‘SLSQP’)
SLSQP需要如下形式:

设置约束

线性和非线性约束都被定义为字典,其键为type、fun和jac:
首先是定义域:
from scipy.optimize import Bounds
bounds = Bounds([0, -0.5], [1.0, 2.0])
等式与不等式约束:
#不等式约束
ineq_cons = {'type': 'ineq',
'fun' : lambda x: np.array([1 - x[0] - 2*x[1],
1 - x[0]**2 - x[1],
1 - x[0]**2 + x[1]]),
#jac是对fun的求导
'jac' : lambda x: np.array([[-1.0, -2.0],
[-2*x[0], -1.0],
[-2*x[0], 1.0]])}
#等式约束
eq_cons = {'type': 'eq',
'fun' : lambda x: np.array([2*x[0] + x[1] - 1]),
'jac' : lambda x: np.array([2.0, 1.0])}
求解
x0 = np.array([0.5, 0])
res = minimize(rosen, x0, method='SLSQP', jac=rosen_der,
constraints=[eq_cons, ineq_cons], options={'ftol': 1e-9, 'disp': True},
bounds=bounds)
print(res.x)
Optimization terminated successfully (Exit mode 0)
Current function value: 0.34271757499419825
Iterations: 4
Function evaluations: 5
Gradient evaluations: 4
[0.41494475 0.1701105 ]
完整代码
import numpy as np
from scipy.optimize import minimize
#定义无约束的目标函数
def rosen(x):
"""The Rosenbrock function"""
return sum(100.0*(x[1:]-x[:-1]**2.0)**2.0 + (1-x[:-1])**2.0)
def rosen_der(x):
xm = x[1:-1]
xm_m1 = x[:-2]
xm_p1 = x[2:]
der = np.zeros_like(x)
der[1:-1] = 200*(xm-xm_m1**2) - 400*(xm_p1 - xm**2)*xm - 2*(1-xm)
der[0] = -400*x[0]*(x[1]-x[0]**2) - 2*(1-x[0])
der[-1] = 200*(x[-1]-x[-2]**2)
return der
def rosen_hess(x):
x = np.asarray(x)
H = np.diag(-400*x[:-1],1) - np.diag(400*x[:-1],-1)
diagonal = np.zeros_like(x)
diagonal[0] = 1200*x[0]**2-400*x[1]+2
diagonal[-1] = 200
diagonal[1:-1] = 202 + 1200*x[1:-1]**2 - 400*x[2:]
H = H + np.diag(diagonal)
return H
#定义约束
from scipy.optimize import Bounds
bounds = Bounds([0, -0.5], [1.0, 2.0])
ineq_cons = {'type': 'ineq',
'fun' : lambda x: np.array([1 - x[0] - 2*x[1],
1 - x[0]**2 - x[1],
1 - x[0]**2 + x[1]]),
'jac' : lambda x: np.array([[-1.0, -2.0],
[-2*x[0], -1.0],
[-2*x[0], 1.0]])}
eq_cons = {'type': 'eq',
'fun' : lambda x: np.array([2*x[0] + x[1] - 1]),
'jac' : lambda x: np.array([2.0, 1.0])}
#求解
x0 = np.array([0.5, 0])
res = minimize(rosen, x0, method='SLSQP', jac=rosen_der,
constraints=[eq_cons, ineq_cons], options={'ftol': 1e-9, 'disp': True},
bounds=bounds)
print(res.x)
PS:大部分 trust-constr方法可用的选项对 SLSQP来说是不可用的。
本文详细介绍了如何利用Scipy的minimize函数解决有约束的多变量函数最小化问题,包括边界约束、线性约束和非线性约束的定义,并给出了完整代码示例。涉及的方法包括信赖域约束算法('trust-constr')和序列最小二乘法('SLSQP')。通过实例展示了Rosenbrock函数的约束优化解,强调了在处理复杂约束时的不同方法和技巧。

3030

被折叠的 条评论
为什么被折叠?



