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.
1 Answer 1
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 ofm.sos1()when potential values are integers. Them.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.apmas a text file in the run directorym._path(or open tmp folder withm.open_folder()).
3 Comments
gekko is with continuous variables that often get stuck near the switching conditions, similar to MPCCs.m.sos1() function. It creates a binary variable for each option. If you do use an integer variable then only one integer variable is created instead of multiple binary variables.Explore related questions
See similar questions with these tags.