Skip to content

Commit

Permalink
Fix support for external sets in loadfromstring (#2177)
Browse files Browse the repository at this point in the history
  • Loading branch information
odow authored May 29, 2023
1 parent 936e475 commit c18c8b2
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 2 deletions.
34 changes: 32 additions & 2 deletions src/Utilities/parser.jl
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,36 @@ function _parsed_to_moi(model, f::_ParsedVariableIndex)
return _parsed_to_moi(model, f.variable)
end

_walk_expr(f::F, expr) where {F<:Function} = f(expr)

function _walk_expr(f::F, expr::Expr) where {F<:Function}
for (i, arg) in enumerate(expr.args)
expr.args[i] = _walk_expr(f, arg)
end
return expr
end

function _parse_set(expr::Expr)
expr = _walk_expr(expr) do arg
if arg isa Symbol && arg in (:MOI, :MathOptInterface)
return MOI
end
return arg
end
@assert Meta.isexpr(expr, :call)
if expr.args[1] isa Symbol
# If the set is a Symbol, it must be one of the MOI sets. We need to
# eval this in the MOI module.
return Core.eval(MOI, expr)
elseif Meta.isexpr(expr.args[1], :curly) && expr.args[1].args[1] isa Symbol
# Something like Indicator{}()
return Core.eval(MOI, expr)
end
# If the set is an expression, it must be something like
# `SCS.ScaledPSDCone()`. We need to eval this in `Main`.
return Core.eval(Main, expr)
end

# Ideally, this should be load_from_string
"""
loadfromstring!(model, s)
Expand Down Expand Up @@ -333,7 +363,7 @@ function loadfromstring!(model, s)
elseif label == :constrainedvariable
@assert length(ex.args) == 3
@assert ex.args[1] == :in
set = Core.eval(MOI, ex.args[3])
set = _parse_set(ex.args[3])
if isa(ex.args[2], Symbol)
# constrainedvariable: x in LessThan(1.0)
x, _ = MOI.add_constrained_variable(model, set)
Expand Down Expand Up @@ -362,7 +392,7 @@ function loadfromstring!(model, s)
f = _parsed_to_moi(model, _parse_function(ex.args[2], T))
if ex.args[1] == :in
# Could be safer here
set = Core.eval(MOI, ex.args[3])
set = _parse_set(ex.args[3])
elseif ex.args[1] == :<=
set = MOI.LessThan(Core.eval(Base, ex.args[3]))
elseif ex.args[1] == :>=
Expand Down
36 changes: 36 additions & 0 deletions test/Utilities/parser.jl
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,42 @@ function test_eltypes_complex_float64()
return
end

struct Set2175 <: MOI.AbstractScalarSet end

function test_parse_external_set_constraint()
model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}())
MOI.Utilities.loadfromstring!(
model,
"variables: x\nx in $(@__MODULE__).Set2175()",
)
constraints = MOI.get(model, MOI.ListOfConstraintTypesPresent())
@test (MOI.VariableIndex, Set2175) in constraints
return
end

function test_parse_external_set_constrained_variable()
model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}())
MOI.Utilities.loadfromstring!(
model,
"constrainedvariable: x in $(@__MODULE__).Set2175()",
)
constraints = MOI.get(model, MOI.ListOfConstraintTypesPresent())
@test (MOI.VariableIndex, Set2175) in constraints
return
end

function test_parse_scope()
@test !isdefined(@__MODULE__, :MathOptInterface)
model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}())
MOI.Utilities.loadfromstring!(
model,
"variables: x\nx in MathOptInterface.ZeroOne()",
)
attr = MOI.NumberOfConstraints{MOI.VariableIndex,MOI.ZeroOne}()
@test MOI.get(model, attr) == 1
return
end

end # module

TestParser.runtests()

0 comments on commit c18c8b2

Please sign in to comment.