Skip to content

Support to bridges with external sets/functions #490

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 28 additions & 6 deletions src/Bridges/singlebridgeoptimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,36 @@ bridgedmodel = SplitInterval(model)
will additionally support `ScalarAffineFunction`-in-`Interval`.
"""
macro bridge(modelname, bridge, ss, sst, vs, vst, sf, sft, vf, vft)
code = bridge_impl(modelname, bridge, ss, sst, vs, vst, sf, sft, vf, vft)
esc(code)
end

macro external_bridge(modelname, bridge, ss, sst, vs, vst, sf, sft, vf, vft)
code = bridge_impl(modelname, bridge, ss, sst, vs, vst, sf, sft, vf, vft,
_prefix_func = (x->x.args), use_external_model = true)
esc(code)
end

function bridge_impl(modelname, bridge, ss, sst, vs, vst, sf, sft, vf, vft;
_prefix_func = _tuple_prefix_moi, use_external_model = false)
bridgedmodelname = Symbol(string(modelname) * "Instance")
bridgedfuns = :(Union{$(_tuple_prefix_moi(sf)...), $(_tuple_prefix_moi(sft)...), $(_tuple_prefix_moi(vf)...), $(_tuple_prefix_moi(vft)...)})
bridgedsets = :(Union{$(_tuple_prefix_moi(ss)...), $(_tuple_prefix_moi(sst)...), $(_tuple_prefix_moi(vs)...), $(_tuple_prefix_moi(vst)...)})
bridgedfuns = :(Union{$(_prefix_func(sf)...), $(_prefix_func(sft)...), $(_prefix_func(vf)...), $(_prefix_func(vft)...)})
bridgedsets = :(Union{$(_prefix_func(ss)...), $(_prefix_func(sst)...), $(_prefix_func(vs)...), $(_prefix_func(vst)...)})

esc(quote
$MOIU.@model $bridgedmodelname $ss $sst $vs $vst $sf $sft $vf $vft
if use_external_model
code = quote
$MOIU.@external_model $bridgedmodelname $ss $sst $vs $vst $sf $sft $vf $vft
end
else
code = quote
$MOIU.@model $bridgedmodelname $ss $sst $vs $vst $sf $sft $vf $vft
end
end
code = quote
$code
const $modelname{T, OT<:MOI.ModelLike} = $MOIB.SingleBridgeOptimizer{$bridge{T}, $bridgedmodelname{T}, OT}
isbridged(::$modelname, ::Type{<:$bridgedfuns}, ::Type{<:$bridgedsets}) = true
MathOptInterface.Bridges.isbridged(::$modelname, ::Type{<:$bridgedfuns}, ::Type{<:$bridgedsets}) = true
supportsbridgingconstraint(::$modelname, ::Type{<:$bridgedfuns}, ::Type{<:$bridgedsets}) = true
end)
end
return code
end
69 changes: 54 additions & 15 deletions src/Utilities/model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -406,18 +406,35 @@ end
# Expr(:., MOI, :($(QuoteNode(s)))) is Expr(:., MOI, :(:EqualTo)) <- what we want

# (MOI, :Zeros) -> :(MOI.Zeros)
_mod(m::Module, s::Symbol) = Expr(:., m, :($(QuoteNode(s))))
_mod(m::Module, s::Symbol) = s
# (:Zeros) -> :(MOI.Zeros)
_moi(s::Symbol) = _mod(MOI, s)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not simply removing this function and force the user to write

@model LPModel () (MOI.EqualTo, MOI.GreaterThan, MOI.LessThan, MOI.Interval) (MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives) () (MOI.SingleVariable,) (MOI.ScalarAffineFunction,) (MOI.VectorOfVariables,) (MOI.VectorAffineFunction,)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @model might be called within the definition of module NewModule in which case you can't pass NewModule.NewSet for example. Can you?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you remove this _moi function and all calls to it then it should work

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW the escaping/scoping rules for macros changed between 0.6 and 0.7. You might run into this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forcing the module prefix sounds like the most robust way to go.

_set(s::SymbolSet) = _moi(s.s)
_external_set(s::SymbolSet) = s.s
_fun(s::SymbolFun) = _moi(s.s)
_external_fun(s::SymbolFun) = s.s

function _external_typedset(s::SymbolSet)
if s.typed
:($(s.s){T})
else
s.s
end
end
function _typedset(s::SymbolSet)
if s.typed
:($(_set(s)){T})
else
_set(s)
end
end
function _external_typedfun(s::SymbolFun)
if s.typed
:($(s.s){T})
else
s.s
end
end
function _typedfun(s::SymbolFun)
if s.typed
:($(_fun(s)){T})
Expand All @@ -432,10 +449,14 @@ if VERSION >= v"0.7.0-DEV.2813"
end
_field(s::SymbolFS) = Symbol(lowercase(string(s.s)))

_external_getC(s::SymbolSet) = :($MOIU.C{F, $(_external_typedset(s))})
_getC(s::SymbolSet) = :($MOIU.C{F, $(_typedset(s))})
_external_getC(s::SymbolFun) = _external_typedfun(s)
_getC(s::SymbolFun) = _typedfun(s)

_external_getCV(s::SymbolSet) = :($(_external_getC(s))[])
_getCV(s::SymbolSet) = :($(_getC(s))[])
_external_getCV(s::SymbolFun) = :($(s.cname){T, $(_external_getC(s))}())
_getCV(s::SymbolFun) = :($(s.cname){T, $(_getC(s))}())

_callfield(f, s::SymbolFS) = :($f(model.$(_field(s))))
Expand Down Expand Up @@ -494,6 +515,23 @@ end
The type `LPModel` implements the MathOptInterface API except methods specific to solver models like `optimize!` or `getattribute` with `VariablePrimal`.
"""
macro model(modelname, ss, sst, vs, vst, sf, sft, vf, vft)
code = model_impl(modelname, ss, sst, vs, vst, sf, sft, vf, vft)
esc(code)
end

macro external_model(modelname, ss, sst, vs, vst, sf, sft, vf, vft)
code = model_impl(modelname, ss, sst, vs, vst, sf, sft, vf, vft,
_set_func = _external_set, _typedset_func = _external_typedset,
_getC_func = _external_getC, _getCV_func = _external_getCV,
_fun_func = _external_fun, _typedfun_func = _external_typedfun)
esc(code)
end

function model_impl(modelname, ss, sst, vs, vst, sf, sft, vf, vft;
_set_func = _set, _typedset_func = _typedset,
_getC_func = _getC, _getCV_func = _getCV,
_fun_func = _fun, _typedfun_func = _typedfun)

scalarsets = [SymbolSet.(ss.args, false); SymbolSet.(sst.args, true)]
vectorsets = [SymbolSet.(vs.args, false); SymbolSet.(vst.args, true)]

Expand All @@ -509,7 +547,7 @@ macro model(modelname, ss, sst, vs, vst, sf, sft, vf, vft)
for (c, sets) in ((scalarconstraints, scalarsets), (vectorconstraints, vectorsets))
for s in sets
field = _field(s)
push!(c.args[3].args, :($field::Vector{$(_getC(s))}))
push!(c.args[3].args, :($field::Vector{$(_getC_func(s))}))
end
end

Expand All @@ -533,7 +571,7 @@ macro model(modelname, ss, sst, vs, vst, sf, sft, vf, vft)
for f in funs
cname = f.cname
field = _field(f)
push!(modeldef.args[2].args[3].args, :($field::$cname{T, $(_getC(f))}))
push!(modeldef.args[2].args[3].args, :($field::$cname{T, $(_getC_func(f))}))
end

code = quote
Expand Down Expand Up @@ -579,34 +617,34 @@ macro model(modelname, ss, sst, vs, vst, sf, sft, vf, vft)
funct = _mod(MOIU, func)
for (c, sets) in ((scname, scalarsets), (vcname, vectorsets))
for s in sets
set = _set(s)
set = _set_func(s) #s.s for external
field = _field(s)
code = quote
$code
$funct(model::$c, ci::$T{F, <:$set}, args...) where F = $funct(model.$field, ci, args...)
MathOptInterface.Utilities.$funct(model::$c, ci::$T{F, <:$set}, args...) where F = MathOptInterface.Utilities.$funct(model.$field, ci, args...)
end
end
end

for f in funs
fun = _fun(f)
fun = _fun_func(f)
field = _field(f)
code = quote
$code
$funct(model::$modelname, ci::$T{<:$fun}, args...) = $funct(model.$field, ci, args...)
MathOptInterface.Utilities.$funct(model::$modelname, ci::$T{<:$fun}, args...) = MathOptInterface.Utilities.$funct(model.$field, ci, args...)
end
end
end

return esc(quote
code = quote
$scalarconstraints
function $scname{T, F}() where {T, F}
$scname{T, F}($(_getCV.(scalarsets)...))
$scname{T, F}($(_getCV_func.(scalarsets)...))
end

$vectorconstraints
function $vcname{T, F}() where {T, F}
$vcname{T, F}($(_getCV.(vectorsets)...))
$vcname{T, F}($(_getCV_func.(vectorsets)...))
end

$modeldef
Expand All @@ -617,10 +655,11 @@ macro model(modelname, ss, sst, vs, vst, sf, sft, vf, vft)
$(_getCV.(funs)...))
end

$MOI.supportsconstraint(model::$modelname{T}, ::Type{<:Union{$(_typedfun.(scalarfuns)...)}}, ::Type{<:Union{$(_typedset.(scalarsets)...)}}) where T = true
$MOI.supportsconstraint(model::$modelname{T}, ::Type{<:Union{$(_typedfun.(vectorfuns)...)}}, ::Type{<:Union{$(_typedset.(vectorsets)...)}}) where T = true
$MOI.supportsconstraint(model::$modelname{T}, ::Type{<:Union{$(_typedfun_func.(scalarfuns)...)}}, ::Type{<:Union{$(_typedset_func.(scalarsets)...)}}) where T = true
$MOI.supportsconstraint(model::$modelname{T}, ::Type{<:Union{$(_typedfun_func.(vectorfuns)...)}}, ::Type{<:Union{$(_typedset_func.(vectorsets)...)}}) where T = true

$code

end)
end
end

return code
end