diff --git a/src/Utilities/distance_to_set.jl b/src/Utilities/distance_to_set.jl index 83e76ef51a..7039d818cb 100644 --- a/src/Utilities/distance_to_set.jl +++ b/src/Utilities/distance_to_set.jl @@ -194,7 +194,7 @@ function _reshape( MOI.LogDetConeTriangle, MOI.RootDetConeTriangle, }, -) where {T} +) where {T<:Real} n = isqrt(2 * length(x)) # The type annotation is needed for JET. X = zeros(T, n, n)::Matrix{T} @@ -208,6 +208,24 @@ function _reshape( return LinearAlgebra.Symmetric(X) end +function _reshape( + x::AbstractVector{T}, + set::MOI.PositiveSemidefiniteConeTriangle, +) where {T<:Complex} + n = isqrt(2 * length(x)) + # The type annotation is needed for JET. + X = zeros(T, n, n)::Matrix{T} + k = 1 + for i in 1:n + for j in 1:i + X[i, j] = conj(x[k]) + X[j, i] = x[k] + k += 1 + end + end + return LinearAlgebra.Hermitian(X) +end + # This is the minimal L2-norm. function distance_to_set( ::ProjectionUpperBoundDistance, @@ -553,7 +571,7 @@ function distance_to_set( MOI.PositiveSemidefiniteConeSquare, MOI.PositiveSemidefiniteConeTriangle, }, -) where {T<:Real} +) where {T<:Union{Real,Complex}} _check_dimension(x, set) # We should return the norm of `A` defined by: # ```julia @@ -565,8 +583,7 @@ function distance_to_set( # The norm should correspond to `MOI.Utilities.set_dot` so it's the # Frobenius norm, which is the Euclidean norm of the vector of eigenvalues. eigvals = LinearAlgebra.eigvals(_reshape(x, set)) - eigvals .= min.(zero(T), eigvals) - return LinearAlgebra.norm(eigvals, 2) + return LinearAlgebra.norm(min.(0, eigvals), 2) end """ @@ -702,3 +719,44 @@ function distance_to_set( push!(eigvals_neg, max(x[1] - x[2] * sum(log.(eigvals_pos)), zero(T))) return LinearAlgebra.norm(eigvals_neg, 2) end + +""" + distance_to_set( + ::ProjectionUpperBoundDistance, + x::AbstractVector, + set::MOI.Scaled{S}, + ) + +This is the distance in the un-scaled space. +""" +function distance_to_set( + dist::ProjectionUpperBoundDistance, + x::AbstractVector{T}, + set::MOI.Scaled{S}, +) where {T,S<:MOI.AbstractVectorSet} + _check_dimension(x, set) + scale = MOI.Utilities.SetDotScalingVector{T}(set.set) + return distance_to_set(dist, x ./ scale, set.set) +end + +function distance_to_set( + dist::ProjectionUpperBoundDistance, + x::AbstractVector{T}, + set::MOI.HermitianPositiveSemidefiniteConeTriangle, +) where {T<:Real} + _check_dimension(x, set) + output_set = MOI.PositiveSemidefiniteConeTriangle(set.side_dimension) + y = zeros(Complex{T}, MOI.dimension(output_set)) + real_offset, imag_offset = 0, length(y) + for col in 1:set.side_dimension + for row in 1:col + real_offset += 1 + y[real_offset] = x[real_offset] + if row != col + imag_offset += 1 + y[real_offset] += x[imag_offset] * im + end + end + end + return distance_to_set(dist, y, output_set) +end diff --git a/test/Utilities/distance_to_set.jl b/test/Utilities/distance_to_set.jl index 6016d09421..49739e90b7 100644 --- a/test/Utilities/distance_to_set.jl +++ b/test/Utilities/distance_to_set.jl @@ -320,7 +320,8 @@ function test_positivesemidefiniteconesquare() [1.0, 0.0, 0.0, 1.0] => 0.0, [1.0, -1.0, -1.0, 1.0] => 0.0, [1.0, -2.0, -2.0, 1.0] => 1.0, - [1.0, 1.1, 1.1, -2.3] => 2.633053201505194; + [1.0, 1.1, 1.1, -2.3] => 2.633053201505194, + [1.0, -2.0, -2.0, 1.0] => 1.0; mismatch = [1.0], ) return @@ -476,6 +477,44 @@ function test_LogDetConeSquare() return end +function test_Scaled() + _test_set( + MOI.Scaled(MOI.PositiveSemidefiniteConeTriangle(2)), + [1.0, 0.0, 1.0] => 0.0, + [1.0, -1.0, 1.0] => 0.0, + [1.0, -2.0 * sqrt(2), 1.0] => 1.0, + [1.0, 1.1 * sqrt(2), -2.3] => 2.633053201505194; + mismatch = [1.0], + ) + return +end + +function test_PositiveSemidefiniteConeTriangle_Complex() + _test_set( + MOI.PositiveSemidefiniteConeTriangle(2), + ComplexF64[1.0, 0.0, 1.0] => 0.0, + ComplexF64[1.0, -1.0, 1.0] => 0.0, + ComplexF64[1.0, -2.0, 1.0] => 1.0, + ComplexF64[1.0, 1.1, -2.3] => 2.633053201505194, + ComplexF64[1.0, 1-im, 1.0] => 0.41421356237309537; + mismatch = [1.0], + ) + return +end + +function test_HermitianPositiveSemidefiniteConeTriangle() + _test_set( + MOI.HermitianPositiveSemidefiniteConeTriangle(2), + [1.0, 0.0, 1.0, 0.0] => 0.0, + [1.0, -1.0, 1.0, 0.0] => 0.0, + [1.0, -2.0, 1.0, 0.0] => 1.0, + [1.0, 1.1, -2.3, 0.0] => 2.633053201505194, + [1.0, 1.0, 1.0, -1.0] => 0.41421356237309537; + mismatch = [1.0], + ) + return +end + end TestFeasibilityChecker.runtests()