@@ -104,17 +104,24 @@ for (RefType, loc) in ((:DisjunctConstraintRef, :disjunct_constraints),
104
104
end
105
105
end
106
106
107
- # Extend delete
108
107
"""
109
108
JuMP.delete(model::Model, cref::DisjunctionRef)
110
109
111
110
Delete a disjunction constraint from the `GDP model`.
112
111
"""
113
112
function JuMP. delete (model:: Model , cref:: DisjunctionRef )
114
- @assert is_valid (model, cref) " Disjunctive constraint does not belong to model."
115
- cidx = index (cref)
116
- dict = _disjunctions (model)
117
- delete! (dict, cidx)
113
+ @assert is_valid (model, cref) " Disjunction does not belong to model."
114
+ if JuMP. constraint_object (cref). nested
115
+ lvref = gdp_data (model). constraint_to_indicator[cref]
116
+ filter! (Base. Fix2 (!= , cref), _indicator_to_constraints (model)[lvref])
117
+ delete! (gdp_data (model). constraint_to_indicator, cref)
118
+ end
119
+ delete! (_disjunctions (model), index (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)
124
+ end
118
125
_set_ready_to_optimize (model, false )
119
126
return
120
127
end
@@ -126,9 +133,10 @@ Delete a disjunct constraint from the `GDP model`.
126
133
"""
127
134
function JuMP. delete (model:: Model , cref:: DisjunctConstraintRef )
128
135
@assert is_valid (model, cref) " Disjunctive constraint does not belong to model."
129
- cidx = index (cref)
130
- dict = _disjunct_constraints (model)
131
- delete! (dict, cidx)
136
+ delete! (_disjunct_constraints (model), index (cref))
137
+ lvref = gdp_data (model). constraint_to_indicator[cref]
138
+ filter! (Base. Fix2 (!= , cref), _indicator_to_constraints (model)[lvref])
139
+ delete! (gdp_data (model). constraint_to_indicator, cref)
132
140
_set_ready_to_optimize (model, false )
133
141
return
134
142
end
@@ -140,9 +148,7 @@ Delete a logical constraint from the `GDP model`.
140
148
"""
141
149
function JuMP. delete (model:: Model , cref:: LogicalConstraintRef )
142
150
@assert is_valid (model, cref) " Logical constraint does not belong to model."
143
- cidx = index (cref)
144
- dict = _logical_constraints (model)
145
- delete! (dict, cidx)
151
+ delete! (_logical_constraints (model), index (cref))
146
152
_set_ready_to_optimize (model, false )
147
153
return
148
154
end
@@ -263,6 +269,7 @@ function _add_indicator_var(
263
269
_indicator_to_constraints (model)[con. lvref] = Vector {Union{DisjunctConstraintRef, DisjunctionRef}} ()
264
270
end
265
271
push! (_indicator_to_constraints (model)[con. lvref], cref)
272
+ gdp_data (model). constraint_to_indicator[cref] = con. lvref
266
273
return
267
274
end
268
275
# check disjunction
@@ -308,17 +315,34 @@ function _disjunction(
308
315
_error:: Function ,
309
316
model:: Model , # TODO : generalize to AbstractModel
310
317
structure:: AbstractVector , # generalize for containers
311
- name:: String
318
+ name:: String ;
319
+ exactly1:: Bool = true ,
320
+ extra_kwargs...
312
321
)
313
- return _create_disjunction (_error, model, structure, name, false )
322
+ # check for unneeded keywords
323
+ for (kwarg, _) in extra_kwargs
324
+ _error (" Unrecognized keyword argument $kwarg ." )
325
+ end
326
+ # create the disjunction
327
+ dref = _create_disjunction (_error, model, structure, name, false )
328
+ # add the exactly one constraint if desired
329
+ if exactly1
330
+ lvars = JuMP. constraint_object (dref). indicators
331
+ func = Union{Number, LogicalVariableRef}[1 , lvars... ]
332
+ set = _MOIExactly (length (lvars) + 1 )
333
+ cref = JuMP. add_constraint (model, JuMP. VectorConstraint (func, set))
334
+ gdp_data (model). exactly1_constraints[dref] = cref
335
+ end
336
+ return dref
314
337
end
315
338
316
339
# Fallback disjunction build for nonvector structure
317
340
function _disjunction (
318
341
_error:: Function ,
319
342
model:: Model , # TODO : generalize to AbstractModel
320
343
structure,
321
- name:: String
344
+ name:: String ;
345
+ kwargs...
322
346
)
323
347
_error (" Unrecognized disjunction input structure." )
324
348
end
@@ -329,11 +353,26 @@ function _disjunction(
329
353
model:: Model , # TODO : generalize to AbstractModel
330
354
structure,
331
355
name:: String ,
332
- tag:: Disjunct
356
+ tag:: Disjunct ;
357
+ exactly1:: Bool = true ,
358
+ extra_kwargs...
333
359
)
360
+ # check for unneeded keywords
361
+ for (kwarg, _) in extra_kwargs
362
+ _error (" Unrecognized keyword argument $kwarg ." )
363
+ end
364
+ # create the disjunction
334
365
dref = _create_disjunction (_error, model, structure, name, true )
335
366
obj = constraint_object (dref)
336
367
_add_indicator_var (_DisjunctConstraint (obj, tag. indicator), dref, model)
368
+ # add the exactly one constraint if desired
369
+ if exactly1
370
+ lvars = JuMP. constraint_object (dref). indicators
371
+ func = LogicalVariableRef[tag. indicator, lvars... ]
372
+ set = _MOIExactly (length (lvars) + 1 )
373
+ cref = JuMP. add_constraint (model, JuMP. VectorConstraint (func, set))
374
+ gdp_data (model). exactly1_constraints[dref] = cref
375
+ end
337
376
return dref
338
377
end
339
378
@@ -343,45 +382,49 @@ function _disjunction(
343
382
model:: Model , # TODO : generalize to AbstractModel
344
383
structure,
345
384
name:: String ,
346
- extra...
385
+ extra... ;
386
+ kwargs...
347
387
)
348
388
for arg in extra
349
389
_error (" Unrecognized argument `$arg `." )
350
390
end
351
391
end
352
392
353
393
"""
354
- disjunction(
355
- model::Model,
356
- disjunct_indicators::Vector{LogicalVariableRef}
357
- name::String = ""
358
- )
359
-
360
- Function to add a [`Disjunction`](@ref) to a [`GDPModel`](@ref).
361
-
362
394
disjunction(
363
395
model::Model,
364
396
disjunct_indicators::Vector{LogicalVariableRef},
365
- nested_tag::Disjunct,
366
- name::String = ""
397
+ [nested_tag::Disjunct],
398
+ [name::String = ""];
399
+ [exactly1::Bool = true]
367
400
)
368
401
369
- Function to add a nested [`Disjunction`](@ref) to a [`GDPModel`](@ref).
402
+ Create a disjunction comprised of disjuncts with indicator variables `disjunct_indicators`
403
+ 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, `exactly1` adds
405
+ a constraint of the form `@constraint(model, disjunct_indicators in Exactly(1))` making
406
+ the disjuncts exclusive to one another; this is required for certain reformulations like
407
+ [`Hull`](@ref). To conveniently generate many disjunctions at once, see [`@disjunction`](@ref)
408
+ and [`@disjunctions`](@ref).
370
409
"""
371
410
function disjunction (
372
411
model:: Model ,
373
412
disjunct_indicators,
374
- name:: String = " "
375
- ) # TODO add kw argument to build exactly 1 constraint
376
- return _disjunction (error, model, disjunct_indicators, name)
413
+ name:: String = " " ,
414
+ extra... ;
415
+ kwargs...
416
+ )
417
+ return _disjunction (error, model, disjunct_indicators, name, extra... ; kwargs... )
377
418
end
378
419
function disjunction (
379
420
model:: Model ,
380
421
disjunct_indicators,
381
422
nested_tag:: Disjunct ,
382
- name:: String = " "
383
- ) # TODO add kw argument to build exactly 1 constraint
384
- return _disjunction (error, model, disjunct_indicators, name, nested_tag)
423
+ name:: String = " " ,
424
+ extra... ;
425
+ kwargs...
426
+ )
427
+ return _disjunction (error, model, disjunct_indicators, name, nested_tag, extra... ; kwargs... )
385
428
end
386
429
387
430
# ###############################################################################
0 commit comments