JuMP: Solver independent callback doesnt work for SCIP

The docs state that JuMP’s solver-independent callback feature supports SCIP.

However, if I replace the lazy constraint example (which uses GLPK) with SCIP, I get the following error

julia> using JuMP

julia> import SCIP

julia> model = Model(SCIP.Optimizer);

julia> @variable(model, x <= 10, Int)
x

julia> @objective(model, Max, x)
x

julia> function my_callback_function(cb_data)
           status = callback_node_status(cb_data, model)
           if status == MOI.CALLBACK_NODE_STATUS_FRACTIONAL
               # `callback_value(cb_data, x)` is not integer (to some tolerance).
               # If, for example, your lazy constraint generator requires an
               # integer-feasible primal solution, you can add a `return` here.
               return
           elseif status == MOI.CALLBACK_NODE_STATUS_INTEGER
               # `callback_value(cb_data, x)` is integer (to some tolerance).
           else
               @assert status == MOI.CALLBACK_NODE_STATUS_UNKNOWN
               # `callback_value(cb_data, x)` might be fractional or integer.
           end
           x_val = callback_value(cb_data, x)
           if x_val > 2 + 1e-6
               con = @build_constraint(x <= 2)
               MOI.submit(model, MOI.LazyConstraint(cb_data), con)
           end
       end
my_callback_function (generic function with 1 method)

julia> set_attribute(model, MOI.LazyConstraintCallback(), my_callback_function)

julia> optimize!(model)
ERROR: MathOptInterface.UnsupportedAttribute{MathOptInterface.LazyConstraintCallback}: Attribute MathOptInterface.LazyConstraintCallback() is not supported by the model.
Stacktrace:
  [1] throw_set_error_fallback(model::SCIP.Optimizer, attr::MathOptInterface.LazyConstraintCallback, value::Function; error_if_supported::MathOptInterface.SetAttributeNotAllowed{MathOptInterface.LazyConstraintCallback})
    @ MathOptInterface ~/.julia/packages/MathOptInterface/yczX1/src/attributes.jl:586
  [2] throw_set_error_fallback(model::SCIP.Optimizer, attr::MathOptInterface.LazyConstraintCallback, value::Function)
    @ MathOptInterface ~/.julia/packages/MathOptInterface/yczX1/src/attributes.jl:577
  [3] set(model::SCIP.Optimizer, attr::MathOptInterface.LazyConstraintCallback, args::Function)
    @ MathOptInterface ~/.julia/packages/MathOptInterface/yczX1/src/attributes.jl:550
  [4] set(b::MathOptInterface.Bridges.LazyBridgeOptimizer{SCIP.Optimizer}, attr::MathOptInterface.LazyConstraintCallback, value::Function)
    @ MathOptInterface.Bridges ~/.julia/packages/MathOptInterface/yczX1/src/Bridges/bridge_optimizer.jl:955
  [5] _pass_attribute(dest::MathOptInterface.Bridges.LazyBridgeOptimizer{SCIP.Optimizer}, src::MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}, index_map::MathOptInterface.Utilities.IndexMap, attr::MathOptInterface.LazyConstraintCallback)
    @ MathOptInterface.Utilities ~/.julia/packages/MathOptInterface/yczX1/src/Utilities/copy.jl:55
  [6] pass_attributes(dest::MathOptInterface.Bridges.LazyBridgeOptimizer{SCIP.Optimizer}, src::MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}, index_map::MathOptInterface.Utilities.IndexMap)
    @ MathOptInterface.Utilities ~/.julia/packages/MathOptInterface/yczX1/src/Utilities/copy.jl:42
  [7] default_copy_to(dest::MathOptInterface.Bridges.LazyBridgeOptimizer{SCIP.Optimizer}, src::MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}})
    @ MathOptInterface.Utilities ~/.julia/packages/MathOptInterface/yczX1/src/Utilities/copy.jl:506
  [8] copy_to
    @ ~/.julia/packages/MathOptInterface/yczX1/src/Bridges/bridge_optimizer.jl:455 [inlined]
  [9] attach_optimizer(model::MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{SCIP.Optimizer}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}})
    @ MathOptInterface.Utilities ~/.julia/packages/MathOptInterface/yczX1/src/Utilities/cachingoptimizer.jl:220
 [10] optimize!(m::MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{SCIP.Optimizer}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}})
    @ MathOptInterface.Utilities ~/.julia/packages/MathOptInterface/yczX1/src/Utilities/cachingoptimizer.jl:312
 [11] optimize!(model::Model; ignore_optimize_hook::Bool, _differentiation_backend::MathOptInterface.Nonlinear.SparseReverseMode, kwargs::@Kwargs{})
    @ JuMP ~/.julia/packages/JuMP/027Gt/src/optimizer_interface.jl:448
 [12] optimize!(model::Model)
    @ JuMP ~/.julia/packages/JuMP/027Gt/src/optimizer_interface.jl:409
 [13] top-level scope
    @ REPL[8]:1

It runs fine if I replace SCIP with GLPK (as in the example from the documentation).

Any ideas on why this might be the case? Are solver-independent callbacks actually supported by SCIP?

Thanks in advance!

Julia version 1.10.0 on WSL2 with

(@v1.10) pkg> st JuMP SCIP
Status `~/.julia/environments/v1.10/Project.toml`
  [4076af6c] JuMP v1.18.1
  [82193955] SCIP v0.11.14

Heuristic and user-cut are, but I don’t think lazy constraints are supported; Design restricted version of ConstraintHandler for LazyConstraint · Issue #271 · scipopt/SCIP.jl · GitHub (admittedly vague issue)

Oh interesting.

I can confirm the user-cut and heuristic callbacks are working with SCIP as they should for me.

Thanks for clarifying that and pointing out the GitHub issue (no way I would have realised that was what the issue was referring to!)

1 Like

No problem, welcome to the forum.

no way I would have realised that was what the issue was referring to!

We can blame @mbesancon for that one :laughing:

1 Like

Haha fair point I rephrased the issue.

As an aside, I sadly don’t have the time for this now and would be grateful if some people want to take a swing at this