From ebc0e75c6cbcee78b0319b4c47a2597245a2303a Mon Sep 17 00:00:00 2001 From: odow Date: Fri, 14 May 2021 12:51:04 +1200 Subject: [PATCH 01/14] Add error checks for set dimensions --- src/Bridges/Constraint/geomean.jl | 4 - src/sets.jl | 204 +++++++++++++++++++++++++++++- test/sets.jl | 27 +++- 3 files changed, 226 insertions(+), 9 deletions(-) diff --git a/src/Bridges/Constraint/geomean.jl b/src/Bridges/Constraint/geomean.jl index 47f35d4d97..ce27a86124 100644 --- a/src/Bridges/Constraint/geomean.jl +++ b/src/Bridges/Constraint/geomean.jl @@ -64,10 +64,6 @@ function bridge_constraint( s::MOI.GeometricMeanCone, ) where {T,F,G,H} d = s.dimension - if d <= 1 - # TODO change to a standard error: https://github.com/jump-dev/MathOptInterface.jl/issues/967 - error("Dimension of GeometricMeanCone must be greater than 1.") - end n = d - 1 l = ilog2(n) N = 1 << l diff --git a/src/sets.jl b/src/sets.jl index 26d1a0d386..8a0412fdce 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -112,6 +112,17 @@ The set ``\\mathbb{R}^{dimension}`` (containing all points) of dimension `dimens """ struct Reals <: AbstractVectorSet dimension::Int + function Reals(dimension::Int) + if !(dimension > 0) + throw( + DimensionMismatch( + "Dimension of Reals must be greater than 0, not " * + "$(dimension).", + ), + ) + end + return new(dimension) + end end dual_set(s::Reals) = Zeros(dimension(s)) @@ -124,6 +135,17 @@ The set ``\\{ 0 \\}^{dimension}`` (containing only the origin) of dimension `dim """ struct Zeros <: AbstractVectorSet dimension::Int + function Zeros(dimension::Int) + if !(dimension > 0) + throw( + DimensionMismatch( + "Dimension of Zeros must be greater than 0, not " * + "$(dimension).", + ), + ) + end + return new(dimension) + end end dual_set(s::Zeros) = Reals(dimension(s)) @@ -136,6 +158,17 @@ The nonnegative orthant ``\\{ x \\in \\mathbb{R}^{dimension} : x \\ge 0 \\}`` of """ struct Nonnegatives <: AbstractVectorSet dimension::Int + function Nonnegatives(dimension::Int) + if !(dimension > 0) + throw( + DimensionMismatch( + "Dimension of Nonnegatives must be greater than 0, not " * + "$(dimension).", + ), + ) + end + return new(dimension) + end end dual_set(s::Nonnegatives) = copy(s) @@ -148,6 +181,17 @@ The nonpositive orthant ``\\{ x \\in \\mathbb{R}^{dimension} : x \\le 0 \\}`` of """ struct Nonpositives <: AbstractVectorSet dimension::Int + function Nonpositives(dimension::Int) + if !(dimension > 0) + throw( + DimensionMismatch( + "Dimension of Nonpositives must be greater than 0, not " * + "$(dimension).", + ), + ) + end + return new(dimension) + end end dual_set(s::Nonpositives) = copy(s) @@ -230,6 +274,17 @@ The ``\\ell_\\infty``-norm cone ``\\{ (t,x) \\in \\mathbb{R}^{dimension} : t \\g """ struct NormInfinityCone <: AbstractVectorSet dimension::Int + function NormInfinityCone(dimension::Int) + if !(dimension > 1) + throw( + DimensionMismatch( + "Dimension of NormInfinityCone must be greater than 1, " * + "not $(dimension).", + ), + ) + end + return new(dimension) + end end dual_set(s::NormInfinityCone) = NormOneCone(dimension(s)) @@ -242,6 +297,17 @@ The ``\\ell_1``-norm cone ``\\{ (t,x) \\in \\mathbb{R}^{dimension} : t \\ge \\lV """ struct NormOneCone <: AbstractVectorSet dimension::Int + function NormOneCone(dimension::Int) + if !(dimension > 1) + throw( + DimensionMismatch( + "Dimension of NormOneCone must be greater than 1, not " * + "$(dimension).", + ), + ) + end + return new(dimension) + end end dual_set(s::NormOneCone) = NormInfinityCone(dimension(s)) @@ -254,6 +320,17 @@ The second-order cone (or Lorenz cone or ``\\ell_2``-norm cone) ``\\{ (t,x) \\in """ struct SecondOrderCone <: AbstractVectorSet dimension::Int + function SecondOrderCone(dimension::Int) + if !(dimension > 1) + throw( + DimensionMismatch( + "Dimension of SecondOrderCone must be greater than 1, " * + "not $(dimension).", + ), + ) + end + return new(dimension) + end end dual_set(s::SecondOrderCone) = copy(s) @@ -266,6 +343,17 @@ The rotated second-order cone ``\\{ (t,u,x) \\in \\mathbb{R}^{dimension} : 2tu \ """ struct RotatedSecondOrderCone <: AbstractVectorSet dimension::Int + function RotatedSecondOrderCone(dimension::Int) + if !(dimension > 2) + throw( + DimensionMismatch( + "Dimension of RotatedSecondOrderCone must be greater " * + "than 2, not $(dimension).", + ), + ) + end + return new(dimension) + end end dual_set(s::RotatedSecondOrderCone) = copy(s) @@ -283,6 +371,17 @@ The dual of the geometric mean cone is """ struct GeometricMeanCone <: AbstractVectorSet dimension::Int + function GeometricMeanCone(dimension::Int) + if !(dimension > 1) + throw( + DimensionMismatch( + "Dimension of GeometricMeanCone must be greater than 1, " * + "not $(dimension).", + ), + ) + end + return new(dimension) + end end """ @@ -351,6 +450,17 @@ The dual of the relative entropy cone is """ struct RelativeEntropyCone <: AbstractVectorSet dimension::Int + function RelativeEntropyCone(dimension::Int) + if !(dimension > 1 && isodd(dimension)) + throw( + DimensionMismatch( + "Dimension of RelativeEntropyCone must be greater an odd " * + "integer greater than 2, not $(dimension).", + ), + ) + end + return new(dimension) + end end """ @@ -362,6 +472,17 @@ The matrix X is vectorized by stacking the columns, matching the behavior of Jul struct NormSpectralCone <: AbstractVectorSet row_dim::Int column_dim::Int + function NormSpectralCone(row_dim::Int, column_dim::Int) + if !(row_dim > 0 && column_dim > 0) + throw( + DimensionMismatch( + "Dimension of NormSpectralCone must be strictly " * + "positive, not ($(row_dim), $(column_dim)).", + ), + ) + end + return new(row_dim, column_dim) + end end dual_set(s::NormSpectralCone) = NormNuclearCone(s.row_dim, s.column_dim) @@ -376,6 +497,17 @@ The matrix X is vectorized by stacking the columns, matching the behavior of Jul struct NormNuclearCone <: AbstractVectorSet row_dim::Int column_dim::Int + function NormNuclearCone(row_dim::Int, column_dim::Int) + if !(row_dim > 0 && column_dim > 0) + throw( + DimensionMismatch( + "Dimension of NormNuclearCone must be strictly " * + "positive, not ($(row_dim), $(column_dim)).", + ), + ) + end + return new(row_dim, column_dim) + end end dual_set(s::NormNuclearCone) = NormSpectralCone(s.row_dim, s.column_dim) @@ -560,6 +692,17 @@ form. """ struct PositiveSemidefiniteConeTriangle <: AbstractSymmetricMatrixSetTriangle side_dimension::Int + function PositiveSemidefiniteConeTriangle(side_dimension::Int) + if !(side_dimension > 0) + throw( + DimensionMismatch( + "Side dimension of PositiveSemidefiniteConeTriangle must " * + "be strictly positive, not $(side_dimension).", + ), + ) + end + return new(side_dimension) + end end dual_set(s::PositiveSemidefiniteConeTriangle) = copy(s) @@ -593,6 +736,17 @@ It both constrains ``y = z`` and ``(1, -y, 0)`` (or ``(1, -z, 0)``) to be in `Po """ struct PositiveSemidefiniteConeSquare <: AbstractSymmetricMatrixSetSquare side_dimension::Int + function PositiveSemidefiniteConeSquare(side_dimension::Int) + if !(side_dimension > 0) + throw( + DimensionMismatch( + "Side dimension of PositiveSemidefiniteConeSquare must " * + "be strictly positive, not $(side_dimension).", + ), + ) + end + return new(side_dimension) + end end function _dual_set_square_error() @@ -620,6 +774,17 @@ The argument `side_dimension` is the side dimension of the matrix `X`, i.e., its """ struct LogDetConeTriangle <: AbstractVectorSet side_dimension::Int + function LogDetConeTriangle(side_dimension::Int) + if !(side_dimension > 0) + throw( + DimensionMismatch( + "Side dimension of LogDetConeTriangle must " * + "be strictly positive, not $(side_dimension).", + ), + ) + end + return new(side_dimension) + end end function dimension(s::LogDetConeTriangle) @@ -635,6 +800,17 @@ The argument `side_dimension` is the side dimension of the matrix `X`, i.e., its """ struct LogDetConeSquare <: AbstractVectorSet side_dimension::Int + function LogDetConeSquare(side_dimension::Int) + if !(side_dimension > 0) + throw( + DimensionMismatch( + "Side dimension of LogDetConeSquare must " * + "be strictly positive, not $(side_dimension).", + ), + ) + end + return new(side_dimension) + end end dimension(s::LogDetConeSquare) = 2 + s.side_dimension^2 @@ -647,6 +823,17 @@ The argument `side_dimension` is the side dimension of the matrix `X`, i.e., its """ struct RootDetConeTriangle <: AbstractVectorSet side_dimension::Int + function RootDetConeTriangle(side_dimension::Int) + if !(side_dimension > 0) + throw( + DimensionMismatch( + "Side dimension of RootDetConeTriangle must " * + "be strictly positive, not $(side_dimension).", + ), + ) + end + return new(side_dimension) + end end function dimension(s::RootDetConeTriangle) @@ -662,6 +849,17 @@ The argument `side_dimension` is the side dimension of the matrix `X`, i.e., its """ struct RootDetConeSquare <: AbstractVectorSet side_dimension::Int + function RootDetConeSquare(side_dimension::Int) + if !(side_dimension > 0) + throw( + DimensionMismatch( + "Side dimension of RootDetConeSquare must " * + "be strictly positive, not $(side_dimension).", + ), + ) + end + return new(side_dimension) + end end dimension(s::RootDetConeSquare) = 1 + s.side_dimension^2 @@ -855,10 +1053,10 @@ struct Complements <: AbstractVectorSet # Need an explicit Int64 here, because JSON parses Int as Int64, even on # 32-bit machines. function Complements(dimension::Union{Int,Int64}) - if !iseven(dimension) + if !(dimension > 0 && iseven(dimension)) throw( - ArgumentError( - "The dimension of a Complements set must be even.", + DimensionMismatch( + "Dimension of Complements must be even, not $(dimension).", ), ) end diff --git a/test/sets.jl b/test/sets.jl index 829ee99797..52bd68d471 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -114,8 +114,31 @@ function test_sets_dimension() @test MOI.dimension(MOI.Complements(10)) == 10 end -function test_sets_complement() - @test_throws ArgumentError MOI.Complements(3) +function test_sets_DimensionMismatch() + @test_throws DimensionMismatch MOI.Reals(0) + @test_throws DimensionMismatch MOI.Zeros(0) + @test_throws DimensionMismatch MOI.Nonnegatives(0) + @test_throws DimensionMismatch MOI.Nonpositives(0) + @test_throws DimensionMismatch MOI.NormInfinityCone(1) + @test_throws DimensionMismatch MOI.NormOneCone(1) + @test_throws DimensionMismatch MOI.SecondOrderCone(1) + @test_throws DimensionMismatch MOI.RotatedSecondOrderCone(2) + @test_throws DimensionMismatch MOI.GeometricMeanCone(1) + @test_throws DimensionMismatch MOI.RelativeEntropyCone(1) + @test_throws DimensionMismatch MOI.RelativeEntropyCone(4) + @test_throws DimensionMismatch MOI.NormSpectralCone(1, 0) + @test_throws DimensionMismatch MOI.NormSpectralCone(0, 1) + @test_throws DimensionMismatch MOI.NormNuclearCone(1, 0) + @test_throws DimensionMismatch MOI.NormNuclearCone(0, 1) + @test_throws DimensionMismatch MOI.PositiveSemidefiniteConeTriangle(0) + @test_throws DimensionMismatch MOI.PositiveSemidefiniteConeSquare(0) + @test_throws DimensionMismatch MOI.LogDetConeTriangle(0) + @test_throws DimensionMismatch MOI.LogDetConeSquare(0) + @test_throws DimensionMismatch MOI.RootDetConeTriangle(0) + @test_throws DimensionMismatch MOI.RootDetConeSquare(0) + @test_throws DimensionMismatch MOI.Complements(0) + @test_throws DimensionMismatch MOI.Complements(3) + return end function _dual_set_test(set1, set2) From be15d9e086870e17ef612dd98efb68b82558aa41 Mon Sep 17 00:00:00 2001 From: odow Date: Fri, 14 May 2021 13:20:17 +1200 Subject: [PATCH 02/14] Fix --- src/sets.jl | 44 +++++++++++++++++++++----------------------- test/sets.jl | 6 +++--- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/sets.jl b/src/sets.jl index 8a0412fdce..fc1143e97b 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -112,7 +112,7 @@ The set ``\\mathbb{R}^{dimension}`` (containing all points) of dimension `dimens """ struct Reals <: AbstractVectorSet dimension::Int - function Reals(dimension::Int) + function Reals(dimension::Base.Integer) if !(dimension > 0) throw( DimensionMismatch( @@ -135,7 +135,7 @@ The set ``\\{ 0 \\}^{dimension}`` (containing only the origin) of dimension `dim """ struct Zeros <: AbstractVectorSet dimension::Int - function Zeros(dimension::Int) + function Zeros(dimension::Base.Integer) if !(dimension > 0) throw( DimensionMismatch( @@ -158,7 +158,7 @@ The nonnegative orthant ``\\{ x \\in \\mathbb{R}^{dimension} : x \\ge 0 \\}`` of """ struct Nonnegatives <: AbstractVectorSet dimension::Int - function Nonnegatives(dimension::Int) + function Nonnegatives(dimension::Base.Integer) if !(dimension > 0) throw( DimensionMismatch( @@ -181,7 +181,7 @@ The nonpositive orthant ``\\{ x \\in \\mathbb{R}^{dimension} : x \\le 0 \\}`` of """ struct Nonpositives <: AbstractVectorSet dimension::Int - function Nonpositives(dimension::Int) + function Nonpositives(dimension::Base.Integer) if !(dimension > 0) throw( DimensionMismatch( @@ -274,7 +274,7 @@ The ``\\ell_\\infty``-norm cone ``\\{ (t,x) \\in \\mathbb{R}^{dimension} : t \\g """ struct NormInfinityCone <: AbstractVectorSet dimension::Int - function NormInfinityCone(dimension::Int) + function NormInfinityCone(dimension::Base.Integer) if !(dimension > 1) throw( DimensionMismatch( @@ -297,7 +297,7 @@ The ``\\ell_1``-norm cone ``\\{ (t,x) \\in \\mathbb{R}^{dimension} : t \\ge \\lV """ struct NormOneCone <: AbstractVectorSet dimension::Int - function NormOneCone(dimension::Int) + function NormOneCone(dimension::Base.Integer) if !(dimension > 1) throw( DimensionMismatch( @@ -320,7 +320,7 @@ The second-order cone (or Lorenz cone or ``\\ell_2``-norm cone) ``\\{ (t,x) \\in """ struct SecondOrderCone <: AbstractVectorSet dimension::Int - function SecondOrderCone(dimension::Int) + function SecondOrderCone(dimension::Base.Integer) if !(dimension > 1) throw( DimensionMismatch( @@ -343,7 +343,7 @@ The rotated second-order cone ``\\{ (t,u,x) \\in \\mathbb{R}^{dimension} : 2tu \ """ struct RotatedSecondOrderCone <: AbstractVectorSet dimension::Int - function RotatedSecondOrderCone(dimension::Int) + function RotatedSecondOrderCone(dimension::Base.Integer) if !(dimension > 2) throw( DimensionMismatch( @@ -371,7 +371,7 @@ The dual of the geometric mean cone is """ struct GeometricMeanCone <: AbstractVectorSet dimension::Int - function GeometricMeanCone(dimension::Int) + function GeometricMeanCone(dimension::Base.Integer) if !(dimension > 1) throw( DimensionMismatch( @@ -450,7 +450,7 @@ The dual of the relative entropy cone is """ struct RelativeEntropyCone <: AbstractVectorSet dimension::Int - function RelativeEntropyCone(dimension::Int) + function RelativeEntropyCone(dimension::Base.Integer) if !(dimension > 1 && isodd(dimension)) throw( DimensionMismatch( @@ -472,7 +472,7 @@ The matrix X is vectorized by stacking the columns, matching the behavior of Jul struct NormSpectralCone <: AbstractVectorSet row_dim::Int column_dim::Int - function NormSpectralCone(row_dim::Int, column_dim::Int) + function NormSpectralCone(row_dim::Base.Integer, column_dim::Base.Integer) if !(row_dim > 0 && column_dim > 0) throw( DimensionMismatch( @@ -497,7 +497,7 @@ The matrix X is vectorized by stacking the columns, matching the behavior of Jul struct NormNuclearCone <: AbstractVectorSet row_dim::Int column_dim::Int - function NormNuclearCone(row_dim::Int, column_dim::Int) + function NormNuclearCone(row_dim::Base.Integer, column_dim::Base.Integer) if !(row_dim > 0 && column_dim > 0) throw( DimensionMismatch( @@ -692,7 +692,7 @@ form. """ struct PositiveSemidefiniteConeTriangle <: AbstractSymmetricMatrixSetTriangle side_dimension::Int - function PositiveSemidefiniteConeTriangle(side_dimension::Int) + function PositiveSemidefiniteConeTriangle(side_dimension::Base.Integer) if !(side_dimension > 0) throw( DimensionMismatch( @@ -736,7 +736,7 @@ It both constrains ``y = z`` and ``(1, -y, 0)`` (or ``(1, -z, 0)``) to be in `Po """ struct PositiveSemidefiniteConeSquare <: AbstractSymmetricMatrixSetSquare side_dimension::Int - function PositiveSemidefiniteConeSquare(side_dimension::Int) + function PositiveSemidefiniteConeSquare(side_dimension::Base.Integer) if !(side_dimension > 0) throw( DimensionMismatch( @@ -774,7 +774,7 @@ The argument `side_dimension` is the side dimension of the matrix `X`, i.e., its """ struct LogDetConeTriangle <: AbstractVectorSet side_dimension::Int - function LogDetConeTriangle(side_dimension::Int) + function LogDetConeTriangle(side_dimension::Base.Integer) if !(side_dimension > 0) throw( DimensionMismatch( @@ -800,7 +800,7 @@ The argument `side_dimension` is the side dimension of the matrix `X`, i.e., its """ struct LogDetConeSquare <: AbstractVectorSet side_dimension::Int - function LogDetConeSquare(side_dimension::Int) + function LogDetConeSquare(side_dimension::Base.Integer) if !(side_dimension > 0) throw( DimensionMismatch( @@ -823,7 +823,7 @@ The argument `side_dimension` is the side dimension of the matrix `X`, i.e., its """ struct RootDetConeTriangle <: AbstractVectorSet side_dimension::Int - function RootDetConeTriangle(side_dimension::Int) + function RootDetConeTriangle(side_dimension::Base.Integer) if !(side_dimension > 0) throw( DimensionMismatch( @@ -849,7 +849,7 @@ The argument `side_dimension` is the side dimension of the matrix `X`, i.e., its """ struct RootDetConeSquare <: AbstractVectorSet side_dimension::Int - function RootDetConeSquare(side_dimension::Int) + function RootDetConeSquare(side_dimension::Base.Integer) if !(side_dimension > 0) throw( DimensionMismatch( @@ -997,7 +997,7 @@ function Base.:(==)( end """ - Complements(dimension::Int) + Complements(dimension::Base.Integer) The set corresponding to a mixed complementarity constraint. @@ -1050,9 +1050,7 @@ defines the complementarity problem where `0 <= x_1 ⟂ x_3 >= 0` and """ struct Complements <: AbstractVectorSet dimension::Int - # Need an explicit Int64 here, because JSON parses Int as Int64, even on - # 32-bit machines. - function Complements(dimension::Union{Int,Int64}) + function Complements(dimension::Base.Integer) if !(dimension > 0 && iseven(dimension)) throw( DimensionMismatch( @@ -1060,7 +1058,7 @@ struct Complements <: AbstractVectorSet ), ) end - return new(convert(Int, dimension)) + return new(dimension) end end diff --git a/test/sets.jl b/test/sets.jl index 52bd68d471..910e151a5f 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -205,10 +205,10 @@ function test_sets_dual_soc() end function test_sets_dual_rsoc() - rsoc2 = MOI.RotatedSecondOrderCone(2) + # rsoc2 = MOI.RotatedSecondOrderCone(2) rsoc3 = MOI.RotatedSecondOrderCone(3) - _self_dual_set_test(rsoc2) - @test MOI.dual_set(rsoc2) != rsoc3 + # _self_dual_set_test(rsoc2) + # @test MOI.dual_set(rsoc2) != rsoc3 _self_dual_set_test(rsoc3) return end From b3817368d4300a539e6d86324f295262202f1d0a Mon Sep 17 00:00:00 2001 From: odow Date: Fri, 14 May 2021 14:22:24 +1200 Subject: [PATCH 03/14] Fix Zeros(0) --- test/Bridges/Variable/map.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Bridges/Variable/map.jl b/test/Bridges/Variable/map.jl index 6d5ab34aa4..b3bffd5cb9 100644 --- a/test/Bridges/Variable/map.jl +++ b/test/Bridges/Variable/map.jl @@ -87,7 +87,7 @@ v2, c2 = MOIB.Variable.add_keys_for_bridge(map, () -> b2, set2) end b3 = VariableDummyBridge(3) -set3 = MOI.Zeros(0) +set3 = MOI.Zeros(1) v3, c3 = MOIB.Variable.add_keys_for_bridge(map, () -> b3, set3) @testset "Vector set of length 0" begin @test isempty(v3) From 563a9659477cb6860e0efa7eef56d124c25a8293 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 17 May 2021 12:10:17 +1200 Subject: [PATCH 04/14] Reduce dimension of some cones --- src/sets.jl | 23 +++++++++++------------ test/sets.jl | 14 +++++++------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/sets.jl b/src/sets.jl index fc1143e97b..5cb7d058e4 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -275,11 +275,11 @@ The ``\\ell_\\infty``-norm cone ``\\{ (t,x) \\in \\mathbb{R}^{dimension} : t \\g struct NormInfinityCone <: AbstractVectorSet dimension::Int function NormInfinityCone(dimension::Base.Integer) - if !(dimension > 1) + if !(dimension >= 1) throw( DimensionMismatch( - "Dimension of NormInfinityCone must be greater than 1, " * - "not $(dimension).", + "Dimension of NormInfinityCone must be >= 1, not" * + "$(dimension).", ), ) end @@ -298,11 +298,10 @@ The ``\\ell_1``-norm cone ``\\{ (t,x) \\in \\mathbb{R}^{dimension} : t \\ge \\lV struct NormOneCone <: AbstractVectorSet dimension::Int function NormOneCone(dimension::Base.Integer) - if !(dimension > 1) + if !(dimension >= 1) throw( DimensionMismatch( - "Dimension of NormOneCone must be greater than 1, not " * - "$(dimension).", + "Dimension of NormOneCone must be >= 1, not $(dimension).", ), ) end @@ -321,11 +320,11 @@ The second-order cone (or Lorenz cone or ``\\ell_2``-norm cone) ``\\{ (t,x) \\in struct SecondOrderCone <: AbstractVectorSet dimension::Int function SecondOrderCone(dimension::Base.Integer) - if !(dimension > 1) + if !(dimension >= 1) throw( DimensionMismatch( - "Dimension of SecondOrderCone must be greater than 1, " * - "not $(dimension).", + "Dimension of SecondOrderCone must be >= 1, not " * + "$(dimension).", ), ) end @@ -344,11 +343,11 @@ The rotated second-order cone ``\\{ (t,u,x) \\in \\mathbb{R}^{dimension} : 2tu \ struct RotatedSecondOrderCone <: AbstractVectorSet dimension::Int function RotatedSecondOrderCone(dimension::Base.Integer) - if !(dimension > 2) + if !(dimension >= 2) throw( DimensionMismatch( - "Dimension of RotatedSecondOrderCone must be greater " * - "than 2, not $(dimension).", + "Dimension of RotatedSecondOrderCone must be >= 2, not " * + "$(dimension).", ), ) end diff --git a/test/sets.jl b/test/sets.jl index 910e151a5f..e98d3e697c 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -119,10 +119,10 @@ function test_sets_DimensionMismatch() @test_throws DimensionMismatch MOI.Zeros(0) @test_throws DimensionMismatch MOI.Nonnegatives(0) @test_throws DimensionMismatch MOI.Nonpositives(0) - @test_throws DimensionMismatch MOI.NormInfinityCone(1) - @test_throws DimensionMismatch MOI.NormOneCone(1) - @test_throws DimensionMismatch MOI.SecondOrderCone(1) - @test_throws DimensionMismatch MOI.RotatedSecondOrderCone(2) + @test_throws DimensionMismatch MOI.NormInfinityCone(0) + @test_throws DimensionMismatch MOI.NormOneCone(0) + @test_throws DimensionMismatch MOI.SecondOrderCone(0) + @test_throws DimensionMismatch MOI.RotatedSecondOrderCone(1) @test_throws DimensionMismatch MOI.GeometricMeanCone(1) @test_throws DimensionMismatch MOI.RelativeEntropyCone(1) @test_throws DimensionMismatch MOI.RelativeEntropyCone(4) @@ -205,10 +205,10 @@ function test_sets_dual_soc() end function test_sets_dual_rsoc() - # rsoc2 = MOI.RotatedSecondOrderCone(2) + rsoc2 = MOI.RotatedSecondOrderCone(2) rsoc3 = MOI.RotatedSecondOrderCone(3) - # _self_dual_set_test(rsoc2) - # @test MOI.dual_set(rsoc2) != rsoc3 + _self_dual_set_test(rsoc2) + @test MOI.dual_set(rsoc2) != rsoc3 _self_dual_set_test(rsoc3) return end From 38ed6b53a5169d91b7aff284f666a091d64ae852 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 17 May 2021 12:12:58 +1200 Subject: [PATCH 05/14] Reword errors --- src/sets.jl | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/sets.jl b/src/sets.jl index 5cb7d058e4..85a490c56b 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -116,8 +116,7 @@ struct Reals <: AbstractVectorSet if !(dimension > 0) throw( DimensionMismatch( - "Dimension of Reals must be greater than 0, not " * - "$(dimension).", + "Dimension of Reals must be > 0, not $(dimension).", ), ) end @@ -139,8 +138,7 @@ struct Zeros <: AbstractVectorSet if !(dimension > 0) throw( DimensionMismatch( - "Dimension of Zeros must be greater than 0, not " * - "$(dimension).", + "Dimension of Zeros must be > 0, not $(dimension).", ), ) end @@ -162,8 +160,7 @@ struct Nonnegatives <: AbstractVectorSet if !(dimension > 0) throw( DimensionMismatch( - "Dimension of Nonnegatives must be greater than 0, not " * - "$(dimension).", + "Dimension of Nonnegatives must be > 0, not $(dimension).", ), ) end @@ -185,8 +182,7 @@ struct Nonpositives <: AbstractVectorSet if !(dimension > 0) throw( DimensionMismatch( - "Dimension of Nonpositives must be greater than 0, not " * - "$(dimension).", + "Dimension of Nonpositives must be > 0, not $(dimension).", ), ) end @@ -374,8 +370,8 @@ struct GeometricMeanCone <: AbstractVectorSet if !(dimension > 1) throw( DimensionMismatch( - "Dimension of GeometricMeanCone must be greater than 1, " * - "not $(dimension).", + "Dimension of GeometricMeanCone must be > 1, not " * + "$(dimension).", ), ) end @@ -450,11 +446,11 @@ The dual of the relative entropy cone is struct RelativeEntropyCone <: AbstractVectorSet dimension::Int function RelativeEntropyCone(dimension::Base.Integer) - if !(dimension > 1 && isodd(dimension)) + if !(dimension >= 3 && isodd(dimension)) throw( DimensionMismatch( - "Dimension of RelativeEntropyCone must be greater an odd " * - "integer greater than 2, not $(dimension).", + "Dimension of RelativeEntropyCone must be an odd integer " * + ">= 3, not $(dimension).", ), ) end From d6ff6df2ac0657a81c10b45c0a3752fe9b8ad05e Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 17 May 2021 13:58:26 +1200 Subject: [PATCH 06/14] Add ZeroDimSet --- test/Bridges/Variable/map.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/Bridges/Variable/map.jl b/test/Bridges/Variable/map.jl index b3bffd5cb9..bf3de204ef 100644 --- a/test/Bridges/Variable/map.jl +++ b/test/Bridges/Variable/map.jl @@ -86,13 +86,15 @@ v2, c2 = MOIB.Variable.add_keys_for_bridge(map, () -> b2, set2) @test collect(values(map)) == [b1, b2] end +struct _ZeroDimSet <: MOI.AbstractVectorSet + dimension::Int +end b3 = VariableDummyBridge(3) -set3 = MOI.Zeros(1) +set3 = _ZeroDimSet(0) v3, c3 = MOIB.Variable.add_keys_for_bridge(map, () -> b3, set3) @testset "Vector set of length 0" begin @test isempty(v3) @test c3.value == 0 - bridges = collect(values(map)) @test sort([b.id for b in bridges]) == 1:2 elements = sort(collect(map), by = el -> el.second.id) From f38e422c53ffc24616182c4cc480da286ae8f464 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 17 May 2021 14:41:45 +1200 Subject: [PATCH 07/14] Fix SDPA --- src/FileFormats/SDPA/SDPA.jl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/FileFormats/SDPA/SDPA.jl b/src/FileFormats/SDPA/SDPA.jl index 3c0aa540f8..079a538ec6 100644 --- a/src/FileFormats/SDPA/SDPA.jl +++ b/src/FileFormats/SDPA/SDPA.jl @@ -164,10 +164,12 @@ function Base.write(io::IO, model::Model{T}) where {T} end println(io) - index_map = Vector{Tuple{Int,Int}}( - undef, - MOI.dimension(MOI.PositiveSemidefiniteConeTriangle(max_dim)), - ) + max_index_dim = if max_dim > 0 + MOI.dimension(MOI.PositiveSemidefiniteConeTriangle(max_dim)) + else + 0 + end + index_map = Vector{Tuple{Int,Int}}(undef, max_index_dim) k = 0 for col in 1:max_dim for row in 1:col From 40034b57a047651f236b5c7f7afe5a5f176e75cd Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 17 May 2021 15:19:13 +1200 Subject: [PATCH 08/14] Fix --- test/Utilities/model.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Utilities/model.jl b/test/Utilities/model.jl index 8af12b62c9..584d6c7de0 100644 --- a/test/Utilities/model.jl +++ b/test/Utilities/model.jl @@ -354,8 +354,8 @@ end }(), ) - f5 = MOI.VectorOfVariables([x]) - c5 = MOI.add_constraint(model, f5, MOI.RotatedSecondOrderCone(1)) + f5 = MOI.VectorOfVariables([x, x]) + c5 = MOI.add_constraint(model, f5, MOI.RotatedSecondOrderCone(2)) @test 1 == @inferred MOI.get( model, MOI.NumberOfConstraints{ From 573be94bca312fdcd8a75b7758b0f0c213e7e089 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 7 Jun 2021 11:05:58 +1200 Subject: [PATCH 09/14] Allow empty sets --- src/sets.jl | 190 ++++++++++++++++++++++++++++++++------------------- test/sets.jl | 53 ++++++++------ 2 files changed, 152 insertions(+), 91 deletions(-) diff --git a/src/sets.jl b/src/sets.jl index 85a490c56b..1c818858ce 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -108,15 +108,16 @@ dimension(s::AbstractVectorSet) = s.dimension # .dimension field is conventional """ Reals(dimension) -The set ``\\mathbb{R}^{dimension}`` (containing all points) of dimension `dimension`. +The set ``\\mathbb{R}^{dimension}`` (containing all points) of dimension +`dimension`. """ struct Reals <: AbstractVectorSet dimension::Int function Reals(dimension::Base.Integer) - if !(dimension > 0) + if !(dimension >= 0) throw( DimensionMismatch( - "Dimension of Reals must be > 0, not $(dimension).", + "Dimension of Reals must be >= 0, not $(dimension).", ), ) end @@ -130,15 +131,16 @@ dual_set_type(::Type{Reals}) = Zeros """ Zeros(dimension) -The set ``\\{ 0 \\}^{dimension}`` (containing only the origin) of dimension `dimension`. +The set ``\\{ 0 \\}^{dimension}`` (containing only the origin) of dimension +`dimension`. """ struct Zeros <: AbstractVectorSet dimension::Int function Zeros(dimension::Base.Integer) - if !(dimension > 0) + if !(dimension >= 0) throw( DimensionMismatch( - "Dimension of Zeros must be > 0, not $(dimension).", + "Dimension of Zeros must be >= 0, not $(dimension).", ), ) end @@ -152,15 +154,16 @@ dual_set_type(::Type{Zeros}) = Reals """ Nonnegatives(dimension) -The nonnegative orthant ``\\{ x \\in \\mathbb{R}^{dimension} : x \\ge 0 \\}`` of dimension `dimension`. +The nonnegative orthant ``\\{ x \\in \\mathbb{R}^{dimension} : x \\ge 0 \\}`` of +dimension `dimension`. """ struct Nonnegatives <: AbstractVectorSet dimension::Int function Nonnegatives(dimension::Base.Integer) - if !(dimension > 0) + if !(dimension >= 0) throw( DimensionMismatch( - "Dimension of Nonnegatives must be > 0, not $(dimension).", + "Dimension of Nonnegatives must be >= 0, not $(dimension).", ), ) end @@ -174,15 +177,16 @@ dual_set_type(::Type{Nonnegatives}) = Nonnegatives """ Nonpositives(dimension) -The nonpositive orthant ``\\{ x \\in \\mathbb{R}^{dimension} : x \\le 0 \\}`` of dimension `dimension`. +The nonpositive orthant ``\\{ x \\in \\mathbb{R}^{dimension} : x \\le 0 \\}`` of +dimension `dimension`. """ struct Nonpositives <: AbstractVectorSet dimension::Int function Nonpositives(dimension::Base.Integer) - if !(dimension > 0) + if !(dimension >= 0) throw( DimensionMismatch( - "Dimension of Nonpositives must be > 0, not $(dimension).", + "Dimension of Nonpositives must be >= 0, not $(dimension).", ), ) end @@ -274,7 +278,7 @@ struct NormInfinityCone <: AbstractVectorSet if !(dimension >= 1) throw( DimensionMismatch( - "Dimension of NormInfinityCone must be >= 1, not" * + "Dimension of NormInfinityCone must be >= 1, not " * "$(dimension).", ), ) @@ -357,20 +361,23 @@ dual_set_type(::Type{RotatedSecondOrderCone}) = RotatedSecondOrderCone """ GeometricMeanCone(dimension) -The geometric mean cone ``\\{ (t,x) \\in \\mathbb{R}^{n+1} : x \\ge 0, t \\le \\sqrt[n]{x_1 x_2 \\cdots x_n} \\}`` of dimension `dimension```{}=n+1``. +The geometric mean cone +``\\{ (t,x) \\in \\mathbb{R}^{n+1} : x \\ge 0, t \\le \\sqrt[n]{x_1 x_2 \\cdots x_n} \\}``, +where `dimension = n + 1 >= 2`. -### Duality note +## Duality note The dual of the geometric mean cone is -``\\{ (u, v) \\in \\mathbb{R}^{n+1} : u \\le 0, v \\ge 0, -u \\le n \\sqrt[n]{\\prod_i v_i} \\}`` of dimension `dimension```{}=n+1``. +``\\{ (u, v) \\in \\mathbb{R}^{n+1} : u \\le 0, v \\ge 0, -u \\le n \\sqrt[n]{\\prod_i v_i} \\}``, +where `dimension = n + 1 >= 2`. """ struct GeometricMeanCone <: AbstractVectorSet dimension::Int function GeometricMeanCone(dimension::Base.Integer) - if !(dimension > 1) + if !(dimension >= 2) throw( DimensionMismatch( - "Dimension of GeometricMeanCone must be > 1, not " * + "Dimension of GeometricMeanCone must be >= 2, not " * "$(dimension).", ), ) @@ -436,7 +443,9 @@ end """ RelativeEntropyCone(dimension) -The relative entropy cone ``\\{ (u, v, w) \\in \\mathbb{R}^{1+2n} : u \\ge \\sum_{i=1}^n w_i \\log (\\frac{w_i}{v_i}), v_i \\ge 0, w_i \\ge 0 \\}`` of dimension `dimension```{}=2n+1``. +The relative entropy cone +``\\{ (u, v, w) \\in \\mathbb{R}^{1+2n} : u \\ge \\sum_{i=1}^n w_i \\log(\\frac{w_i}{v_i}), v_i \\ge 0, w_i \\ge 0 \\}``, +where `dimension = 2n + 1 >= 3`. ### Duality note @@ -446,11 +455,11 @@ The dual of the relative entropy cone is struct RelativeEntropyCone <: AbstractVectorSet dimension::Int function RelativeEntropyCone(dimension::Base.Integer) - if !(dimension >= 3 && isodd(dimension)) + if !(dimension >= 1 && isodd(dimension)) throw( DimensionMismatch( "Dimension of RelativeEntropyCone must be an odd integer " * - ">= 3, not $(dimension).", + ">= 1, not $(dimension).", ), ) end @@ -461,18 +470,23 @@ end """ NormSpectralCone(row_dim, column_dim) -The epigraph of the matrix spectral norm (maximum singular value function) ``\\{ (t, X) \\in \\mathbb{R}^{1 + row_dim \\times column_dim} : t \\ge \\sigma_1(X) \\}`` where ``\\sigma_i`` is the ``i``th singular value of the matrix ``X`` of row dimension `row_dim` and column dimension `column_dim`. -The matrix X is vectorized by stacking the columns, matching the behavior of Julia's `vec` function. +The epigraph of the matrix spectral norm (maximum singular value function) +``\\{ (t, X) \\in \\mathbb{R}^{1 + row_dim \\times column_dim} : t \\ge \\sigma_1(X) \\}``, +where ``\\sigma_i`` is the ``i``th singular value of the matrix ``X`` of row +dimension `row_dim` and column dimension `column_dim`. + +The matrix X is vectorized by stacking the columns, matching the behavior of +Julia's `vec` function. """ struct NormSpectralCone <: AbstractVectorSet row_dim::Int column_dim::Int function NormSpectralCone(row_dim::Base.Integer, column_dim::Base.Integer) - if !(row_dim > 0 && column_dim > 0) + if !(row_dim >= 0 && column_dim >= 0) throw( DimensionMismatch( - "Dimension of NormSpectralCone must be strictly " * - "positive, not ($(row_dim), $(column_dim)).", + "Dimensions of NormSpectralCone must be >= 0, not " * + "($(row_dim), $(column_dim)).", ), ) end @@ -486,18 +500,23 @@ dual_set_type(::Type{NormSpectralCone}) = NormNuclearCone """ NormNuclearCone(row_dim, column_dim) -The epigraph of the matrix nuclear norm (sum of singular values function) ``\\{ (t, X) \\in \\mathbb{R}^{1 + row_dim \\times column_dim} : t \\ge \\sum_i \\sigma_i(X) \\}`` where ``\\sigma_i`` is the ``i``th singular value of the matrix ``X`` of row dimension `row_dim` and column dimension `column_dim`. -The matrix X is vectorized by stacking the columns, matching the behavior of Julia's `vec` function. +The epigraph of the matrix nuclear norm (sum of singular values function) +``\\{ (t, X) \\in \\mathbb{R}^{1 + row_dim \\times column_dim} : t \\ge \\sum_i \\sigma_i(X) \\}``, +where ``\\sigma_i`` is the ``i``th singular value of the matrix ``X`` of row +dimension `row_dim` and column dimension `column_dim`. + +The matrix X is vectorized by stacking the columns, matching the behavior of +Julia's `vec` function. """ struct NormNuclearCone <: AbstractVectorSet row_dim::Int column_dim::Int function NormNuclearCone(row_dim::Base.Integer, column_dim::Base.Integer) - if !(row_dim > 0 && column_dim > 0) + if !(row_dim >= 0 && column_dim >= 0) throw( DimensionMismatch( - "Dimension of NormNuclearCone must be strictly " * - "positive, not ($(row_dim), $(column_dim)).", + "Dimension of NormNuclearCone must be >= 0, not " * + "($(row_dim), $(column_dim)).", ), ) end @@ -681,18 +700,19 @@ end PositiveSemidefiniteConeTriangle(side_dimension) <: AbstractSymmetricMatrixSetTriangle The (vectorized) cone of symmetric positive semidefinite matrices, with -`side_dimension` rows and columns. See -[`AbstractSymmetricMatrixSetTriangle`](@ref) for more details on the vectorized -form. +`side_dimension` rows and columns. + +See [`AbstractSymmetricMatrixSetTriangle`](@ref) for more details on the +vectorized form. """ struct PositiveSemidefiniteConeTriangle <: AbstractSymmetricMatrixSetTriangle side_dimension::Int function PositiveSemidefiniteConeTriangle(side_dimension::Base.Integer) - if !(side_dimension > 0) + if !(side_dimension >= 0) throw( DimensionMismatch( "Side dimension of PositiveSemidefiniteConeTriangle must " * - "be strictly positive, not $(side_dimension).", + "be >= 0, not $(side_dimension).", ), ) end @@ -708,13 +728,18 @@ end """ PositiveSemidefiniteConeSquare(side_dimension) <: AbstractSymmetricMatrixSetSquare -The cone of symmetric positive semidefinite matrices, with side length `side_dimension`. - See [`AbstractSymmetricMatrixSetSquare`](@ref) for more details on the vectorized -form. +The cone of symmetric positive semidefinite matrices, with side length +`side_dimension`. + +See [`AbstractSymmetricMatrixSetSquare`](@ref) for more details on the +vectorized form. + +The entries of the matrix are given column by column (or equivalently, row by +row). -The entries of the matrix are given column by column (or equivalently, row by row). The matrix is both constrained to be symmetric and to be positive semidefinite. -That is, if the functions in entries ``(i, j)`` and ``(j, i)`` are different, then a constraint will be added to make sure that the entries are equal. +That is, if the functions in entries ``(i, j)`` and ``(j, i)`` are different, +then a constraint will be added to make sure that the entries are equal. ### Examples @@ -725,18 +750,21 @@ Constraining the matrix -z & 0\\\\ \\end{bmatrix} ``` -to be symmetric positive semidefinite can be achieved by constraining the vector ``(1, -z, -y, 0)`` (or ``(1, -y, -z, 0)``) -to belong to the `PositiveSemidefiniteConeSquare(2)`. -It both constrains ``y = z`` and ``(1, -y, 0)`` (or ``(1, -z, 0)``) to be in `PositiveSemidefiniteConeTriangle(2)`. +to be symmetric positive semidefinite can be achieved by constraining the vector +``(1, -z, -y, 0)`` (or ``(1, -y, -z, 0)``) to belong to the +`PositiveSemidefiniteConeSquare(2)`. + +It both constrains ``y = z`` and ``(1, -y, 0)`` (or ``(1, -z, 0)``) to be in +`PositiveSemidefiniteConeTriangle(2)`. """ struct PositiveSemidefiniteConeSquare <: AbstractSymmetricMatrixSetSquare side_dimension::Int function PositiveSemidefiniteConeSquare(side_dimension::Base.Integer) - if !(side_dimension > 0) + if !(side_dimension >= 0) throw( DimensionMismatch( "Side dimension of PositiveSemidefiniteConeSquare must " * - "be strictly positive, not $(side_dimension).", + "be >= 0, not $(side_dimension).", ), ) end @@ -764,17 +792,22 @@ end """ LogDetConeTriangle(side_dimension) -The log-determinant cone ``\\{ (t, u, X) \\in \\mathbb{R}^{2 + d(d+1)/2} : t \\le u \\log(\\det(X/u)), u > 0 \\}`` where the matrix `X` is represented in the same symmetric packed format as in the `PositiveSemidefiniteConeTriangle`. -The argument `side_dimension` is the side dimension of the matrix `X`, i.e., its number of rows or columns. +The log-determinant cone +``\\{ (t, u, X) \\in \\mathbb{R}^{2 + d(d+1)/2} : t \\le u \\log(\\det(X/u)), u > 0 \\}``, +where the matrix `X` is represented in the same symmetric packed format as in +the `PositiveSemidefiniteConeTriangle`. + +The argument `side_dimension` is the side dimension of the matrix `X`, i.e., its +number of rows or columns. """ struct LogDetConeTriangle <: AbstractVectorSet side_dimension::Int function LogDetConeTriangle(side_dimension::Base.Integer) - if !(side_dimension > 0) + if !(side_dimension >= 0) throw( DimensionMismatch( - "Side dimension of LogDetConeTriangle must " * - "be strictly positive, not $(side_dimension).", + "Side dimension of LogDetConeTriangle must be >= 0, not " * + "$(side_dimension).", ), ) end @@ -789,18 +822,25 @@ end """ LogDetConeSquare(side_dimension) -The log-determinant cone ``\\{ (t, u, X) \\in \\mathbb{R}^{2 + d^2} : t \\le u \\log(\\det(X/u)), X \\text{ symmetric}, u > 0 \\}`` where the matrix `X` is represented in the same format as in the `PositiveSemidefiniteConeSquare`. -Similarly to `PositiveSemidefiniteConeSquare`, constraints are added to ensures that `X` is symmetric. -The argument `side_dimension` is the side dimension of the matrix `X`, i.e., its number of rows or columns. +The log-determinant cone +``\\{ (t, u, X) \\in \\mathbb{R}^{2 + d^2} : t \\le u \\log(\\det(X/u)), X \\text{ symmetric}, u > 0 \\}``, +where the matrix `X` is represented in the same format as in the +[`PositiveSemidefiniteConeSquare`](@ref). + +Similarly to [`PositiveSemidefiniteConeSquare`](@ref), constraints are added to +ensure that `X` is symmetric. + +The argument `side_dimension` is the side dimension of the matrix `X`, i.e., its +number of rows or columns. """ struct LogDetConeSquare <: AbstractVectorSet side_dimension::Int function LogDetConeSquare(side_dimension::Base.Integer) - if !(side_dimension > 0) + if !(side_dimension >= 0) throw( DimensionMismatch( - "Side dimension of LogDetConeSquare must " * - "be strictly positive, not $(side_dimension).", + "Side dimension of LogDetConeSquare must be >= 0, not " * + "$(side_dimension).", ), ) end @@ -813,17 +853,22 @@ dimension(s::LogDetConeSquare) = 2 + s.side_dimension^2 """ RootDetConeTriangle(side_dimension) -The root-determinant cone ``\\{ (t, X) \\in \\mathbb{R}^{1 + d(d+1)/2} : t \\le \\det(X)^{1/d} \\}`` where the matrix `X` is represented in the same symmetric packed format as in the `PositiveSemidefiniteConeTriangle`. -The argument `side_dimension` is the side dimension of the matrix `X`, i.e., its number of rows or columns. +The root-determinant cone +``\\{ (t, X) \\in \\mathbb{R}^{1 + d(d+1)/2} : t \\le \\det(X)^{1/d} \\}``, +where the matrix `X` is represented in the same symmetric packed format as in +the [`PositiveSemidefiniteConeTriangle`](@ref). + +The argument `side_dimension` is the side dimension of the matrix `X`, i.e., its +number of rows or columns. """ struct RootDetConeTriangle <: AbstractVectorSet side_dimension::Int function RootDetConeTriangle(side_dimension::Base.Integer) - if !(side_dimension > 0) + if !(side_dimension >= 0) throw( DimensionMismatch( - "Side dimension of RootDetConeTriangle must " * - "be strictly positive, not $(side_dimension).", + "Side dimension of RootDetConeTriangle must be >= 0, not " * + "$(side_dimension).", ), ) end @@ -838,18 +883,25 @@ end """ RootDetConeSquare(side_dimension) -The root-determinant cone ``\\{ (t, X) \\in \\mathbb{R}^{1 + d^2} : t \\le \\det(X)^{1/d}, X \\text{ symmetric} \\}`` where the matrix `X` is represented in the same format as in the `PositiveSemidefiniteConeSquare`. -Similarly to `PositiveSemidefiniteConeSquare`, constraints are added to ensure that `X` is symmetric. -The argument `side_dimension` is the side dimension of the matrix `X`, i.e., its number of rows or columns. +The root-determinant cone +``\\{ (t, X) \\in \\mathbb{R}^{1 + d^2} : t \\le \\det(X)^{1/d}, X \\text{ symmetric} \\}``, +where the matrix `X` is represented in the same format as +[`PositiveSemidefiniteConeSquare`](@ref). + +Similarly to [`PositiveSemidefiniteConeSquare`](@ref), constraints are added to +ensure that `X` is symmetric. + +The argument `side_dimension` is the side dimension of the matrix `X`, i.e., its +number of rows or columns. """ struct RootDetConeSquare <: AbstractVectorSet side_dimension::Int function RootDetConeSquare(side_dimension::Base.Integer) - if !(side_dimension > 0) + if !(side_dimension >= 0) throw( DimensionMismatch( - "Side dimension of RootDetConeSquare must " * - "be strictly positive, not $(side_dimension).", + "Side dimension of RootDetConeSquare must be >= 0, not " * + "$(side_dimension).", ), ) end @@ -1046,7 +1098,7 @@ defines the complementarity problem where `0 <= x_1 ⟂ x_3 >= 0` and struct Complements <: AbstractVectorSet dimension::Int function Complements(dimension::Base.Integer) - if !(dimension > 0 && iseven(dimension)) + if !(dimension >= 0 && iseven(dimension)) throw( DimensionMismatch( "Dimension of Complements must be even, not $(dimension).", diff --git a/test/sets.jl b/test/sets.jl index e98d3e697c..6054bc79fe 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -115,28 +115,37 @@ function test_sets_dimension() end function test_sets_DimensionMismatch() - @test_throws DimensionMismatch MOI.Reals(0) - @test_throws DimensionMismatch MOI.Zeros(0) - @test_throws DimensionMismatch MOI.Nonnegatives(0) - @test_throws DimensionMismatch MOI.Nonpositives(0) - @test_throws DimensionMismatch MOI.NormInfinityCone(0) - @test_throws DimensionMismatch MOI.NormOneCone(0) - @test_throws DimensionMismatch MOI.SecondOrderCone(0) - @test_throws DimensionMismatch MOI.RotatedSecondOrderCone(1) - @test_throws DimensionMismatch MOI.GeometricMeanCone(1) - @test_throws DimensionMismatch MOI.RelativeEntropyCone(1) - @test_throws DimensionMismatch MOI.RelativeEntropyCone(4) - @test_throws DimensionMismatch MOI.NormSpectralCone(1, 0) - @test_throws DimensionMismatch MOI.NormSpectralCone(0, 1) - @test_throws DimensionMismatch MOI.NormNuclearCone(1, 0) - @test_throws DimensionMismatch MOI.NormNuclearCone(0, 1) - @test_throws DimensionMismatch MOI.PositiveSemidefiniteConeTriangle(0) - @test_throws DimensionMismatch MOI.PositiveSemidefiniteConeSquare(0) - @test_throws DimensionMismatch MOI.LogDetConeTriangle(0) - @test_throws DimensionMismatch MOI.LogDetConeSquare(0) - @test_throws DimensionMismatch MOI.RootDetConeTriangle(0) - @test_throws DimensionMismatch MOI.RootDetConeSquare(0) - @test_throws DimensionMismatch MOI.Complements(0) + for (S, min_dimension) in ( + (MOI.Reals, 0), + (MOI.Zeros, 0), + (MOI.Nonnegatives, 0), + (MOI.Nonpositives, 0), + (MOI.NormInfinityCone, 1), + (MOI.NormOneCone, 1), + (MOI.SecondOrderCone, 1), + (MOI.RotatedSecondOrderCone, 2), + (MOI.GeometricMeanCone, 2), + (MOI.Complements, 0), + (MOI.RelativeEntropyCone, 1), + (MOI.PositiveSemidefiniteConeTriangle, 0), + (MOI.PositiveSemidefiniteConeSquare, 0), + (MOI.LogDetConeTriangle, 0), + (MOI.LogDetConeSquare, 0), + (MOI.RootDetConeTriangle, 0), + (MOI.RootDetConeSquare, 0), + ) + @test_throws DimensionMismatch S(min_dimension-1) + @test S(min_dimension) isa S + end + @test_throws DimensionMismatch MOI.NormSpectralCone(-1, 0) + @test_throws DimensionMismatch MOI.NormSpectralCone(0, -1) + @test MOI.NormSpectralCone(0, 0) isa MOI.NormSpectralCone + @test_throws DimensionMismatch MOI.NormNuclearCone(-1, 0) + @test_throws DimensionMismatch MOI.NormNuclearCone(0, -1) + @test MOI.NormNuclearCone(0, 0) isa MOI.NormNuclearCone + # Other dimension checks + @test_throws DimensionMismatch MOI.RelativeEntropyCone(2) + @test_throws DimensionMismatch MOI.Complements(-3) @test_throws DimensionMismatch MOI.Complements(3) return end From 326f1e638971d394a71b20df7d83d9084e882295 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 7 Jun 2021 15:40:54 +1200 Subject: [PATCH 10/14] Update sets.jl --- test/sets.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/sets.jl b/test/sets.jl index 6054bc79fe..7f8fcab254 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -134,7 +134,7 @@ function test_sets_DimensionMismatch() (MOI.RootDetConeTriangle, 0), (MOI.RootDetConeSquare, 0), ) - @test_throws DimensionMismatch S(min_dimension-1) + @test_throws DimensionMismatch S(min_dimension - 1) @test S(min_dimension) isa S end @test_throws DimensionMismatch MOI.NormSpectralCone(-1, 0) From 7efd7d94824464de9129dee2d4a143562124fa37 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Fri, 18 Jun 2021 14:45:51 +1200 Subject: [PATCH 11/14] Update SDPA.jl --- src/FileFormats/SDPA/SDPA.jl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/FileFormats/SDPA/SDPA.jl b/src/FileFormats/SDPA/SDPA.jl index 079a538ec6..5fa9e8d971 100644 --- a/src/FileFormats/SDPA/SDPA.jl +++ b/src/FileFormats/SDPA/SDPA.jl @@ -164,11 +164,8 @@ function Base.write(io::IO, model::Model{T}) where {T} end println(io) - max_index_dim = if max_dim > 0 + max_index_dim = MOI.dimension(MOI.PositiveSemidefiniteConeTriangle(max_dim)) - else - 0 - end index_map = Vector{Tuple{Int,Int}}(undef, max_index_dim) k = 0 for col in 1:max_dim From db0c1962b31c0f7769e68d6748fdb997c1345a27 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Fri, 18 Jun 2021 14:46:25 +1200 Subject: [PATCH 12/14] Update map.jl --- test/Bridges/Variable/map.jl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/Bridges/Variable/map.jl b/test/Bridges/Variable/map.jl index bf3de204ef..7dcc31172e 100644 --- a/test/Bridges/Variable/map.jl +++ b/test/Bridges/Variable/map.jl @@ -86,11 +86,8 @@ v2, c2 = MOIB.Variable.add_keys_for_bridge(map, () -> b2, set2) @test collect(values(map)) == [b1, b2] end -struct _ZeroDimSet <: MOI.AbstractVectorSet - dimension::Int -end b3 = VariableDummyBridge(3) -set3 = _ZeroDimSet(0) +set3 = MOI.Zeros(0) v3, c3 = MOIB.Variable.add_keys_for_bridge(map, () -> b3, set3) @testset "Vector set of length 0" begin @test isempty(v3) From 58430a0fab80c9461ff7692830630489f94ee24e Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Fri, 18 Jun 2021 14:48:02 +1200 Subject: [PATCH 13/14] Update sets.jl --- src/sets.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sets.jl b/src/sets.jl index 1c818858ce..88564ec3d8 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -445,7 +445,7 @@ end The relative entropy cone ``\\{ (u, v, w) \\in \\mathbb{R}^{1+2n} : u \\ge \\sum_{i=1}^n w_i \\log(\\frac{w_i}{v_i}), v_i \\ge 0, w_i \\ge 0 \\}``, -where `dimension = 2n + 1 >= 3`. +where `dimension = 2n + 1 >= 1`. ### Duality note From 0e6076a9047d007a4927c20009a076befec60905 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 21 Jun 2021 09:14:00 +1200 Subject: [PATCH 14/14] Update SDPA.jl --- src/FileFormats/SDPA/SDPA.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/FileFormats/SDPA/SDPA.jl b/src/FileFormats/SDPA/SDPA.jl index 5fa9e8d971..a85e75cfb6 100644 --- a/src/FileFormats/SDPA/SDPA.jl +++ b/src/FileFormats/SDPA/SDPA.jl @@ -164,8 +164,7 @@ function Base.write(io::IO, model::Model{T}) where {T} end println(io) - max_index_dim = - MOI.dimension(MOI.PositiveSemidefiniteConeTriangle(max_dim)) + max_index_dim = MOI.dimension(MOI.PositiveSemidefiniteConeTriangle(max_dim)) index_map = Vector{Tuple{Int,Int}}(undef, max_index_dim) k = 0 for col in 1:max_dim