diff --git a/src/QCQP/MOI_wrapper.jl b/src/QCQP/MOI_wrapper.jl index d64e37f..6597fa9 100644 --- a/src/QCQP/MOI_wrapper.jl +++ b/src/QCQP/MOI_wrapper.jl @@ -10,9 +10,16 @@ mutable struct Optimizer{T,O<:MOI.ModelLike} <: MOI.AbstractOptimizer index_map::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex} end -function Optimizer{T}(model::MOI.ModelLike) where {T} - return Optimizer{T,typeof(model)}( - model, +function Optimizer{T}(optimizer::MOI.ModelLike) where {T} + if !MOI.supports_incremental_interface(optimizer) + cache = MOI.default_cache(optimizer, T) + optimizer = MOI.Utilities.CachingOptimizer(cache, optimizer) + end + # This bridge layer is needed for instance if `optimizer` needs to bridge + # quadratic objective into quadratic constraints with a slack bridge (e.g., like SCIP) + inner = MOI.Bridges.full_bridge_optimizer(optimizer, T) + return Optimizer{T,typeof(inner)}( + inner, nothing, DataStructures.OrderedDict{Type,MOI.Utilities.VectorOfConstraints}(), Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}(), diff --git a/test/qcqp.jl b/test/qcqp.jl index d08d3fb..7bb1183 100644 --- a/test/qcqp.jl +++ b/test/qcqp.jl @@ -49,6 +49,25 @@ MOI.Utilities.@model( (), ) +MOI.Utilities.@model( + AffineObjectiveModel, + (), + (MOI.LessThan, MOI.GreaterThan, MOI.EqualTo, MOI.Interval), + (), + (), + (), + (MOI.ScalarAffineFunction, MOI.ScalarQuadraticFunction), + (), + (), +) + +function MOI.supports( + ::AffineObjectiveModel{T}, + ::MOI.ObjectiveFunction{F}, +) where {T,F<:MOI.AbstractFunction} + return F == MOI.ScalarAffineFunction{T} +end + function MOI.supports( ::Model, ::MOI.ObjectiveFunction{MOI.ScalarNonlinearFunction}, @@ -336,6 +355,21 @@ function test_start(x, y, T) @test sort(MOI.get(inner, MOI.VariablePrimalStart(), vis)) == T[2, 3, 4, 9] end +function test_inner_bridge(x, y, T) + # The quadratic objective should be bridged after the QCQP layer + inner = AffineObjectiveModel{T}() + model = PolyJuMP.QCQP.Optimizer{T}(inner) + a = MOI.add_variable(model) + b = MOI.add_variable(model) + p = PolyJuMP.ScalarPolynomialFunction(one(T) * x^3 - x * y^2, [a, b]) + MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) + MOI.set(model, MOI.ObjectiveFunction{typeof(p)}(), p) + MOI.Utilities.final_touch(model, nothing) + F = MOI.ScalarAffineFunction{T} + @test MOI.ObjectiveFunction{F}() in + MOI.get(inner, MOI.ListOfModelAttributesSet()) +end + function runtests(x, y) for name in names(@__MODULE__; all = true) if startswith("$name", "test_")