@@ -66,6 +66,7 @@ function BMR(m, constr, bin_var, i, j, k, M)
66
66
end
67
67
68
68
function apply_interval_arithmetic (ref)
69
+ # convert constraints into Expr to replace variables with interval sets and determine bounds
69
70
if ref isa NonlinearConstraintRef
70
71
ref_str = string (ref)
71
72
@assert length (findall (r" [<>]" , ref_str)) <= 1 " $ref must be one of the following: GreaterThan, LessThan, or EqualTo."
@@ -79,16 +80,20 @@ function apply_interval_arithmetic(ref)
79
80
ref_type = fieldnames (typeof (ref_obj. set))[1 ]
80
81
ref_rhs = normalized_rhs (ref)
81
82
end
83
+ ref_func_expr = Meta. parse (ref_func)
84
+ # create a map of variables to their bounds
85
+ interval_map = Dict ()
82
86
vars = all_variables (ref. model) # get all variable names
83
87
for var in vars
84
88
ub = has_upper_bound (var) ? upper_bound (var) : (is_binary (var) ? 1 : Inf )
85
89
lb = has_lower_bound (var) ? lower_bound (var) : (is_binary (var) ? 0 : Inf )
86
- ref_func = replace (ref_func, " $ var" => " ( $ lb ..$ub ) " )
90
+ interval_map[ string ( var)] = lb.. ub
87
91
end
88
- func_bounds = eval (Meta. parse (ref_func))
92
+ ref_func_expr = replace_vars! (ref_func_expr, interval_map)
93
+ # get bounds on the entire expression
94
+ func_bounds = eval (ref_func_expr)
89
95
if ref_type == :lower
90
96
M = func_bounds. lo - ref_rhs
91
-
92
97
else
93
98
M = func_bounds. hi - ref_rhs
94
99
end
@@ -207,27 +212,28 @@ function nl_perspective_function(ref, bin_var_ref, i, j, k, eps)
207
212
# operator (not the symbol).
208
213
# This is done to later replace the symbolic variables with JuMP variables,
209
214
# without messing with the math operators.
210
- pers_func_expr = Base. remove_linenums! (build_function (op ( pers_func,rhs) )). args[2 ]. args[1 ]
215
+ pers_func_expr = Base. remove_linenums! (build_function (pers_func)). args[2 ]. args[1 ]
211
216
212
217
# replace symbolic variables by their JuMP variables
213
218
replace_JuMPvars! (pers_func_expr, m)
214
219
# replace the math operators by symbols
215
220
replace_operators! (pers_func_expr)
216
- # add the constraint
217
- add_NL_constraint (m, pers_func_expr)
218
-
219
- # NOTE: the NLconstraint defined by `ref` needs to be deleted. However, this
220
- # is not currently possible: https://github.com/jump-dev/JuMP.jl/issues/2355.
221
- # As of today (5/12/21), JuMP is behind on its support for nonlinear systems.
222
-
221
+ # determine bounds of original constraint
222
+ upper_b = (op == >= ) ? Inf : rhs
223
+ lower_b = (op == <= ) ? - Inf : rhs
224
+ # replace NL constraint currently in the model with the reformulated one
225
+ new = JuMP. _NonlinearConstraint (JuMP. _NonlinearExprData (m, pers_func_expr),
226
+ lower_b, upper_b)
227
+ m. nlp_data. nlconstr[ref. index. value] = new
228
+
223
229
# NOTE: the new NLconstraint cannot be assigned a name (not an option in add_NL_constraint)
224
230
# pers_func_name = Symbol("perspective_func_$(disj_name)$(j)$(k)")
225
231
end
226
232
227
233
function replace_JuMPvars! (expr, model)
228
- if expr isa Symbol
234
+ if expr isa Symbol # replace symbolic variables with JuMP variables
229
235
return variable_by_name (model, string (expr))
230
- elseif expr isa Expr
236
+ elseif expr isa Expr # run recursion
231
237
for i in eachindex (expr. args)
232
238
expr. args[i] = replace_JuMPvars! (expr. args[i], model)
233
239
end
@@ -236,16 +242,31 @@ function replace_JuMPvars!(expr, model)
236
242
end
237
243
238
244
function replace_operators! (expr)
239
- if expr isa Expr
245
+ if expr isa Expr # run recursion
240
246
for i in eachindex (expr. args)
241
247
expr. args[i] = replace_operators! (expr. args[i])
242
248
end
243
- elseif ! isa ( expr, Symbol) && ! isa (expr, Number) && ! isa (expr, VariableRef)
249
+ elseif expr isa Function # replace Function with its symbol
244
250
return Symbol (expr)
245
251
end
246
252
expr
247
253
end
248
254
255
+ function replace_vars! (expr, intervals)
256
+ if string (expr) in keys (intervals) # check if expression is one of the model variables in the intervals dict
257
+ return intervals[string (expr)] # replace expression with interval
258
+ elseif expr isa Expr
259
+ if length (expr. args) == 1 # run recursive relation on the leaf node on expression tree
260
+ expr. args[i] = replace_vars! (expr. args[i], intervals)
261
+ else # run recursive relation on each internal node of the expression tree, but skip the first element, which will always be the operator (this will avoid issues if the user creates a model variable called exp)
262
+ for i in 2 : length (expr. args)
263
+ expr. args[i] = replace_vars! (expr. args[i], intervals)
264
+ end
265
+ end
266
+ end
267
+ expr
268
+ end
269
+
249
270
function add_disaggregated_constr (m, disj, vars)
250
271
for var in vars
251
272
d_vars = []
0 commit comments