I am trying to forward the @variable macro from JuMP, so that instead of passing a model to it, I pass my custom struct that has a model as a field. I was able to do it when the new macro is declared locally:
using JuMP
struct Player
model::Model
end
macro pvar(player, args...) # forwards JuMP.@variable to Player.model
return :(JuMP.@variable($(esc(player)).model, $(args...)))
end
p = Player(Model())
@pvar(p, y >= 1) # works!
However, as soon as I move the code to my module, it breaks. I believe my problem is in trying to return a local variable (the one created inside JuMP.@variable). MWE:
using JuMP
module MyModule
using JuMP
struct Player
model::Model
end
macro pvar(player, args...) # forwards JuMP.@variable to Player.model
return :(JuMP.@variable($(esc(player)).model, $(args...)))
end
end
p = MyModule.Player(Model())
MyModule.@pvar(p, y >= 1) # fails :(
The error is:
ERROR: LoadError: Global MyModule.y does not exist and cannot be assigned. Declare it using `global` before attempting assignment.
I tried prepending a global to the inner macro call and escaping the whole thing. I am really new to using macros and I have a hard time understanding the order of interpolations and executions.
Before telling you how to do this, why do you want to do this? What is the higher-level goal you are trying to achieve?
I would strongly encourage you not to define new macros. As you have seen, they can be tricky to get correct. Is there some other way that you can achieve what you are trying to do?
Each Player has a feasible space and an objective function, but I will manipulate the objective function (change parameters, explore separability), so I think I cannot just define it as a Model. At first, I thought about extending Model, but I got discouraged by Defining new JuMP models. So now, my idea is to have a Model as a field of Player, and forward the @variable and @constraint macros, so that the user can easily define the feasible space. As an alternative, I thought about forwarding add_variable and add_constraint, but that is also discouraged.
My last resort is for the user to build a Model with the feasible region and pass that when declaring the Player. But I would then make a deepcopy of the model to avoid counterintuitive interferences with the algorithm.
Feel free to let me know what you think of it. Your suggestions are highly appreciated.
Defining a new macro is should be a last resort. JuMP uses macros so that we can intercept x >= 0 as bound and constraints instead of comparisons, and so we can build linear expressions more efficiently.
If you tell people “just write p.model, then you can use all of JuMP” it is much simpler than opting into a subset of the JuMP features or defining new macros.