I just came across the 2019 post Pyomo style "blocks" in JuMP by @jonmat because the more I dig into it, the more I realize that I’m indeed in search of the “namespace” feature mentioned here (and I didn’t know that Pyomo provides this). Indeed, for some other project, I’m may be looking for a Modelica-style object/component oriented structuring of JuMP model.
Now, I understand that in the present state of JuMP it’s indeed possible to implement such things using anonymous variables. However, looking at my “add_variable” wrapper of the classical @variable
macro (see below), I feel like I’m just creating a more cumbersome version of @variable
(see e.g. the presence of lb
and ub
keyword args since I cannot use the nice lb <= x <= ub
syntax without a macro), for the only reason that I want to store the VariableRef
in my container of choice (see model_data::Dict{String,Any}
) rather than in the existing global Model
container of registered variables.
So my question is: what would be the difficulty to bake this “reference storage hijacking” directly in the @variable
macro? Or should I copy-paste the @variable
source code and try to adapt it for my use case?
my “add_variable” wrapper
This is the present state of my custom “add_variable” wrapper around @variable
(early version discussed in Using anonymous and or symbols to create dynamic variable names? - #3 by pierre-haessig). Main features are:
- uses anonymous variables to avoid registration in the global namespace of
Model
- but instead store the
VariableRef
in aDict
- and then returns the
VariableRef
to make it easy to bind it to a similarly name Julia variable in the caller.
but to get these features, as said above, I have to pay the price of a much worse API compared to the original @variable
.
"""
add_var!(model_data::Dict{String,Any}, name::String;
lb=nothing, ub=nothing, fix=nothing, K::Int=1, stage="")
add a variable to the JuMP `Model` `model_data["model"]` and store the reference
to that variable in `model_data[name]`.
Returns the variable reference.
`name` is also used as the `base_name` of the JuMP variable (used for printing),
with the optional `stage` argument (`Int`, `String`...) used as a suffix.
# Other optional arguments:
- lower and/or upper bounds can be set with `lb` and `ub`
- equal bounds can be set with `fix`
- the variable can be a Vector of length `K` if `K` >1 (timeseries).
"""
function add_var!(model_data::Dict{String,Any}, name::String;
lb=nothing, ub=nothing, fix=nothing, K::Int=1, stage="")
model = model_data["model"]
name_suffix = "$name$stage"
if K==1
v = @variable(model, base_name=name_suffix)
elseif K>1
v = @variable(model, [1:K], base_name=name_suffix)
else
throw(ArgumentError("`K` array size parameter should be >=1"))
end
model_data[name] = v
# Bounds:
if (lb!==nothing || ub!==nothing) && fix!==nothing
throw(ArgumentError("`lb` and `ub` bounds cannot be used in conjunction with `fix`"))
end
if fix !==nothing
fix.(v)
end
if lb !==nothing
set_lower_bound.(v, lb)
end
if ub !==nothing
set_upper_bound.(v, ub)
end
return v
end
which is to be used as
# One shot model setup
md = Dict{String,Any}()
md["model"] = Model(HiGHS.Optimizer)
# add piles of variables:
x = add_var!(model_data, "x", lb=0.0, ub=power_rated_gen_max)
y = add_var!(model_data, "y", lb=0.0, ub=power_rated_gen_max)