Skip to content

Commit

Permalink
Merge branch 'main' into update-problem-size-table
Browse files Browse the repository at this point in the history
  • Loading branch information
ZedongPeng committed Aug 20, 2024
2 parents 5331c55 + d00d572 commit 69f5699
Show file tree
Hide file tree
Showing 10 changed files with 4,216 additions and 1,918 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
BSD 3-Clause License

Copyright (c) 2020, Ignacio Grossmann Research Group
Copyright (c) 2024, SECQUOIA Research Group
All rights reserved.

Redistribution and use in source and binary forms, with or without
Expand Down
263 changes: 263 additions & 0 deletions gdplib/ ex1_linan_2023/ ex1_linan_2023.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
"""
ex1_linan_2023.py: Toy problem from Liñán and Ricardez-Sandoval (2023) [1]
TThe ex1_linan.py file 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.
The Boolean variables are associated with disjuncts that define the feasible regions of the continuous variables.
The problem 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.
The objective function is -0.9995999999999999 when the continuous variables are alpha = 0 (Y1[2]=True) and beta=-0.7 (Y2[3]=True).
References
----------
[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
[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
"""

import pyomo.environ as pyo
from pyomo.gdp import Disjunct, Disjunction


def build_model():
"""
Build the toy problem model
Returns
-------
Pyomo.ConcreteModel
Toy problem model
"""

# Build Model
m = pyo.ConcreteModel()

# Sets
m.set1 = pyo.RangeSet(1, 5, doc="set of first group of Boolean variables")
m.set2 = pyo.RangeSet(1, 5, doc="set of second group of Boolean variables")

m.sub1 = pyo.Set(initialize=[3], within=m.set1)

# Variables
m.Y1 = pyo.BooleanVar(m.set1, doc="Boolean variable associated to set 1")
m.Y2 = pyo.BooleanVar(m.set2, doc="Boolean variable associated to set 2")

m.alpha = pyo.Var(
within=pyo.Reals, bounds=(-0.1, 0.4), doc="continuous variable alpha"
)
m.beta = pyo.Var(
within=pyo.Reals, bounds=(-0.9, -0.5), doc="continuous variable beta"
)

# Objective Function
def obj_fun(m):
"""
Objective function
Parameters
----------
m : Pyomo.ConcreteModel
Toy problem model
Returns
-------
Pyomo.Objective
Build the objective function of the toy problem
"""
return (
4 * (pow(m.alpha, 2))
- 2.1 * (pow(m.alpha, 4))
+ (1 / 3) * (pow(m.alpha, 6))
+ m.alpha * m.beta
- 4 * (pow(m.beta, 2))
+ 4 * (pow(m.beta, 4))
)

m.obj = pyo.Objective(rule=obj_fun, sense=pyo.minimize, doc="Objective function")

# First Disjunction
def build_disjuncts1(m, set1): # Disjuncts for first Boolean variable
"""
Build disjuncts for the first Boolean variable
Parameters
----------
m : Pyomo.ConcreteModel
Toy problem model
set1 : RangeSet
Set of first group of Boolean variables
"""

def constraint1(m):
"""_summary_
Parameters
----------
m : Pyomo.ConcreteModel
Toy problem model
Returns
-------
Pyomo.Constraint
Constraint that defines the value of alpha for each disjunct
"""
return m.model().alpha == -0.1 + 0.1 * (
set1 - 1
) # .model() is required when writing constraints inside disjuncts

m.constraint1 = pyo.Constraint(rule=constraint1)

m.Y1_disjunct = Disjunct(
m.set1, rule=build_disjuncts1, doc="each disjunct is defined over set 1"
)

def Disjunction1(m): # Disjunction for first Boolean variable
"""
Disjunction for first Boolean variable
Parameters
----------
m : Pyomo.ConcreteModel
Toy problem model
Returns
-------
Pyomo.Disjunction
Build the disjunction for the first Boolean variable set
"""
return [m.Y1_disjunct[j] for j in m.set1]

m.Disjunction1 = Disjunction(rule=Disjunction1, xor=False)

# Associate boolean variables to disjuncts
for n1 in m.set1:
m.Y1[n1].associate_binary_var(m.Y1_disjunct[n1].indicator_var)

# Second disjunction
def build_disjuncts2(m, set2): # Disjuncts for second Boolean variable
"""
Build disjuncts for the second Boolean variable
Parameters
----------
m : Pyomo.ConcreteModel
Toy problem model
set2 : RangeSet
Set of second group of Boolean variables
"""

def constraint2(m):
"""_summary_
Parameters
----------
m : Pyomo.ConcreteModel
Toy problem model
Returns
-------
Pyomo.Constraint
Constraint that defines the value of beta for each disjunct
"""
return m.model().beta == -0.9 + 0.1 * (
set2 - 1
) # .model() is required when writing constraints inside disjuncts

m.constraint2 = pyo.Constraint(rule=constraint2)

m.Y2_disjunct = Disjunct(
m.set2, rule=build_disjuncts2, doc="each disjunct is defined over set 2"
)

def Disjunction2(m): # Disjunction for first Boolean variable
"""
Disjunction for second Boolean variable
Parameters
----------
m : Pyomo.ConcreteModel
Toy problem model
Returns
-------
Pyomo.Disjunction
Build the disjunction for the second Boolean variable set
"""
return [m.Y2_disjunct[j] for j in m.set2]

m.Disjunction2 = Disjunction(rule=Disjunction2, xor=False)

# Associate boolean variables to disjuncts
for n2 in m.set2:
m.Y2[n2].associate_binary_var(m.Y2_disjunct[n2].indicator_var)

# Logical constraints

# Constraint that allow to apply the reformulation over Y1
def select_one_Y1(m):
"""
Logical constraint that allows to apply the reformulation over Y1
Parameters
----------
m : Pyomo.ConcreteModel
Toy problem model
Returns
-------
Pyomo.LogicalConstraint
Logical constraint that make Y1 to be true for only one element
"""
return pyo.exactly(1, m.Y1)

m.oneY1 = pyo.LogicalConstraint(rule=select_one_Y1)

# Constraint that allow to apply the reformulation over Y2
def select_one_Y2(m):
"""
Logical constraint that allows to apply the reformulation over Y2
Parameters
----------
m : Pyomo.ConcreteModel
Toy problem model
Returns
-------
Pyomo.LogicalConstraint
Logical constraint that make Y2 to be true for only one element
"""
return pyo.exactly(1, m.Y2)

m.oneY2 = pyo.LogicalConstraint(rule=select_one_Y2)

# Constraint that define an infeasible region with respect to Boolean variables

def infeasR_rule(m):
"""
Logical constraint that defines an infeasible region with respect to Boolean variables
Parameters
----------
m : Pyomo.ConcreteModel
Toy problem model
Returns
-------
Pyomo.LogicalConstraint
Logical constraint that defines an infeasible region on Y1[3]
"""
return pyo.land([pyo.lnot(m.Y1[j]) for j in m.sub1])

m.infeasR = pyo.LogicalConstraint(rule=infeasR_rule)

return m


if __name__ == "__main__":
m = build_model()
pyo.TransformationFactory("gdp.bigm").apply_to(m)
solver = pyo.SolverFactory("gams")
solver.solve(m, solver="baron", tee=True)
print("Solution: alpha=", pyo.value(m.alpha), " beta=", pyo.value(m.beta))
print("Objective function value: ", pyo.value(m.obj))
14 changes: 14 additions & 0 deletions gdplib/ ex1_linan_2023/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Example 1 Problem of Liñán (2023)

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.

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.

The objective function is -0.9995999999999999 when the continuous variables are alpha = 0 (`Y1[2]=True`) and beta=-0.7 (`Y2[3]=True`).

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.

### References

[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
[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
Loading

0 comments on commit 69f5699

Please sign in to comment.