From a1f31ab0cbee6f39709100fa2af2e4f1475bb558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 18 Dec 2019 19:24:04 +0100 Subject: [PATCH] Only use variable bridge if it is optimal --- .../Constraint/single_bridge_optimizer.jl | 4 +++ .../Objective/single_bridge_optimizer.jl | 4 +++ .../Variable/single_bridge_optimizer.jl | 4 +++ src/Bridges/bridge_optimizer.jl | 17 ++++++++-- src/Bridges/debug.jl | 2 +- src/Bridges/graph.jl | 14 ++++++-- src/Bridges/lazy_bridge_optimizer.jl | 5 ++- test/Bridges/lazy_bridge_optimizer.jl | 33 +++++++++++++++++++ 8 files changed, 76 insertions(+), 7 deletions(-) diff --git a/src/Bridges/Constraint/single_bridge_optimizer.jl b/src/Bridges/Constraint/single_bridge_optimizer.jl index 5fe1700407..3927768067 100644 --- a/src/Bridges/Constraint/single_bridge_optimizer.jl +++ b/src/Bridges/Constraint/single_bridge_optimizer.jl @@ -33,6 +33,10 @@ function MOIB.supports_bridging_constraint( S::Type{<:MOI.AbstractSet}) where BT return MOI.supports_constraint(BT, F, S) end +function is_bridged_with_variable_bridge(::SingleBridgeOptimizer, + ::Type{<:MOI.AbstractSet}) + return false +end function MOIB.is_bridged(b::SingleBridgeOptimizer, F::Type{<:MOI.AbstractFunction}, S::Type{<:MOI.AbstractSet}) return MOIB.supports_bridging_constraint(b, F, S) diff --git a/src/Bridges/Objective/single_bridge_optimizer.jl b/src/Bridges/Objective/single_bridge_optimizer.jl index 5d87d1fd44..f5b8b3d486 100644 --- a/src/Bridges/Objective/single_bridge_optimizer.jl +++ b/src/Bridges/Objective/single_bridge_optimizer.jl @@ -32,6 +32,10 @@ function MOIB.supports_bridging_objective_function( ::SingleBridgeOptimizer{BT}, F::Type{<:MOI.AbstractScalarFunction}) where BT return supports_objective_function(BT, F) end +function is_bridged_with_variable_bridge(::SingleBridgeOptimizer, + ::Type{<:MOI.AbstractSet}) + return false +end function MOIB.is_bridged( b::SingleBridgeOptimizer, F::Type{<:MOI.AbstractScalarFunction}) return MOIB.supports_bridging_objective_function(b, F) diff --git a/src/Bridges/Variable/single_bridge_optimizer.jl b/src/Bridges/Variable/single_bridge_optimizer.jl index 47aa24c6ee..81357d8216 100644 --- a/src/Bridges/Variable/single_bridge_optimizer.jl +++ b/src/Bridges/Variable/single_bridge_optimizer.jl @@ -36,6 +36,10 @@ function MOIB.supports_bridging_constrained_variable( ::SingleBridgeOptimizer{BT}, S::Type{<:MOI.AbstractSet}) where BT return supports_constrained_variable(BT, S) end +function is_bridged_with_variable_bridge(b::SingleBridgeOptimizer, + S::Type{<:MOI.AbstractSet}) + return is_bridged(b, S) +end function MOIB.is_bridged(b::SingleBridgeOptimizer, S::Type{<:MOI.AbstractSet}) return MOIB.supports_bridging_constrained_variable(b, S) end diff --git a/src/Bridges/bridge_optimizer.jl b/src/Bridges/bridge_optimizer.jl index 8e4e003aaa..40e053be50 100644 --- a/src/Bridges/bridge_optimizer.jl +++ b/src/Bridges/bridge_optimizer.jl @@ -90,6 +90,19 @@ function supports_bridging_constrained_variable( return false end +""" + is_bridged_with_variable_bridge( + ::AbstractBridgeOptimizer, ::Type{<:MOI.AbstractSet}) + +Return a `Bool` indicating whether `b` bridges constrained variable in +`S` using a variable bridge. If it returns `false`, it means that free +variables and a constraint on these variables should be added instead. +The constraint is then bridged by a constraint bridge. +""" +function is_bridged_with_variable_bridge( + ::AbstractBridgeOptimizer, ::Type{<:MOI.AbstractSet}) +end + """ supports_bridging_constraint( b::AbstractBridgeOptimizer, @@ -1141,7 +1154,7 @@ function MOI.add_constrained_variables(b::AbstractBridgeOptimizer, set::MOI.AbstractVectorSet) if is_bridged(b, typeof(set)) || is_bridged(b, MOI.VectorOfVariables, typeof(set)) - if set isa MOI.Reals || supports_bridging_constrained_variable(b, typeof(set)) + if set isa MOI.Reals || is_bridged_with_variable_bridge(b, typeof(set)) BridgeType = Variable.concrete_bridge_type(b, typeof(set)) return Variable.add_keys_for_bridge(Variable.bridges(b), () -> Variable.bridge_constrained_variable(BridgeType, b, set), @@ -1159,7 +1172,7 @@ function MOI.add_constrained_variable(b::AbstractBridgeOptimizer, set::MOI.AbstractScalarSet) if is_bridged(b, typeof(set)) || is_bridged(b, MOI.SingleVariable, typeof(set)) - if supports_bridging_constrained_variable(b, typeof(set)) + if is_bridged_with_variable_bridge(b, typeof(set)) BridgeType = Variable.concrete_bridge_type(b, typeof(set)) return Variable.add_key_for_bridge(Variable.bridges(b), () -> Variable.bridge_constrained_variable(BridgeType, b, set), diff --git a/src/Bridges/debug.jl b/src/Bridges/debug.jl index fcac980a32..6ee1df3611 100644 --- a/src/Bridges/debug.jl +++ b/src/Bridges/debug.jl @@ -18,7 +18,7 @@ function print_node_info(io::IO, b::LazyBridgeOptimizer, node::AbstractNode) print(io, " not supported\n") else index = bridge_index(b.graph, node) - if iszero(index) + if iszero(index) || (node isa VariableNode && !is_variable_edge_best(b.graph, node)) @assert node isa VariableNode println(io, " supported (distance $d) by adding free variables and then constrain them, see ($(b.graph.variable_constraint_node[node.index].index)).") else diff --git a/src/Bridges/graph.jl b/src/Bridges/graph.jl index e727bbd801..e2c4db9cb2 100644 --- a/src/Bridges/graph.jl +++ b/src/Bridges/graph.jl @@ -152,6 +152,17 @@ function bridge_index(graph::Graph, node::ObjectiveNode) return graph.objective_best[node.index] end +""" + is_variable_edge_best(graph::Graph, node::VariableNode) + +Return a `Bool` indicating whether the value of `_dist(graph, node)` can be +achieved with a variable bridge. +""" +function is_variable_edge_best(graph::Graph, node::VariableNode) + bellman_ford!(graph) + return graph.variable_dist[node.index] == _dist(graph, node) +end + # Update `b.variable_dist`, `b.constraint_dist` `b.variable_best` and # `b.constraint_best` for constrained variable types in `variables` and # constraint types in `constraints`. @@ -218,9 +229,6 @@ function _dist(graph::Graph, node::VariableNode) # If free variables are bridged but the functionize bridge was not added, # constraint_node is `ConstraintNode(INVALID_NODE_INDEX)`. dc = constraint_node.index == INVALID_NODE_INDEX ? INFINITY : _dist(graph, constraint_node) - if iszero(dc) - return dc - end dv = graph.variable_dist[node.index] if dc == INFINITY return dv diff --git a/src/Bridges/lazy_bridge_optimizer.jl b/src/Bridges/lazy_bridge_optimizer.jl index b59a730c36..2e3e055e2a 100644 --- a/src/Bridges/lazy_bridge_optimizer.jl +++ b/src/Bridges/lazy_bridge_optimizer.jl @@ -308,7 +308,10 @@ function supports_bridging_objective_function( ) return !iszero(bridge_index(b, F)) end - +function is_bridged_with_variable_bridge(b::LazyBridgeOptimizer, + S::Type{<:MOI.AbstractSet}) + return is_variable_edge_best(b.graph, node(b, S)) +end function bridge_type(b::LazyBridgeOptimizer, ::Type{MOI.Reals}) if b.variable_free_bridge_type === nothing throw(MOI.UnsupportedConstraint{MOI.VectorOfVariables, MOI.Reals}()) diff --git a/test/Bridges/lazy_bridge_optimizer.jl b/test/Bridges/lazy_bridge_optimizer.jl index dc60f890d4..07d9824573 100644 --- a/test/Bridges/lazy_bridge_optimizer.jl +++ b/test/Bridges/lazy_bridge_optimizer.jl @@ -689,6 +689,39 @@ function MOI.supports_constraint(::NoVariableModel{T}, ::Type{MOI.SingleVariable ::Type{<:MOIU.SUPPORTED_VARIABLE_SCALAR_SETS{T}}) where T return false end + +@testset "Continuous Conic with NoVariableModel{Float64}" begin + model = NoVariableModel{Float64}() + bridged = MOIB.full_bridge_optimizer(model, Float64) + @show MOIB.bridge_type(bridged, MOI.SecondOrderCone) + @show MOIB.bridge_type(bridged, MOI.RotatedSecondOrderCone) + MOIT.soctest(bridged, MOIT.TestConfig(solve=false)) + MOIT.rsoctest(bridged, MOIT.TestConfig(solve=false)) + @test sprint(MOIB.print_graph, bridged) == """ +Bridge graph with 9 variable nodes, 11 variable nodes and 0 objective nodes. + [1] constrained variables in `MOI.SecondOrderCone` are supported (distance 2) by adding free variables and then constrain them, see (1). + [2] constrained variables in `MOI.RotatedSecondOrderCone` are supported (distance 2) by adding free variables and then constrain them, see (3). + [3] constrained variables in `MOI.PositiveSemidefiniteConeTriangle` are not supported + [4] constrained variables in `MOI.Nonnegatives` are supported (distance 2) by adding free variables and then constrain them, see (6). + [5] constrained variables in `MOI.Zeros` are bridged (distance 1) by MOIB.Variable.ZerosBridge{Float64}. + [6] constrained variables in `MOI.EqualTo{Float64}` are bridged (distance 2) by MOIB.Variable.VectorizeBridge{Float64,MOI.Zeros}. + [7] constrained variables in `MOI.GreaterThan{Float64}` are supported (distance 2) by adding free variables and then constrain them, see (7). + [8] constrained variables in `MOI.LessThan{Float64}` are supported (distance 2) by adding free variables and then constrain them, see (10). + [9] constrained variables in `MOI.Nonpositives` are supported (distance 2) by adding free variables and then constrain them, see (11). + (1) `MOI.VectorOfVariables`-in-`MOI.SecondOrderCone` constraints are bridged (distance 1) by MOIB.Constraint.VectorFunctionizeBridge{Float64,MOI.SecondOrderCone}. + (2) `MOI.VectorAffineFunction{Float64}`-in-`MOI.RotatedSecondOrderCone` constraints are bridged (distance 1) by MOIB.Constraint.RSOCBridge{Float64,MOI.VectorAffineFunction{Float64},MOI.VectorAffineFunction{Float64}}. + (3) `MOI.VectorOfVariables`-in-`MOI.RotatedSecondOrderCone` constraints are bridged (distance 1) by MOIB.Constraint.RSOCBridge{Float64,MOI.VectorAffineFunction{Float64},MOI.VectorOfVariables}. + (4) `MOI.VectorAffineFunction{Float64}`-in-`MOI.PositiveSemidefiniteConeTriangle` constraints are not supported + (5) `MOI.VectorOfVariables`-in-`MOI.PositiveSemidefiniteConeTriangle` constraints are not supported + (6) `MOI.VectorOfVariables`-in-`MOI.Nonnegatives` constraints are bridged (distance 1) by MOIB.Constraint.NonnegToNonposBridge{Float64,MOI.VectorAffineFunction{Float64},MOI.VectorOfVariables}. + (7) `MOI.SingleVariable`-in-`MOI.GreaterThan{Float64}` constraints are bridged (distance 1) by MOIB.Constraint.GreaterToLessBridge{Float64,MOI.ScalarAffineFunction{Float64},MOI.SingleVariable}. + (8) `MOI.SingleVariable`-in-`MOI.EqualTo{Float64}` constraints are bridged (distance 1) by MOIB.Constraint.VectorizeBridge{Float64,MOI.VectorAffineFunction{Float64},MOI.Zeros,MOI.SingleVariable}. + (9) `MOI.VectorOfVariables`-in-`MOI.Zeros` constraints are bridged (distance 1) by MOIB.Constraint.VectorFunctionizeBridge{Float64,MOI.Zeros}. + (10) `MOI.SingleVariable`-in-`MOI.LessThan{Float64}` constraints are bridged (distance 1) by MOIB.Constraint.LessToGreaterBridge{Float64,MOI.ScalarAffineFunction{Float64},MOI.SingleVariable}. + (11) `MOI.VectorOfVariables`-in-`MOI.Nonpositives` constraints are bridged (distance 1) by MOIB.Constraint.NonposToNonnegBridge{Float64,MOI.VectorAffineFunction{Float64},MOI.VectorOfVariables}. +""" +end + MOIU.@model(OnlyNonnegVAF, (), (), (MOI.Nonnegatives,), (), (), (), (), (MOI.VectorAffineFunction,))