Reputation: 21
I have an MINLP problem and let's say the continuous variable Q
can only be 0
when the binary variable z
is 0
. Two ways to formulate this would be:
m.Equation(Q*(1-z) == 0)
(1)
or
m.Equation(Q < z*10000)
(2)
whereby 10000
would be the upper bound to the continuous variable Q
. Does (1) or (2) have any benefits over the other?
I've used (1) in my model for heat exchanger network synthesis and I got a good solution pretty quickly. Using (2) takes around 100x longer and it gives worse solution than the one given using (1).
From what I can see, the MINLP relaxation to (1) would still require z
to be exactly 1
for Q
to take on non-zero values. Does this have any effect on how APOPT solve the problem?
Upvotes: 1
Views: 36
Reputation: 14376
I agree with Erwin's response. One other suggestion is to try using Q*(1-z)<eps
where eps
is a small value such as 1e-3
to improve convergence. This helps the solver explore the solution space to find the optimal solution. This strategy is also commonly used in Mathematical Programs with Complementarity Constraints. Here is an example that demonstrates the approach.
Option 1: Nonlinear Constraint Q*(1-z)<eps
from gekko import GEKKO
m = GEKKO(remote=False)
eps = 1e-3
Q = m.Var(value=0, lb=0, ub=1000)
z = m.Var(value=0.5, lb=0, ub=1, integer=True)
# Objective: maximize Q
m.Maximize(Q)
# Nonlinear constraint
m.Equation(Q * (1-z) < eps)
# Solve the problem
m.options.SOLVER = 1 # APOPT solver
m.solve(disp=True)
print(f"Q = {Q.value[0]}, z = {z.value[0]}")
Solution summary
Number of state variables: 3
Number of total equations: - 1
Number of slack variables: - 1
---------------------------------------
Degrees of freedom : 1
----------------------------------------------
Steady State Optimization with APOPT Solver
----------------------------------------------
Iter: 1 I: 0 Tm: 0.00 NLPi: 12 Dpth: 0 Lvs: 0 Obj: -1.00E+03 Gap: 0.00E+00
Successful solution
---------------------------------------------------
Solver : APOPT (v1.0)
Solution time : 8.199999982025474E-003 sec
Objective : -1000.00000000000
Successful solution
---------------------------------------------------
Q = 1000.0, z = 1.0
Option 2: Big-M Formulation
from gekko import GEKKO
m = GEKKO(remote=False)
Q = m.Var(value=0, lb=0, ub=1000)
z = m.Var(value=0.5, lb=0, ub=1, integer=True)
m.Maximize(Q)
# Big-M constraint with M = 10000
m.Equation(Q <= 10000 * z)
# Solve the problem
m.options.SOLVER = 1 # APOPT solver
m.solve(disp=True)
print(f"Q = {Q.value[0]}, z = {z.value[0]}")
The solution requires 2 iterations.
----------------------------------------------
Steady State Optimization with APOPT Solver
----------------------------------------------
Iter: 1 I: 0 Tm: 0.00 NLPi: 3 Dpth: 0 Lvs: 2 Obj: -1.00E+03 Gap: NaN
--Integer Solution: -1.00E+03 Lowest Leaf: -1.00E+03 Gap: 0.00E+00
Iter: 2 I: 0 Tm: 0.00 NLPi: 2 Dpth: 1 Lvs: 2 Obj: -1.00E+03 Gap: 0.00E+00
Successful solution
---------------------------------------------------
Solver : APOPT (v1.0)
Solution time : 9.599999990314245E-003 sec
Objective : -1000.00000000000
Successful solution
---------------------------------------------------
Q = 1000.0, z = 1.0
Gekko automatically translates user-defined inequality constraints into an equality constraint plus a slack variable as required by some solvers.
The inequality constraint Q < 10000*z
becomes 10000*z-Q + s ==0
and s>0
.
Number of state variables: 3
Number of total equations: - 1
Number of slack variables: - 1
---------------------------------------
Degrees of freedom : 1
For APOPT solver, use Q*(1-z)<eps
unless a linear form is required. If a Mixed-Integer Linear Programming (MILP) solver is required, use a Big-M or indicator constraints.
Upvotes: 1
Reputation: 16782
Q(1-z) = 0
is non-linear and non-convex while Q <= 10000z
is linear (and convex). The last one is much better as long as the big-M constant is small.
If you can't reduce the size of the big-M constant, consider using indicator constraints (using suitable modeling tools and solvers).
Upvotes: 1