From 01c7bf39a3b78d94fd5a1a67065f72778e8c2e76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 23 Aug 2018 17:45:39 +0200 Subject: [PATCH 01/12] Add QuadtoSOC bridge --- src/Bridges/Bridges.jl | 3 + src/Bridges/quadtosocbridge.jl | 179 +++++++++++++++++++++++++++++++++ test/bridge.jl | 17 ++++ 3 files changed, 199 insertions(+) create mode 100644 src/Bridges/quadtosocbridge.jl diff --git a/src/Bridges/Bridges.jl b/src/Bridges/Bridges.jl index 83900237b7..2e21fe5a33 100644 --- a/src/Bridges/Bridges.jl +++ b/src/Bridges/Bridges.jl @@ -43,6 +43,7 @@ Returns a `LazyBridgeOptimizer` bridging `model` for every bridge defined in thi function fullbridgeoptimizer(model::MOI.ModelLike, ::Type{T}) where T bridgedmodel = MOIB.LazyBridgeOptimizer(model, AllBridgedConstraints{T}()) add_bridge(bridgedmodel, MOIB.SplitIntervalBridge{T}) + add_bridge(bridgedmodel, MOIB.QuadtoSOCBridge{T}) add_bridge(bridgedmodel, MOIB.GeoMeanBridge{T}) add_bridge(bridgedmodel, MOIB.SquarePSDBridge{T}) add_bridge(bridgedmodel, MOIB.LogDetBridge{T}) @@ -57,6 +58,8 @@ include("intervalbridge.jl") @bridge SplitInterval SplitIntervalBridge () (MOI.Interval,) () () (MOI.SingleVariable,) (MOI.ScalarAffineFunction, MOI.ScalarQuadraticFunction) () () include("rsocbridge.jl") @bridge RSOC RSOCBridge () () (MOI.RotatedSecondOrderCone,) () () () (MOI.VectorOfVariables,) (MOI.VectorAffineFunction,) +include("quadtosocbridge.jl") +@bridge QuadtoSOC QuadtoSOCBridge () (MOI.LessThan, MOI.GreaterThan) () () () (MOI.ScalarQuadraticFunction,) () () include("geomeanbridge.jl") @bridge GeoMean GeoMeanBridge () () (MOI.GeometricMeanCone,) () () () (MOI.VectorOfVariables,) (MOI.VectorAffineFunction,) include("squarepsdbridge.jl") diff --git a/src/Bridges/quadtosocbridge.jl b/src/Bridges/quadtosocbridge.jl new file mode 100644 index 0000000000..c187a30682 --- /dev/null +++ b/src/Bridges/quadtosocbridge.jl @@ -0,0 +1,179 @@ +using Compat.LinearAlgebra + +""" + QuadtoSOCBridge{T} + +The set of points `x` satisfying the constraint +```math +\\frac{1}{2}x^T Q x + a^T x + b \\le 0 +``` +is a convex set if `Q` is positive semidefinite and is the union of two convex +cones if `a` and `b` are zero (i.e. *homogeneous* case) and `Q` has only one +negative eigenvalue. + +## Non-homogeneous case + +If `Q` is positive semidefinite, there exists `U` such that ``Q = U^T U``, the +inequality can then be rewritten as +```math +\\|U x\\|_2 \\le 2 (a^T x + b) +``` +which is equivalent to the membership of `(1, a^T x + b, Ux)` to the rotated +second-order cone. + +## Homogeneous case + +If `Q` has only one negative eigenvalue, the set of `x` such that ``x^T Q x \\le +0`` is the union of a convex cone and its opposite. We can choose which one to +model by checking the existence of bounds on variables as shown below. + +### Second-order cone + +If `Q` is diagonal and has eigenvalues `(1, 1, -1)`, the inequality +``x^2 + x^2 \\le z^2`` combined with ``z \\ge 0`` defines the Lorenz cone (i.e. +the second-order cone) but when combined with ``z \\le 0``, it gives the +opposite of the second order cone. Therefore, we need to check if the variable +`z` has a lower bound 0 or an upper bound 0 in order to determine which cone is + +### Rotated second-order cone + +The matrix `Q` corresponding to the inequality ``x^2 \\le 2yz`` has one +eigenvalue 1 with eigenvectors `(1, 0, 0)` and `(0, 1, -1)` and one eigenvalue +`-1` corresponding to the eigenvector `(0, 1, 1)`. Hence if we intersect this +union of two convex cone with the halfspace ``x + y \\ge 0``, we get the rotated +second-order cone and if we intersect it with the halfspace ``x + y \\le 0`` we +get the opposite of the rotated second-order cone. Note that `y` and `z` have +the same sign since `yz` is nonnegative hence ``x + y \\ge 0`` is equivalent to +``x \\ge 0`` and ``y \\ge 0``. + +### Note + +The check for existence of bound can be implemented (but inefficiently) with the +current interface but if bound is removed or transformed (e.g. `≤ 0` transformed +into `≥ 0`) then the bridge is no longer valid. For this reason the homogeneous +version of the bridge is not implemented yet. +""" +struct QuadtoSOCBridge{T} <: AbstractBridge + soc::CI{MOI.VectorAffineFunction{T}, MOI.RotatedSecondOrderCone} + dimension::Int # dimension of the SOC constraint + less_than::Bool # whether the constraint was ≤ or ≥ + set_constant::T # the constant that was on the set +end +function QuadtoSOCBridge{T}(model, func::MOI.ScalarQuadraticFunction{T}, + set::Union{MOI.LessThan{T}, + MOI.GreaterThan{T}}) where T + set_constant = MOIU.getconstant(set) + less_than = set isa MOI.LessThan + if !less_than + set_constant = -set_constant + end + Q, index_to_variable_map = matrix_from_quadratic_terms(func.quadratic_terms) + @show Q + if !less_than + rmul!(Q, -1) + end + @show Q + U = cholesky(Symmetric(Q)).U + Ux_terms = matrix_to_vector_affine_terms(U, index_to_variable_map) + Ux = MOI.VectorAffineFunction(Ux_terms, zeros(T, size(U, 1))) + t = MOI.ScalarAffineFunction(less_than ? MOIU.operate_terms(-, func.affine_terms) : func.affine_terms, + less_than ? set_constant - func.constant : func.constant - set_constant) + f = MOIU.operate(vcat, T, one(T), t, Ux) + dimension = MOI.output_dimension(f) + soc = MOI.addconstraint!(model, f, + MOI.RotatedSecondOrderCone(dimension)) + return QuadtoSOCBridge(soc, dimension, less_than, set_constant) +end + +function matrix_from_quadratic_terms(terms::Vector{MOI.ScalarQuadraticTerm{T}}) where T + variable_to_index_map = Dict{VI, Int}() + index_to_variable_map = Dict{Int, VI}() + n = 0 + for term in terms + for variable in (term.variable_index_1, term.variable_index_2) + if !(variable in keys(variable_to_index_map)) + n += 1 + variable_to_index_map[variable] = n + index_to_variable_map[n] = variable + end + end + end + Q = zeros(T, n, n) + for term in terms + i = variable_to_index_map[term.variable_index_1] + j = variable_to_index_map[term.variable_index_2] + Q[i, j] += term.coefficient + if i != j + Q[j, i] += term.coefficient + end + end + return Q, index_to_variable_map +end + +function matrix_to_vector_affine_terms(matrix::AbstractMatrix{T}, + index_to_variable_map::Dict{Int, VI}) where T + terms = MOI.VectorAffineTerm{T}[] + for row in 1:size(matrix, 1) + for col in 1:size(matrix, 2) + if !iszero(matrix[row, col]) + push!(terms, MOI.VectorAffineTerm(row, + MOI.ScalarAffineTerm(matrix[row, col], + index_to_variable_map[col]))) + end + end + end + return terms +end + +function MOI.supportsconstraint(::Type{QuadtoSOCBridge{T}}, + ::Type{MOI.ScalarQuadraticFunction{T}}, + ::Type{<:Union{MOI.LessThan{T}, + MOI.GreaterThan{T}}}) where T + return true +end +function addedconstrainttypes(::Type{QuadtoSOCBridge{T}}) where T + return [(MOI.VectorAffineFunction{T}, MOI.RotatedSecondOrderCone)] +end +function concrete_bridge_type(::Type{<:QuadtoSOCBridge{T}}, + ::Type{MOI.ScalarQuadraticFunction{T}}, + ::Type{<:Union{MOI.LessThan{T}, + MOI.GreaterThan{T}}}) where T + return QuadtoSOCBridge{T} +end + +# Attributes, Bridge acting as an model +function MOI.get(::QuadtoSOCBridge{T}, + ::MOI.NumberOfConstraints{MOI.VectorAffineFunction{T}, + MOI.RotatedSecondOrderCone}) where T + return 1 +end +function MOI.get(bridge::QuadtoSOCBridge{T}, + ::MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{T}, + MOI.RotatedSecondOrderCone}) where T + return [bridge.soc] +end + +# References +function MOI.delete!(model::MOI.ModelLike, bridge::QuadtoSOCBridge) + MOI.delete!(model, bridge.soc) +end + +# Attributes, Bridge acting as a constraint +function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintPrimal, + bridge::QuadtoSOCBridge) + soc = MOI.get(model, attr, bridge.soc) + @show soc + output = sum(soc[i]^2 for i in 3:bridge.dimension) + @show output + output /= 2 + @show output + output -= soc[1] * soc[2] + @show output + if !bridge.less_than + output = -output + end + @show output + output += bridge.set_constant + @show output + return output +end diff --git a/test/bridge.jl b/test/bridge.jl index 90da0bff4d..43fb898b0d 100644 --- a/test/bridge.jl +++ b/test/bridge.jl @@ -255,6 +255,23 @@ end test_delete_bridge(bridgedmock, ci, 2, ((MOI.VectorAffineFunction{Float64}, MOI.SecondOrderCone, 0),)) end + @testset "QuadtoSOC" begin + bridgedmock = MOIB.QuadtoSOC{Float64}(mock) + MOIU.set_mock_optimize!(mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1/2, 7/4], MOI.FeasiblePoint)) + MOIT.qcp1test(bridgedmock, config) + MOIU.set_mock_optimize!(mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [√2], MOI.FeasiblePoint)) + MOIT.qcp2test(bridgedmock, config) + MOIU.set_mock_optimize!(mock, + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [√2], MOI.FeasiblePoint)) + MOIT.qcp3test(bridgedmock, config) + ci = first(MOI.get(bridgedmock, + MOI.ListOfConstraintIndices{MOI.ScalarQuadraticFunction{Float64}, + MOI.LessThan{Float64}}())) + test_delete_bridge(bridgedmock, ci, 1, ((MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone, 0),)) + end + @testset "SquarePSD" begin bridgedmock = MOIB.SquarePSD{Float64}(mock) mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, ones(4), From 6b36098336368ed8f8713d71b6052bf2b3717d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 2 Oct 2018 08:59:56 +0200 Subject: [PATCH 02/12] Remove debugging output --- src/Bridges/quadtosocbridge.jl | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Bridges/quadtosocbridge.jl b/src/Bridges/quadtosocbridge.jl index c187a30682..864de44061 100644 --- a/src/Bridges/quadtosocbridge.jl +++ b/src/Bridges/quadtosocbridge.jl @@ -68,11 +68,9 @@ function QuadtoSOCBridge{T}(model, func::MOI.ScalarQuadraticFunction{T}, set_constant = -set_constant end Q, index_to_variable_map = matrix_from_quadratic_terms(func.quadratic_terms) - @show Q if !less_than rmul!(Q, -1) end - @show Q U = cholesky(Symmetric(Q)).U Ux_terms = matrix_to_vector_affine_terms(U, index_to_variable_map) Ux = MOI.VectorAffineFunction(Ux_terms, zeros(T, size(U, 1))) @@ -162,18 +160,12 @@ end function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintPrimal, bridge::QuadtoSOCBridge) soc = MOI.get(model, attr, bridge.soc) - @show soc output = sum(soc[i]^2 for i in 3:bridge.dimension) - @show output output /= 2 - @show output output -= soc[1] * soc[2] - @show output if !bridge.less_than output = -output end - @show output output += bridge.set_constant - @show output return output end From 78d69287fee73925c7c2846dd1f927e4c7c550ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 2 Oct 2018 10:43:41 +0200 Subject: [PATCH 03/12] Renaming updates --- src/Bridges/quadtosocbridge.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Bridges/quadtosocbridge.jl b/src/Bridges/quadtosocbridge.jl index 864de44061..5667f11cea 100644 --- a/src/Bridges/quadtosocbridge.jl +++ b/src/Bridges/quadtosocbridge.jl @@ -123,13 +123,13 @@ function matrix_to_vector_affine_terms(matrix::AbstractMatrix{T}, return terms end -function MOI.supportsconstraint(::Type{QuadtoSOCBridge{T}}, - ::Type{MOI.ScalarQuadraticFunction{T}}, - ::Type{<:Union{MOI.LessThan{T}, - MOI.GreaterThan{T}}}) where T +function MOI.supports_constraint(::Type{QuadtoSOCBridge{T}}, + ::Type{MOI.ScalarQuadraticFunction{T}}, + ::Type{<:Union{MOI.LessThan{T}, + MOI.GreaterThan{T}}}) where T return true end -function addedconstrainttypes(::Type{QuadtoSOCBridge{T}}) where T +function added_constraint_types(::Type{QuadtoSOCBridge{T}}) where T return [(MOI.VectorAffineFunction{T}, MOI.RotatedSecondOrderCone)] end function concrete_bridge_type(::Type{<:QuadtoSOCBridge{T}}, @@ -152,8 +152,8 @@ function MOI.get(bridge::QuadtoSOCBridge{T}, end # References -function MOI.delete!(model::MOI.ModelLike, bridge::QuadtoSOCBridge) - MOI.delete!(model, bridge.soc) +function MOI.delete(model::MOI.ModelLike, bridge::QuadtoSOCBridge) + MOI.delete(model, bridge.soc) end # Attributes, Bridge acting as a constraint From ff4cb48343e94cd1de3f52f4eba5303b146e6f87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 2 Oct 2018 10:43:52 +0200 Subject: [PATCH 04/12] Better error if cholesky fails --- src/Bridges/quadtosocbridge.jl | 17 +++++++++++++++-- test/bridge.jl | 17 +++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/Bridges/quadtosocbridge.jl b/src/Bridges/quadtosocbridge.jl index 5667f11cea..87f1f8947e 100644 --- a/src/Bridges/quadtosocbridge.jl +++ b/src/Bridges/quadtosocbridge.jl @@ -71,14 +71,27 @@ function QuadtoSOCBridge{T}(model, func::MOI.ScalarQuadraticFunction{T}, if !less_than rmul!(Q, -1) end - U = cholesky(Symmetric(Q)).U + U = try + cholesky(Symmetric(Q)).U + catch err + if err isa PosDefException + error("The optimizer supports second-order cone constraints and", + " not quadratic constraints but you entered a quadratic", + " constraint of type: `$(typeof(func))`-in-`$(typeof(set))`.", + " A bridge attempted to transform the quadratic constraint", + " to a second order cone constraint but the constraint is not", + " convex.") + else + rethrow(err) + end + end Ux_terms = matrix_to_vector_affine_terms(U, index_to_variable_map) Ux = MOI.VectorAffineFunction(Ux_terms, zeros(T, size(U, 1))) t = MOI.ScalarAffineFunction(less_than ? MOIU.operate_terms(-, func.affine_terms) : func.affine_terms, less_than ? set_constant - func.constant : func.constant - set_constant) f = MOIU.operate(vcat, T, one(T), t, Ux) dimension = MOI.output_dimension(f) - soc = MOI.addconstraint!(model, f, + soc = MOI.add_constraint(model, f, MOI.RotatedSecondOrderCone(dimension)) return QuadtoSOCBridge(soc, dimension, less_than, set_constant) end diff --git a/test/bridge.jl b/test/bridge.jl index 43fb898b0d..b39acfa656 100644 --- a/test/bridge.jl +++ b/test/bridge.jl @@ -257,6 +257,23 @@ end @testset "QuadtoSOC" begin bridgedmock = MOIB.QuadtoSOC{Float64}(mock) + @testset "Error for non-convex quadratic constraints" begin + x = MOI.add_variable(bridgedmock) + @test_throws ErrorException begin + MOI.add_constraint(bridgedmock, + MOI.ScalarQuadraticFunction(MOI.ScalarAffineTerm{Float64}[], + [MOI.ScalarQuadraticTerm(1.0, x, x)], + 0.0), + MOI.GreaterThan(0.0)) + end + @test_throws ErrorException begin + MOI.add_constraint(bridgedmock, + MOI.ScalarQuadraticFunction(MOI.ScalarAffineTerm{Float64}[], + [MOI.ScalarQuadraticTerm(-1.0, x, x)], + 0.0), + MOI.LessThan(0.0)) + end + end MOIU.set_mock_optimize!(mock, (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1/2, 7/4], MOI.FeasiblePoint)) MOIT.qcp1test(bridgedmock, config) From e14daa0cefc895a66036bbc8b7cc1178cb61f3aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 2 Oct 2018 12:36:15 +0200 Subject: [PATCH 05/12] Fix for Julia v0.6 --- src/Bridges/quadtosocbridge.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Bridges/quadtosocbridge.jl b/src/Bridges/quadtosocbridge.jl index 87f1f8947e..cb936acb0e 100644 --- a/src/Bridges/quadtosocbridge.jl +++ b/src/Bridges/quadtosocbridge.jl @@ -72,7 +72,11 @@ function QuadtoSOCBridge{T}(model, func::MOI.ScalarQuadraticFunction{T}, rmul!(Q, -1) end U = try - cholesky(Symmetric(Q)).U + @static if VERSION >= v"0.7-" + cholesky(Symmetric(Q)).U + else + chol(Symmetric(Q)) + end catch err if err isa PosDefException error("The optimizer supports second-order cone constraints and", From ad7acef90fa382d2b34afa758f3d6b03d3a8fde4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 9 Oct 2018 18:24:25 +0200 Subject: [PATCH 06/12] =?UTF-8?q?=F0=9F=90=9B=20Fix=20rmul!=20in=20Julia?= =?UTF-8?q?=20v0.6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Bridges/quadtosocbridge.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bridges/quadtosocbridge.jl b/src/Bridges/quadtosocbridge.jl index cb936acb0e..9d35e5119c 100644 --- a/src/Bridges/quadtosocbridge.jl +++ b/src/Bridges/quadtosocbridge.jl @@ -69,7 +69,7 @@ function QuadtoSOCBridge{T}(model, func::MOI.ScalarQuadraticFunction{T}, end Q, index_to_variable_map = matrix_from_quadratic_terms(func.quadratic_terms) if !less_than - rmul!(Q, -1) + Compat.rmul!(Q, -1) end U = try @static if VERSION >= v"0.7-" From 74fff008533ce8e7ddfee0822f2dfc3ba98d915b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 13 Oct 2018 13:01:00 +0200 Subject: [PATCH 07/12] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Switch=20to=20sparse?= =?UTF-8?q?=20cholesky?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Bridges/quadtosocbridge.jl | 49 ++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/src/Bridges/quadtosocbridge.jl b/src/Bridges/quadtosocbridge.jl index 9d35e5119c..eeb3dc4355 100644 --- a/src/Bridges/quadtosocbridge.jl +++ b/src/Bridges/quadtosocbridge.jl @@ -1,4 +1,4 @@ -using Compat.LinearAlgebra +using Compat.LinearAlgebra, Compat.SparseArrays """ QuadtoSOCBridge{T} @@ -71,11 +71,14 @@ function QuadtoSOCBridge{T}(model, func::MOI.ScalarQuadraticFunction{T}, if !less_than Compat.rmul!(Q, -1) end - U = try + # We have L × L' ≈ Q[p, p] + L, p = try @static if VERSION >= v"0.7-" - cholesky(Symmetric(Q)).U + F = cholesky(Symmetric(Q)) + sparse(F.L), F.p else - chol(Symmetric(Q)) + F = cholfact(Symmetric(Q)) + sparse(F[:L]), F[:p] end catch err if err isa PosDefException @@ -89,8 +92,8 @@ function QuadtoSOCBridge{T}(model, func::MOI.ScalarQuadraticFunction{T}, rethrow(err) end end - Ux_terms = matrix_to_vector_affine_terms(U, index_to_variable_map) - Ux = MOI.VectorAffineFunction(Ux_terms, zeros(T, size(U, 1))) + Ux_terms = matrix_to_vector_affine_terms(L, p, index_to_variable_map) + Ux = MOI.VectorAffineFunction(Ux_terms, zeros(T, size(L, 2))) t = MOI.ScalarAffineFunction(less_than ? MOIU.operate_terms(-, func.affine_terms) : func.affine_terms, less_than ? set_constant - func.constant : func.constant - set_constant) f = MOIU.operate(vcat, T, one(T), t, Ux) @@ -113,31 +116,37 @@ function matrix_from_quadratic_terms(terms::Vector{MOI.ScalarQuadraticTerm{T}}) end end end - Q = zeros(T, n, n) + I = Int[] + J = Int[] + V = T[] for term in terms i = variable_to_index_map[term.variable_index_1] j = variable_to_index_map[term.variable_index_2] - Q[i, j] += term.coefficient + push!(I, i) + push!(J, j) + push!(V, term.coefficient) if i != j - Q[j, i] += term.coefficient + push!(I, i) + push!(J, j) + push!(V, term.coefficient) end end + # Duplicate terms are summed together in `sparse` + Q = sparse(I, J, V, n, n) return Q, index_to_variable_map end -function matrix_to_vector_affine_terms(matrix::AbstractMatrix{T}, +function matrix_to_vector_affine_terms(L::SparseMatrixCSC{T}, + p::Vector, index_to_variable_map::Dict{Int, VI}) where T - terms = MOI.VectorAffineTerm{T}[] - for row in 1:size(matrix, 1) - for col in 1:size(matrix, 2) - if !iszero(matrix[row, col]) - push!(terms, MOI.VectorAffineTerm(row, - MOI.ScalarAffineTerm(matrix[row, col], - index_to_variable_map[col]))) - end - end + # We know that L × L' ≈ Q[p, p] hence (L × L')[i, :] ≈ Q[p[i], p] + # We precompute the map to avoid having to do a dictionary lookup for every + # term + variable = map(i -> index_to_variable_map[p[i]], 1:size(L, 1)) + function term(i::Integer, j::Integer, v::T) + return MOI.VectorAffineTerm(j, MOI.ScalarAffineTerm(v, variable[i])) end - return terms + return map(ijv -> term(ijv...), zip(findnz(L)...)) end function MOI.supports_constraint(::Type{QuadtoSOCBridge{T}}, From 4582427f16e30bcf8cbbe651eaf1b13bb5477f0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sun, 14 Oct 2018 18:10:37 +0200 Subject: [PATCH 08/12] Add comment about what is implemented at the beginning --- src/Bridges/quadtosocbridge.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Bridges/quadtosocbridge.jl b/src/Bridges/quadtosocbridge.jl index eeb3dc4355..2dbf919525 100644 --- a/src/Bridges/quadtosocbridge.jl +++ b/src/Bridges/quadtosocbridge.jl @@ -9,7 +9,8 @@ The set of points `x` satisfying the constraint ``` is a convex set if `Q` is positive semidefinite and is the union of two convex cones if `a` and `b` are zero (i.e. *homogeneous* case) and `Q` has only one -negative eigenvalue. +negative eigenvalue. Currently, only the non-homogeneous transformation +is implemented, see the [Note](@ref) section for more details. ## Non-homogeneous case From 67969d90e9e396eaad5ebbb7a1a8ee904033de4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sun, 14 Oct 2018 18:13:33 +0200 Subject: [PATCH 09/12] =?UTF-8?q?=F0=9F=93=9D=20convex=20->=20strongly=20c?= =?UTF-8?q?onvex?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Bridges/quadtosocbridge.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Bridges/quadtosocbridge.jl b/src/Bridges/quadtosocbridge.jl index 2dbf919525..2cad198d25 100644 --- a/src/Bridges/quadtosocbridge.jl +++ b/src/Bridges/quadtosocbridge.jl @@ -87,8 +87,9 @@ function QuadtoSOCBridge{T}(model, func::MOI.ScalarQuadraticFunction{T}, " not quadratic constraints but you entered a quadratic", " constraint of type: `$(typeof(func))`-in-`$(typeof(set))`.", " A bridge attempted to transform the quadratic constraint", - " to a second order cone constraint but the constraint is not", - " convex.") + " to a second order cone constraint but the constraint is", + " not strongly convex, i.e., the symmetric matrix of" + " quadratic coefficients is not positive definite.") else rethrow(err) end From 866608502893f967adc0e4b735b42e58f250faaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sun, 14 Oct 2018 18:27:59 +0200 Subject: [PATCH 10/12] =?UTF-8?q?=F0=9F=9A=B8=20Mention=20not=20supported?= =?UTF-8?q?=20yet=20in=20error=20message?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Bridges/quadtosocbridge.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Bridges/quadtosocbridge.jl b/src/Bridges/quadtosocbridge.jl index 2cad198d25..7e04cbdb7a 100644 --- a/src/Bridges/quadtosocbridge.jl +++ b/src/Bridges/quadtosocbridge.jl @@ -89,7 +89,10 @@ function QuadtoSOCBridge{T}(model, func::MOI.ScalarQuadraticFunction{T}, " A bridge attempted to transform the quadratic constraint", " to a second order cone constraint but the constraint is", " not strongly convex, i.e., the symmetric matrix of" - " quadratic coefficients is not positive definite.") + " quadratic coefficients is not positive definite. Convex" + " constraint that are not stronly convex, i.e. the matrix is", + " positive semidefinite but not positive definite, is not",, + " supported yet.") else rethrow(err) end From 06741d4567987b96bedf60ddc97d987804553ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Mon, 15 Oct 2018 21:18:26 +0200 Subject: [PATCH 11/12] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Typo=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Bridges/quadtosocbridge.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Bridges/quadtosocbridge.jl b/src/Bridges/quadtosocbridge.jl index 7e04cbdb7a..f1b122f0a0 100644 --- a/src/Bridges/quadtosocbridge.jl +++ b/src/Bridges/quadtosocbridge.jl @@ -88,10 +88,10 @@ function QuadtoSOCBridge{T}(model, func::MOI.ScalarQuadraticFunction{T}, " constraint of type: `$(typeof(func))`-in-`$(typeof(set))`.", " A bridge attempted to transform the quadratic constraint", " to a second order cone constraint but the constraint is", - " not strongly convex, i.e., the symmetric matrix of" - " quadratic coefficients is not positive definite. Convex" + " not strongly convex, i.e., the symmetric matrix of", + " quadratic coefficients is not positive definite. Convex", " constraint that are not stronly convex, i.e. the matrix is", - " positive semidefinite but not positive definite, is not",, + " positive semidefinite but not positive definite, is not", " supported yet.") else rethrow(err) From 4ae48271dfe2f1cf833aaa7b91b3fc65c5814b6f Mon Sep 17 00:00:00 2001 From: Miles Lubin Date: Fri, 19 Oct 2018 14:09:32 -0400 Subject: [PATCH 12/12] Grammar fix --- src/Bridges/quadtosocbridge.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bridges/quadtosocbridge.jl b/src/Bridges/quadtosocbridge.jl index f1b122f0a0..35278db383 100644 --- a/src/Bridges/quadtosocbridge.jl +++ b/src/Bridges/quadtosocbridge.jl @@ -90,8 +90,8 @@ function QuadtoSOCBridge{T}(model, func::MOI.ScalarQuadraticFunction{T}, " to a second order cone constraint but the constraint is", " not strongly convex, i.e., the symmetric matrix of", " quadratic coefficients is not positive definite. Convex", - " constraint that are not stronly convex, i.e. the matrix is", - " positive semidefinite but not positive definite, is not", + " constraints that are not strongly convex, i.e. the matrix is", + " positive semidefinite but not positive definite, are not", " supported yet.") else rethrow(err)