Getting JuMP model variables and containers in order of creation

Hi!

I want to get all variables in my model organized in containers, whenever possible. I am currently using object_dictionary for that, which seems quite satisfactory, but the order does not follow the creation order, which is important for me.

MWE:

julia> m = Model()
@variable(m, x)
@variable(m, y)
@variable(m, z[1:2])
;

julia> collect(keys(object_dictionary(m)))
3-element Vector{Symbol}:
 :y
 :z
 :x

I would expect the order to be :x → :y → :z, since that is (roughly) what I get from all_variables(m) (and that is the creation order, as per the documentation).

I am now recovering the creation order by backwards-searching the all_variables(m) list, but that seems quite sketchy. On top of that, I am not quite sure how reliable the object dictionary is for getting the variables as containers (when one exists), as I need to check whether every item is a VariableRef of an AbstractArray of it.

Is there a better way to get those containers and variablerefs in order of creation?

I noticed that the obj_dict is not stored as an ordered dict. Maybe converting it to an ordered dict could make things more intuitive? Although, I haven’t looked at non-GenericModels, so I have not idea how big the downstream impacts would be.

Why do you need this?

Elements are only stored in object_dictionary if they are named. Anonymous variables like x = @variable(m) are not stored.

Maybe converting it to an ordered dict could make things more intuitive?

We could consider this, but I’d like to understand the motivation first (in more detail than “I’d like them ordered”).

So, this is related somewhat to an older Post of mine.

The input of my algorithm is a list of players (in a Nash game), each with a jump model (feasible space) and a function (payoff). Each payoff is a function of the player’s variables and is parameterized by the other players’ variables. Up to now, I was requiring the function to be defined over all_variables, but that gets really inconvenient if the variables are defined as containers. By using object_dictionary, the function could be defined over a more familiar structure, if the order was known.

Writing this made me realize maybe I should reconsider extending the Model.

Don’t rely on the variables being added in a particular order, or the fact that they are registered in the object_dictionary. Create a proper data structure that you control. For example, ask the user to provide a list of their arguments to the payoff function.

1 Like

I think with some refactor that will do the trick!

On a side note, I have read a bit of the implementation of BilevelJuMP.jl and it seems their approach is very suitable for what I envision here. Generalizing it beyond two-player-Stackelberg games would work wonders for my case and would allow players to have constraints that depend on other players’ variables (completely out-of-scope for me right now, but very helpful for the community).

1 Like

Now that I am handling the variable references in my custom struct, how can I create copies of it?

using JuMP

struct Foo
    X::Model
    vars::Vector{Union{VariableRef, AbstractArray{VariableRef}}}
end

function Base.copy(f::Foo)
    X_copy = copy(f.X)
    vars_copy = ?
    return Foo(X_copy, vars_copy)
end

I feel like the only way would be to try and do a reverse search on object_dictionary, but that would again have to rely on variables being registered there, and I don’t even know how to do that for containers.

Why do you need a copy? In most cases we do not suggest that you copy a JuMP model.

In my tests I check that a payoff function is working by comparing it to other payoff functions over the same Model, so that is when I define the model once and copy it multiple times.

That is also similar to a use case that I expect my user to have. A game in which all players have the same constraints, but different payoff functions. So it would be nice to have a way to copy the models and vars.