本系列涉及线性与非线性规划中的几种规划算法
1.本节介绍混合罚函数方法
2.参考:高立-数值最优化方法
3.采用python编程实现,已测试,代码可行
惩罚函数法的基本原理是将约束优化问题的约束函数经过加权后和原目标函数结合形成新的目标函数–惩罚函数。根据迭代过程是否在可行域内进行,惩罚函数法又可以分为内点惩罚函数法、外点惩罚函数法和混合惩罚函数法三种。本篇直接介绍混合罚函数法。
代码如下(示例):
#参考资料,高立-数值最优化方法,老师的课件
#采用混合点惩罚函数法,对于无约束问题采用牛顿法求解,无约束问题的收敛准则采用惩罚项足够小
#牛顿法,输入符号变量,和目标函数的系数,得到目标函数
from sympy import *
import numpy as np
import math
#混合点罚函数法,采用内点法的r0经验公式来确定
class Penalty_Function:
def __init__(self,F,Xi,X0,G=None,H=None):
self.Xi = Xi
self.f = F
self.g = G #不等式约束
self.h = H #等式约束
self.i = len(X0)
def dict(self,X):
dict = {}
for index,item in enumerate(self.Xi):#存储初始值
dict[item] = X[index]
Xk = dict
return Xk
######################################################牛顿法
def d_function(self,f):#如何直接求解出矩阵排列的形式
df = []
for i in self.Xi:
df.append(diff(f,i))
df = np.array(df)
#print(f'一阶偏导为{df}')
return df
def dd_function(self,df):#如何直接求解出矩阵排列的形式
ddf = []
for i in df:
for j in self.Xi:
ddf.append(diff(i,j))
ddf = np.array(ddf)
#print(f'二阶偏导为{ddf}')
return ddf
def D_fun(self,X,df):
Df = []
for i in df:
Df.append(i.subs(self.dict(X)))
Dfk = np.array(Df)
#print(f'梯度gk={Dfk}')
return Dfk
def H_fun(self,X,ddf):
Hf = []
for i in ddf:
Hf.append(i.subs(self.dict(X)))
##如果不进行数据类型的转换,会出现self.Hfk是o类型(对象类型)?里面的数值类型是class 'sympy.core.numbers.Integer'
Hfk = np.array(Hf,dtype=float).reshape(self.i,self.i)
#print(f'海塞矩阵={Hfk}')
return Hfk
def iteration_x(self,f,X,acc2):
df = self.d_function(f)
ddf = self.dd_function(df)
k = 0
flag = 0
while True:
#print(f'迭代点xk={X}')
Dfk = self.D_fun(X,df)
Hfk = self.H_fun(X,ddf)
if np.linalg.norm(Dfk, ord=np.inf) <= acc2:#向量的无穷范数小于一个很小的正数
flag = 1#对于内点法来说,若无法收敛到极小值点,就需要调整r了
print(f'收敛到稳定点{X}')
#实对称矩阵是正定矩阵的充分必要条件是它的所有特征值都大于0,而海塞矩阵如果存在,则是实对称的
if min(np.linalg.eig(Hfk)[0]) > 0:#判断
flag = 1
print(f'收敛到极小点{X}')
break
else:
break
elif np.linalg.matrix_rank(Hfk) < self.i :
print(f'海塞矩阵在{X}处奇异,牛顿法失败')
break
else:
X -= np.dot(np.linalg.inv(Hfk),Dfk)###这个操作会自动把列表X转为数组
k += 1
if flag == 1:
return X
######################################################罚函数
def calculate_r0(self,X0):
x0 = self.dict(X0)
f0 = self.f.evalf(subs=x0)
#f0 = self.f.subs(x0)
g0 = 0
for i in self.g:
#g0 += 1/(i.evalf(subs=x0))
g0 += 1/(i.subs(x0))
#g0 += log(-i.subs(x0))
r0 = abs(f0/g0)#求出r0
print(f'罚函数因子r={r0}')
return r0
def composite_function(self,r):
gx = 0
hx = 0
if self.g:
for gi in self.g:
gx += 1/(gi)
#gx += log(-gi)
if self.h:
for hj in self.h:
hx += hj**2
p = -r*gx + 1/sqrt(r) * hx #增加项
fai = self.f + p#复合后的函数
print(f'罚函数因子r={r}')
return fai,p
#求解acc1罚函数停止,acc2牛顿停止,n牛顿法迭代次数
def solve_x(self,X0,c,acc1,acc2):
r0 = self.calculate_r0(X0)
k = 0
while True:
print(f'###############求解无约束问题######################')
fai, p = self.composite_function(r0)
print(f'r={r0}')
print(f'第{k}次迭代点x={X0}')
k += 1
x_ = self.iteration_x(fai,X0,acc2)
if p.subs(self.dict(x_)) <= acc1:
###这块,收敛采用附加项就可以,原因对于不同的障碍因子r,不同的函数的差几乎就是附加项
x_best = x_#得到最优解
f_best = self.f.subs(self.dict(x_))
print(f'极小点X*={x_best}')
print(f'原问题目标函数值f*={f_best}')
break
else:
r0 *= c
X0 = x_
xi = symbols('x1 x2')#测试:x1 x2 x3 x4 x5 x6
X0 = [2,2]#牛顿法对于初值要求高;测试:[1,1,1,3,0,5]
c = 0.7#对于c也有要求,r太小,没法收敛到极小值,测试c = 0.5
acc1 = 1E-5
acc2 = 1E-5
F = (xi[0]-2)**2+(xi[1]-1)**2#定义目标函数#测试(xi[0]-xi[3])**2+(xi[1]-xi[4])**2+(xi[2]-xi[5])**2.
G=[0.25*xi[0]**2+xi[1]**2-1]#测试:[xi[0]**2+xi[1]**2+xi[2]**2-5,(xi[3]-3)**2+xi[4]**2-1,xi[5]-8,4-xi[5]]
H=[xi[0]-2*xi[1]+1]
Q1 = Penalty_Function(F,xi,X0,G,H)
Q1.solve_x(X0,c,acc1,acc2)
上述代码中:用混合点惩罚函数法,对于无约束问题采用牛顿法求解,无约束问题的收敛准则采用惩罚项足够小。
第73次迭代点x=[0.822875618433224 0.911435695138376]
收敛到稳定点[0.822875624493398 0.911436043481617]
收敛到极小点[0.822875624493398 0.911436043481617]
极小点X*=[0.822875624493398 0.911436043481617]
原问题目标函数值f*=1.39346536980600
暂且告一段落。。。。。