`JuMP` macros with `RuntimeGeneratedFunctions`

The following MWE

import JuMP, RuntimeGeneratedFunctions


RuntimeGeneratedFunctions.init(@__MODULE__)

code = """
model = JuMP.owner_model(x[1])
JuMP.@constraint(model, [t = 1:10], x[t] >= 0)
"""

code_ex = Meta.parse("""begin\n$(code)\nend""")

rgf = RuntimeGeneratedFunctions.@RuntimeGeneratedFunction(
    :(function (x)
        $code_ex
    end)
)

rgf(JuMP.@variable(JuMP.Model(), [1:10]))

fails with

ERROR: The function body AST defined by this @generated function is not pure. This likely means it contains a closure, a comprehension or a generator.

whereas it works when manually creating each constraint using

code = """
for t = 1:10
    JuMP.@constraint(JuMP.owner_model(x[t]), x[t] >= 0)
end
"""

Am I misusing RuntimeGeneratedFunctions? Or is there something else I can do to work around this?

1 Like

The error seems pretty reasonable.

The expanded code from @constraint contains a bunch of stuff that RuntimeGeneratedFunctions might find troublesome:

julia> using JuMP

julia> begin
           model = Model()
           @variable(model, x[1:10])
           @macroexpand @constraint(model, [t = 1:10], x[t] >= 0)
       end
quote
    #= REPL[6]:4 =#
    JuMP._valid_model(model, :model)
    begin
        #= /Users/oscar/.julia/dev/JuMP/src/macros.jl:399 =#
        let model = model
            #= /Users/oscar/.julia/dev/JuMP/src/macros.jl:400 =#
            (JuMP.Containers.container)(((t,)->begin
                        #= /Users/oscar/.julia/dev/JuMP/src/Containers/macro.jl:550 =#
                        begin
                            #= /Users/oscar/.julia/dev/JuMP/src/macros/@constraint.jl:171 =#
                            begin
                                #= /Users/oscar/.julia/dev/JuMP/src/macros.jl:264 =#
                                var"#18###233" = begin
                                        #= /Users/oscar/.julia/packages/MutableArithmetics/tNSBd/src/rewrite.jl:374 =#
                                        let
                                            #= /Users/oscar/.julia/packages/MutableArithmetics/tNSBd/src/rewrite.jl:375 =#
                                            begin
                                                #= /Users/oscar/.julia/packages/MutableArithmetics/tNSBd/src/rewrite.jl:371 =#
                                                var"#19###234" = (MutableArithmetics.copy_if_mutable)(x[t])
                                                var"#20###235" = (MutableArithmetics.operate!!)(MutableArithmetics.sub_mul, var"#19###234", 0)
                                            end
                                            #= /Users/oscar/.julia/packages/MutableArithmetics/tNSBd/src/rewrite.jl:376 =#
                                            var"#20###235"
                                        end
                                    end
                                #= /Users/oscar/.julia/dev/JuMP/src/macros.jl:265 =#
                                var"#21###236" = (JuMP.flatten!)(var"#18###233")
                            end
                            #= /Users/oscar/.julia/dev/JuMP/src/macros/@constraint.jl:172 =#
                            var"#22#build" = JuMP.model_convert(model, JuMP.build_constraint(JuMP.Containers.var"#error_fn#98"{String}("At REPL[6]:4: `@constraint(model, [t = 1:10], x[t] >= 0)`: "), JuMP._functionize(var"#21###236"), GreaterThanZero()))
                            #= /Users/oscar/.julia/dev/JuMP/src/macros/@constraint.jl:173 =#
                            JuMP.add_constraint(model, var"#22#build", "")
                        end
                    end), (JuMP.Containers).vectorized_product((JuMP.Base).OneTo(begin
                            #= /Users/oscar/.julia/dev/JuMP/src/Containers/macro.jl:95 =#
                            try
                                #= /Users/oscar/.julia/dev/JuMP/src/Containers/macro.jl:96 =#
                                1:10
                            catch
                                #= /Users/oscar/.julia/dev/JuMP/src/Containers/macro.jl:98 =#
                                (JuMP.Containers.var"#error_fn#98"{String}("At REPL[6]:4: `@constraint(model, [t = 1:10], x[t] >= 0)`: "))("unexpected error parsing reference set: ", $(Expr(:copyast, :($(QuoteNode(:(1:10)))))))
                            end
                        end)), JuMP.Containers.AutoContainerType, Any[:t])
        end
    end
end

If you compare it to

julia> using JuMP

julia> begin
           model = Model()
           @variable(model, x[1:10])
           t = 1
           @macroexpand @constraint(model, x[t] >= 0)
       end
quote
    #= REPL[8]:5 =#
    JuMP._valid_model(model, :model)
    begin
        #= /Users/oscar/.julia/dev/JuMP/src/macros.jl:399 =#
        let model = model
            #= /Users/oscar/.julia/dev/JuMP/src/macros.jl:400 =#
            begin
                #= /Users/oscar/.julia/dev/JuMP/src/macros/@constraint.jl:171 =#
                begin
                    #= /Users/oscar/.julia/dev/JuMP/src/macros.jl:264 =#
                    var"#28###238" = begin
                            #= /Users/oscar/.julia/packages/MutableArithmetics/tNSBd/src/rewrite.jl:374 =#
                            let
                                #= /Users/oscar/.julia/packages/MutableArithmetics/tNSBd/src/rewrite.jl:375 =#
                                begin
                                    #= /Users/oscar/.julia/packages/MutableArithmetics/tNSBd/src/rewrite.jl:371 =#
                                    var"#29###239" = (MutableArithmetics.copy_if_mutable)(x[t])
                                    var"#30###240" = (MutableArithmetics.operate!!)(MutableArithmetics.sub_mul, var"#29###239", 0)
                                end
                                #= /Users/oscar/.julia/packages/MutableArithmetics/tNSBd/src/rewrite.jl:376 =#
                                var"#30###240"
                            end
                        end
                    #= /Users/oscar/.julia/dev/JuMP/src/macros.jl:265 =#
                    var"#31###241" = (JuMP.flatten!)(var"#28###238")
                end
                #= /Users/oscar/.julia/dev/JuMP/src/macros/@constraint.jl:172 =#
                var"#32#build" = JuMP.model_convert(model, JuMP.build_constraint(JuMP.Containers.var"#error_fn#98"{String}("At REPL[8]:5: `@constraint(model, x[t] >= 0)`: "), JuMP._functionize(var"#31###241"), GreaterThanZero()))
                #= /Users/oscar/.julia/dev/JuMP/src/macros/@constraint.jl:173 =#
                JuMP.add_constraint(model, var"#32#build", "")
            end
        end
    end
end

you’ll see no closure.