Skip to content

Commit 50cdc2a

Browse files
committed
change exclusive to exactly1
1 parent 3675178 commit 50cdc2a

File tree

13 files changed

+49
-47
lines changed

13 files changed

+49
-47
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ Disjunctions can be nested by passing an additional `Disjunct` tag. The Logical
107107

108108
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.
109109

110-
For convenience, the `Exactly(1)` selector constraint is added by default when adding a disjunction to the model. In other words, `@disjunction(model, Y)` will add the disjunction and automatically add the logical constraint `Y in Exactly(1)`. For nested disjunctions, the appropriate `Exactly` constraint is added (e.g., `@constraint(model, Y[1:2] in Exactly(Y[3]))`) to indicate that `Exactly 1` logical variable in `Y[1:2]` is set to `true` when `Y[3]` is `true`, and both variables in `Y[1:2]` are set to `false` when `Y[3]` is `false`, meaning the parent disjunct is not selected. Adding the `Exactly` selector constraint by default can be disabled by setting the keyword argument `exclusive` to `false` in the `@disjunction` macro.
110+
For convenience, the `Exactly(1)` selector constraint is added by default when adding a disjunction to the model. In other words, `@disjunction(model, Y)` will add the disjunction and automatically add the logical constraint `Y in Exactly(1)`. For nested disjunctions, the appropriate `Exactly` constraint is added (e.g., `@constraint(model, Y[1:2] in Exactly(Y[3]))`) to indicate that `Exactly 1` logical variable in `Y[1:2]` is set to `true` when `Y[3]` is `true`, and both variables in `Y[1:2]` are set to `false` when `Y[3]` is `false`, meaning the parent disjunct is not selected. Adding the `Exactly` selector constraint by default can be disabled by setting the keyword argument `exactly1` to `false` in the `@disjunction` macro.
111111

112112
## MIP Reformulations
113113

docs/src/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ Disjunctions can be nested by passing an additional `Disjunct` tag. The Logical
107107

108108
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.
109109

110-
For convenience, the `Exactly(1)` selector constraint is added by default when adding a disjunction to the model. In other words, `@disjunction(model, Y)` will add the disjunction and automatically add the logical constraint `Y in Exactly(1)`. For nested disjunctions, the appropriate `Exactly` constraint is added (e.g., `@constraint(model, Y[1:2] in Exactly(Y[3]))`) to indicate that `Exactly 1` logical variable in `Y[1:2]` is set to `true` when `Y[3]` is `true`, and both variables in `Y[1:2]` are set to `false` when `Y[3]` is `false`, meaning the parent disjunct is not selected. Adding the `Exactly` selector constraint by default can be disabled by setting the keyword argument `exclusive` to `false` in the `@disjunction` macro.
110+
For convenience, the `Exactly(1)` selector constraint is added by default when adding a disjunction to the model. In other words, `@disjunction(model, Y)` will add the disjunction and automatically add the logical constraint `Y in Exactly(1)`. For nested disjunctions, the appropriate `Exactly` constraint is added (e.g., `@constraint(model, Y[1:2] in Exactly(Y[3]))`) to indicate that `Exactly 1` logical variable in `Y[1:2]` is set to `true` when `Y[3]` is `true`, and both variables in `Y[1:2]` are set to `false` when `Y[3]` is `false`, meaning the parent disjunct is not selected. Adding the `Exactly` selector constraint by default can be disabled by setting the keyword argument `exactly1` to `false` in the `@disjunction` macro.
111111

112112
## MIP Reformulations
113113

src/constraints.jl

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,10 @@ function JuMP.delete(model::Model, cref::DisjunctionRef)
117117
delete!(gdp_data(model).constraint_to_indicator, cref)
118118
end
119119
delete!(_disjunctions(model), index(cref))
120-
exclusive_dict = gdp_data(model).exclusive_constraints
121-
if haskey(exclusive_dict, cref)
122-
JuMP.delete(model, exclusive_dict[cref])
123-
delete!(exclusive_dict, cref)
120+
exactly1_dict = gdp_data(model).exactly1_constraints
121+
if haskey(exactly1_dict, cref)
122+
JuMP.delete(model, exactly1_dict[cref])
123+
delete!(exactly1_dict, cref)
124124
end
125125
_set_ready_to_optimize(model, false)
126126
return
@@ -316,7 +316,7 @@ function _disjunction(
316316
model::Model, # TODO: generalize to AbstractModel
317317
structure::AbstractVector, #generalize for containers
318318
name::String;
319-
exclusive::Bool = true,
319+
exactly1::Bool = true,
320320
extra_kwargs...
321321
)
322322
# check for unneeded keywords
@@ -326,12 +326,12 @@ function _disjunction(
326326
# create the disjunction
327327
dref = _create_disjunction(_error, model, structure, name, false)
328328
# add the exactly one constraint if desired
329-
if exclusive
329+
if exactly1
330330
lvars = JuMP.constraint_object(dref).indicators
331331
func = Union{Number, LogicalVariableRef}[1, lvars...]
332332
set = _MOIExactly(length(lvars) + 1)
333333
cref = JuMP.add_constraint(model, JuMP.VectorConstraint(func, set))
334-
gdp_data(model).exclusive_constraints[dref] = cref
334+
gdp_data(model).exactly1_constraints[dref] = cref
335335
end
336336
return dref
337337
end
@@ -354,7 +354,7 @@ function _disjunction(
354354
structure,
355355
name::String,
356356
tag::Disjunct;
357-
exclusive::Bool = true,
357+
exactly1::Bool = true,
358358
extra_kwargs...
359359
)
360360
# check for unneeded keywords
@@ -366,12 +366,12 @@ function _disjunction(
366366
obj = constraint_object(dref)
367367
_add_indicator_var(_DisjunctConstraint(obj, tag.indicator), dref, model)
368368
# add the exactly one constraint if desired
369-
if exclusive
369+
if exactly1
370370
lvars = JuMP.constraint_object(dref).indicators
371371
func = LogicalVariableRef[tag.indicator, lvars...]
372372
set = _MOIExactly(length(lvars) + 1)
373373
cref = JuMP.add_constraint(model, JuMP.VectorConstraint(func, set))
374-
gdp_data(model).exclusive_constraints[dref] = cref
374+
gdp_data(model).exactly1_constraints[dref] = cref
375375
end
376376
return dref
377377
end
@@ -396,15 +396,15 @@ end
396396
disjunct_indicators::Vector{LogicalVariableRef},
397397
[nested_tag::Disjunct],
398398
[name::String = ""];
399-
[exclusive::Bool = true]
399+
[exactly1::Bool = true]
400400
)
401401
402402
Create a disjunction comprised of disjuncts with indicator variables `disjunct_indicators`
403403
and add it to `model`. For nested disjunctions, the `nested_tag` is required to indicate
404-
which disjunct it will be part of in the parent disjunction. By default, `exclusive` adds
404+
which disjunct it will be part of in the parent disjunction. By default, `exactly1` adds
405405
a constraint of the form `@constraint(model, disjunct_indicators in Exactly(1))` only
406406
allowing one of the disjuncts to be selected; this is required for certain reformulations like
407-
[`Hull`](@ref). For nested disjunctions, `exclusive` creates a constraint of the form
407+
[`Hull`](@ref). For nested disjunctions, `exactly1` creates a constraint of the form
408408
`@constraint(model, disjunct_indicators in Exactly(nested_tag.indicator))`.
409409
To conveniently generate many disjunctions at once, see [`@disjunction`](@ref)
410410
and [`@disjunctions`](@ref).

src/datatypes.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ mutable struct GDPData
391391
disjunctions::_MOIUC.CleverDict{DisjunctionIndex, ConstraintData{Disjunction}}
392392

393393
# Exactly one constraint mappings
394-
exclusive_constraints::Dict{DisjunctionRef, LogicalConstraintRef}
394+
exactly1_constraints::Dict{DisjunctionRef, LogicalConstraintRef}
395395

396396
# Indicator variable mappings
397397
indicator_to_binary::Dict{LogicalVariableRef, VariableRef}

src/hull.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ end
149149
################################################################################
150150
# HULL REFORMULATION
151151
################################################################################
152-
requires_exclusive(::Hull) = true
152+
requires_exactly1(::Hull) = true
153153

154154
function _reformulate_disjunctions(model::Model, method::Hull)
155155
_query_variable_bounds(model, method)

src/macros.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ The recognized keyword arguments in `kw_args` are the following:
155155
the constraint names are set to `base_name[...]` for each index `...`
156156
of the axes `axes`.
157157
- `container`: Specify the container type.
158-
- `exclusive`: Specify a `Bool` whether a constraint should be added to
158+
- `exactly1`: Specify a `Bool` whether a constraint should be added to
159159
only allow selecting one disjunct in the disjunction.
160160
161161
To create disjunctions without macros, see [`disjunction`](@ref).

src/model.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,14 @@ _logical_variables(model::Model) = gdp_data(model).logical_variables
5454
_logical_constraints(model::Model) = gdp_data(model).logical_constraints
5555
_disjunct_constraints(model::Model) = gdp_data(model).disjunct_constraints
5656
_disjunctions(model::Model) = gdp_data(model).disjunctions
57+
_exactly1_constraints(model::Model) = gdp_data(model).exactly1_constraints
5758
_indicator_to_binary(model::Model) = gdp_data(model).indicator_to_binary
5859
_indicator_to_constraints(model::Model) = gdp_data(model).indicator_to_constraints
60+
_constraint_to_indicator(model::Model) = gdp_data(model).constraint_to_indicator
5961
_reformulation_variables(model::Model) = gdp_data(model).reformulation_variables
6062
_reformulation_constraints(model::Model) = gdp_data(model).reformulation_constraints
61-
_ready_to_optimize(model::Model) = gdp_data(model).ready_to_optimize # Determine if the model is ready to call `optimize!` without a optimize hook
6263
_solution_method(model::Model) = gdp_data(model).solution_method # Get the current solution method
64+
_ready_to_optimize(model::Model) = gdp_data(model).ready_to_optimize # Determine if the model is ready to call `optimize!` without a optimize hook
6365

6466
# Update the ready_to_optimize field
6567
function _set_ready_to_optimize(model::Model, is_ready::Bool)

src/reformulate.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,22 +47,22 @@ end
4747
# DISJUNCTIONS
4848
################################################################################
4949
"""
50-
requires_exclusive(method::AbstractReformulationMethod)
50+
requires_exactly1(method::AbstractReformulationMethod)
5151
5252
Return a `Bool` whether `method` requires that `Exactly 1` disjunct be selected
5353
as true for each disjunction. For new reformulation method types, this should be
5454
extended to return `true` if such a constraint is required (defaults to `false` otherwise).
5555
"""
56-
requires_exclusive(::AbstractReformulationMethod) = false
56+
requires_exactly1(::AbstractReformulationMethod) = false
5757

5858
# disjunctions
5959
function _reformulate_all_disjunctions(model::Model, method::AbstractReformulationMethod)
6060
for (idx, disj) in _disjunctions(model)
6161
disj.constraint.nested && continue #only reformulate top level disjunctions
6262
dref = DisjunctionRef(model, idx)
63-
if requires_exclusive(method) && !haskey(gdp_data(model).exclusive_constraints, dref)
64-
error("Reformulation method `$method` requires exclusive disjuncts for " *
65-
"disjunctions, but `exclusive = false` for disjunction `$dref`.")
63+
if requires_exactly1(method) && !haskey(gdp_data(model).exactly1_constraints, dref)
64+
error("Reformulation method `$method` requires disjunctions where only 1 disjunct is selected, " *
65+
"but `exactly1 = false` for disjunction `$dref`.")
6666
end
6767
ref_cons = reformulate_disjunction(model, disj.constraint, method)
6868
for (i, ref_con) in enumerate(ref_cons)

test/constraints/bigm.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,10 +288,10 @@ function test_nested_bigm()
288288
@variable(model, z[1:2], Logical)
289289
@constraint(model, x <= 5, Disjunct(y[1]))
290290
@constraint(model, x >= 5, Disjunct(y[2]))
291-
@disjunction(model, inner, y, Disjunct(z[1]), exclusive = false)
291+
@disjunction(model, inner, y, Disjunct(z[1]), exactly1 = false)
292292
@constraint(model, x <= 10, Disjunct(z[1]))
293293
@constraint(model, x >= 10, Disjunct(z[2]))
294-
@disjunction(model, outer, z, exclusive = false)
294+
@disjunction(model, outer, z, exactly1 = false)
295295

296296
reformulate_model(model, BigM())
297297
bvrefs = DP._indicator_to_binary(model)

test/constraints/disjunction.jl

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ function test_disjunction_add_success()
5151
disj = DisjunctionRef(model, DisjunctionIndex(1))
5252
disj2 = DisjunctionRef(model, DisjunctionIndex(2))
5353
@test @disjunction(model, y) == disj
54-
@test @disjunction(model, disj2, y, exclusive = false) == disj2
54+
@test @disjunction(model, disj2, y, exactly1 = false) == disj2
5555
@test owner_model(disj) == model
5656
@test is_valid(model, disj)
5757
@test index(disj) == DisjunctionIndex(1)
@@ -62,8 +62,8 @@ function test_disjunction_add_success()
6262
@test DP._disjunctions(model)[index(disj)] == DP._constraint_data(disj)
6363
@test !constraint_object(disj).nested
6464
@test constraint_object(disj).indicators == y
65-
@test haskey(gdp_data(model).exclusive_constraints, disj)
66-
@test !haskey(gdp_data(model).exclusive_constraints, disj2)
65+
@test haskey(gdp_data(model).exactly1_constraints, disj)
66+
@test !haskey(gdp_data(model).exactly1_constraints, disj2)
6767
@test disj == copy(disj)
6868
end
6969

@@ -89,7 +89,7 @@ function test_disjunction_add_nested()
8989
@test !constraint_object(outer).nested
9090
@test haskey(DP._indicator_to_constraints(model), z[1])
9191
@test inner in DP._indicator_to_constraints(model)[z[1]]
92-
@test haskey(gdp_data(model).exclusive_constraints, inner)
92+
@test haskey(gdp_data(model).exactly1_constraints, inner)
9393
end
9494

9595
function test_disjunction_add_array()
@@ -189,15 +189,15 @@ function test_disjunction_delete()
189189
@test delete(model, disj) isa Nothing
190190
@test !haskey(gdp_data(model).disjunctions, index(disj))
191191
@test !DP._ready_to_optimize(model)
192-
@test !haskey(gdp_data(model).exclusive_constraints, disj)
192+
@test !haskey(gdp_data(model).exactly1_constraints, disj)
193193

194194
model = GDPModel()
195195
@variable(model, x)
196196
@variable(model, y[1:2], Logical)
197197
@variable(model, z[1:2], Logical)
198198
@constraint(model, x <= 5, Disjunct(y[1]))
199199
@constraint(model, x >= 5, Disjunct(y[2]))
200-
@disjunction(model, inner, y, Disjunct(z[1]), exclusive = false)
200+
@disjunction(model, inner, y, Disjunct(z[1]), exactly1 = false)
201201

202202
@test delete(model, inner) isa Nothing
203203
@test !haskey(gdp_data(model).disjunctions, index(inner))
@@ -219,7 +219,7 @@ function test_disjunction_function()
219219
set_name(disj, "new_name")
220220
@test name(disj) == "new_name"
221221
@test haskey(DP._disjunctions(model), index(disj))
222-
@test haskey(gdp_data(model).exclusive_constraints, disj)
222+
@test haskey(gdp_data(model).exactly1_constraints, disj)
223223
end
224224

225225
function test_disjunction_function_nested()
@@ -233,7 +233,7 @@ function test_disjunction_function_nested()
233233
@constraint(model, x >= 10, Disjunct(z[2]))
234234
disj1 = DisjunctionRef(model, DisjunctionIndex(1))
235235
disj2 = DisjunctionRef(model, DisjunctionIndex(2))
236-
@test disjunction(model, y, Disjunct(z[1]), "inner", exclusive = false) == disj1
236+
@test disjunction(model, y, Disjunct(z[1]), "inner", exactly1 = false) == disj1
237237
@test disjunction(model, z, "outer") == disj2
238238

239239
@test is_valid(model, disj1)
@@ -244,7 +244,7 @@ function test_disjunction_function_nested()
244244
@test !constraint_object(disj2).nested
245245
@test haskey(DP._indicator_to_constraints(model), z[1])
246246
@test disj1 in DP._indicator_to_constraints(model)[z[1]]
247-
@test !haskey(gdp_data(model).exclusive_constraints, disj1)
247+
@test !haskey(gdp_data(model).exactly1_constraints, disj1)
248248
end
249249

250250
@testset "Disjunction" begin

0 commit comments

Comments
 (0)