diff --git a/src/Bridges/debug.jl b/src/Bridges/debug.jl index d937871204..09dc90c337 100644 --- a/src/Bridges/debug.jl +++ b/src/Bridges/debug.jl @@ -542,7 +542,9 @@ Print the set of bridges that are active in the model `b`. """ function print_active_bridges(io::IO, b::MOI.Bridges.LazyBridgeOptimizer) F = MOI.get(b, MOI.ObjectiveFunctionType()) - print_active_bridges(io, b, F) + if F !== nothing + print_active_bridges(io, b, F) + end types = MOI.get(b, MOI.ListOfConstraintTypesPresent()) # We add a sort here to make the order reproducible, and to group similar # constraints together. @@ -639,6 +641,11 @@ function print_active_bridges( offset::String = "", ) where {F<:MOI.AbstractFunction,S<:MOI.AbstractSet} if !MOI.supports_constraint(b, F, S) + if F == MOI.VariableIndex || F == MOI.VectorOfVariables + # Okay. If you don't support the constraint, maybe you support + # adding as a constrained variable. + return print_active_bridges(io, b, S, offset) + end throw( MOI.UnsupportedConstraint{F,S}( "The constraint cannot be bridged using the set of available " * @@ -651,35 +658,31 @@ function print_active_bridges( _print_supported(io, "Supported constraint: $F-in-$S\n") return end - is_constraint_bridged = true c_node = b.constraint_node[(F, S)] if F == MOI.VariableIndex || F == MOI.VectorOfVariables - # Call `MOI.supports_` here to build the necessary nodes in the bridging - # graph. - if F == MOI.VariableIndex - MOI.supports_add_constrained_variable(b, S) + if is_bridged(b, S) + # The constraint can be both variable bridged and constraint + # bridged. Which is cheaper? + v_node = b.variable_node[(S,)] + if bridging_cost(b.graph, v_node) <= bridging_cost(b.graph, c_node) + return print_active_bridges(io, b, S, offset) + end else - MOI.supports_add_constrained_variables(b, S) + # The constraint is bridged via add_constraint, and not via + # add_constrained_variable(s). + return print_active_bridges(io, b, S, offset) end - v_node = b.variable_node[(S,)] - if bridging_cost(b.graph, v_node) <= bridging_cost(b.graph, c_node) - is_constraint_bridged = false - end - end - if is_constraint_bridged - index = MOI.Bridges.bridge_index(b.graph, c_node) - B = b.constraint_bridge_types[index] - BT = MOI.Bridges.Constraint.concrete_bridge_type(B, F, S) - print(io, offset, " * ") - _print_unsupported(io, "Unsupported constraint: $F-in-$S\n") - println(io, offset, " | bridged by:") - print(io, offset, " | ") - MOI.Utilities.print_with_acronym(io, "$BT\n") - println(io, offset, " | may introduce:") - _print_bridge(io, b, BT, offset) - else - print_active_bridges(io, b, S, offset) end + index = MOI.Bridges.bridge_index(b.graph, c_node) + B = b.constraint_bridge_types[index] + BT = MOI.Bridges.Constraint.concrete_bridge_type(B, F, S) + print(io, offset, " * ") + _print_unsupported(io, "Unsupported constraint: $F-in-$S\n") + println(io, offset, " | bridged by:") + print(io, offset, " | ") + MOI.Utilities.print_with_acronym(io, "$BT\n") + println(io, offset, " | may introduce:") + _print_bridge(io, b, BT, offset) return end diff --git a/test/Bridges/General/test_debug.jl b/test/Bridges/General/test_debug.jl index 9be0192e6d..8f5bcee3de 100644 --- a/test/Bridges/General/test_debug.jl +++ b/test/Bridges/General/test_debug.jl @@ -322,6 +322,116 @@ function test_print_graph_stdout() return end +struct ModelPOIIssue201{T} <: MOI.ModelLike + supports_equal_to::Bool + supports_parameter::Bool + parameter::Vector{T} + equal_to::Vector{T} + function ModelPOIIssue201{T}(; + supports_equal_to::Bool = true, + supports_parameter::Bool = true, + ) where {T} + return new{T}(supports_equal_to, supports_parameter, T[], T[]) + end +end + +function MOI.supports_add_constrained_variable( + model::ModelPOIIssue201{T}, + ::Type{MOI.Parameter{T}}, +) where {T} + return model.supports_parameter +end + +function MOI.supports_add_constrained_variable( + model::ModelPOIIssue201{T}, + ::Type{MOI.EqualTo{T}}, +) where {T} + return model.supports_equal_to +end + +function MOI.supports_constraint( + model::ModelPOIIssue201{T}, + ::Type{MOI.ScalarAffineFunction{T}}, + ::Type{MOI.EqualTo{T}}, +) where {T} + return model.supports_equal_to +end + +MOI.get(::ModelPOIIssue201, ::MOI.ObjectiveFunctionType) = nothing + +function MOI.get( + model::ModelPOIIssue201{T}, + ::MOI.ListOfConstraintTypesPresent, +) where {T} + ret = Tuple{Type,Type}[] + if !isempty(model.parameter) + push!(ret, (MOI.VariableIndex, MOI.Parameter{T})) + end + if !isempty(model.equal_to) + push!(ret, (MOI.VariableIndex, MOI.EqualTo{T})) + end + return ret +end + +function MOI.get( + model::ModelPOIIssue201{T}, + ::MOI.NumberOfConstraints{MOI.VariableIndex,MOI.Parameter{T}}, +) where {T} + return length(model.parameter) +end + +function MOI.get( + model::ModelPOIIssue201{T}, + ::MOI.NumberOfConstraints{MOI.VariableIndex,MOI.EqualTo{T}}, +) where {T} + return length(model.equal_to) +end + +function MOI.add_constrained_variable( + model::ModelPOIIssue201{T}, + set::MOI.Parameter{T}, +) where {T} + push!(model.parameter, set.value) + x = MOI.VariableIndex(length(model.parameter)) + return x, MOI.ConstraintIndex{MOI.VariableIndex,typeof(set)}(x.value) +end + +function MOI.add_constrained_variable( + model::ModelPOIIssue201{T}, + set::MOI.EqualTo{T}, +) where {T} + push!(model.equal_to, set.value) + x = MOI.VariableIndex(length(model.equal_to)) + return x, MOI.ConstraintIndex{MOI.VariableIndex,typeof(set)}(x.value) +end + +function test_parametricoptinterface_issue_201_case_1() + model = ModelPOIIssue201{Float64}(; supports_equal_to = false) + model = MOI.Bridges.full_bridge_optimizer(model, Float64) + p, _ = MOI.add_constrained_variable(model, MOI.Parameter(1.0)) + @test sprint(MOI.Bridges.print_active_bridges, model) == + " * Supported variable: MOI.Parameter{Float64}\n" + return +end + +function test_parametricoptinterface_issue_201_case_2() + model = ModelPOIIssue201{Float64}(; supports_equal_to = true) + model = MOI.Bridges.full_bridge_optimizer(model, Float64) + p, _ = MOI.add_constrained_variable(model, MOI.Parameter(1.0)) + @test sprint(MOI.Bridges.print_active_bridges, model) == + " * Supported variable: MOI.Parameter{Float64}\n" + return +end + +function test_parametricoptinterface_issue_201_case_3() + model = ModelPOIIssue201{Float64}(; supports_parameter = false) + model = MOI.Bridges.full_bridge_optimizer(model, Float64) + p, _ = MOI.add_constrained_variable(model, MOI.Parameter(1.0)) + @test sprint(MOI.Bridges.print_active_bridges, model) == + " * Unsupported variable: MOI.Parameter{Float64}\n | bridged by:\n | MOIB.Variable.ParameterToEqualToBridge{Float64}\n | may introduce:\n | * Supported variable: MOI.EqualTo{Float64}\n" + return +end + end TestBridgesDebug.runtests()