Python用scipy.optimize约束的非线性优化无法找到最佳解决方案

时间:2019-06-21 19:52:59

标签: python numpy scipy mathematical-optimization

我有一个问题,我试图找到一种解决方案,其中有5个单变量多项式,这些单项多项式在我关注的范围内有一个峰值。我的目标是找到每个多项式的某些变量值(在一定的最小/最大值和所有变量约束的总和下),以使这些曲线的值乘以每个曲线的常数而最大化。

我已经使用scipy.optimize包和numpy设置了一些代码。它似乎能够达到解决方案,但是它所达到的解决方案似乎并不是最佳解决方案。例如,琐碎的情况是输入488 MW。这个特定的输入值有一个解决方案,其中每个x0-x4变量都在其函数的峰值处,如下所示:

x0=90
x1=100
x2=93
x3=93
x4=112

The result it provides we with is:

x0=80
x1=97
x2=105
x3=80
x4=126

This does satisfy my constraint, but it does not appear to minimize the objective function. 

import numpy as np
import matplotlib.pyplot as plt

from scipy.optimize import minimize



U1_Max=103
U1_Min=80
U2_Max=102
U2_Min=80
U3_Max=105
U3_Min=80
U4_Max=100
U4_Min=80
U5_Max=126
U5_Min=90


# Our whole goal here is to maximze the sum of several efficiency efficiency curves times the
# output MW of each unit. "The most efficiency where it matters the most"
# Assuming all units are available for assignment his would look something like:

#Where have the following efficiency curves:
#U1: Efficiency=-0.0231*(MW)^2+4.189*MW-102.39
#U2: Efficiency= -0.01*(MW)^2+1.978*MW-8.7451
#U3: Efficiency= -0.025*MW^2+4.5017*MW-115.37
#U4: Efficiency= -0.01*(MW)^2+1.978*MW-8.7451
#U5: Efficiency= -0.0005*(MW)^2+0.1395*(MW)^2-13.327*MW+503.41

#So I think we want to
#Maximize U1(x0)*U1_MAX+U2(x1)*U2_MAX+U3(x2)*U3_MAX+U4(x3)*U4_MAX+U5(x4)*U5_MAX
#I think this can also be stated as:
#Minimize (U1(x0)-100)*U1_MAX+(U2(x1)-100)*U2_MAX)+(U3(x2)-100)*U3_MAX)+(U4(x3)-100)*U4_MAX)+(U5(x4)-100)*U5_MAX)
#Which means 'minimize the sum of the products of the difference between 100% efficient and actual and the unit nameplates'

#By Choosing {x1, x2, x3, x4, x5}

#Such that x1+x2+x3+x4+x5=MW_Total
#Such that U1_Min<x1<U1Max
#Such that U2_Min<x2<U2Max
#Such that U3_Min<x3<U3Max
#Such that U4_Min<x4<U4Max
#Such that U5_Min<x5<U5Max

##so let's type that out....
#stack overflow says the optimizer does best if the function being optimized is around 1-5ish so we will get it there-ish. 
def objective(x):
  return (
      (
      ((100-0.0231*x[0]**2+4.189*x[0]-102.39))*U1_Max
      +((100-0.01*x[1]**2+1.978*x[1]-8.7451))*U2_Max
      +((100-0.025*x[2]**2+4.5017*x[2]-115.37))*U3_Max
      +((100-0.01*x[3]**2+1.978*x[3]-8.7451))*U4_Max
      +((100-0.0005*x[4]**3+0.1395*x[4]**2-13.327*x[4]+503.41))*U5_Max
      )

      )

x=np.zeros(5)

print(
      (
      ((100-0.0231*x[0]**2+4.189*x[0]-102.39))*U1_Max
      +((100-0.01*x[1]**2+1.978*x[1]-8.7451))*U2_Max
      +((100-0.025*x[2]**2+4.5017*x[2]-115.37))*U3_Max
      +((100-0.01*x[3]**2+1.978*x[3]-8.7451))*U4_Max
      +((100-0.0005*x[4]**3+0.1395*x[4]**2-13.327*x[4]+503.41))*U5_Max
      )

      )

#Now, let's formally define our constraints
#Note that this must be of a form that satisfies 'constraint equal to zero'
#First, the sum of all MW commands should be qual to the total MW commanded
def constraint1(x):
    return -x[0]-x[1]-x[2]-x[3]-x[4]+MW_Total

#Since this is a numeric process let's give it some starting 'guess' conditions.
n=5
x0=np.zeros(n)
x0[0]=90
x0[1]=100
x0[2]=93
x0[3]=93
x0[4]=112

# show initial starting uess
print('Start by guessing: ')
print(x0)
print('Which gives a scaled algorithim value of: ')
print(
      (
      ((100-0.0231*x0[0]**2+4.189*x0[0]-102.39))*U1_Max
      +((100-0.01*x0[1]**2+1.978*x0[1]-8.7451))*U2_Max
      +((100-0.025*x0[2]**2+4.5017*x0[2]-115.37))*U3_Max
      +((100-0.01*x0[3]**2+1.978*x0[3]-8.7451))*U4_Max
      +((100-0.0005*x0[4]**3+0.1395*x0[4]**2-13.327*x0[4]+503.41))*U5_Max
      )

      )
print('Which gives actual MW total of: ')
print(x0[0]+x0[1]+x0[2]+x0[3]+x0[4])


#Next, Let's give it some bounds to operate in
U1_Bnds=(U1_Min, U1_Max)
U2_Bnds=(U2_Min, U2_Max)
U3_Bnds=(U3_Min, U3_Max)
U4_Bnds=(U4_Min, U4_Max)
U5_Bnds=(U5_Min, U5_Max)
Bnds=(U1_Bnds, U2_Bnds, U3_Bnds, U4_Bnds, U5_Bnds)

con1 = {'type': 'eq', 'fun': constraint1}
print('MW Generated is: ')
for i in range (410,536):
  MW_Total=i
  solution = minimize(objective,x0,method='SLSQP',bounds=Bnds,constraints=con1,options={'maxiter': 10000000, 'eps':1.4901161193847656e-10})
  x = solution.x
  print(solution.x[0],solution.x[1],solution.x[2],solution.x[3],solution.x[4])

我希望对于488 MW的微不足道的情况,它将为我提供最佳的答案。我在做什么错了?

1 个答案:

答案 0 :(得分:1)

通过查看目标和约束定义,看起来就像是具有线性约束的二次目标函数。

关于这一点的理论是众所周知的,它提供了收敛保证,您可以参考the wikipedia page

我不太了解Scipy SLSQP界面,但是看来您使用的信息少于您能做的事情。尝试以具有线性约束的二次目标函数的形式来提出问题。还可以将约束投射到scipy.optimize.LinearConstraint对象中。

并且请在代码中使用print(objective(x))print(solution.x)之类的函数调用,这将提高可读性。