Skip to content

Commit 896293e

Browse files
committed
fix bug: don't disaggregate boolean variables and disaggregated vars (from nested disjunctions)
1 parent 29faaf2 commit 896293e

File tree

3 files changed

+32
-9
lines changed

3 files changed

+32
-9
lines changed

src/hull.jl

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,20 @@ Call the hull reformulation on a constraint at index k of constraint j in disjun
1313
"""
1414
function hull_reformulation!(constr::ConstraintRef{<:AbstractModel, MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},V}}, bin_var, args...) where {T,V}
1515
#check constraint type
16+
m = constr.model
1617
i = args[2] #get disjunct index
17-
bin_var_ref = constr.model[bin_var][i]
18+
bin_var_ref = m[bin_var][i]
1819
#replace each variable with its disaggregated version
19-
for var_ref in get_constraint_variables(constr)
20+
var_refs = setdiff(
21+
get_constraint_variables(constr),
22+
m.ext[:disaggregated_variables],
23+
m.ext[:boolean_variables]
24+
)
25+
for var_ref in var_refs
2026
is_binary(var_ref) && continue #NOTE: binaries from nested disjunctions are not disaggregated and don't need to be swapped out
2127
#get disaggregated variable reference
2228
var_name_i = name_disaggregated_variable(var_ref, bin_var, i)
23-
var_i_ref = variable_by_name(constr.model, var_name_i)
29+
var_i_ref = variable_by_name(m, var_name_i)
2430
#check var_ref is present in the constraint
2531
coeff = normalized_coefficient(constr, var_ref)
2632
iszero(coeff) && continue #if not present, skip
@@ -76,7 +82,11 @@ Disaggregate all variables in the model and tag them with the disjunction name.
7682
"""
7783
function disaggregate_variables(m::Model, disj, bin_var)
7884
#check that variables are bounded
79-
var_refs = get_constraint_variables(disj)
85+
var_refs = setdiff(
86+
get_constraint_variables(disj),
87+
m.ext[:disaggregated_variables],
88+
m.ext[:boolean_variables]
89+
)
8090
@assert all((has_upper_bound.(var_refs) .&& has_lower_bound.(var_refs)) .|| is_binary.(var_refs)) "All variables must be bounded to perform the Hull reformulation."
8191
#reformulate variables
8292
obj_dict = object_dictionary(m)
@@ -91,17 +101,21 @@ function disaggregate_variables(m::Model, disj, bin_var)
91101
var_name_i_str = name_disaggregated_variable(var,bin_var,i)
92102
var_name_i = Symbol(var_name_i_str)
93103
#create disaggregated variable
94-
m[var_name_i] = add_disaggregated_variable(m, var, LB, UB, var_name_i_str)
104+
var_i = add_disaggregated_variable(m, var, LB, UB, var_name_i_str)
105+
push!(
106+
m.ext[:disaggregated_variables],
107+
var_i
108+
)
95109
#apply bounding constraints on disaggregated variable
96110
var_i_lb = "$(var_name_i)_lb"
97111
var_i_ub = "$(var_name_i)_ub"
98112
push!(
99113
m.ext[bin_var],
100-
@constraint(m, LB * m[bin_var][i] .- m[var_name_i] .<= 0, base_name = var_i_lb),
101-
@constraint(m, m[var_name_i] .- UB * m[bin_var][i] .<= 0, base_name = var_i_ub)
114+
@constraint(m, LB * m[bin_var][i] .- var_i .<= 0, base_name = var_i_lb),
115+
@constraint(m, var_i .- UB * m[bin_var][i] .<= 0, base_name = var_i_ub)
102116
)
103117
#update disaggregated sum expression
104-
add_to_expression!(sum_vars, 1, m[var_name_i])
118+
add_to_expression!(sum_vars, 1, var_i)
105119
end
106120
#sum disaggregated variables
107121
aggr_con = "$(var)_$(bin_var)_aggregation"

src/macros.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ function add_disjunction!(m::Model,disj...;reformulation::Symbol,M=missing,ϵ=1e
9696
m[bin_var] = @variable(m, [eachindex(disj)], Bin, base_name = string(bin_var))
9797
end
9898

99+
#record boolean variable
100+
if !in(:boolean_variables, keys(m.ext))
101+
m.ext[:boolean_variables] = [] #store boolean variables to avoid disaggregating (nested disjunctions)
102+
end
103+
push!(m.ext[:boolean_variables], m[bin_var])
104+
99105
#reformulate disjunction
100106
param = reformulation == :big_m ? M : ϵ
101107
reformulate_disjunction(m, disj...; bin_var, reformulation, param)

src/reformulate.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@ Reformulate disjunction.
1010
function reformulate_disjunction(m::Model, disj...; bin_var, reformulation, param)
1111
#placeholder to store new constraints (reformulated)
1212
@assert !in(bin_var, keys(m.ext)) "$bin_var cannot be used as the indicator variable for the disjunction because it has already been used on another disjunction."
13-
m.ext[bin_var] = []
13+
m.ext[bin_var] = [] #store constraints associated with indicator variable
1414
#check disj
1515
disj = [check_constraint!(m, constr) for constr in disj]#check_disjunction!(m, disj)
1616
#run reformulation
1717
if reformulation == :hull
18+
if !in(:disaggregated_variables, keys(m.ext))
19+
m.ext[:disaggregated_variables] = [] #record disaggregated variables to avoid duplicating disaggregation (nested disjunctions)
20+
end
1821
disaggregate_variables(m, disj, bin_var)
1922
end
2023
reformulate_disjunction(m, disj, bin_var, reformulation, param)

0 commit comments

Comments
 (0)