Skip to content

add_constrained_variable(s) can break for CachingOptimizer if an optimizer has been attached #1083

@martinbiel

Description

@martinbiel

If a CachingOptimizer has an attached optimizer, and add_constrained_variable(opt, set) is called, it defaults to the fallback

function add_constrained_variable(model::ModelLike, set::AbstractScalarSet)
    variable = add_variable(model)
    constraint = add_constraint(model, SingleVariable(variable), set)
    return variable, constraint
end

in variables.jl. If the attached optimizer only supports the set through add_constrained_variable, then this errors. Here is a MWE:

using MathOptInterface
const MOI = MathOptInterface
const MOIU = MOI.Utilities

struct DummySet <: MOI.AbstractScalarSet end

MOI.supports_add_constrained_variable(::MOIU.MockOptimizer, ::Type{DummySet}) = true
function MOI.add_constrained_variable(::MOIU.MockOptimizer, ::DummySet)
    # Dummy support
    return MOI.VariableIndex(1), MOI.ConstraintIndex{MOI.SingleVariable, DummySet}(1)
end

cache = MOIU.UniversalFallback(MOIU.Model{Float64}())
m = MOIU.CachingOptimizer(cache, MOIU.MANUAL)
s = MOIU.MockOptimizer(MOIU.Model{Float64}(), supports_names=false)
MOIU.reset_optimizer(m, s)
# Catched by universal fallback
MOI.add_constrained_variable(m, DummySet())
# Properly calls add_constrained_variable during copy
MOIU.attach_optimizer(m)
# Adding constrained var after optimizer has been attached
# errors even though optimizer supports the dummy set
if MOI.supports_add_constrained_variable(m.optimizer, DummySet)
    MOI.add_constrained_variable(m, DummySet())
end
ERROR: MathOptInterface.UnsupportedConstraint{MathOptInterface.SingleVariable,DummySet}: `MathOptInterface.SingleVariable`-in-`DummySet` constraint is not supported by the model.
Stacktrace:
 [1] add_constraint(::MathOptInterface.Utilities.Model{Float64}, ::MathOptInterface.SingleVariable, ::DummySet) at /home/mbiel/.julia/dev/MathOptInterface/src/Utilities/model.jl:539
 [2] add_constraint(::MathOptInterface.Utilities.MockOptimizer{MathOptInterface.Utilities.Model{Float64}}, ::MathOptInterface.SingleVariable, ::DummySet) at /home/mbiel/.julia/dev/MathOptInterface/src/Utilities/mockoptimizer.jl:113
 [3] add_constraint(::MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.AbstractOptimizer,MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}, ::MathOptInterface.SingleVariable, ::DummySet) at /home/mbiel/.julia/dev/MathOptInterface/src/Utilities/cachingoptimizer.jl:262
 [4] add_constrained_variable(::MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.AbstractOptimizer,MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}, ::DummySet) at /home/mbiel/.julia/dev/MathOptInterface/src/variables.jl:88

A more relevant example would be a custom set that requires a variable bridge and must be added through add_constrained_variable. The problem holds for vector sets as well.

A suggested fix is to implement add_constrained_variable(s) for CachingOptimizer in the same way as add_variable and add_constraint.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions