首页 > 解决方案 > 如何在 python 中为优化求解器定义合适的目标函数

问题描述

我需要为 Scipy SLSQP slover 定义一个优化目标函数。困难在于我的目标函数中自变量的数量不固定。我试图使用以下代码:

from scipy.optimize import minimize, Bounds, LinearConstraint
import scipy.special as sc
import numpy as np
import pandas as pd
import warnings
warnings.filterwarnings("ignore")

Lmin, Lnom, Lmax = 0.8035, 2.3811, 3.084
L10, L20 = 2.3, 2.3
# one step look ahead
# Eindf = [1116, 1160, 841, 1116]
# Dindf = [44, 861, 275]
# Ldf = [4.2, 5.2, 4.8, 4.4]
Eindf = [1116, 1160, 841]
Dindf = [44, 861]
Ldf = [4.2, 5.2, 4.8]

R10, R20 = 0.1803, 0.1803
Rfc1, Rfc2 = R10 + 0.003, R20 + 0.0

if Rfc1 > Rfc2:
    w1 = (Rfc1 * np.exp((Rfc1-Rfc2)) + Rfc1) / (Rfc2 + np.exp((Rfc1-Rfc2))*Rfc1 + Rfc1)
    w2 = Rfc2 / (Rfc2 + np.exp((Rfc1-Rfc2))*Rfc1+ Rfc1)
else:
    w1 = ( Rfc1) / (Rfc2 + np.exp((Rfc1-Rfc2))*Rfc1+ Rfc1)
    w2 = (Rfc2 * np.exp((Rfc2-Rfc1) + Rfc2)) / (Rfc2 * np.exp((Rfc2-Rfc1))+ Rfc2 + Rfc1)

β = 0.00873 

Ratio_VtoR = 5.39
Dv_ΔL = 5.93e-7
RatioD_ΔL = 20 
Dr_ΔL = RatioD_ΔL * Dv_ΔL * Ratio_VtoR
k = (R10 * Dr_ΔL) / (Lmax - Lmin)

def alpha_beta(x):
    # a, b are params fitted for parabola functions
    a = 0.0019727939
    b = 0.0078887
    Lmin, Lnom, Lmax = 0.8035, 2.3811, 3.084
    return np.piecewise(x, [np.logical_and(Lmin <= x, x < Lnom),
                            np.logical_and(Lnom <= x, x <= Lmax)],
                        [lambda x: a * ((x - Lnom)**2) + 0.006226,
                         lambda x: b * ((x - Lnom)**2) + 0.006226, 0])

def func1(Dind): 
    crit = lambda x: (k*(w1*abs(x[0]-L10)+w2*abs(x[1]-L20))+\
                    β*Dind[0]*(w1*alpha_beta(x[0])+w2*alpha_beta(x[1]))+ β*Dind[1]*(w1*alpha_beta(x[2])+w2*alpha_beta(x[3]))+\
                      k*(w1*abs(x[2]-x[0]) + w1*abs(x[4]-x[2]) + w2*abs(x[3]-x[1])+w2*abs(x[5]-x[3])))
    return crit

def func2(Dind): 
    crit = lambda x: (k*(w1*abs(x[0]-L10)+w2*abs(x[1]-L20))+ β*Dind[1]*(w1*alpha_beta(x[2])+w2*alpha_beta(x[3]))+\
                      β*Dind[0]*(w1*alpha_beta(x[0])+w2*alpha_beta(x[1]))+\
                      k*(w1*abs(x[2]-x[0]) + w1*abs(x[4]-x[2]) + w2*abs(x[3]-x[1])+w2*abs(x[5]-x[3])))
    return crit
                     
def func(Dind):
    Cr1 = lambda x: (k*(w1*abs(x[0]-L10)+w2*abs(x[1]-L20)) +\
                     sum((β*Dind[i]*(w1*alpha_beta(x[2*i])+w2*alpha_beta(x[2*i+1])) +\
                    k*(w1*abs(x[2*(i+1)]-x[2*i]) + w2*abs(x[2*(i+1)+1]-x[2*i+1]))) for i in range(len(Dind))))                
    return Cr1

bounds = Bounds([Lmin]*2*len(Ldf), [Lmax]*2*len(Ldf))

A_mat = np.zeros((len(Ldf), 2*len(Ldf)))
for i in range(len(Ldf)):
    A_mat[i, 2*i] = 1
    A_mat[i, 2*i+1] = 1
    
linear_cons = LinearConstraint(A_mat, np.array(Ldf), (1.08 * np.array(Ldf)))

x0 = np.repeat(Ldf, 2) / 2 

res = minimize(func(Dindf), x0, method='trust-constr', constraints=linear_cons, options={'disp': 0, 'xtol':1e-5,
                'maxiter': 80, 'gtol':1e-7, 'verbose': 0}, bounds=bounds)
print(f'func:{res.x}')


res = minimize(func1(Dindf), x0, method='trust-constr', constraints=linear_cons, options={'disp': 0, 'xtol':1e-5,
                'maxiter': 80, 'gtol':1e-7, 'verbose': 0}, bounds=bounds)
print(f'func1:{res.x}')

res = minimize(func2(Dindf), x0, method='trust-constr', constraints=linear_cons, options={'disp': 0, 'xtol':1e-5,
                'maxiter': 80, 'gtol':1e-7, 'verbose': 0}, bounds=bounds)
print(f'func2:{res.x}')

我得到了输出:

func:[2.30419673 2.23050723 2.52527556 2.67472855 2.52537813 2.5305927 ]
func1:[2.30460228 2.22989643 2.52506878 2.67493795 2.52505983 2.54172443]
func2:[2.30369304 2.22975576 2.52527861 2.67474766 2.52559372 2.52896126]

为了更清楚,我放了几个优化案例。func() 是一种通用形式,用于处理任意数量的自变量。func1() 和 fucn2() 实际上是相同的,我只是改变了一些术语的顺序。而func1()、func2()的结构与func()相同。我不明白的是为什么优化结果不同?

标签: pythonfunctionoptimizationlambdascipy-optimize-minimize

解决方案


您已经错误地定义了其中一个功能,尽管我很难说哪个是“技术上正确的”。无论哪种方式,您的特殊情况函数都与通用函数在以下情况下显示的内容不匹配len(Dind) = 4

form = ['k*(w1*abs(x[0]-L10)+w2*abs(x[1]-L20))']

for i in range(0,4):
  form.append(f'β*Dind[{i}]*(w1*alpha_beta(x[{2*i}])+w2*alpha_beta(x[{2*i+1}])) + k*(w1*abs(x[{2*(i+1)}]-x[{2*i}]) + w2*abs(x[{2*(i+1)+1}]-x[{2*i+1}]))')

print('+'.join(form))

这给了我:

crit = lambda x: k*(w1*abs(x[0]-L10)+w2*abs(x[1]-L20))+β*Dind[0]*(w1*alpha_beta(x[0])+w2*alpha_beta(x[1])) + k*(w1*abs(x[2]-x[0]) + w2*abs(x[3]-x[1]))+β*Dind[1]*(w1*alpha_beta(x[2])+w2*alpha_beta(x[3])) + k*(w1*abs(x[4]-x[2]) + w2*abs(x[5]-x[3]))+β*Dind[2]*(w1*alpha_beta(x[4])+w2*alpha_beta(x[5])) + k*(w1*abs(x[6]-x[4]) + w2*abs(x[7]-x[5]))+β*Dind[3]*(w1*alpha_beta(x[6])+w2*alpha_beta(x[7])) + k*(w1*abs(x[8]-x[6]) + w2*abs(x[9]-x[7]))

推荐阅读