Skip to content

Commit ba67fd3

Browse files
committed
Support AbstractModels
1 parent b26e1ed commit ba67fd3

22 files changed

+1273
-646
lines changed

src/bigm.jl

Lines changed: 101 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,22 @@ function _get_tight_M(func::AbstractJuMPScalar, set::_MOI.AbstractSet, method::B
2121
end
2222

2323
# Get user-specified Big-M value
24-
function _get_M(::AbstractJuMPScalar, ::Union{_MOI.LessThan, _MOI.GreaterThan, _MOI.Nonnegatives, _MOI.Nonpositives}, method::BigM)
24+
function _get_M(
25+
::AbstractJuMPScalar,
26+
::Union{_MOI.LessThan, _MOI.GreaterThan, _MOI.Nonnegatives, _MOI.Nonpositives},
27+
method::BigM
28+
)
2529
M = method.value
2630
if isinf(M)
2731
error("A finite Big-M value must be used. The value given was $M.")
2832
end
2933
return M
3034
end
31-
function _get_M(::AbstractJuMPScalar, ::Union{_MOI.Interval, _MOI.EqualTo, _MOI.Zeros}, method::BigM)
35+
function _get_M(
36+
::AbstractJuMPScalar,
37+
::Union{_MOI.Interval, _MOI.EqualTo, _MOI.Zeros},
38+
method::BigM
39+
)
3240
M = method.value
3341
if isinf(M)
3442
error("A finite Big-M value must be used. The value given was $M.")
@@ -37,79 +45,102 @@ function _get_M(::AbstractJuMPScalar, ::Union{_MOI.Interval, _MOI.EqualTo, _MOI.
3745
end
3846

3947
# Apply interval arithmetic on a linear constraint to infer the tightest Big-M value from the bounds on the constraint.
40-
function _calculate_tight_M(func::AffExpr, set::_MOI.LessThan, method::BigM)
48+
function _calculate_tight_M(
49+
func::JuMP.GenericAffExpr,
50+
set::_MOI.LessThan,
51+
method::BigM
52+
)
4153
return _interval_arithmetic_LessThan(func, -set.upper, method)
4254
end
43-
function _calculate_tight_M(func::AffExpr, set::_MOI.GreaterThan, method::BigM)
55+
function _calculate_tight_M(
56+
func::JuMP.GenericAffExpr,
57+
set::_MOI.GreaterThan,
58+
method::BigM
59+
)
4460
return _interval_arithmetic_GreaterThan(func, -set.lower, method)
4561
end
46-
function _calculate_tight_M(func::AffExpr, ::_MOI.Nonpositives, method::BigM)
47-
return _interval_arithmetic_LessThan(func, 0.0, method)
62+
function _calculate_tight_M(
63+
func::JuMP.GenericAffExpr{C, V},
64+
::_MOI.Nonpositives,
65+
method::BigM
66+
) where {C, V}
67+
return _interval_arithmetic_LessThan(func, zero(C), method)
4868
end
49-
function _calculate_tight_M(func::AffExpr, ::_MOI.Nonnegatives, method::BigM)
50-
return _interval_arithmetic_GreaterThan(func, 0.0, method)
69+
function _calculate_tight_M(
70+
func::JuMP.GenericAffExpr{C, V},
71+
::_MOI.Nonnegatives,
72+
method::BigM
73+
) where {C, V}
74+
return _interval_arithmetic_GreaterThan(func, zero(C), method)
5175
end
52-
function _calculate_tight_M(func::AffExpr, set::_MOI.Interval, method::BigM)
76+
function _calculate_tight_M(
77+
func::JuMP.GenericAffExpr,
78+
set::_MOI.Interval,
79+
method::BigM
80+
)
5381
return (
5482
_interval_arithmetic_GreaterThan(func, -set.lower, method),
5583
_interval_arithmetic_LessThan(func, -set.upper, method)
5684
)
5785
end
58-
function _calculate_tight_M(func::AffExpr, set::_MOI.EqualTo, method::BigM)
86+
function _calculate_tight_M(
87+
func::JuMP.GenericAffExpr,
88+
set::_MOI.EqualTo,
89+
method::BigM
90+
)
5991
return (
6092
_interval_arithmetic_GreaterThan(func, -set.value, method),
6193
_interval_arithmetic_LessThan(func, -set.value, method)
6294
)
6395
end
64-
function _calculate_tight_M(func::AffExpr, ::_MOI.Zeros, method::BigM)
96+
function _calculate_tight_M(
97+
func::JuMP.GenericAffExpr{C, V},
98+
::_MOI.Zeros,
99+
method::BigM
100+
) where {C, V}
65101
return (
66-
_interval_arithmetic_GreaterThan(func, 0.0, method),
67-
_interval_arithmetic_LessThan(func, 0.0, method)
102+
_interval_arithmetic_GreaterThan(func, zero(C), method),
103+
_interval_arithmetic_LessThan(func, zero(C), method)
68104
)
69105
end
70106
# fallbacks for other scalar constraints
71-
_calculate_tight_M(func::Union{QuadExpr, NonlinearExpr}, set::Union{_MOI.Interval, _MOI.EqualTo, _MOI.Zeros}, method::BigM) = (Inf, Inf)
72-
_calculate_tight_M(func::Union{QuadExpr, NonlinearExpr}, set::Union{_MOI.LessThan, _MOI.GreaterThan, _MOI.Nonnegatives, _MOI.Nonpositives}, method::BigM) = Inf
73-
_calculate_tight_M(func, set, method::BigM) = error("BigM method not implemented for constraint type $(typeof(func)) in $(typeof(set))")
74-
75-
# get variable bounds for interval arithmetic
76-
function _update_variable_bounds(vref::VariableRef, method::BigM)
77-
if is_binary(vref)
78-
lb = 0
79-
elseif !has_lower_bound(vref)
80-
lb = -Inf
81-
else
82-
lb = lower_bound(vref)
83-
end
84-
if is_binary(vref)
85-
ub = 1
86-
elseif !has_upper_bound(vref)
87-
ub = Inf
88-
else
89-
ub = upper_bound(vref)
90-
end
91-
return lb, ub
107+
function _calculate_tight_M(
108+
func::Union{JuMP.GenericQuadExpr, JuMP.GenericNonlinearExpr},
109+
set::Union{_MOI.Interval, _MOI.EqualTo, _MOI.Zeros},
110+
method::BigM
111+
)
112+
return (Inf, Inf)
113+
end
114+
function _calculate_tight_M(
115+
func::Union{JuMP.GenericQuadExpr, JuMP.GenericNonlinearExpr},
116+
set::Union{_MOI.LessThan, _MOI.GreaterThan, _MOI.Nonnegatives, _MOI.Nonpositives},
117+
method::BigM
118+
)
119+
return Inf
120+
end
121+
function _calculate_tight_M(::F, ::S, ::BigM) where {F, S}
122+
error("BigM method not implemented for constraint type `$(F)` in `$(S)`.")
92123
end
93124

94125
# perform interval arithmetic to update the initial M value
95-
function _interval_arithmetic_LessThan(func::AffExpr, M::Float64, method::BigM)
126+
function _interval_arithmetic_LessThan(func::JuMP.GenericAffExpr, M, ::BigM)
96127
for (var,coeff) in func.terms
97-
is_binary(var) && continue #skip binary variables
128+
JuMP.is_binary(var) && continue
98129
if coeff > 0
99-
M += coeff*method.variable_bounds[var][2]
130+
M += coeff*variable_bound_info(var)[2]
100131
else
101-
M += coeff*method.variable_bounds[var][1]
132+
M += coeff*variable_bound_info(var)[1]
102133
end
103134
end
104135
return M + func.constant
105136
end
106-
function _interval_arithmetic_GreaterThan(func::AffExpr, M::Float64, method::BigM)
137+
function _interval_arithmetic_GreaterThan(func::JuMP.GenericAffExpr, M, ::BigM)
107138
for (var,coeff) in func.terms
108-
is_binary(var) && continue #skip binary variables
139+
JuMP.is_binary(var) && continue
109140
if coeff < 0
110-
M += coeff*method.variable_bounds[var][2]
141+
M += coeff*variable_bound_info(var)[2]
111142
else
112-
M += coeff*method.variable_bounds[var][1]
143+
M += coeff*variable_bound_info(var)[1]
113144
end
114145
end
115146
return -(M + func.constant)
@@ -118,15 +149,27 @@ end
118149
################################################################################
119150
# BIG-M REFORMULATION
120151
################################################################################
121-
function _reformulate_disjunctions(model::Model, method::BigM)
122-
method.tighten && _query_variable_bounds(model, method)
123-
_reformulate_all_disjunctions(model, method)
152+
requires_variable_bound_info(method::BigM) = method.tighten
153+
154+
# get variable bounds for interval arithmetic (note these cannot be binary)
155+
function set_variable_bound_info(vref::JuMP.AbstractVariableRef, ::BigM)
156+
if !has_lower_bound(vref)
157+
lb = -Inf
158+
else
159+
lb = lower_bound(vref)
160+
end
161+
if !has_upper_bound(vref)
162+
ub = Inf
163+
else
164+
ub = upper_bound(vref)
165+
end
166+
return lb, ub
124167
end
125168

126169
function reformulate_disjunct_constraint(
127-
model::Model,
170+
model::JuMP.AbstractModel,
128171
con::ScalarConstraint{T, S},
129-
bvref::VariableRef,
172+
bvref::JuMP.AbstractVariableRef,
130173
method::BigM
131174
) where {T, S <: _MOI.LessThan}
132175
M = _get_M_value(con.func, con.set, method)
@@ -135,9 +178,9 @@ function reformulate_disjunct_constraint(
135178
return [reform_con]
136179
end
137180
function reformulate_disjunct_constraint(
138-
model::Model,
181+
model::JuMP.AbstractModel,
139182
con::VectorConstraint{T, S, R},
140-
bvref::VariableRef,
183+
bvref::JuMP.AbstractVariableRef,
141184
method::BigM
142185
) where {T, S <: _MOI.Nonpositives, R}
143186
M = [_get_M_value(func, con.set, method) for func in con.func]
@@ -148,9 +191,9 @@ function reformulate_disjunct_constraint(
148191
return [reform_con]
149192
end
150193
function reformulate_disjunct_constraint(
151-
model::Model,
194+
model::JuMP.AbstractModel,
152195
con::ScalarConstraint{T, S},
153-
bvref::VariableRef,
196+
bvref::JuMP.AbstractVariableRef,
154197
method::BigM
155198
) where {T, S <: _MOI.GreaterThan}
156199
M = _get_M_value(con.func, con.set, method)
@@ -159,9 +202,9 @@ function reformulate_disjunct_constraint(
159202
return [reform_con]
160203
end
161204
function reformulate_disjunct_constraint(
162-
model::Model,
205+
model::JuMP.AbstractModel,
163206
con::VectorConstraint{T, S, R},
164-
bvref::VariableRef,
207+
bvref::JuMP.AbstractVariableRef,
165208
method::BigM
166209
) where {T, S <: _MOI.Nonnegatives, R}
167210
M = [_get_M_value(func, con.set, method) for func in con.func]
@@ -172,9 +215,9 @@ function reformulate_disjunct_constraint(
172215
return [reform_con]
173216
end
174217
function reformulate_disjunct_constraint(
175-
model::Model,
218+
model::JuMP.AbstractModel,
176219
con::ScalarConstraint{T, S},
177-
bvref::VariableRef,
220+
bvref::JuMP.AbstractVariableRef,
178221
method::BigM
179222
) where {T, S <: Union{_MOI.Interval, _MOI.EqualTo}}
180223
M = _get_M_value(con.func, con.set, method)
@@ -186,9 +229,9 @@ function reformulate_disjunct_constraint(
186229
return [reform_con_gt, reform_con_lt]
187230
end
188231
function reformulate_disjunct_constraint(
189-
model::Model,
232+
model::JuMP.AbstractModel,
190233
con::VectorConstraint{T, S, R},
191-
bvref::VariableRef,
234+
bvref::JuMP.AbstractVariableRef,
192235
method::BigM
193236
) where {T, S <: _MOI.Zeros, R}
194237
M = [_get_M_value(func, con.set, method) for func in con.func]
@@ -201,4 +244,4 @@ function reformulate_disjunct_constraint(
201244
reform_con_nn = build_constraint(error, new_func_nn, _MOI.Nonnegatives(con.set.dimension))
202245
reform_con_np = build_constraint(error, new_func_np, _MOI.Nonpositives(con.set.dimension))
203246
return [reform_con_nn, reform_con_np]
204-
end
247+
end

0 commit comments

Comments
 (0)