Skip to content

Commit 6e97577

Browse files
committed
use interval arithmetic to infer BigM
1 parent 6c998a1 commit 6e97577

File tree

4 files changed

+80
-36
lines changed

4 files changed

+80
-36
lines changed

Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ authors = ["hdavid16 <[email protected]>"]
44
version = "0.1.0"
55

66
[deps]
7+
IntervalArithmetic = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253"
78
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
89

910
[compat]

examples/ex4.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ m = Model()
1313

1414
M = (10,(10,10,10))
1515
@disjunction(m,(con1,con2,con3),(con4,con5,con6),
16-
reformulation=:CHR,
16+
reformulation=:BMR,
1717
M=M)

src/JuGDP.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module JuGDP
22

3-
using JuMP
3+
using JuMP, IntervalArithmetic
44

55
export add_disjunction
66
export @disjunction

src/reformulate.jl

Lines changed: 77 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -52,51 +52,53 @@ function BMR(m, constr, M, bin_var, i, k = missing)
5252
ref = constr[k...]
5353
end
5454
if ismissing(M)
55-
M = infer_BigM(ref, k)
55+
M = apply_interval_arithmetic(ref)
5656
@warn "No M value passed for $ref. M = $M was inferred from the variable bounds."
5757
end
5858
add_to_function_constant(ref, -M)
5959
bin_var_ref = variable_by_name(ref.model, string(bin_var[i]))
6060
set_normalized_coefficient(ref, bin_var_ref , M)
6161
end
6262

63-
function infer_BigM(ref, k = missing)
64-
constr_set = constraint_object(ref).set
65-
set_fields = fieldnames(typeof(constr_set))
66-
@assert length(set_fields) == 1 "A reformulation cannot be done on constraint $ref because it is not one of the following GreaterThan, LessThan, or EqualTo."
67-
@assert :value in set_fields || :lower in set_fields || :upper in set_fields "$ref must be one the following: GreaterThan, LessThan, or EqualTo."
63+
function apply_interval_arithmetic(ref)
64+
if ref isa ConstraintRef
65+
ref_obj = constraint_object(ref)
66+
@assert ref_obj.set isa MOI.LessThan || ref_obj.set isa MOI.GreaterThan || ref_obj.set isa MOI.EqualTo "$ref must be one the following: GreaterThan, LessThan, or EqualTo."
67+
ref_func = string(ref_obj.func)
68+
ref_type = fieldnames(typeof(ref_obj.set))[1]
69+
ref_rhs = normalized_rhs(ref)
70+
elseif ref isa NonlinearConstraintRef
71+
ref_str = string(ref)
72+
@assert length(findall(r"[<>]", ref_str)) <= 1 "$ref must be one of the following: GreaterThan, LessThan, or EqualTo."
73+
ref_func = replace(split(ref_str, r"[=<>]")[1], " " => "")
74+
ref_type = occursin(">", ref_str) ? :lower : :upper
75+
ref_rhs = split(ref_str, " ")[end]
76+
end
6877
vars = all_variables(ref.model) #get all variable names
69-
M = 0 #initialize M
7078
for var in vars
71-
coeff = normalized_coefficient(ref,var)
72-
iszero(coeff) && continue
73-
has_bounds = (has_lower_bound(var), has_upper_bound(var))
74-
if coeff > 0
75-
if :lower in set_fields && has_bounds[1]
76-
bound = lower_bound(var)
77-
elseif :upper in set_fields || :value in set_fields && has_bounds[2]
78-
bound = upper_bound(var)
79-
else
80-
error("M parameter cannot be infered due to lack of variable bounds for variable $var.")
81-
end
82-
M += coeff*bound
83-
elseif coeff < 0
84-
if :lower in set_fields && has_bounds[2]
85-
bound = upper_bound(var)
86-
elseif :upper in set_fields || :value in set_fields && has_bounds[1]
87-
bound = lower_bound(var)
88-
else
89-
error("M parameter cannot be infered due to lack of variable bounds for variable $var.")
90-
end
91-
M += coeff*bound
79+
if has_upper_bound(var)
80+
ub = upper_bound(var)
81+
else
82+
ub = Inf
83+
end
84+
if has_lower_bound(var)
85+
lb = lower_bound(var)
86+
else
87+
lb = -Inf
9288
end
89+
ref_func = replace(ref_func, string(var) => "($lb..$ub)")
9390
end
94-
if :lower in set_fields
95-
M -= constr_set.lower
96-
elseif :upper in set_fields
97-
M -= constr_set.upper
98-
elseif :value in set_fields
99-
M -= constr_set.value
91+
func_bounds = eval(Meta.parse(ref_func))
92+
if ref_type == :lower
93+
M = func_bounds.lo - ref_rhs
94+
95+
else
96+
M = func_bounds.hi - ref_rhs
97+
end
98+
if isinf(M)
99+
error("M parameter for $ref cannot be infered due to lack of variable bounds.")
100+
else
101+
return M
100102
end
101103
end
102104

@@ -146,3 +148,44 @@ function add_disaggregated_constr(m, disj, vars)
146148
!isempty(d_vars) && eval(:(@constraint($m, $var == sum($d_vars))))
147149
end
148150
end
151+
152+
### DEPRECATED ###
153+
function infer_BigM(ref)
154+
constr_set = constraint_object(ref).set
155+
set_fields = fieldnames(typeof(constr_set))
156+
@assert length(set_fields) == 1 "A reformulation cannot be done on constraint $ref because it is not one of the following GreaterThan, LessThan, or EqualTo."
157+
@assert :value in set_fields || :lower in set_fields || :upper in set_fields "$ref must be one the following: GreaterThan, LessThan, or EqualTo."
158+
vars = all_variables(ref.model) #get all variable names
159+
M = 0 #initialize M
160+
for var in vars
161+
coeff = normalized_coefficient(ref,var)
162+
iszero(coeff) && continue
163+
has_bounds = (has_lower_bound(var), has_upper_bound(var))
164+
if coeff > 0
165+
if :lower in set_fields && has_bounds[1]
166+
bound = lower_bound(var)
167+
elseif :upper in set_fields || :value in set_fields && has_bounds[2]
168+
bound = upper_bound(var)
169+
else
170+
error("M parameter cannot be infered due to lack of variable bounds for variable $var.")
171+
end
172+
M += coeff*bound
173+
elseif coeff < 0
174+
if :lower in set_fields && has_bounds[2]
175+
bound = upper_bound(var)
176+
elseif :upper in set_fields || :value in set_fields && has_bounds[1]
177+
bound = lower_bound(var)
178+
else
179+
error("M parameter cannot be infered due to lack of variable bounds for variable $var.")
180+
end
181+
M += coeff*bound
182+
end
183+
end
184+
if :lower in set_fields
185+
M -= constr_set.lower
186+
elseif :upper in set_fields
187+
M -= constr_set.upper
188+
elseif :value in set_fields
189+
M -= constr_set.value
190+
end
191+
end

0 commit comments

Comments
 (0)