You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
After defining a JuMP model, disjunctions can be added to the model by specifying which of the original JuMP model constraints should be assigned to each disjunction. The constraints that are assigned to the disjunctions will no longer be general model constraints, but will belong to the disjunction that they are assigned to. These constraints must be either `GreaterThan`, `LessThan`, `EqualTo`, or `Interval` constraints. Constraints that are of `Interval` type are split into two constraints (one for each bound). It is assumed that the disjuncts belonging to a disjunction are proper disjunctions (mutually exclussive) and only one of them will be selected (`XOR`).
13
+
After defining a JuMP model, disjunctions can be added to the model by using the `@disjunction` macro. This macro is called by `@disjunction(m, disjuncts...; kwargs...), where `disjuncts...` is a list of at least two expressions of the form:
14
+
1. A valid expression accepted by [JuMP.@constraint](https://jump.dev/JuMP.jl/stable/reference/constraints/#JuMP.@constraint). Names for the constraints or containers of constraints cannot be passed (use option 2).
15
+
2. A valid expression accepted by [JuMP.@constraints](https://jump.dev/JuMP.jl/stable/reference/constraints/#JuMP.@constraints) (using `begin...end)
16
+
3. A valid expression accepted by [JuMP.@NLconstraint](https://jump.dev/JuMP.jl/stable/reference/nlp/#JuMP.@NLconstraint). Containers of constraints cannot be passed (use option 4). Naming of non-linear constraints is not currently supported.
17
+
4. A valid expression accepted by [JuMP.@NLconstraints](https://jump.dev/JuMP.jl/stable/reference/nlp/#JuMP.@NLconstraints) (using `begin...end)
18
+
5.`Tuple` of expressions accepted by options 1 and/or 3.
14
19
15
-
When a disjunction is defined using the `@disjunction` macro, the disjunctions are reformulated to algebraic constraints via either,
16
-
- The Big-M method (when `reformulation = :BMR` in the `@disjunction` macro)
17
-
- The Convex-Hull (when `reformulation = :CHR` in the `@disjunction` macro)
18
-
These approaches are described [here](https://optimization.mccormick.northwestern.edu/index.php/Disjunctive_inequalities). For the Convex-Hull reformulation, disaggregated variables are generated by adding the suffix `_$name$i` to the original variables, where `i` is the index of the disjunct in that disjunction. Bounding constraints are applied to the disaggregated variables and can be accessed with `model[Symbol("$<original var>_$name$i_lb")]` and `model[Symbol("$<original var>_$name$i_ub")]` for the lower bound and upper bound constraints, respectively. The aggregation constraint can be accessed with `model[Symbol("$<original var>_aggregation")]` When the Convex-Hull reformulation is applied to a nonlinear model, the perspective function proposed in [Furman, et al. [2020]](https://link.springer.com/article/10.1007/s10589-020-00176-0) is used.
20
+
NOTE: Any constraints that are of `Interval` type are split into two constraints (one for each bound). It is assumed that the disjuncts belonging to a disjunction are proper disjunctions (mutually exclussive) and only one of them will be selected (`XOR`).
19
21
20
-
When calling the `@disjunction` macro, a `name::Symbol` keyword argument can be specified to define the name of the binary indicator variable to be used for that disjunction. Otherwise, (`name = missing`) a symbolic name will be generated with the prefix `disj`. The mutual exclussion constraint on the binary indicator variables can be accessed with `model[Symbol("XOR($name)")]`.
22
+
The valid key-word arguments for the `@disjunction` macro are:
23
+
-`reformulation::Symbol`: `:BMR` for [Big-M Reformulation](https://optimization.mccormick.northwestern.edu/index.php/Disjunctive_inequalities#Big-M_Reformulation), `:CHR` for [Convex-Hull Reformulation](https://optimization.mccormick.northwestern.edu/index.php/Disjunctive_inequalities#Convex-Hull_Reformulation)
24
+
-`M`: Big-M value used when `reformulation = :BMR`.
25
+
-`ϵ`: epsilon tolerance for the perspective function proposed by [Furman, et al. [2020]](https://link.springer.com/article/10.1007/s10589-020-00176-0). Only used when `reformulation = :CHR`.
26
+
-`name::Symbol`: Name for the disjunction (also name for indicator variable used on that disjunction). If not passed (`name = missing`), a symbolic name will be generated with the prefix `disj`. The mutual exclussion constraint on the binary indicator variables can be accessed with `model[Symbol("XOR(disj_$name)")]`.
21
27
22
-
For Big-M reformulations, the user may provide an `M` object that represents the BigM value(s). The `M` object can be a `Number` that is applied to all constraints in the disjuncts, or a `Vector`/`Tuple` of values that are used for each of the disjuncts. For Convex-Hull reformulations, the user may provide an `ϵ` value for the perspective function (default is `ϵ = 1e-6`). The `ϵ` object can be a `Number` that is applied to all perspective functions, or a `Vector`/`Tuple` of values that are used for each of the disjuncts.
28
+
When a disjunction is defined using the `@disjunction` macro, the disjunctions are reformulated to algebraic constraints via either Big-M or Convex-Hull reformulations. For the Convex-Hull reformulation, disaggregated variables are generated by adding the suffix `_$name$i` to the original variables, where `i` is the index of the disjunct in that disjunction. Bounding constraints are applied to the disaggregated variables and can be accessed with `model[Symbol("$<original var>_$name$i_lb")]` and `model[Symbol("$<original var>_$name$i_ub")]` for the lower bound and upper bound constraints, respectively. The aggregation constraint can be accessed with `model[Symbol("$<original var>_aggregation")]`. For Big-M reformulations, the user may provide an `M` object that represents the BigM value(s). The `M` object can be a `Number` that is applied to all constraints in the disjuncts, or a `Vector`/`Tuple` of values that are used for each of the disjuncts. For Convex-Hull reformulations, the user may provide an `ϵ` value for the perspective function (default is `ϵ = 1e-6`). The `ϵ` object can be a `Number` that is applied to all perspective functions, or a `Vector`/`Tuple` of values that are used for each of the disjuncts.
23
29
24
-
For empty disjuncts, use `nothing` for their positional argument (e.g., `@disjunction(m, con1, nothing, reformulation = :BMR)`).
30
+
For empty disjuncts, use `nothing` for their positional argument (e.g., `@disjunction(m, x <= 1, nothing, reformulation = :BMR)`).
25
31
26
32
NOTE: `:gdp_variable_refs` and `:gdp_variable_names` are forbidden JuMP model object names when using *DisjunctiveProgramming.jl*. They are used to store the variable names and variable references in the original model.
27
33
@@ -39,7 +45,7 @@ The logical proposition is then internally reformulated to an algebraic constrai
39
45
40
46
The example below is from the [Northwestern University Process Optimization Open Textbook](https://optimization.mccormick.northwestern.edu/index.php/Disjunctive_inequalities).
41
47
42
-
To perform the Big-M reformulation, `:BMR` is passed to the `reformulation` keyword argument. If nothing is passed to the keyword argument `M`, tight Big-M values will be inferred from the variable bounds using IntervalArithmetic.jl. If `x` is not bounded, Big-M values must be provided for either the whole system (e.g., `M = 10`) or for each of the constraint arrays in the example (e.g., `M = ((10,10),(10,10))`).
48
+
To perform the Big-M reformulation, `:BMR` is passed to the `reformulation` keyword argument. If nothing is passed to the keyword argument `M`, tight Big-M values will be inferred from the variable bounds using IntervalArithmetic.jl. If `x` is not bounded, Big-M values must be provided for either the whole system (e.g., `M = 10`) or for each of the constraint arrays in the example (e.g., `M = (10,10)`).
43
49
44
50
To perform the Convex-Hull reformulation, `reformulation = :CHR`. Variables must have bounds for the reformulation to work.
@proposition(m, y[1] ∨ y[2]) #this is a redundant proposition
58
66
59
67
print(m)
60
68
61
-
┌ Warning: con1 : x in [0.0, 3.0] uses the `MOI.Interval` set. Each instance of the interval set has been split into two constraints, one for each bound.
62
-
┌ Warning: con2 : x in [5.0, 9.0] uses the `MOI.Interval` set. Each instance of the interval set has been split into two constraints, one for each bound.
63
-
69
+
┌ Warning: disj_y[1] : x in [0.0, 3.0] uses the `MOI.Interval` set. Each instance of the interval set has been split into two constraints, one for each bound.
70
+
┌ Warning: disj_y[2] : x in [5.0, 9.0] uses the `MOI.Interval` set. Each instance of the interval set has been split into two constraints, one for each bound.
64
71
Feasibility
65
72
Subject to
66
-
XOR(y) : y[1] + y[2] ==1.0
67
-
y[1] ∨ y[2] : y[1] + y[2] >=1.0
68
-
con1[lb] :-x + y[1] <=1.0
69
-
con1[ub] : x +7 y[1] <=10.0
70
-
con2[lb] :-x +6 y[2] <=1.0
71
-
con2[ub] : x + y[2] <=10.0
72
-
x >=-1.0
73
-
x <=10.0
74
-
y[1] binary
75
-
y[2] binary
73
+
XOR(disj_y) : y[1] + y[2] ==1.0<- XOR constraint
74
+
y[1] ∨ y[2] : y[1] + y[2] >=1.0<- reformulated logical proposition (name is the proposition)
75
+
disj_y[1][lb] :-x +5y[1] <=5.0<- left-side of constraint in1st disjunct (name is assigned to disj_y[1][lb])
76
+
disj_y[1][ub] : x +7 y[1] <=10.0<- right-side of constraint in1st disjunct (name is assigned to disj_y[1][ub])
77
+
disj_y[2][lb] :-x +10 y[2] <=5.0<- left-side of constraint in2nd disjunct (name is assigned to disj_y[2][lb])
78
+
disj_y[2][ub] : x + y[2] <=10.0<- right-side of constraint in2nd disjunct (name is assigned to disj_y[2][ub])
79
+
x >=-5.0<- variable lower bound
80
+
x <=10.0<- variable upper bound
81
+
y[1] binary<- indicator variable (1st disjunct) is binary
82
+
y[2] binary<- indicator variable (2nd disjunct) is binary
@proposition(m, y[1] ∨ y[2]) #this is a redundant proposition
12
14
13
-
print(m)
15
+
print(m)
16
+
17
+
# ┌ Warning: disj_y[1] : x in [0.0, 3.0] uses the `MOI.Interval` set. Each instance of the interval set has been split into two constraints, one for each bound.
18
+
# ┌ Warning: disj_y[2] : x in [5.0, 9.0] uses the `MOI.Interval` set. Each instance of the interval set has been split into two constraints, one for each bound.
# ┌ Warning: [con1[1] : x[1] in [0.0, 3.0], con1[2] : x[2] in [0.0, 4.0]] uses the `MOI.Interval` set. Each instance of the interval set has been split into two constraints, one for each bound.
21
+
# ┌ Warning: [con2[1] : x[1] in [5.0, 9.0], con2[2] : x[2] in [4.0, 6.0]] uses the `MOI.Interval` set. Each instance of the interval set has been split into two constraints, one for each bound.
elseif constr isa Union{ConstraintRef, Array, Containers.DenseAxisArray, Containers.SparseAxisArray}
15
-
push!(disj_new, check_constraint!(m, constr))
16
-
end
17
-
end
18
-
19
-
return disj_new
20
-
end
21
-
22
5
functioncheck_constraint!(m, constr)
23
6
@assertall(is_valid.(m, constr)) "$constr is not a valid constraint."
24
7
split_flag =false
@@ -55,8 +38,10 @@ function check_constraint!(m, constr)
55
38
end
56
39
end
57
40
58
-
split_flag &&@warn"$constr uses the `MOI.Interval` set. Each instance of the interval set has been split into two constraints, one for each bound."
59
-
delete_original_constraint!(m, constr)
41
+
if split_flag
42
+
@warn"$(split(string(constr),"}")[end]) uses the `MOI.Interval` set. Each instance of the interval set has been split into two constraints, one for each bound."
0 commit comments