Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 116 additions & 22 deletions src/Utilities/distance_to_set.jl
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,39 @@ function _check_dimension(v::AbstractVector, s)
return
end

function _reshape(
x::AbstractVector,
set::Union{
MOI.PositiveSemidefiniteConeSquare,
MOI.LogDetConeSquare,
MOI.RootDetConeSquare,
},
)
n = isqrt(length(x))
return reshape(x, (n, n))
end

function _reshape(
x::AbstractVector{T},
set::Union{
MOI.PositiveSemidefiniteConeTriangle,
MOI.LogDetConeTriangle,
MOI.RootDetConeTriangle,
},
) where {T}
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[j, i] = X[i, j] = x[k]
k += 1
end
end
return LinearAlgebra.Symmetric(X)
end

# This is the minimal L2-norm.
function distance_to_set(
::ProjectionUpperBoundDistance,
Expand Down Expand Up @@ -499,28 +532,6 @@ function distance_to_set(
return LinearAlgebra.norm(elements, 2)
end

function _reshape(x::AbstractVector, set::MOI.PositiveSemidefiniteConeSquare)
n = MOI.side_dimension(set)
return reshape(x, (n, n))
end

function _reshape(
x::AbstractVector{T},
set::MOI.PositiveSemidefiniteConeTriangle,
) where {T}
n = MOI.side_dimension(set)
# 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[j, i] = X[i, j] = x[k]
k += 1
end
end
return LinearAlgebra.Symmetric(X)
end

"""
distance_to_set(
::ProjectionUpperBoundDistance,
Expand Down Expand Up @@ -608,3 +619,86 @@ function distance_to_set(
sqrt(x[1]^2 + distance_to_set(distance, x[2], set.set)^2),
)
end

"""
distance_to_set(::ProjectionUpperBoundDistance, x, set::MOI.NormNuclearCone)

Let `(t, y...) = x`. Return the epigraph distance `d` such that `(t + d, y...)`
belongs to the set.
"""
function distance_to_set(
::ProjectionUpperBoundDistance,
x::AbstractVector{T},
set::MOI.NormNuclearCone,
) where {T}
_check_dimension(x, set)
X = reshape(x[2:end], set.row_dim, set.column_dim)
return max(sum(LinearAlgebra.svdvals(X)) - x[1], zero(T))
end

"""
distance_to_set(::ProjectionUpperBoundDistance, x, set::MOI.NormSpectralCone)

Let `(t, y...) = x`. Return the epigraph distance `d` such that `(t + d, y...)`
belongs to the set.
"""
function distance_to_set(
::ProjectionUpperBoundDistance,
x::AbstractVector{T},
set::MOI.NormSpectralCone,
) where {T}
_check_dimension(x, set)
X = reshape(x[2:end], set.row_dim, set.column_dim)
return max(maximum(LinearAlgebra.svdvals(X)) - x[1], zero(T))
end

"""
distance_to_set(
::ProjectionUpperBoundDistance,
x::AbstractVector,
set::Union{MOI.RootDetConeSquare,MOI.RootDetConeTriangle},
)

Let ``Y`` be `y` in `x = (t, y)`, reshaped into the appropriate matrix. The
returned distance is ``||Y - Z||_2^2`` where ``Z`` is the eigen decomposition of
``Y`` with negative eigen values removed, plus the epigraph distance in `t`
needed to satisfy the root-determinant constraint.
"""
function distance_to_set(
::ProjectionUpperBoundDistance,
x::AbstractVector{T},
set::Union{MOI.RootDetConeSquare,MOI.RootDetConeTriangle},
) where {T<:Real}
_check_dimension(x, set)
eigvals = LinearAlgebra.eigvals(_reshape(x[2:end], set))
eigvals_neg = min.(zero(T), eigvals)
eigvals_pos = max.(zero(T), eigvals)
rootdet = prod(eigvals_pos)^(1 / set.side_dimension)
push!(eigvals_neg, max(x[1] - rootdet, zero(T)))
return LinearAlgebra.norm(eigvals_neg, 2)
end

"""
distance_to_set(
::ProjectionUpperBoundDistance,
x::AbstractVector,
set::Union{MOI.LogDetConeSquare,MOI.LogDetConeTriangle},
)

Let ``Y`` be `y` in `x = (t, y)`, reshaped into the appropriate matrix. The
returned distance is ``||Y/u - Z||_2^2`` where ``Z`` is the eigen decomposition
of ``Y`` with negative eigen values removed, plus the epigraph distance in `t`
needed to satisfy the log-determinant constraint.
"""
function distance_to_set(
::ProjectionUpperBoundDistance,
x::AbstractVector{T},
set::Union{MOI.LogDetConeSquare,MOI.LogDetConeTriangle},
) where {T<:Real}
_check_dimension(x, set)
eigvals = LinearAlgebra.eigvals(_reshape(x[3:end] ./ x[2], set))
eigvals_neg = min.(eps(T), eigvals)
eigvals_pos = max.(eps(T), eigvals)
push!(eigvals_neg, max(x[1] - x[2] * sum(log.(eigvals_pos)), zero(T)))
return LinearAlgebra.norm(eigvals_neg, 2)
end
78 changes: 77 additions & 1 deletion test/Utilities/distance_to_set.jl
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,83 @@ function test_indicator()
[0.99, 1.0] => 0.01,
[0.99, 1.1] => sqrt(0.01^2 + 0.1^2),
[0.5, 1.1] => 0.5,
[0.8, 1.1] => sqrt(0.2^2 + 0.1^2),
[0.8, 1.1] => sqrt(0.2^2 + 0.1^2);
mismatch = [1.0],
)
return
end

function test_NormNuclearCone()
_test_set(
MOI.NormNuclearCone(2, 3),
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0] => 10.039818672223756,
[11.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0] => 0.0;
mismatch = [1.0],
)
return
end

function test_NormSpectralCone()
_test_set(
MOI.NormSpectralCone(2, 3),
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0] => 9.525518091565111,
[9.6, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0] => 0.0;
mismatch = [1.0],
)
return
end

function test_RootDetConeTriangle()
_test_set(
MOI.RootDetConeTriangle(2),
[2.0, 1.0, 0.0, 1.0] => 1.0,
[0.9, 1.0, 0.0, 1.0] => 0.0,
[2.0, 1.0, 0.0, 2.0] => 2 - sqrt(2),
# Projection onto PSD
[0.0, 1.0, 2.0, 3.0] => 0.2360679774997897,
# Projection onto PSD+t
[1.0, 1.0, 2.0, 3.0] => sqrt(1 + 0.2360679774997897^2);
mismatch = [1.0],
)
return
end

function test_RootDetConeSquare()
_test_set(
MOI.RootDetConeSquare(2),
[2.0, 1.0, 0.0, 0.0, 1.0] => 1.0,
[0.9, 1.0, 0.0, 0.0, 1.0] => 0.0,
[2.0, 1.0, 0.0, 0.0, 2.0] => 2 - sqrt(2),
# Projection onto PSD
[0.0, 1.0, 2.0, 2.0, 3.0] => 0.2360679774997897,
# Projection onto PSD+t
[1.0, 1.0, 2.0, 2.0, 3.0] => sqrt(1 + 0.2360679774997897^2);
mismatch = [1.0],
)
return
end

function test_LogDetConeTriangle()
_test_set(
MOI.LogDetConeTriangle(2),
[2.0, 1.0, 2.0, 0.0, 1.0] => 2 - 0.6931471805599453,
[0.69, 1.0, 2.0, 0.0, 1.0] => 0.0,
[0.0, 2.0, 2.0, 0.0, 1.0] => 1.3862943611198906,
mismatch = [1.0],
)
return
end

function test_LogDetConeSquare()
_test_set(
MOI.LogDetConeSquare(2),
[2.0, 1.0, 2.0, 0.0, 0.0, 1.0] => 2 - 0.6931471805599453,
[0.69, 1.0, 2.0, 0.0, 0.0, 1.0] => 0.0,
[0.0, 2.0, 2.0, 0.0, 0.0, 1.0] => 1.3862943611198906,
# Projection onto PSD
[0.0, 1.0, 1.0, 2.0, 2.0, 3.0] => 34.60082322336934,
# Projection onto PSD+t
[1.0, 1.0, 1.0, 2.0, 2.0, 3.0] => 35.60080060283381;
mismatch = [1.0],
)
return
Expand Down
Loading