diff --git a/docs/src/apireference.md b/docs/src/apireference.md index 319db570ba..8120a338b9 100644 --- a/docs/src/apireference.md +++ b/docs/src/apireference.md @@ -288,6 +288,7 @@ AbstractVectorSet Functions for getting properties of sets. ```@docs dimension +dual_set constant(s::EqualTo) supports_dimension_update update_dimension diff --git a/src/sets.jl b/src/sets.jl index 9ff4b316ea..6d951b60b9 100644 --- a/src/sets.jl +++ b/src/sets.jl @@ -30,6 +30,31 @@ julia> dimension(PositiveSemidefiniteConeTriangle(2)) """ function dimension end +""" + dual_set(s::AbstractSet) + +Return the dual set of `s`, that is the dual cone of the set. This follows the +definition of duality discussed in [Duals](@ref). +See [Dual cone](https://en.wikipedia.org/wiki/Dual_cone_and_polar_cone) for more information. +If the dual cone is not defined it returns an error. + +### Examples + +```jldocstest +julia> dual_set(Reals(4)) +Zeros(4) + +julia> dual_set(SecondOrderCone(5)) +SecondOrderCone(5) + +julia> dual_set(ExponentialCone()) +DualExponentialCone() +``` +""" +function dual_set end + +dual_set(s::AbstractSet) = error("Dual of $s is not implemented.") + """ AbstractScalarSet @@ -59,6 +84,8 @@ struct Reals <: AbstractVectorSet dimension::Int end +dual_set(s::Reals) = Zeros(dimension(s)) + """ Zeros(dimension) @@ -68,6 +95,8 @@ struct Zeros <: AbstractVectorSet dimension::Int end +dual_set(s::Zeros) = Reals(dimension(s)) + """ Nonnegatives(dimension) @@ -77,6 +106,8 @@ struct Nonnegatives <: AbstractVectorSet dimension::Int end +dual_set(s::Nonnegatives) = copy(s) + """ Nonpositives(dimension) @@ -86,6 +117,8 @@ struct Nonpositives <: AbstractVectorSet dimension::Int end +dual_set(s::Nonpositives) = copy(s) + """ GreaterThan{T <: Real}(lower::T) @@ -158,6 +191,8 @@ struct SecondOrderCone <: AbstractVectorSet dimension::Int end +dual_set(s::SecondOrderCone) = copy(s) + """ RotatedSecondOrderCone(dimension) @@ -167,6 +202,8 @@ struct RotatedSecondOrderCone <: AbstractVectorSet dimension::Int end +dual_set(s::RotatedSecondOrderCone) = copy(s) + """ GeometricMeanCone(dimension) @@ -183,6 +220,8 @@ The 3-dimensional exponential cone ``\\{ (x,y,z) \\in \\mathbb{R}^3 : y \\exp (x """ struct ExponentialCone <: AbstractVectorSet end +dual_set(s::ExponentialCone) = DualExponentialCone() + """ DualExponentialCone() @@ -190,6 +229,8 @@ The 3-dimensional dual exponential cone ``\\{ (u,v,w) \\in \\mathbb{R}^3 : -u \\ """ struct DualExponentialCone <: AbstractVectorSet end +dual_set(s::DualExponentialCone) = ExponentialCone() + """ PowerCone{T <: Real}(exponent::T) @@ -199,6 +240,8 @@ struct PowerCone{T <: Real} <: AbstractVectorSet exponent::T end +dual_set(s::PowerCone{T}) where T <: Real = DualPowerCone{T}(s.exponent) + """ DualPowerCone{T <: Real}(exponent::T) @@ -208,6 +251,8 @@ struct DualPowerCone{T <: Real} <: AbstractVectorSet exponent::T end +dual_set(s::DualPowerCone{T}) where T <: Real = PowerCone{T}(s.exponent) + dimension(s::Union{ExponentialCone, DualExponentialCone, PowerCone, DualPowerCone}) = 3 """ @@ -383,6 +428,8 @@ struct PositiveSemidefiniteConeTriangle <: AbstractSymmetricMatrixSetTriangle side_dimension::Int end +dual_set(s::PositiveSemidefiniteConeTriangle) = copy(s) + """ PositiveSemidefiniteConeSquare(side_dimension) <: AbstractSymmetricMatrixSetSquare @@ -411,6 +458,11 @@ struct PositiveSemidefiniteConeSquare <: AbstractSymmetricMatrixSetSquare side_dimension::Int end +function dual_set(s::PositiveSemidefiniteConeSquare) + return error("""Dual of $s is not defined in MathOptInterface. + For more details see the comments in src/Bridges/Constraint/square.jl""") +end + triangular_form(::Type{PositiveSemidefiniteConeSquare}) = PositiveSemidefiniteConeTriangle """ diff --git a/test/Utilities/sets.jl b/test/Utilities/sets.jl index 12ca6c5684..e2db3594fc 100644 --- a/test/Utilities/sets.jl +++ b/test/Utilities/sets.jl @@ -26,6 +26,75 @@ end @test MOI.dimension(MOI.SOS2(collect(1:6))) === 6 end +@testset "Dual Set" begin + # Nonpositives + nonpositives3 = MOI.Nonpositives(3) + nonpositives4 = MOI.Nonpositives(4) + @test MOI.dual_set(nonpositives3) == nonpositives3 + @test MOI.dual_set(nonpositives3) != nonpositives4 + @test MOI.dual_set(nonpositives4) == nonpositives4 + # Nonnegatives + nonnegatives3 = MOI.Nonnegatives(3) + nonnegatives4 = MOI.Nonnegatives(4) + @test MOI.dual_set(nonnegatives3) == nonnegatives3 + @test MOI.dual_set(nonnegatives3) != nonnegatives4 + @test MOI.dual_set(nonnegatives4) == nonnegatives4 + # Zeros and Reals + zeros3 = MOI.Zeros(3) + zeros4 = MOI.Zeros(4) + reals3 = MOI.Reals(3) + reals4 = MOI.Reals(4) + @test MOI.dual_set(zeros3) == reals3 + @test MOI.dual_set(reals3) == zeros3 + @test MOI.dual_set(reals3) != zeros4 + @test MOI.dual_set(zeros4) == reals4 + @test MOI.dual_set(reals4) == zeros4 + @test MOI.dual_set(zeros4) != reals3 + #SOC + soc2 = MOI.SecondOrderCone(2) + soc3 = MOI.SecondOrderCone(3) + @test MOI.dual_set(soc2) == soc2 + @test MOI.dual_set(soc2) != soc3 + @test MOI.dual_set(soc3) == soc3 + #RSOC + rsoc2 = MOI.RotatedSecondOrderCone(2) + rsoc3 = MOI.RotatedSecondOrderCone(3) + @test MOI.dual_set(rsoc2) == rsoc2 + @test MOI.dual_set(rsoc2) != rsoc3 + @test MOI.dual_set(rsoc3) == rsoc3 + #PSDtriangle + psd2 = MOI.PositiveSemidefiniteConeTriangle(2) + psd3 = MOI.PositiveSemidefiniteConeTriangle(3) + @test MOI.dual_set(psd2) == psd2 + @test MOI.dual_set(psd2) != psd3 + @test MOI.dual_set(psd3) == psd3 + # Exponential + exp = MOI.ExponentialCone() + dual_exp = MOI.DualExponentialCone() + @test MOI.dual_set(exp) == dual_exp + @test MOI.dual_set(exp) != exp + @test MOI.dual_set(dual_exp) == exp + @test MOI.dual_set(dual_exp) != dual_exp + # Power + pow03 = MOI.PowerCone(0.3) + pow04 = MOI.PowerCone(0.4) + dual_pow03 = MOI.DualPowerCone(0.3) + @test MOI.dual_set(pow03) == dual_pow03 + @test MOI.dual_set(pow03) != pow03 + @test MOI.dual_set(dual_pow03) == pow03 + @test MOI.dual_set(dual_pow03) != pow04 + @test MOI.dual_set(dual_pow03) != dual_pow03 + # PSDSquare error + s = MOI.PositiveSemidefiniteConeSquare(4) + err = ErrorException("""Dual of $s is not defined in MathOptInterface. + For more details see the comments in src/Bridges/Constraint/square.jl""") + @test_throws err MOI.dual_set(MOI.PositiveSemidefiniteConeSquare(4)) + # Not implemented + s = MOI.LogDetConeTriangle(4) + err = ErrorException("Dual of $s is not implemented.") + @test_throws err MOI.dual_set(MOI.LogDetConeTriangle(4)) +end + @testset "Set dot" begin vec = zeros(6) @test MOIU.set_dot(vec, vec, MOI.SecondOrderCone(6)) == 0 @@ -114,4 +183,4 @@ end @test MOIU. dot_coefficients(sp_vec, MOI.LogDetConeTriangle(3)) == sp_vec sp_vec[5] = 1 @test MOIU.dot_coefficients(sp_vec, MOI.LogDetConeTriangle(3)) == sp_vec -end \ No newline at end of file +end