Skip to content

Commit 4b522a3

Browse files
authored
Merge pull request #80 from hdavid16/juliacon_proceedings
2 parents 605198f + ded3433 commit 4b522a3

18 files changed

+2041
-101
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "DisjunctiveProgramming"
22
uuid = "0d27d021-0159-4c7d-b4a7-9ccb5d9366cf"
33
authors = ["hdavid16 <[email protected]>"]
4-
version = "0.4.0"
4+
version = "0.4.1"
55

66
[deps]
77
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"

README.md

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -134,19 +134,28 @@ The example below is from the [Cornell University Computational Optimization Ope
134134

135135
```julia
136136
using DisjunctiveProgramming
137+
using HiGHS
137138
138-
m = GDPModel()
139+
m = GDPModel(HiGHS.Optimizer)
139140
@variable(m, 0 ≤ x[1:2] ≤ 20)
140141
@variable(m, Y[1:2], Logical)
141142
@constraint(m, [i = 1:2], [2,5][i] ≤ x[i] ≤ [6,9][i], DisjunctConstraint(Y[1]))
142143
@constraint(m, [i = 1:2], [8,10][i] ≤ x[i] ≤ [11,15][i], DisjunctConstraint(Y[2]))
143144
@disjunction(m, Y)
144145
@constraint(m, Y in Exactly(1)) #logical constraint
146+
@objective(m, Max, sum(x))
147+
print(m)
148+
# Max x[1] + x[2]
149+
# Subject to
150+
# x[1] ≥ 0
151+
# x[2] ≥ 0
152+
# x[1] ≤ 20
153+
# x[2] ≤ 20
145154
146-
## Big-M
147-
reformulate_model(m, BigM(100, false)) #specify M value and disable M-tightening
155+
##
156+
optimize!(m, method = BigM(100, false)) #specify M value and disable M-tightening
148157
print(m)
149-
# Feasibility
158+
# Max x[1] + x[2]
150159
# Subject to
151160
# Y[1] + Y[2] = 1
152161
# x[1] - 100 Y[1] ≥ -98
@@ -164,10 +173,10 @@ print(m)
164173
# Y[1] binary
165174
# Y[2] binary
166175
167-
## Hull
168-
reformulate_model(m, Hull())
176+
##
177+
optimize!(m, method = Hull())
169178
print(m)
170-
# Feasibility
179+
# Max x[1] + x[2]
171180
# Subject to
172181
# -x[2] + x[2]_Y[1] + x[2]_Y[2] = 0
173182
# -x[1] + x[1]_Y[1] + x[1]_Y[2] = 0
@@ -202,21 +211,8 @@ print(m)
202211
# x[1]_Y[2] ≤ 20
203212
# Y[1] binary
204213
# Y[2] binary
205-
206-
## Indicator
207-
reformulate_model(m, Indicator())
208-
print(m)
209-
# Feasibility
210-
# Subject to
211-
# Y[1] + Y[2] = 1
212-
# x[1] ≥ 0
213-
# x[2] ≥ 0
214-
# x[1] ≤ 20
215-
# x[2] ≤ 20
216-
# Y[1] binary
217-
# Y[2] binary
218-
# Y[1] => {x[1] ∈ [2, 6]}
219-
# Y[1] => {x[2] ∈ [5, 9]}
220-
# Y[2] => {x[1] ∈ [8, 11]}
221-
# Y[2] => {x[2] ∈ [10, 15]}
222214
```
215+
216+
## Contributing
217+
`DisjunctiveProgramming` is being actively developed and suggestions or other forms of contribution are encouraged.
218+
There are many ways to contribute to this package. Feel free to create an issue to address questions or provide feedback.

docs/make.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ makedocs(
44
sitename = "DisjunctiveProgramming.jl",
55
modules = [DisjunctiveProgramming],
66
pages=[
7-
"Home" => "index.md"
7+
"Home" => "index.md",
8+
"API" => "api.md"
89
],
910
checkdocs = :none
1011
)

docs/src/api.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# API
2+
3+
```@autodocs
4+
Modules = [DisjunctiveProgramming]
5+
Order = [:type, :function]
6+
```

docs/src/assets/logo.png

434 KB
Loading

docs/src/index.md

Lines changed: 218 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,218 @@
1-
# Overview
2-
DisjunctiveProgramming.jl
1+
# DisjunctiveProgramming.jl
2+
Generalized Disjunctive Programming (GDP) extension to JuMP, based on the GDP modeling paradigm described in [Perez and Grossmann, 2023](https://arxiv.org/abs/2303.04375).
3+
4+
![](assets/logo.png)
5+
6+
[![codecov](https://codecov.io/gh/hdavid16/DisjunctiveProgramming.jl/graph/badge.svg?token=3FRPGMWF0J)](https://codecov.io/gh/hdavid16/DisjunctiveProgramming.jl)
7+
[![Docs](https://img.shields.io/badge/docs-stable-blue.svg)](https://hdavid16.github.io/InventoryManagement.jl/stable/)
8+
[![Docs](https://img.shields.io/badge/docs-latest-blue.svg)](https://hdavid16.github.io/InventoryManagement.jl/dev/)
9+
10+
## Installation
11+
12+
```julia
13+
using Pkg
14+
Pkg.add("DisjunctiveProgramming")
15+
```
16+
17+
## Model
18+
19+
A generalized disjunctive programming (GDP) model is created using `GDPModel()`, where the optimizer can be passed at model creation, along with other keyword arguments supported by JuMP Models.
20+
21+
```julia
22+
using DisjunctiveProgramming
23+
using HiGHS
24+
25+
model = GDPModel(HiGHS.Optimizer)
26+
```
27+
28+
A `GDPModel` is a `JuMP Model` with a `GDPData` field in the model's `.ext` dictionary, which stores the following:
29+
30+
- `Logical Variables`: Indicator variables used for the various disjuncts involved in the model's disjunctions.
31+
- `Logical Constraints`: Selector (cardinality) or proposition (Boolean) constraints describing the relationships between the logical variables.
32+
- `Disjunct Constraints`: Constraints associated with each disjunct in the model.
33+
- `Disjunctions`: Disjunction constraints.
34+
- `Solution Method`: The reformulation technique or solution method. Currently supported methods include Big-M, Hull, and Indicator Constraints.
35+
- `Reformulation Variables`: List of JuMP variables created when reformulating a GDP model into a MIP model.
36+
- `Reformulation Constraints`: List of constraints created when reformulating a GDP model into a MIP model.
37+
- `Ready to Optimize`: Flag indicating if the model can be optimized.
38+
39+
Additionally, the following mapping dictionaries are stored in `GDPData`:
40+
41+
- `Indicator to Binary`: Maps the Logical variables to their respective reformulated Binary variables.
42+
- `Indicator to Constraints`: Maps the Logical variables to the disjunct constraints associated with them.
43+
44+
A GDP Model's `GDPData` can be accessed via:
45+
46+
```julia
47+
data = gdp_data(model)
48+
```
49+
50+
## Logical Variables
51+
52+
Logical variables are JuMP `AbstractVariable`s with two fields: `fix_value` and `start_value`. These can be optionally specified at variable creation. Logical variables are created with the `@variable` JuMP macro by adding the tag `Logical` as the last keyword argument. As with the regular `@variable` macro, variables can be named and indexed:
53+
54+
```julia
55+
@variable(model, Y[1:3], Logical)
56+
```
57+
58+
## Logical Constraints
59+
60+
Two types of logical constraints are supported:
61+
62+
1. `Selector` or cardinality constraints: A subset of Logical variables is passed and `Exactly`, `AtMost`, or `AtLeast` `n` of these is allowed to be `True`. These constraints are specified with the `func` $\in$ `set` notation in `MathOptInterface` in a `@constraint` JuMP macro. It is not assumed that disjunctions have an `Exactly(1)` constraint enforced on their disjuncts upon creation. This constraint must be explicitly specified.
63+
64+
```julia
65+
@constraint(model, [Y[1], Y[2]] in Exactly(1))
66+
```
67+
68+
2. `Proposition` or Boolean constraints: These describe the relationships between Logical variables via Boolean algebra. Supported logical operators include:
69+
70+
- `` or `logical_or` (OR, typed with `\vee + tab`).
71+
- `` or `logical_and` (AND, typed with `\wedge + tab`).
72+
- `¬` or `logical_not` (NOT, typed with `\neg + tab`).
73+
- `` of `implies` (Implication, typed with `\Longrightarrow + tab`).
74+
- `` or `iff` (double implication or equivalence, typed with `\Leftrightarrow + tab`).
75+
76+
The `@constraint` JuMP macro is used to create these constraints with the `IsTrue` set:
77+
78+
```julia
79+
@constraint(model, (Y[1] ⟹ Y[2]) in IsTrue())
80+
```
81+
82+
_Note_: The parenthesis in the example above around the implication clause are only required when the parent logical operator is `` or `` to avoid parsing errors.
83+
84+
Logical propositions can be reformulated to IP constraints by automatic reformulation to [Conjunctive Normal Form](https://en.wikipedia.org/wiki/Conjunctive_normal_form).
85+
86+
## Disjunctions
87+
88+
Disjunctions are built by first defining the constraints associated with each disjunct. This is done via the `@constraint` JuMP macro with the extra `DisjunctConstraint` tag specifying the Logical variable associated with the constraint:
89+
90+
```julia
91+
@variable(model, x)
92+
@constraint(model, x ≤ 100, DisjunctConstraint(Y[1]))
93+
@constraint(model, x ≥ 200, DisjunctConstraint(Y[2]))
94+
```
95+
96+
After all disjunct constraints associated with a disjunction have been defined, the disjunction is created with the `@disjunction` macro, where the disjunction is defined as a `Vector` of Logical variables associated with each disjunct:
97+
98+
```julia
99+
@disjunction(model, [Y[1], Y[2]])
100+
```
101+
102+
Disjunctions can be nested by passing an additional `DisjunctConstraint` tag. The Logical variable in the `DisjunctConstraint` tag specifies which disjunct, the nested disjunction belongs to:
103+
104+
```julia
105+
@disjunction(model, Y[1:2], DisjunctConstraint(Y[3]))
106+
```
107+
108+
Empty disjuncts are supported in GDP models. When used, the only constraints enforced on the model when the empty disjunct is selected are the global constraints and any other disjunction constraints defined.
109+
110+
## MIP Reformulations
111+
112+
The following reformulation methods are currently supported:
113+
114+
1. [Big-M](https://optimization.cbe.cornell.edu/index.php?title=Disjunctive_inequalities#Big-M_Reformulation[1][2]): The `BigM` struct is created with the following optional arguments:
115+
116+
- `value`: Big-M value to use. Default: `1e9`. Big-M values are currently global to the model. Constraint specific Big-M values can be supported in future releases.
117+
- `tighten`: Boolean indicating if tightening the Big-M value should be attempted (currently supported only for linear disjunct constraints when variable bounds have been set or specified in the `variable_bounds` field). Default: `true`.
118+
- `variable_bounds`: Dictionary specifying the lower and upper bounds for each `VariableRef` (e.g., `Dict(x => (lb, ub))`). Default: populate when calling the reformulation method.
119+
120+
2. [Hull](https://optimization.cbe.cornell.edu/index.php?title=Disjunctive_inequalities#Convex-Hull_Reformulation[1][2]): The `Hull` struct is created with the following optional arguments:
121+
122+
- `value`: `ϵ` value to use when reformulating quadratic or nonlinear constraints via the perspective function proposed by [Furman, et al. [2020]](https://link.springer.com/article/10.1007/s10589-020-00176-0). Default: `1e-6`. `ϵ` values are currently global to the model. Constraint specific tolerances can be supported in future releases.
123+
- `variable_bounds`: Dictionary specifying the lower and upper bounds for each `VariableRef` (e.g., `Dict(x => (lb, ub))`). Default: populate when calling the reformulation method.
124+
125+
3. [Indicator](https://jump.dev/JuMP.jl/stable/manual/constraints/#Indicator-constraints): This method reformulates each disjunct constraint into an indicator constraint with the Boolean reformulation counterpart of the Logical variable used to define the disjunct constraint.
126+
127+
## Release Notes
128+
129+
Prior to `v0.4.0`, the package did not leverage the JuMP extension capabilities and was not as robust. For these earlier releases, refer to [Perez, Joshi, and Grossmann, 2023](https://arxiv.org/abs/2304.10492v1) and the following [JuliaCon 2022 Talk](https://www.youtube.com/watch?v=AMIrgTTfUkI).
130+
131+
## Example
132+
133+
The example below is from the [Cornell University Computational Optimization Open Textbook](https://optimization.cbe.cornell.edu/index.php?title=Disjunctive_inequalities#Big-M_Reformulation[1][2]).
134+
135+
```julia
136+
using DisjunctiveProgramming
137+
using HiGHS
138+
139+
m = GDPModel(HiGHS.Optimizer)
140+
@variable(m, 0 ≤ x[1:2] ≤ 20)
141+
@variable(m, Y[1:2], Logical)
142+
@constraint(m, [i = 1:2], [2,5][i] ≤ x[i] ≤ [6,9][i], DisjunctConstraint(Y[1]))
143+
@constraint(m, [i = 1:2], [8,10][i] ≤ x[i] ≤ [11,15][i], DisjunctConstraint(Y[2]))
144+
@disjunction(m, Y)
145+
@constraint(m, Y in Exactly(1)) #logical constraint
146+
@objective(m, Max, sum(x))
147+
print(m)
148+
# Max x[1] + x[2]
149+
# Subject to
150+
# x[1] ≥ 0
151+
# x[2] ≥ 0
152+
# x[1] ≤ 20
153+
# x[2] ≤ 20
154+
155+
##
156+
optimize!(m, method = BigM(100, false)) #specify M value and disable M-tightening
157+
print(m)
158+
# Max x[1] + x[2]
159+
# Subject to
160+
# Y[1] + Y[2] = 1
161+
# x[1] - 100 Y[1] ≥ -98
162+
# x[2] - 100 Y[1] ≥ -95
163+
# x[1] - 100 Y[2] ≥ -92
164+
# x[2] - 100 Y[2] ≥ -90
165+
# x[1] + 100 Y[1] ≤ 106
166+
# x[2] + 100 Y[1] ≤ 109
167+
# x[1] + 100 Y[2] ≤ 111
168+
# x[2] + 100 Y[2] ≤ 115
169+
# x[1] ≥ 0
170+
# x[2] ≥ 0
171+
# x[1] ≤ 20
172+
# x[2] ≤ 20
173+
# Y[1] binary
174+
# Y[2] binary
175+
176+
##
177+
optimize!(m, method = Hull())
178+
print(m)
179+
# Max x[1] + x[2]
180+
# Subject to
181+
# -x[2] + x[2]_Y[1] + x[2]_Y[2] = 0
182+
# -x[1] + x[1]_Y[1] + x[1]_Y[2] = 0
183+
# Y[1] + Y[2] = 1
184+
# -2 Y[1] + x[1]_Y[1] ≥ 0
185+
# -5 Y[1] + x[2]_Y[1] ≥ 0
186+
# -8 Y[2] + x[1]_Y[2] ≥ 0
187+
# -10 Y[2] + x[2]_Y[2] ≥ 0
188+
# x[2]_Y[1]_lower_bound : -x[2]_Y[1] ≤ 0
189+
# x[2]_Y[1]_upper_bound : -20 Y[1] + x[2]_Y[1] ≤ 0
190+
# x[1]_Y[1]_lower_bound : -x[1]_Y[1] ≤ 0
191+
# x[1]_Y[1]_upper_bound : -20 Y[1] + x[1]_Y[1] ≤ 0
192+
# x[2]_Y[2]_lower_bound : -x[2]_Y[2] ≤ 0
193+
# x[2]_Y[2]_upper_bound : -20 Y[2] + x[2]_Y[2] ≤ 0
194+
# x[1]_Y[2]_lower_bound : -x[1]_Y[2] ≤ 0
195+
# x[1]_Y[2]_upper_bound : -20 Y[2] + x[1]_Y[2] ≤ 0
196+
# -6 Y[1] + x[1]_Y[1] ≤ 0
197+
# -9 Y[1] + x[2]_Y[1] ≤ 0
198+
# -11 Y[2] + x[1]_Y[2] ≤ 0
199+
# -15 Y[2] + x[2]_Y[2] ≤ 0
200+
# x[1] ≥ 0
201+
# x[2] ≥ 0
202+
# x[2]_Y[1] ≥ 0
203+
# x[1]_Y[1] ≥ 0
204+
# x[2]_Y[2] ≥ 0
205+
# x[1]_Y[2] ≥ 0
206+
# x[1] ≤ 20
207+
# x[2] ≤ 20
208+
# x[2]_Y[1] ≤ 20
209+
# x[1]_Y[1] ≤ 20
210+
# x[2]_Y[2] ≤ 20
211+
# x[1]_Y[2] ≤ 20
212+
# Y[1] binary
213+
# Y[2] binary
214+
```
215+
216+
## Contributing
217+
`DisjunctiveProgramming` is being actively developed and suggestions or other forms of contribution are encouraged.
218+
There are many ways to contribute to this package. Feel free to create an issue to address questions or provide feedback.

0 commit comments

Comments
 (0)