Skip to content

Commit 0768056

Browse files
committed
add binary_variable and fix tests
1 parent 14e960a commit 0768056

File tree

12 files changed

+116
-182
lines changed

12 files changed

+116
-182
lines changed

src/datatypes.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,17 @@ struct Logical{T}
5252
end
5353

5454
"""
55-
LogicalVariableData{V}
55+
LogicalVariableData
5656
5757
A type for storing [`LogicalVariable`](@ref)s and any meta-data they
5858
possess.
5959
6060
**Fields**
61-
- `variable::V`: The logical variable object.
61+
- `variable::LogicalVariable`: The logical variable object.
6262
- `name::String`: The name of the variable.
6363
"""
64-
mutable struct LogicalVariableData{V}
65-
variable::V
64+
mutable struct LogicalVariableData
65+
variable::LogicalVariable
6666
name::String
6767
end
6868

src/hull.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ function _disaggregate_variable(
6868
dvref = make_disaggregated_variable(model, vref, "$(vref)_$(lvref)", lb, ub)
6969
push!(_reformulation_variables(model), dvref)
7070
#get binary indicator variable
71-
bvref = _indicator_to_binary(model)[lvref]
71+
bvref = binary_variable(lvref)
7272
#temp storage
7373
push!(method.disjunction_variables[vref], dvref)
7474
method.disjunct_variables[vref, bvref] = dvref

src/logic.jl

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,7 @@ function _reformulate_selector(
223223
func::Vector{AbstractJuMPScalar},
224224
set::AbstractCardinalitySet
225225
)
226-
dict = _indicator_to_binary(model)
227-
bvrefs = [dict[lvref] for lvref in func[2:end]]
226+
bvrefs = [binary_variable(lvref) for lvref in func[2:end]]
228227
c = JuMP.constant(func[1])
229228
new_set = _vec_to_scalar_set(set)(c)
230229
cref = @constraint(model, sum(bvrefs) in new_set)
@@ -235,8 +234,7 @@ function _reformulate_selector(
235234
func::Vector{<:LogicalVariableRef},
236235
set::AbstractCardinalitySet
237236
)
238-
dict = _indicator_to_binary(model)
239-
bvref, bvrefs... = [dict[lvref] for lvref in func]
237+
bvref, bvrefs... = [binary_variable(lvref) for lvref in func]
240238
new_set = _vec_to_scalar_set(set)(0)
241239
cref = @constraint(model, sum(bvrefs) - bvref in new_set)
242240
push!(_reformulation_constraints(model), cref)
@@ -276,7 +274,7 @@ function _add_reformulated_proposition(
276274
end
277275

278276
function _reformulate_clause(model::JuMP.AbstractModel, lvref::LogicalVariableRef)
279-
func = 1 * _indicator_to_binary(model)[lvref]
277+
func = 1 * binary_variable(lvref)
280278
return func
281279
end
282280

src/reformulate.jl

Lines changed: 1 addition & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ function reformulate_model(model::JuMP.AbstractModel, method::AbstractSolutionMe
1111
#clear all previous reformulations
1212
_clear_reformulations(model)
1313
#reformulate
14-
_reformulate_logical_variables(model)
1514
_reformulate_disjunctions(model, method)
1615
_reformulate_logical_constraints(model)
1716
#set solution method
@@ -29,48 +28,6 @@ function _clear_reformulations(model::JuMP.AbstractModel)
2928
return
3029
end
3130

32-
################################################################################
33-
# LOGICAL VARIABLES
34-
################################################################################
35-
# Helper function to add start value and fix value
36-
function _add_logical_info(bvref, var::LogicalVariable)
37-
if !isnothing(var.fix_value)
38-
JuMP.fix(bvref, var.fix_value)
39-
end
40-
if !isnothing(var.start_value)
41-
JuMP.set_start_value(bvref, var.start_value)
42-
end
43-
return
44-
end
45-
function _add_logical_info(bvref, var::_TaggedLogicalVariable)
46-
return _add_logical_info(bvref, var.variable)
47-
end
48-
49-
# Dispatch on logical variable type to create a binary variable
50-
function _make_binary_variable(model, ::LogicalVariable, name)
51-
return JuMP.@variable(model, base_name = name, binary = true)
52-
end
53-
function _make_binary_variable(model, var::_TaggedLogicalVariable, name)
54-
return JuMP.@variable(
55-
model,
56-
base_name = name,
57-
binary = true,
58-
variable_type = var.tag_data
59-
)
60-
end
61-
62-
# create binary (indicator) variables for logic variables.
63-
function _reformulate_logical_variables(model::JuMP.AbstractModel)
64-
for (lv_idx, lv_data) in _logical_variables(model)
65-
var = lv_data.variable
66-
lvref = LogicalVariableRef(model, lv_idx)
67-
bvref = _make_binary_variable(model, var, lv_data.name)
68-
_add_logical_info(bvref, var)
69-
push!(_reformulation_variables(model), bvref)
70-
_indicator_to_binary(model)[lvref] = bvref
71-
end
72-
end
73-
7431
################################################################################
7532
# DISJUNCTIONS
7633
################################################################################
@@ -167,7 +124,7 @@ function _reformulate_disjunct(
167124
method::AbstractReformulationMethod
168125
)
169126
#reformulate each constraint and add to the model
170-
bvref = _indicator_to_binary(model)[lvref]
127+
bvref = binary_variable(lvref)
171128
!haskey(_indicator_to_constraints(model), lvref) && return #skip if disjunct is empty
172129
for cref in _indicator_to_constraints(model)[lvref]
173130
con = constraint_object(cref)

src/variables.jl

Lines changed: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,37 @@ function JuMP.build_variable(
4646
return _TaggedLogicalVariable(lvar, tag.tag_data)
4747
end
4848

49+
# Helper functions to extract the core variable object
50+
_get_variable(v::_TaggedLogicalVariable) = v.variable
51+
_get_variable(v) = v
52+
53+
# Helper function to add start value and fix value
54+
function _add_logical_info(bvref, var::LogicalVariable)
55+
if !isnothing(var.fix_value)
56+
JuMP.fix(bvref, var.fix_value)
57+
end
58+
if !isnothing(var.start_value)
59+
JuMP.set_start_value(bvref, var.start_value)
60+
end
61+
return
62+
end
63+
function _add_logical_info(bvref, var::_TaggedLogicalVariable)
64+
return _add_logical_info(bvref, var.variable)
65+
end
66+
67+
# Dispatch on logical variable type to create a binary variable
68+
function _make_binary_variable(model, ::LogicalVariable, name)
69+
return JuMP.@variable(model, base_name = name, binary = true)
70+
end
71+
function _make_binary_variable(model, var::_TaggedLogicalVariable, name)
72+
return JuMP.@variable(
73+
model,
74+
base_name = name,
75+
binary = true,
76+
variable_type = var.tag_data
77+
)
78+
end
79+
4980
"""
5081
JuMP.add_variable(model::Model, v::LogicalVariable,
5182
name::String = "")::LogicalVariableRef
@@ -59,10 +90,16 @@ function JuMP.add_variable(
5990
name::String = ""
6091
)
6192
is_gdp_model(model) || error("Can only add logical variables to `GDPModel`s.")
62-
data = LogicalVariableData(v, name)
93+
# add the logical variable
94+
data = LogicalVariableData(_get_variable(v), name)
6395
idx = _MOIUC.add_item(_logical_variables(model), data)
96+
lvref = LogicalVariableRef(model, idx)
6497
_set_ready_to_optimize(model, false)
65-
return LogicalVariableRef(model, idx)
98+
# add the assocviated binary variables
99+
bvref = _make_binary_variable(model, v, name)
100+
_add_logical_info(bvref, v)
101+
_indicator_to_binary(model)[lvref] = bvref
102+
return lvref
66103
end
67104

68105
# Base extensions
@@ -77,31 +114,15 @@ end
77114
# end
78115

79116
# Define helpful getting functions
80-
function _variable_object(data::LogicalVariableData{<:_TaggedLogicalVariable})
81-
return data.variable.variable
82-
end
83-
function _variable_object(data::LogicalVariableData)
84-
return data.variable
85-
end
86117
function _variable_object(lvref::LogicalVariableRef)
87118
dict = _logical_variables(JuMP.owner_model(lvref))
88-
return _variable_object(dict[JuMP.index(lvref)])
119+
return dict[JuMP.index(lvref)].variable
89120
end
90121

91122
# Define helpful setting functions
92-
function _set_variable_object(data::LogicalVariableData{<:_TaggedLogicalVariable}, var)
93-
tag = data.variable.tag_data
94-
data.variable = _TaggedLogicalVariable(var, tag)
95-
return
96-
end
97-
function _set_variable_object(data::LogicalVariableData, var)
98-
data.variable = var
99-
return
100-
end
101123
function _set_variable_object(lvref::LogicalVariableRef, var::LogicalVariable)
102124
model = JuMP.owner_model(lvref)
103-
dict = _logical_variables(model)
104-
_set_variable_object(dict[JuMP.index(lvref)], var)
125+
_logical_variables(model)[JuMP.index(lvref)].variable = var
105126
_set_ready_to_optimize(model, false)
106127
return
107128
end
@@ -158,6 +179,7 @@ function JuMP.set_name(vref::LogicalVariableRef, name::String)
158179
data = gdp_data(model)
159180
data.logical_variables[index(vref)].name = name
160181
_set_ready_to_optimize(model, false)
182+
JuMP.set_name(binary_variable(vref), name)
161183
return
162184
end
163185

@@ -183,6 +205,7 @@ function JuMP.set_start_value(
183205
)
184206
new_var = LogicalVariable(JuMP.fix_value(vref), value)
185207
_set_variable_object(vref, new_var)
208+
JuMP.set_start_value(binary_variable(vref), value)
186209
return
187210
end
188211
"""
@@ -215,6 +238,7 @@ new one.
215238
function JuMP.fix(vref::LogicalVariableRef, value::Bool)
216239
new_var = LogicalVariable(value, JuMP.start_value(vref))
217240
_set_variable_object(vref, new_var)
241+
JuMP.fix(binary_variable(vref), value)
218242
return
219243
end
220244

@@ -226,9 +250,22 @@ Delete the fixed value of a logical variable.
226250
function JuMP.unfix(vref::LogicalVariableRef)
227251
new_var = LogicalVariable(nothing, JuMP.start_value(vref))
228252
_set_variable_object(vref, new_var)
253+
JuMP.unfix(binary_variable(vref))
229254
return
230255
end
231256

257+
"""
258+
binary_variable(vref::LogicalVariableRef)::JuMP.AbstractVariableRef
259+
260+
Returns the underlying binary variable for the logical variable `vref` which
261+
is used in the reformulated model. This is helpful to embed logical variables
262+
in algebraic constraints.
263+
"""
264+
function binary_variable(vref::LogicalVariableRef)
265+
model = JuMP.owner_model(vref)
266+
return _indicator_to_binary(model)[vref]
267+
end
268+
232269
"""
233270
JuMP.delete(model::JuMP.AbstractModel, vref::LogicalVariableRef)::Nothing
234271
@@ -241,24 +278,25 @@ function JuMP.delete(model::JuMP.AbstractModel, vref::LogicalVariableRef)
241278
#delete any disjunct constraints associated with the logical variables in the disjunction
242279
if haskey(_indicator_to_constraints(model), vref)
243280
crefs = _indicator_to_constraints(model)[vref]
244-
delete.(model, crefs)
281+
JuMP.delete.(model, crefs)
245282
delete!(_indicator_to_constraints(model), vref)
246283
end
247284
#delete any disjunctions that have the logical variable
248285
for (didx, ddata) in _disjunctions(model)
249286
if vref in ddata.constraint.indicators
250-
delete(model, DisjunctionRef(model, didx))
287+
JuMP.delete(model, DisjunctionRef(model, didx))
251288
end
252289
end
253290
#delete any logical constraints involving the logical variables
254291
for (cidx, cdata) in _logical_constraints(model)
255292
lvars = _get_logical_constraint_variables(model, cdata.constraint)
256293
if vref in lvars
257-
delete(model, LogicalConstraintRef(model, cidx))
294+
JuMP.delete(model, LogicalConstraintRef(model, cidx))
258295
end
259296
end
260297
#delete the logical variable
261298
delete!(dict, vidx)
299+
JuMP.delete(model, binary_variable(vref))
262300
delete!(_indicator_to_binary(model), vref)
263301
#not ready to optimize
264302
_set_ready_to_optimize(model, false)

test/constraints/bigm.jl

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,7 @@ function test_lessthan_bigm()
166166
@variable(model, y, Logical)
167167
@constraint(model, con, x <= 5, Disjunct(y))
168168

169-
DP._reformulate_logical_variables(model)
170-
bvref = DP._indicator_to_binary(model)[y]
169+
bvref = binary_variable(y)
171170
ref = reformulate_disjunct_constraint(model, constraint_object(con), bvref, BigM(100, false))
172171
@test length(ref) == 1
173172
@test ref[1].func == x - 100*(-bvref)
@@ -180,8 +179,7 @@ function test_nonpositives_bigm()
180179
@variable(model, y, Logical)
181180
@constraint(model, con, [x; x] <= [5; 5], Disjunct(y))
182181

183-
DP._reformulate_logical_variables(model)
184-
bvref = DP._indicator_to_binary(model)[y]
182+
bvref = binary_variable(y)
185183
ref = reformulate_disjunct_constraint(model, constraint_object(con), bvref, BigM(100, false))
186184
@test length(ref) == 1
187185
@test ref[1].func[1] == x - 5 - 100*(1-bvref)
@@ -195,8 +193,7 @@ function test_greaterthan_bigm()
195193
@variable(model, y, Logical)
196194
@constraint(model, con, x >= 5, Disjunct(y))
197195

198-
DP._reformulate_logical_variables(model)
199-
bvref = DP._indicator_to_binary(model)[y]
196+
bvref = binary_variable(y)
200197
ref = reformulate_disjunct_constraint(model, constraint_object(con), bvref, BigM(100, false))
201198
@test length(ref) == 1
202199
@test ref[1].func == x + 100*(-bvref)
@@ -209,8 +206,7 @@ function test_nonnegatives_bigm()
209206
@variable(model, y, Logical)
210207
@constraint(model, con, [x; x] >= [5; 5], Disjunct(y))
211208

212-
DP._reformulate_logical_variables(model)
213-
bvref = DP._indicator_to_binary(model)[y]
209+
bvref = binary_variable(y)
214210
ref = reformulate_disjunct_constraint(model, constraint_object(con), bvref, BigM(100, false))
215211
@test length(ref) == 1
216212
@test ref[1].func[1] == x - 5 + 100*(1-bvref)
@@ -224,8 +220,7 @@ function test_equalto_bigm()
224220
@variable(model, y, Logical)
225221
@constraint(model, con, x == 5, Disjunct(y))
226222

227-
DP._reformulate_logical_variables(model)
228-
bvref = DP._indicator_to_binary(model)[y]
223+
bvref = binary_variable(y)
229224
ref = reformulate_disjunct_constraint(model, constraint_object(con), bvref, BigM(100, false))
230225
@test length(ref) == 2
231226
@test ref[1].func == x + 100*(-bvref)
@@ -240,8 +235,7 @@ function test_interval_bigm()
240235
@variable(model, y, Logical)
241236
@constraint(model, con, 5 <= x <= 5, Disjunct(y))
242237

243-
DP._reformulate_logical_variables(model)
244-
bvref = DP._indicator_to_binary(model)[y]
238+
bvref = binary_variable(y)
245239
ref = reformulate_disjunct_constraint(model, constraint_object(con), bvref, BigM(100, false))
246240
@test length(ref) == 2
247241
@test ref[1].func == x + 100*(-bvref)
@@ -256,8 +250,7 @@ function test_zeros_bigm()
256250
@variable(model, y, Logical)
257251
@constraint(model, con, [x; x] == [5; 5], Disjunct(y))
258252

259-
DP._reformulate_logical_variables(model)
260-
bvref = DP._indicator_to_binary(model)[y]
253+
bvref = binary_variable(y)
261254
ref = reformulate_disjunct_constraint(model, constraint_object(con), bvref, BigM(100, false))
262255
@test length(ref) == 2
263256
@test ref[1].func[1] == x - 5 + 100*(1-bvref)

0 commit comments

Comments
 (0)