Skip to content

Commit

Permalink
Only use variable bridge if it is optimal
Browse files Browse the repository at this point in the history
  • Loading branch information
blegat committed Dec 18, 2019
1 parent 762fcf4 commit a1f31ab
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 7 deletions.
4 changes: 4 additions & 0 deletions src/Bridges/Constraint/single_bridge_optimizer.jl
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions src/Bridges/Objective/single_bridge_optimizer.jl
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions src/Bridges/Variable/single_bridge_optimizer.jl
Expand Up @@ -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
Expand Down
17 changes: 15 additions & 2 deletions src/Bridges/bridge_optimizer.jl
Expand Up @@ -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,
Expand Down Expand Up @@ -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),
Expand All @@ -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),
Expand Down
2 changes: 1 addition & 1 deletion src/Bridges/debug.jl
Expand Up @@ -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
Expand Down
14 changes: 11 additions & 3 deletions src/Bridges/graph.jl
Expand Up @@ -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`.
Expand Down Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion src/Bridges/lazy_bridge_optimizer.jl
Expand Up @@ -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}())
Expand Down
33 changes: 33 additions & 0 deletions test/Bridges/lazy_bridge_optimizer.jl
Expand Up @@ -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,))
Expand Down

0 comments on commit a1f31ab

Please sign in to comment.