From 1b322ba1005aa9588f39ea6dc3365e86e3b54048 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Sat, 29 Feb 2020 21:28:20 -0300 Subject: [PATCH 01/13] fixes in docs --- docs/src/apimanual.md | 4 ++-- src/variables.jl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/apimanual.md b/docs/src/apimanual.md index ee7b3a6f69..e3a586eee2 100644 --- a/docs/src/apimanual.md +++ b/docs/src/apimanual.md @@ -1069,7 +1069,7 @@ appropriate bridges for unsupported constrained variables. A constraint can often be written in a number of equivalent formulations. For example, the constraint ``l \le a^\top x \le u`` (`ScalarAffineFunction`-in-`Interval`) could be re-formulated as two -constraints: ``a^\top x ge l`` (`ScalarAffineFunction`-in-`GreaterThan`) and +constraints: ``a^\top x \ge l`` (`ScalarAffineFunction`-in-`GreaterThan`) and ``a^\top x \le u`` (`ScalarAffineFunction`-in-`LessThan`). An alternative re-formulation is to add a dummy variable `y` with the constraints ``l \le y \le u`` (`SingleVariable`-in-`Interval`) and ``a^\top x - y = 0`` @@ -1127,7 +1127,7 @@ MOI.set(model, MyPackage.PrintLevel(), 0) ### Supported constrained variables and constraints -The solver interface should only implement support for support for variables +The solver interface should only implement support for variables constrained on creation (see [`add_constrained_variable`](@ref)/[`add_constrained_variables`](@ref)) or constraints that directly map to a structure exploited by the solver algorithm. diff --git a/src/variables.jl b/src/variables.jl index c0a5d5010f..763b189785 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -92,7 +92,7 @@ end """ supports_add_constrained_variables( model::ModelLike, - S::Type{<:AbstractScalarSet} + S::Type{<:AbstractVectorSet} )::Bool Return a `Bool` indicating whether `model` supports constraining a vector of From d634515a78a1a95a07b3980027677e55627cc56f Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Sun, 8 Mar 2020 21:01:59 -0300 Subject: [PATCH 02/13] add soc to nonconvex quad bridge --- src/Bridges/Constraint/Constraint.jl | 6 + .../Constraint/soc_to_nonconvex_quad.jl | 191 ++++++++++++++++++ .../Constraint/soc_to_nonconvex_quad.jl | 62 ++++++ 3 files changed, 259 insertions(+) create mode 100644 src/Bridges/Constraint/soc_to_nonconvex_quad.jl create mode 100644 test/Bridges/Constraint/soc_to_nonconvex_quad.jl diff --git a/src/Bridges/Constraint/Constraint.jl b/src/Bridges/Constraint/Constraint.jl index 770a6f2ecf..2e16014890 100644 --- a/src/Bridges/Constraint/Constraint.jl +++ b/src/Bridges/Constraint/Constraint.jl @@ -41,6 +41,9 @@ include("interval.jl") const SplitInterval{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{SplitIntervalBridge{T}, OT} include("quad_to_soc.jl") const QuadtoSOC{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{QuadtoSOCBridge{T}, OT} +include("soc_to_nonconvex_quad.jl") # do not add these bridges by default +const SOCtoNonConvexQuad{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{SOCtoNonConvexQuadBridge{T}, OT} +const RSOCtoNonConvexQuad{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{RSOCtoNonConvexQuadBridge{T}, OT} include("norm_to_lp.jl") const NormInfinity{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormInfinityBridge{T}, OT} const NormOne{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormOneBridge{T}, OT} @@ -82,6 +85,9 @@ function add_all_bridges(bridged_model, ::Type{T}) where {T} MOIB.add_bridge(bridged_model, VectorFunctionizeBridge{T}) MOIB.add_bridge(bridged_model, SplitIntervalBridge{T}) MOIB.add_bridge(bridged_model, QuadtoSOCBridge{T}) + # We do not add `(R)SOCtoNonConvexQuad` because it starts with a convex + # conic constraint and generate a non-convex constraint (in the QCP + # interpretation). MOIB.add_bridge(bridged_model, NormInfinityBridge{T}) MOIB.add_bridge(bridged_model, NormOneBridge{T}) MOIB.add_bridge(bridged_model, GeoMeanBridge{T}) diff --git a/src/Bridges/Constraint/soc_to_nonconvex_quad.jl b/src/Bridges/Constraint/soc_to_nonconvex_quad.jl new file mode 100644 index 0000000000..45aa7078f8 --- /dev/null +++ b/src/Bridges/Constraint/soc_to_nonconvex_quad.jl @@ -0,0 +1,191 @@ +abstract type AbstractSOCtoNonConvexQuadBridge{T} <: AbstractBridge end + +""" + SOCtoNonConvexQuadBridge{T} + +Constraints of the form `VectorOfVariables`-in-`SecondOrderCone` can be +transformed into a `ScalarQuadraticFunction`-in-`LessThan` and a +`ScalarAffineFunction`-in-`GreaterThan`. Indeed, the definition of the +second-order cone +```math +t \\ge || x ||_2 \\ (1) +``` +is equivalent to +```math +\\sum x_i^2 \\le t^2 (2) +``` +with ``t \\ge 0``. (3) + +*WARNING* This transformation starts from a convex constraint (1) and creates a +non-convex constraint (2), because the Q matrix associated with the constraint 2 +has one negative eigenvalue. This might be wrongly interpreted by a solver. +Some solvers can look at (2) and understand that it is a second order cone, but +this is not a general rule. +For these reasons this bridge is not automatically added to the LazyBridgeOptimizer. +Care is recommended when adding this bridge to a optimizer. +""" +struct SOCtoNonConvexQuadBridge{T} <: AbstractSOCtoNonConvexQuadBridge{T} + quad::CI{MOI.ScalarQuadraticFunction{T}, MOI.LessThan{T}} + var_pos::Vector{CI{MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}}} + vars::Vector{MOI.VariableIndex} +end +function bridge_constraint(::Type{SOCtoNonConvexQuadBridge{T}}, model, + func::MOI.VectorOfVariables, + set::MOI.SecondOrderCone) where T + + vis = func.variables + + t = vis[1] + x = vis[2:end] + a_terms = MOI.ScalarAffineTerm{T}[] + q_terms = MOI.ScalarQuadraticTerm{T}[] + push!(q_terms, MOI.ScalarQuadraticTerm(-T(2), t, t)) + for var in x + push!(q_terms, MOI.ScalarQuadraticTerm(T(2), var, var)) + end + + fq = MOI.ScalarQuadraticFunction(a_terms, q_terms, zero(T)) + quad = MOI.add_constraint(model, fq, MOI.LessThan(zero(T))) + fp = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(one(T),t)], zero(T)) + var_pos = MOI.add_constraint(model, fp, MOI.GreaterThan(zero(T))) + + return SOCtoNonConvexQuadBridge(quad, [var_pos], vis) +end + +""" + RSOCtoNonConvexQuadBridge{T} + +Constraints of the form `VectorOfVariables`-in-`SecondOrderCone` can be +transformed into a `ScalarQuadraticFunction`-in-`LessThan` and a +`ScalarAffineFunction`-in-`GreaterThan`. Indeed, the definition of the +second-order cone +```math +2tu \\ge \\lVert x \\rVert_2^2, t,u \\ge 0 (1) +``` +is equivalent to +```math +\\sum x_i^2 \\le 2tu (2) +``` +with ``t,u \\ge 0``. (3) + +*WARNING* This transformation starts from a convex constraint (1) and creates a +non-convex constraint (2), because the Q matrix associated with the constraint 2 +has two negative eigenvalues. This might be wrongly interpreted by a solver. +Some solvers can look at (2) and understand that it is a rotated second order cone, but +this is not a general rule. +For these reasons this bridge is not automatically added to the LazyBridgeOptimizer. +Care is recommended when adding this bridge to a optimizer. +""" +struct RSOCtoNonConvexQuadBridge{T} <: AbstractSOCtoNonConvexQuadBridge{T} + quad::CI{MOI.ScalarQuadraticFunction{T}, MOI.LessThan{T}} + var_pos::Vector{CI{MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}}} + vars::Vector{MOI.VariableIndex} +end +function bridge_constraint(::Type{RSOCtoNonConvexQuadBridge{T}}, model, + func::MOI.VectorOfVariables, + set::MOI.RotatedSecondOrderCone) where T + + vis = func.variables + + t = vis[1] + u = vis[2] + x = vis[3:end] + a_terms = MOI.ScalarAffineTerm{T}[] + q_terms = MOI.ScalarQuadraticTerm{T}[] + push!(q_terms, MOI.ScalarQuadraticTerm(-T(2), t, u)) + for var in x + push!(q_terms, MOI.ScalarQuadraticTerm(T(2), var, var)) + end + + fq = MOI.ScalarQuadraticFunction(a_terms, q_terms, zero(T)) + quad = MOI.add_constraint(model, fq, MOI.LessThan(zero(T))) + fp1 = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(one(T),t)], zero(T)) + var_pos1 = MOI.add_constraint(model, fp1, MOI.GreaterThan(zero(T))) + fp2 = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(one(T),u)], zero(T)) + var_pos2 = MOI.add_constraint(model, fp2, MOI.GreaterThan(zero(T))) + + return RSOCtoNonConvexQuadBridge(quad, [var_pos1, var_pos2], vis) +end + +function MOI.supports_constraint(::Type{SOCtoNonConvexQuadBridge{T}}, + ::Type{MOI.VectorOfVariables}, + ::Type{MOI.SecondOrderCone}) where T + return true +end +function MOI.supports_constraint(::Type{RSOCtoNonConvexQuadBridge{T}}, + ::Type{MOI.VectorOfVariables}, + ::Type{MOI.RotatedSecondOrderCone}) where T + return true +end + +MOIB.added_constrained_variable_types(::Type{<:AbstractSOCtoNonConvexQuadBridge}) = Tuple{DataType}[] +function MOIB.added_constraint_types(::Type{<:AbstractSOCtoNonConvexQuadBridge{T}}) where T + return [ + (MOI.ScalarQuadraticFunction{T}, MOI.LessThan{T}), + (MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}), + ] +end + +function concrete_bridge_type(::Type{SOCtoNonConvexQuadBridge{T}}, + ::Type{MOI.VectorOfVariables}, + ::Type{MOI.SecondOrderCone}) where T + return SOCtoNonConvexQuadBridge{T} +end +function concrete_bridge_type(::Type{RSOCtoNonConvexQuadBridge{T}}, + ::Type{MOI.VectorOfVariables}, + ::Type{MOI.RotatedSecondOrderCone}) where T + return RSOCtoNonConvexQuadBridge{T} +end + +# Attributes, Bridge acting as a model +function MOI.get(::AbstractSOCtoNonConvexQuadBridge{T}, + ::MOI.NumberOfConstraints{MOI.ScalarQuadraticFunction{T}, + MOI.LessThan{T}}) where T + return 1 +end + +function MOI.get(bridge::AbstractSOCtoNonConvexQuadBridge{T}, + ::MOI.ListOfConstraintIndices{ + MOI.ScalarQuadraticFunction{T}, + MOI.LessThan{T}}) where T + return [bridge.quad] +end + +function MOI.get(bridge::AbstractSOCtoNonConvexQuadBridge{T}, + ::MOI.NumberOfConstraints{MOI.ScalarAffineFunction{T}, + MOI.GreaterThan{T}}) where T + return length(bridge.var_pos) +end + +function MOI.get(bridge::AbstractSOCtoNonConvexQuadBridge{T}, + ::MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{T}, + MOI.GreaterThan{T}}) where T + return bridge.var_pos +end + +# References +function MOI.delete(model::MOI.ModelLike, bridge::AbstractSOCtoNonConvexQuadBridge) + MOI.delete(model, bridge.quad) + MOI.delete.(model, bridge.var_pos) +end + +# Attributes, Bridge acting as a constraint +function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintPrimal, + bridge::AbstractSOCtoNonConvexQuadBridge) + vals = MOI.get.(model, MOI.VariablePrimal(attr.N), bridge.vars) + return vals +end + +function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintSet, + b::SOCtoNonConvexQuadBridge{T}) where T + return MOI.SecondOrderCone(length(b.vars)) +end +function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintSet, + b::RSOCtoNonConvexQuadBridge{T}) where T + return MOI.RotatedSecondOrderCone(length(b.vars)) +end + +function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintFunction, + b::AbstractSOCtoNonConvexQuadBridge{T}) where T + return MOI.VectorOfVariables(b.vars) +end diff --git a/test/Bridges/Constraint/soc_to_nonconvex_quad.jl b/test/Bridges/Constraint/soc_to_nonconvex_quad.jl new file mode 100644 index 0000000000..95e776f6f7 --- /dev/null +++ b/test/Bridges/Constraint/soc_to_nonconvex_quad.jl @@ -0,0 +1,62 @@ +using Test + +using MathOptInterface +const MOI = MathOptInterface +const MOIT = MathOptInterface.Test +const MOIU = MathOptInterface.Utilities +const MOIB = MathOptInterface.Bridges + +include("../utilities.jl") + +mock = MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())) +config = MOIT.TestConfig(duals = false) + + +@testset "RSOCtoNonConvexQuad" begin + bridged_mock = MOIB.Constraint.RSOCtoNonConvexQuad{Float64}(mock) + + MOIT.basic_constraint_tests( + bridged_mock, config, + include = [(F, S) + for F in [MOI.VectorOfVariables] + for S in [MOI.RotatedSecondOrderCone]]) + + mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0.5, 1.0, 1/√2, 1/√2]) + MOIT.rotatedsoc1vtest(bridged_mock, config) + + ci = first(MOI.get(bridged_mock, + MOI.ListOfConstraintIndices{MOI.VectorOfVariables, + MOI.RotatedSecondOrderCone}())) + + test_delete_bridge(bridged_mock, ci, 4, + ( + (MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}, 0), + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, 0), + )) +end + + +@testset "SOCtoNonConvexQuad" begin + + println("rolling") + bridged_mock = MOIB.Constraint.SOCtoNonConvexQuad{Float64}(mock) + + MOIT.basic_constraint_tests( + bridged_mock, config, + include = [(F, S) + for F in [MOI.VectorOfVariables] + for S in [MOI.SecondOrderCone]]) + + mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 1/√2, 1/√2]) + MOIT.soc1vtest(bridged_mock, config) + + ci = first(MOI.get(bridged_mock, + MOI.ListOfConstraintIndices{MOI.VectorOfVariables, + MOI.SecondOrderCone}())) + + test_delete_bridge(bridged_mock, ci, 3, + ( + (MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}, 0), + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, 0), + )) +end From 00c2f7c99d3c98d9947c016870766b426e42a055 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Mon, 9 Mar 2020 01:10:26 -0300 Subject: [PATCH 03/13] fix coverage --- .../Constraint/soc_to_nonconvex_quad.jl | 4 +-- .../Constraint/soc_to_nonconvex_quad.jl | 27 ++++++++++++++++--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/Bridges/Constraint/soc_to_nonconvex_quad.jl b/src/Bridges/Constraint/soc_to_nonconvex_quad.jl index 45aa7078f8..8ba142f35b 100644 --- a/src/Bridges/Constraint/soc_to_nonconvex_quad.jl +++ b/src/Bridges/Constraint/soc_to_nonconvex_quad.jl @@ -108,8 +108,8 @@ function bridge_constraint(::Type{RSOCtoNonConvexQuadBridge{T}}, model, end function MOI.supports_constraint(::Type{SOCtoNonConvexQuadBridge{T}}, - ::Type{MOI.VectorOfVariables}, - ::Type{MOI.SecondOrderCone}) where T + ::Type{MOI.VectorOfVariables}, + ::Type{MOI.SecondOrderCone}) where T return true end function MOI.supports_constraint(::Type{RSOCtoNonConvexQuadBridge{T}}, diff --git a/test/Bridges/Constraint/soc_to_nonconvex_quad.jl b/test/Bridges/Constraint/soc_to_nonconvex_quad.jl index 95e776f6f7..483cd3b577 100644 --- a/test/Bridges/Constraint/soc_to_nonconvex_quad.jl +++ b/test/Bridges/Constraint/soc_to_nonconvex_quad.jl @@ -5,14 +5,26 @@ const MOI = MathOptInterface const MOIT = MathOptInterface.Test const MOIU = MathOptInterface.Utilities const MOIB = MathOptInterface.Bridges +const MOIBC = MathOptInterface.Bridges.Constraint include("../utilities.jl") mock = MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}())) config = MOIT.TestConfig(duals = false) - @testset "RSOCtoNonConvexQuad" begin + + @test MOIBC.RSOCtoNonConvexQuadBridge{Float64} == MOIBC.concrete_bridge_type( + MOIBC.RSOCtoNonConvexQuadBridge{Float64}, + MOI.VectorOfVariables, + MOI.RotatedSecondOrderCone) + @test MOI.supports_constraint(MOIBC.RSOCtoNonConvexQuadBridge{Float64}, + MOI.VectorOfVariables, + MOI.RotatedSecondOrderCone) + @test !MOI.supports_constraint(MOIBC.RSOCtoNonConvexQuadBridge{Float64}, + MOI.ScalarAffineFunction{Float64}, + MOI.RotatedSecondOrderCone) + bridged_mock = MOIB.Constraint.RSOCtoNonConvexQuad{Float64}(mock) MOIT.basic_constraint_tests( @@ -35,10 +47,19 @@ config = MOIT.TestConfig(duals = false) )) end - @testset "SOCtoNonConvexQuad" begin - println("rolling") + @test MOIBC.SOCtoNonConvexQuadBridge{Float64} == MOIBC.concrete_bridge_type( + MOIBC.SOCtoNonConvexQuadBridge{Float64}, + MOI.VectorOfVariables, + MOI.SecondOrderCone) + @test MOI.supports_constraint(MOIBC.SOCtoNonConvexQuadBridge{Float64}, + MOI.VectorOfVariables, + MOI.SecondOrderCone) + @test !MOI.supports_constraint(MOIBC.SOCtoNonConvexQuadBridge{Float64}, + MOI.ScalarAffineFunction{Float64}, + MOI.SecondOrderCone) + bridged_mock = MOIB.Constraint.SOCtoNonConvexQuad{Float64}(mock) MOIT.basic_constraint_tests( From e6c0bb8b83b77837438c14ff7a8261248b1aed9f Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Mon, 9 Mar 2020 11:04:38 -0300 Subject: [PATCH 04/13] fix style spacing --- src/Bridges/Constraint/soc_to_nonconvex_quad.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Bridges/Constraint/soc_to_nonconvex_quad.jl b/src/Bridges/Constraint/soc_to_nonconvex_quad.jl index 8ba142f35b..5eeba8ed7e 100644 --- a/src/Bridges/Constraint/soc_to_nonconvex_quad.jl +++ b/src/Bridges/Constraint/soc_to_nonconvex_quad.jl @@ -46,7 +46,7 @@ function bridge_constraint(::Type{SOCtoNonConvexQuadBridge{T}}, model, fq = MOI.ScalarQuadraticFunction(a_terms, q_terms, zero(T)) quad = MOI.add_constraint(model, fq, MOI.LessThan(zero(T))) - fp = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(one(T),t)], zero(T)) + fp = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(one(T), t)], zero(T)) var_pos = MOI.add_constraint(model, fp, MOI.GreaterThan(zero(T))) return SOCtoNonConvexQuadBridge(quad, [var_pos], vis) @@ -99,9 +99,9 @@ function bridge_constraint(::Type{RSOCtoNonConvexQuadBridge{T}}, model, fq = MOI.ScalarQuadraticFunction(a_terms, q_terms, zero(T)) quad = MOI.add_constraint(model, fq, MOI.LessThan(zero(T))) - fp1 = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(one(T),t)], zero(T)) + fp1 = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(one(T), t)], zero(T)) var_pos1 = MOI.add_constraint(model, fp1, MOI.GreaterThan(zero(T))) - fp2 = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(one(T),u)], zero(T)) + fp2 = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(one(T), u)], zero(T)) var_pos2 = MOI.add_constraint(model, fp2, MOI.GreaterThan(zero(T))) return RSOCtoNonConvexQuadBridge(quad, [var_pos1, var_pos2], vis) From 2cc53577b5804e316529c48bf34a33c3245a93c0 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Sun, 31 May 2020 19:56:16 -0300 Subject: [PATCH 05/13] improve docs --- docs/src/apireference.md | 20 +++++++++++++++++++ .../Constraint/soc_to_nonconvex_quad.jl | 8 ++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/docs/src/apireference.md b/docs/src/apireference.md index e48e2e1e6d..9c01dd1b2d 100644 --- a/docs/src/apireference.md +++ b/docs/src/apireference.md @@ -555,6 +555,26 @@ constraints of different types. There are two important concepts to distinguish: allow introspection into the bridge selection rationale of [`Bridges.LazyBridgeOptimizer`](@ref). +Most bridges are added by default in [`Bridges.full_bridge_optimizer`](@ref). +However, for technical reasons, some bridges are not added by default, for instance: +[`Bridges.Constraint.SOCtoPSDBridge`](@ref), [`Bridges.Constraint.SOCtoNonConvexQuadBridge`](@ref) +and [`Bridges.Constraint.RSOCtoNonConvexQuadBridge`](@ref). See the docs of those bridges +for more information. + +It is possible to add those bridges and also user defined bridges, following these steps. We present an example for: [`Bridges.Constraint.SOCtoNonConvexQuadBridge`](@ref). + +First, define a single bridge optimizer: + +```julia +const SOCtoNonConvexQuad{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{SOCtoNonConvexQuadBridge{T}, OT} +``` + +Second, add that bridge to a `bridged_model` optimizer, with coefficient type `T,` using [`Bridges.add_bridge`](@ref): + +```julia +MOIB.add_bridge(bridged_model, SOCtoNonConvexQuad{T}) +``` + ```@docs Bridges.AbstractBridge Bridges.AbstractBridgeOptimizer diff --git a/src/Bridges/Constraint/soc_to_nonconvex_quad.jl b/src/Bridges/Constraint/soc_to_nonconvex_quad.jl index 5eeba8ed7e..a7309d6bdb 100644 --- a/src/Bridges/Constraint/soc_to_nonconvex_quad.jl +++ b/src/Bridges/Constraint/soc_to_nonconvex_quad.jl @@ -8,13 +8,13 @@ transformed into a `ScalarQuadraticFunction`-in-`LessThan` and a `ScalarAffineFunction`-in-`GreaterThan`. Indeed, the definition of the second-order cone ```math -t \\ge || x ||_2 \\ (1) +t \\ge \\lVert x \\lVert_2 \\ (1) ``` is equivalent to ```math \\sum x_i^2 \\le t^2 (2) ``` -with ``t \\ge 0``. (3) +with ``t \\ge 0``. (3) *WARNING* This transformation starts from a convex constraint (1) and creates a non-convex constraint (2), because the Q matrix associated with the constraint 2 @@ -73,8 +73,8 @@ non-convex constraint (2), because the Q matrix associated with the constraint 2 has two negative eigenvalues. This might be wrongly interpreted by a solver. Some solvers can look at (2) and understand that it is a rotated second order cone, but this is not a general rule. -For these reasons this bridge is not automatically added to the LazyBridgeOptimizer. -Care is recommended when adding this bridge to a optimizer. +For these reasons, this bridge is not automatically added to the LazyBridgeOptimizer. +Care is recommended when adding this bridge to an optimizer. """ struct RSOCtoNonConvexQuadBridge{T} <: AbstractSOCtoNonConvexQuadBridge{T} quad::CI{MOI.ScalarQuadraticFunction{T}, MOI.LessThan{T}} From 6d902ddc9a8c591910e8ff4b1d397b2e393a7ede Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Sun, 31 May 2020 20:04:12 -0300 Subject: [PATCH 06/13] fix norm in docs --- src/Bridges/Constraint/soc_to_nonconvex_quad.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bridges/Constraint/soc_to_nonconvex_quad.jl b/src/Bridges/Constraint/soc_to_nonconvex_quad.jl index a7309d6bdb..a2f13596f0 100644 --- a/src/Bridges/Constraint/soc_to_nonconvex_quad.jl +++ b/src/Bridges/Constraint/soc_to_nonconvex_quad.jl @@ -8,7 +8,7 @@ transformed into a `ScalarQuadraticFunction`-in-`LessThan` and a `ScalarAffineFunction`-in-`GreaterThan`. Indeed, the definition of the second-order cone ```math -t \\ge \\lVert x \\lVert_2 \\ (1) +t \\ge \\lVert x \\rVert_2 \\ (1) ``` is equivalent to ```math From b820ca51727486ea4d2258048db9249e2d4a796a Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Sun, 31 May 2020 21:34:21 -0300 Subject: [PATCH 07/13] add new bridges to reference --- docs/src/apireference.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/src/apireference.md b/docs/src/apireference.md index 9c01dd1b2d..2851452fc9 100644 --- a/docs/src/apireference.md +++ b/docs/src/apireference.md @@ -789,6 +789,8 @@ Bridges.Constraint.SplitIntervalBridge Bridges.Constraint.RSOCBridge Bridges.Constraint.SOCRBridge Bridges.Constraint.QuadtoSOCBridge +Bridges.Constraint.SOCtoNonConvexQuadBridge +Bridges.Constraint.RSOCtoNonConvexQuadBridge Bridges.Constraint.NormInfinityBridge Bridges.Constraint.NormOneBridge Bridges.Constraint.GeoMeanBridge From ebd47b4f6240c5f0f4c38276ca439e4c26c0f3fb Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Tue, 2 Jun 2020 16:30:47 -0300 Subject: [PATCH 08/13] add suggestions --- docs/src/apireference.md | 13 ++++++------- src/Bridges/Constraint/soc_to_nonconvex_quad.jl | 14 +++++++++----- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/docs/src/apireference.md b/docs/src/apireference.md index 2851452fc9..0f05d00c04 100644 --- a/docs/src/apireference.md +++ b/docs/src/apireference.md @@ -561,17 +561,16 @@ However, for technical reasons, some bridges are not added by default, for insta and [`Bridges.Constraint.RSOCtoNonConvexQuadBridge`](@ref). See the docs of those bridges for more information. -It is possible to add those bridges and also user defined bridges, following these steps. We present an example for: [`Bridges.Constraint.SOCtoNonConvexQuadBridge`](@ref). +It is possible to add those bridges and also user defined bridges, +following these steps. We present an example for: +[`Bridges.Constraint.SOCtoNonConvexQuadBridge`](@ref). -First, define a single bridge optimizer: +First, define a single bridge optimizer, then add that bridge to a +`bridged_model` optimizer, with coefficient type `T,` using +[`Bridges.add_bridge`](@ref): ```julia const SOCtoNonConvexQuad{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{SOCtoNonConvexQuadBridge{T}, OT} -``` - -Second, add that bridge to a `bridged_model` optimizer, with coefficient type `T,` using [`Bridges.add_bridge`](@ref): - -```julia MOIB.add_bridge(bridged_model, SOCtoNonConvexQuad{T}) ``` diff --git a/src/Bridges/Constraint/soc_to_nonconvex_quad.jl b/src/Bridges/Constraint/soc_to_nonconvex_quad.jl index a2f13596f0..c93b8cd5a0 100644 --- a/src/Bridges/Constraint/soc_to_nonconvex_quad.jl +++ b/src/Bridges/Constraint/soc_to_nonconvex_quad.jl @@ -21,7 +21,7 @@ non-convex constraint (2), because the Q matrix associated with the constraint 2 has one negative eigenvalue. This might be wrongly interpreted by a solver. Some solvers can look at (2) and understand that it is a second order cone, but this is not a general rule. -For these reasons this bridge is not automatically added to the LazyBridgeOptimizer. +For these reasons this bridge is not automatically added by full_bridge_optimizer. Care is recommended when adding this bridge to a optimizer. """ struct SOCtoNonConvexQuadBridge{T} <: AbstractSOCtoNonConvexQuadBridge{T} @@ -46,7 +46,9 @@ function bridge_constraint(::Type{SOCtoNonConvexQuadBridge{T}}, model, fq = MOI.ScalarQuadraticFunction(a_terms, q_terms, zero(T)) quad = MOI.add_constraint(model, fq, MOI.LessThan(zero(T))) - fp = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(one(T), t)], zero(T)) + # ScalarAffineFunction's are added instead of SingleVariable's + # because models can only have one SingleVariable per variable + fp = convert(MOI.ScalarAffineFunction{T}, MOI.SingleVariable(t)) var_pos = MOI.add_constraint(model, fp, MOI.GreaterThan(zero(T))) return SOCtoNonConvexQuadBridge(quad, [var_pos], vis) @@ -73,7 +75,7 @@ non-convex constraint (2), because the Q matrix associated with the constraint 2 has two negative eigenvalues. This might be wrongly interpreted by a solver. Some solvers can look at (2) and understand that it is a rotated second order cone, but this is not a general rule. -For these reasons, this bridge is not automatically added to the LazyBridgeOptimizer. +For these reasons, this bridge is not automatically added by full_bridge_optimizer. Care is recommended when adding this bridge to an optimizer. """ struct RSOCtoNonConvexQuadBridge{T} <: AbstractSOCtoNonConvexQuadBridge{T} @@ -99,9 +101,11 @@ function bridge_constraint(::Type{RSOCtoNonConvexQuadBridge{T}}, model, fq = MOI.ScalarQuadraticFunction(a_terms, q_terms, zero(T)) quad = MOI.add_constraint(model, fq, MOI.LessThan(zero(T))) - fp1 = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(one(T), t)], zero(T)) + # ScalarAffineFunction's are added instead of SingleVariable's + # because models can only have one SingleVariable per variable + fp1 = convert(MOI.ScalarAffineFunction{T}, MOI.SingleVariable(t)) var_pos1 = MOI.add_constraint(model, fp1, MOI.GreaterThan(zero(T))) - fp2 = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(one(T), u)], zero(T)) + fp2 = convert(MOI.ScalarAffineFunction{T}, MOI.SingleVariable(u)) var_pos2 = MOI.add_constraint(model, fp2, MOI.GreaterThan(zero(T))) return RSOCtoNonConvexQuadBridge(quad, [var_pos1, var_pos2], vis) From b1172d8c0923381431c5a3bbc100ba6647684a76 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Sun, 7 Jun 2020 22:16:41 -0300 Subject: [PATCH 09/13] docs improvements --- docs/src/apireference.md | 25 +++++++++++++++---- .../Constraint/soc_to_nonconvex_quad.jl | 12 ++++++--- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/docs/src/apireference.md b/docs/src/apireference.md index 0f05d00c04..7e03aa7ef1 100644 --- a/docs/src/apireference.md +++ b/docs/src/apireference.md @@ -562,22 +562,37 @@ and [`Bridges.Constraint.RSOCtoNonConvexQuadBridge`](@ref). See the docs of thos for more information. It is possible to add those bridges and also user defined bridges, -following these steps. We present an example for: +following one of the two methods. We present the examples for: [`Bridges.Constraint.SOCtoNonConvexQuadBridge`](@ref). -First, define a single bridge optimizer, then add that bridge to a -`bridged_model` optimizer, with coefficient type `T,` using +The first option is to add the specific bridges to a +`bridged_model` optimizer, with coefficient type `T`, using [`Bridges.add_bridge`](@ref): ```julia -const SOCtoNonConvexQuad{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{SOCtoNonConvexQuadBridge{T}, OT} -MOIB.add_bridge(bridged_model, SOCtoNonConvexQuad{T}) +MOIB.add_bridge(bridged_model, SOCtoNonConvexQuadBridge{T}) +``` + +Alternatively, it is possible to create a [`Bridges.SingleBridgeOptimizer`](@ref) +and wrap an existing `model` with it: + +```julia +const SOCtoNonConvexQuad{T, OT<:ModelLike} = Bridges.SingleBridgeOptimizer{Bridges.SOCtoNonConvexQuadBridge{T}, OT} +bridged_model = SOCtoNonConvexQuad{Float64}(model) +``` + +Those procedures could be applied to user define bridges. For the +bridges defined in MathOptInterface, the [`Bridges.SingleBridgeOptimizer`](@ref)'s are already created, therefore, for the case of [`Bridges.Constraint.SOCtoNonConvexQuadBridge`](@ref), one could simply use the existing optimizer: + +```julia +bridged_model = Bridges.Constraint.SOCtoNonConvexQuad{Float64}(model) ``` ```@docs Bridges.AbstractBridge Bridges.AbstractBridgeOptimizer Bridges.LazyBridgeOptimizer +Bridges.SingleBridgeOptimizer Bridges.add_bridge Bridges.remove_bridge Bridges.has_bridge diff --git a/src/Bridges/Constraint/soc_to_nonconvex_quad.jl b/src/Bridges/Constraint/soc_to_nonconvex_quad.jl index c93b8cd5a0..f22566aad5 100644 --- a/src/Bridges/Constraint/soc_to_nonconvex_quad.jl +++ b/src/Bridges/Constraint/soc_to_nonconvex_quad.jl @@ -21,7 +21,7 @@ non-convex constraint (2), because the Q matrix associated with the constraint 2 has one negative eigenvalue. This might be wrongly interpreted by a solver. Some solvers can look at (2) and understand that it is a second order cone, but this is not a general rule. -For these reasons this bridge is not automatically added by full_bridge_optimizer. +For these reasons this bridge is not automatically added by [`full_bridge_optimizer`](@ref). Care is recommended when adding this bridge to a optimizer. """ struct SOCtoNonConvexQuadBridge{T} <: AbstractSOCtoNonConvexQuadBridge{T} @@ -47,7 +47,9 @@ function bridge_constraint(::Type{SOCtoNonConvexQuadBridge{T}}, model, fq = MOI.ScalarQuadraticFunction(a_terms, q_terms, zero(T)) quad = MOI.add_constraint(model, fq, MOI.LessThan(zero(T))) # ScalarAffineFunction's are added instead of SingleVariable's - # because models can only have one SingleVariable per variable + # because models can only have one SingleVariable per variable. + # Hence, adding a SingleVariable constraint here could conflict with + # a user defined SingleVariable fp = convert(MOI.ScalarAffineFunction{T}, MOI.SingleVariable(t)) var_pos = MOI.add_constraint(model, fp, MOI.GreaterThan(zero(T))) @@ -75,7 +77,7 @@ non-convex constraint (2), because the Q matrix associated with the constraint 2 has two negative eigenvalues. This might be wrongly interpreted by a solver. Some solvers can look at (2) and understand that it is a rotated second order cone, but this is not a general rule. -For these reasons, this bridge is not automatically added by full_bridge_optimizer. +For these reasons, this bridge is not automatically added by [`full_bridge_optimizer`](@ref). Care is recommended when adding this bridge to an optimizer. """ struct RSOCtoNonConvexQuadBridge{T} <: AbstractSOCtoNonConvexQuadBridge{T} @@ -102,7 +104,9 @@ function bridge_constraint(::Type{RSOCtoNonConvexQuadBridge{T}}, model, fq = MOI.ScalarQuadraticFunction(a_terms, q_terms, zero(T)) quad = MOI.add_constraint(model, fq, MOI.LessThan(zero(T))) # ScalarAffineFunction's are added instead of SingleVariable's - # because models can only have one SingleVariable per variable + # because models can only have one SingleVariable per variable. + # Hence, adding a SingleVariable constraint here could conflict with + # a user defined SingleVariable fp1 = convert(MOI.ScalarAffineFunction{T}, MOI.SingleVariable(t)) var_pos1 = MOI.add_constraint(model, fp1, MOI.GreaterThan(zero(T))) fp2 = convert(MOI.ScalarAffineFunction{T}, MOI.SingleVariable(u)) From 959c5b496cabfece613ae8fcb26886e1a5179e12 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Mon, 8 Jun 2020 02:24:58 -0300 Subject: [PATCH 10/13] add module name --- docs/src/apireference.md | 6 +++--- src/Bridges/Constraint/soc_to_nonconvex_quad.jl | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/src/apireference.md b/docs/src/apireference.md index 7e03aa7ef1..2f06869fcd 100644 --- a/docs/src/apireference.md +++ b/docs/src/apireference.md @@ -573,16 +573,16 @@ The first option is to add the specific bridges to a MOIB.add_bridge(bridged_model, SOCtoNonConvexQuadBridge{T}) ``` -Alternatively, it is possible to create a [`Bridges.SingleBridgeOptimizer`](@ref) +Alternatively, it is possible to create a [`Bridges.Constraint.SingleBridgeOptimizer`](@ref) and wrap an existing `model` with it: ```julia -const SOCtoNonConvexQuad{T, OT<:ModelLike} = Bridges.SingleBridgeOptimizer{Bridges.SOCtoNonConvexQuadBridge{T}, OT} +const SOCtoNonConvexQuad{T, OT<:ModelLike} = Bridges.Constraint.SingleBridgeOptimizer{Bridges.Constraint.SOCtoNonConvexQuadBridge{T}, OT} bridged_model = SOCtoNonConvexQuad{Float64}(model) ``` Those procedures could be applied to user define bridges. For the -bridges defined in MathOptInterface, the [`Bridges.SingleBridgeOptimizer`](@ref)'s are already created, therefore, for the case of [`Bridges.Constraint.SOCtoNonConvexQuadBridge`](@ref), one could simply use the existing optimizer: +bridges defined in MathOptInterface, the [`Bridges.Constraint.SingleBridgeOptimizer`](@ref)'s are already created, therefore, for the case of [`Bridges.Constraint.SOCtoNonConvexQuadBridge`](@ref), one could simply use the existing optimizer: ```julia bridged_model = Bridges.Constraint.SOCtoNonConvexQuad{Float64}(model) diff --git a/src/Bridges/Constraint/soc_to_nonconvex_quad.jl b/src/Bridges/Constraint/soc_to_nonconvex_quad.jl index f22566aad5..6dcbb5093c 100644 --- a/src/Bridges/Constraint/soc_to_nonconvex_quad.jl +++ b/src/Bridges/Constraint/soc_to_nonconvex_quad.jl @@ -21,7 +21,7 @@ non-convex constraint (2), because the Q matrix associated with the constraint 2 has one negative eigenvalue. This might be wrongly interpreted by a solver. Some solvers can look at (2) and understand that it is a second order cone, but this is not a general rule. -For these reasons this bridge is not automatically added by [`full_bridge_optimizer`](@ref). +For these reasons this bridge is not automatically added by [`Bridges.full_bridge_optimizer`](@ref). Care is recommended when adding this bridge to a optimizer. """ struct SOCtoNonConvexQuadBridge{T} <: AbstractSOCtoNonConvexQuadBridge{T} @@ -77,7 +77,7 @@ non-convex constraint (2), because the Q matrix associated with the constraint 2 has two negative eigenvalues. This might be wrongly interpreted by a solver. Some solvers can look at (2) and understand that it is a rotated second order cone, but this is not a general rule. -For these reasons, this bridge is not automatically added by [`full_bridge_optimizer`](@ref). +For these reasons, this bridge is not automatically added by [`Bridges.full_bridge_optimizer`](@ref). Care is recommended when adding this bridge to an optimizer. """ struct RSOCtoNonConvexQuadBridge{T} <: AbstractSOCtoNonConvexQuadBridge{T} From 569d95d7223f3878d93e1fadeee0982399449557 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Mon, 8 Jun 2020 11:17:36 -0300 Subject: [PATCH 11/13] Add more doc fixes --- docs/src/apireference.md | 6 ++++-- src/Bridges/Constraint/soc_to_nonconvex_quad.jl | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/src/apireference.md b/docs/src/apireference.md index 2f06869fcd..c05467b280 100644 --- a/docs/src/apireference.md +++ b/docs/src/apireference.md @@ -566,8 +566,10 @@ following one of the two methods. We present the examples for: [`Bridges.Constraint.SOCtoNonConvexQuadBridge`](@ref). The first option is to add the specific bridges to a -`bridged_model` optimizer, with coefficient type `T`, using -[`Bridges.add_bridge`](@ref): +`bridged_model` optimizer, with coefficient type `T`. The `bridged_model` +optimizer itself must have been constructed with a +[`Bridges.LazyBridgeOptimizer`](@ref). Once such a optimizer is available, we +can proceed using using [`Bridges.add_bridge`](@ref): ```julia MOIB.add_bridge(bridged_model, SOCtoNonConvexQuadBridge{T}) diff --git a/src/Bridges/Constraint/soc_to_nonconvex_quad.jl b/src/Bridges/Constraint/soc_to_nonconvex_quad.jl index 6dcbb5093c..7914a91ea0 100644 --- a/src/Bridges/Constraint/soc_to_nonconvex_quad.jl +++ b/src/Bridges/Constraint/soc_to_nonconvex_quad.jl @@ -77,7 +77,7 @@ non-convex constraint (2), because the Q matrix associated with the constraint 2 has two negative eigenvalues. This might be wrongly interpreted by a solver. Some solvers can look at (2) and understand that it is a rotated second order cone, but this is not a general rule. -For these reasons, this bridge is not automatically added by [`Bridges.full_bridge_optimizer`](@ref). +For these reasons, this bridge is not automatically added by [`MOI.Bridges.full_bridge_optimizer`](@ref). Care is recommended when adding this bridge to an optimizer. """ struct RSOCtoNonConvexQuadBridge{T} <: AbstractSOCtoNonConvexQuadBridge{T} From 57e89f43a673a3990f4ad0a509c455c1598c3071 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Mon, 8 Jun 2020 13:20:28 -0300 Subject: [PATCH 12/13] add missing MOI. --- src/Bridges/Constraint/soc_to_nonconvex_quad.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bridges/Constraint/soc_to_nonconvex_quad.jl b/src/Bridges/Constraint/soc_to_nonconvex_quad.jl index 7914a91ea0..6f6c143f1b 100644 --- a/src/Bridges/Constraint/soc_to_nonconvex_quad.jl +++ b/src/Bridges/Constraint/soc_to_nonconvex_quad.jl @@ -21,7 +21,7 @@ non-convex constraint (2), because the Q matrix associated with the constraint 2 has one negative eigenvalue. This might be wrongly interpreted by a solver. Some solvers can look at (2) and understand that it is a second order cone, but this is not a general rule. -For these reasons this bridge is not automatically added by [`Bridges.full_bridge_optimizer`](@ref). +For these reasons this bridge is not automatically added by [`MOI.Bridges.full_bridge_optimizer`](@ref). Care is recommended when adding this bridge to a optimizer. """ struct SOCtoNonConvexQuadBridge{T} <: AbstractSOCtoNonConvexQuadBridge{T} From d36c202731e4d2912e18813599ff0fa477254b73 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Mon, 8 Jun 2020 14:21:11 -0300 Subject: [PATCH 13/13] remove wrong SBO --- docs/src/apireference.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/src/apireference.md b/docs/src/apireference.md index c05467b280..62088f67d6 100644 --- a/docs/src/apireference.md +++ b/docs/src/apireference.md @@ -594,7 +594,6 @@ bridged_model = Bridges.Constraint.SOCtoNonConvexQuad{Float64}(model) Bridges.AbstractBridge Bridges.AbstractBridgeOptimizer Bridges.LazyBridgeOptimizer -Bridges.SingleBridgeOptimizer Bridges.add_bridge Bridges.remove_bridge Bridges.has_bridge