Search code examples
pythonnonlinear-optimizationgekkomixed-integer-programming

Gekko using APOPT isn't optimizing a single linear equation represented as a PWL


I've run into an issue where I can't get APOPT to optimize an unconstrained single piecewise linear, and it's really throwing me for a loop. I feel like there's something I'm not understanding about model.pwl, but it's hard (for me) to find documentation outside of the GEKKO docs. Here's my minimal example:

model = GEKKO(remote=False)
model.options.SOLVER = 1
model.solver_options = ["minlp_as_nlp 0"]

x = model.sos1([0, 1, 2, 3, 4]) # This can also be model.Var(lb=0, ub=4), same result.
pwl = model.Var()

model.pwl(x, pwl, [0, 1, 2, 3, 4], [30, 30.1, 30.2, 30.3, 30.4], bound_x=True)

model.Minimize(pwl)

model.solve(display=True)

print(x.value)
print(pwl.value)
print(model.options.objfcnval)

The output that I get is:

 ----------------------------------------------------------------
 APMonitor, Version 1.0.3
 APMonitor Optimization Suite
 ----------------------------------------------------------------


 --------- APM Model Size ------------
 Each time step contains
   Objects      :            1
   Constants    :            0
   Variables    :            2
   Intermediates:            0
   Connections  :            2
   Equations    :            1
   Residuals    :            1

 Piece-wise linear model pwl1points:            5
 Number of state variables:             12
 Number of total equations: -            5
 Number of slack variables: -            0
 ---------------------------------------
 Degrees of freedom       :              7

 ----------------------------------------------
 Steady State Optimization with APOPT Solver
 ----------------------------------------------

 Iter    Objective  Convergence
    0  3.39503E+01  3.01000E+01
    1  3.22900E+01  1.00000E-10
    2  3.22000E+01  2.22045E-16
    4  3.22000E+01  0.00000E+00
 Successful solution

 ---------------------------------------------------
 Solver         :  APOPT (v1.0)
 Solution time  :   3.819999999541324E-002 sec
 Objective      :    32.2000000000000
 Successful solution
 ---------------------------------------------------


2.0
30.2
32.2

This is unexpected to me, as the obvious minimal value is 30 for the pwl.


Solution

  • A cubic spline is much more reliable in optimization than a piecewise linear function because it doesn't rely on slack variables and switching conditions.

    from gekko import GEKKO
    model = GEKKO(remote=False)
    model.options.SOLVER = 1
    
    x = model.Var(lb=0, ub=4, integer=True)
    y = model.Var()
    
    model.cspline(x, y, [0, 1, 2, 3, 4], [30, 30.1, 30.2, 30.3, 30.4], bound_x=True)
    
    model.Minimize(y)
    
    model.solve(display=True)
    
    print(x.value)
    print(y.value)
    print(model.options.objfcnval)
    

    Here is the output:

     ----------------------------------------------------------------
     APMonitor, Version 1.0.3
     APMonitor Optimization Suite
     ----------------------------------------------------------------
     
     
     --------- APM Model Size ------------
     Each time step contains
       Objects      :            1
       Constants    :            0
       Variables    :            2
       Intermediates:            0
       Connections  :            2
       Equations    :            1
       Residuals    :            1
     
     Number of state variables:              2
     Number of total equations: -            1
     Number of slack variables: -            0
     ---------------------------------------
     Degrees of freedom       :              1
     
     ----------------------------------------------
     Steady State Optimization with APOPT Solver
     ----------------------------------------------
    Iter:     1 I:  0 Tm:      0.00 NLPi:    2 Dpth:    0 Lvs:    0 Obj:  3.00E+01 Gap:  0.00E+00
     Successful solution
     
     ---------------------------------------------------
     Solver         :  APOPT (v1.0)
     Solution time  :   1.970000000437722E-002 sec
     Objective      :    30.0000000000000     
     Successful solution
     ---------------------------------------------------
     
    
    [0.0]
    [30.0]
    30.0
    

    A few other notes:

    • Use m.Var(integer=True) instead of m.sos1() when potential values are integers. The m.sos1() function is for discrete non-integer values.
    • There is more information on the PWL function in the APMonitor documentation. Gekko is an interface to the APMonitor Modeling Language and writes gk0_model.apm as a text file in the run directory m._path (or open tmp folder with m.open_folder()).