Skip to content

Commit 69f5699

Browse files
committed
Merge branch 'main' into update-problem-size-table
2 parents 5331c55 + d00d572 commit 69f5699

File tree

10 files changed

+4216
-1918
lines changed

10 files changed

+4216
-1918
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
BSD 3-Clause License
22

3-
Copyright (c) 2020, Ignacio Grossmann Research Group
3+
Copyright (c) 2024, SECQUOIA Research Group
44
All rights reserved.
55

66
Redistribution and use in source and binary forms, with or without
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
"""
2+
ex1_linan_2023.py: Toy problem from Liñán and Ricardez-Sandoval (2023) [1]
3+
4+
TThe ex1_linan.py file is a simple optimization problem that involves two Boolean variables, two continuous variables, and a nonlinear objective function.
5+
The problem is formulated as a Generalized Disjunctive Programming (GDP) model.
6+
The Boolean variables are associated with disjuncts that define the feasible regions of the continuous variables.
7+
The problem includes logical constraints that ensure that only one Boolean variable is true at a time.
8+
Additionally, there are two disjunctions, one for each Boolean variable, where only one disjunct in each disjunction must be true.
9+
A specific logical constraint also enforces that Y1[3] must be false, making this particular disjunct infeasible.
10+
The objective function is -0.9995999999999999 when the continuous variables are alpha = 0 (Y1[2]=True) and beta=-0.7 (Y2[3]=True).
11+
12+
References
13+
----------
14+
[1] Liñán, D. A., & Ricardez-Sandoval, L. A. (2023). A Benders decomposition framework for the optimization of disjunctive superstructures with ordered discrete decisions. AIChE Journal, 69(5), e18008. https://doi.org/10.1002/aic.18008
15+
[2] Gomez, S., & Levy, A. V. (1982). The tunnelling method for solving the constrained global optimization problem with several non-connected feasible regions. In Numerical Analysis: Proceedings of the Third IIMAS Workshop Held at Cocoyoc, Mexico, January 1981 (pp. 34-47). Springer Berlin Heidelberg. https://doi.org/10.1007/BFb0092958
16+
"""
17+
18+
import pyomo.environ as pyo
19+
from pyomo.gdp import Disjunct, Disjunction
20+
21+
22+
def build_model():
23+
"""
24+
Build the toy problem model
25+
26+
Returns
27+
-------
28+
Pyomo.ConcreteModel
29+
Toy problem model
30+
"""
31+
32+
# Build Model
33+
m = pyo.ConcreteModel()
34+
35+
# Sets
36+
m.set1 = pyo.RangeSet(1, 5, doc="set of first group of Boolean variables")
37+
m.set2 = pyo.RangeSet(1, 5, doc="set of second group of Boolean variables")
38+
39+
m.sub1 = pyo.Set(initialize=[3], within=m.set1)
40+
41+
# Variables
42+
m.Y1 = pyo.BooleanVar(m.set1, doc="Boolean variable associated to set 1")
43+
m.Y2 = pyo.BooleanVar(m.set2, doc="Boolean variable associated to set 2")
44+
45+
m.alpha = pyo.Var(
46+
within=pyo.Reals, bounds=(-0.1, 0.4), doc="continuous variable alpha"
47+
)
48+
m.beta = pyo.Var(
49+
within=pyo.Reals, bounds=(-0.9, -0.5), doc="continuous variable beta"
50+
)
51+
52+
# Objective Function
53+
def obj_fun(m):
54+
"""
55+
Objective function
56+
57+
Parameters
58+
----------
59+
m : Pyomo.ConcreteModel
60+
Toy problem model
61+
62+
Returns
63+
-------
64+
Pyomo.Objective
65+
Build the objective function of the toy problem
66+
"""
67+
return (
68+
4 * (pow(m.alpha, 2))
69+
- 2.1 * (pow(m.alpha, 4))
70+
+ (1 / 3) * (pow(m.alpha, 6))
71+
+ m.alpha * m.beta
72+
- 4 * (pow(m.beta, 2))
73+
+ 4 * (pow(m.beta, 4))
74+
)
75+
76+
m.obj = pyo.Objective(rule=obj_fun, sense=pyo.minimize, doc="Objective function")
77+
78+
# First Disjunction
79+
def build_disjuncts1(m, set1): # Disjuncts for first Boolean variable
80+
"""
81+
Build disjuncts for the first Boolean variable
82+
83+
Parameters
84+
----------
85+
m : Pyomo.ConcreteModel
86+
Toy problem model
87+
set1 : RangeSet
88+
Set of first group of Boolean variables
89+
"""
90+
91+
def constraint1(m):
92+
"""_summary_
93+
94+
Parameters
95+
----------
96+
m : Pyomo.ConcreteModel
97+
Toy problem model
98+
99+
Returns
100+
-------
101+
Pyomo.Constraint
102+
Constraint that defines the value of alpha for each disjunct
103+
"""
104+
return m.model().alpha == -0.1 + 0.1 * (
105+
set1 - 1
106+
) # .model() is required when writing constraints inside disjuncts
107+
108+
m.constraint1 = pyo.Constraint(rule=constraint1)
109+
110+
m.Y1_disjunct = Disjunct(
111+
m.set1, rule=build_disjuncts1, doc="each disjunct is defined over set 1"
112+
)
113+
114+
def Disjunction1(m): # Disjunction for first Boolean variable
115+
"""
116+
Disjunction for first Boolean variable
117+
118+
Parameters
119+
----------
120+
m : Pyomo.ConcreteModel
121+
Toy problem model
122+
123+
Returns
124+
-------
125+
Pyomo.Disjunction
126+
Build the disjunction for the first Boolean variable set
127+
"""
128+
return [m.Y1_disjunct[j] for j in m.set1]
129+
130+
m.Disjunction1 = Disjunction(rule=Disjunction1, xor=False)
131+
132+
# Associate boolean variables to disjuncts
133+
for n1 in m.set1:
134+
m.Y1[n1].associate_binary_var(m.Y1_disjunct[n1].indicator_var)
135+
136+
# Second disjunction
137+
def build_disjuncts2(m, set2): # Disjuncts for second Boolean variable
138+
"""
139+
Build disjuncts for the second Boolean variable
140+
141+
Parameters
142+
----------
143+
m : Pyomo.ConcreteModel
144+
Toy problem model
145+
set2 : RangeSet
146+
Set of second group of Boolean variables
147+
"""
148+
149+
def constraint2(m):
150+
"""_summary_
151+
152+
Parameters
153+
----------
154+
m : Pyomo.ConcreteModel
155+
Toy problem model
156+
157+
Returns
158+
-------
159+
Pyomo.Constraint
160+
Constraint that defines the value of beta for each disjunct
161+
"""
162+
return m.model().beta == -0.9 + 0.1 * (
163+
set2 - 1
164+
) # .model() is required when writing constraints inside disjuncts
165+
166+
m.constraint2 = pyo.Constraint(rule=constraint2)
167+
168+
m.Y2_disjunct = Disjunct(
169+
m.set2, rule=build_disjuncts2, doc="each disjunct is defined over set 2"
170+
)
171+
172+
def Disjunction2(m): # Disjunction for first Boolean variable
173+
"""
174+
Disjunction for second Boolean variable
175+
176+
Parameters
177+
----------
178+
m : Pyomo.ConcreteModel
179+
Toy problem model
180+
181+
Returns
182+
-------
183+
Pyomo.Disjunction
184+
Build the disjunction for the second Boolean variable set
185+
"""
186+
return [m.Y2_disjunct[j] for j in m.set2]
187+
188+
m.Disjunction2 = Disjunction(rule=Disjunction2, xor=False)
189+
190+
# Associate boolean variables to disjuncts
191+
for n2 in m.set2:
192+
m.Y2[n2].associate_binary_var(m.Y2_disjunct[n2].indicator_var)
193+
194+
# Logical constraints
195+
196+
# Constraint that allow to apply the reformulation over Y1
197+
def select_one_Y1(m):
198+
"""
199+
Logical constraint that allows to apply the reformulation over Y1
200+
201+
Parameters
202+
----------
203+
m : Pyomo.ConcreteModel
204+
Toy problem model
205+
206+
Returns
207+
-------
208+
Pyomo.LogicalConstraint
209+
Logical constraint that make Y1 to be true for only one element
210+
"""
211+
return pyo.exactly(1, m.Y1)
212+
213+
m.oneY1 = pyo.LogicalConstraint(rule=select_one_Y1)
214+
215+
# Constraint that allow to apply the reformulation over Y2
216+
def select_one_Y2(m):
217+
"""
218+
Logical constraint that allows to apply the reformulation over Y2
219+
220+
Parameters
221+
----------
222+
m : Pyomo.ConcreteModel
223+
Toy problem model
224+
225+
Returns
226+
-------
227+
Pyomo.LogicalConstraint
228+
Logical constraint that make Y2 to be true for only one element
229+
"""
230+
return pyo.exactly(1, m.Y2)
231+
232+
m.oneY2 = pyo.LogicalConstraint(rule=select_one_Y2)
233+
234+
# Constraint that define an infeasible region with respect to Boolean variables
235+
236+
def infeasR_rule(m):
237+
"""
238+
Logical constraint that defines an infeasible region with respect to Boolean variables
239+
240+
Parameters
241+
----------
242+
m : Pyomo.ConcreteModel
243+
Toy problem model
244+
245+
Returns
246+
-------
247+
Pyomo.LogicalConstraint
248+
Logical constraint that defines an infeasible region on Y1[3]
249+
"""
250+
return pyo.land([pyo.lnot(m.Y1[j]) for j in m.sub1])
251+
252+
m.infeasR = pyo.LogicalConstraint(rule=infeasR_rule)
253+
254+
return m
255+
256+
257+
if __name__ == "__main__":
258+
m = build_model()
259+
pyo.TransformationFactory("gdp.bigm").apply_to(m)
260+
solver = pyo.SolverFactory("gams")
261+
solver.solve(m, solver="baron", tee=True)
262+
print("Solution: alpha=", pyo.value(m.alpha), " beta=", pyo.value(m.beta))
263+
print("Objective function value: ", pyo.value(m.obj))

gdplib/ ex1_linan_2023/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
## Example 1 Problem of Liñán (2023)
2+
3+
The Example 1 Problem of Liñán (2023) is a simple optimization problem that involves two Boolean variables, two continuous variables, and a nonlinear objective function. The problem is formulated as a Generalized Disjunctive Programming (GDP) model.
4+
5+
The Boolean variables are associated with disjuncts that define the feasible regions of the continuous variables. The problem also includes logical constraints that ensure that only one Boolean variable is true at a time. Additionally, there are two disjunctions, one for each Boolean variable, where only one disjunct in each disjunction must be true. A specific logical constraint also enforces that `Y1[3]` must be false, making this particular disjunct infeasible.
6+
7+
The objective function is -0.9995999999999999 when the continuous variables are alpha = 0 (`Y1[2]=True`) and beta=-0.7 (`Y2[3]=True`).
8+
9+
The objective function originates from Problem No. 6 of Gomez's paper, and Liñán introduced logical propositions, logical disjunctions, and the following equations as constraints.
10+
11+
### References
12+
13+
[1] Liñán, D. A., & Ricardez-Sandoval, L. A. (2023). A Benders decomposition framework for the optimization of disjunctive superstructures with ordered discrete decisions. AIChE Journal, 69(5), e18008. https://doi.org/10.1002/aic.18008
14+
[2] Gomez, S., & Levy, A. V. (1982). The tunnelling method for solving the constrained global optimization problem with several non-connected feasible regions. In Numerical Analysis: Proceedings of the Third IIMAS Workshop Held at Cocoyoc, Mexico, January 1981 (pp. 34-47). Springer Berlin Heidelberg. https://doi.org/10.1007/BFb0092958

0 commit comments

Comments
 (0)