Skip to content

Commit 3a70ff0

Browse files
authored
[Utilities] add distance_to_set for PositiveSemidefiniteCone (#2729)
1 parent 3c19dec commit 3a70ff0

File tree

2 files changed

+80
-1
lines changed

2 files changed

+80
-1
lines changed

src/Utilities/distance_to_set.jl

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,3 +489,58 @@ function distance_to_set(
489489
elements = [x[i] for i in eachindex(x) if !(i in pairs[k])]
490490
return LinearAlgebra.norm(elements, 2)
491491
end
492+
493+
function _reshape(x::AbstractVector, set::MOI.PositiveSemidefiniteConeSquare)
494+
n = MOI.side_dimension(set)
495+
return reshape(x, (n, n))
496+
end
497+
498+
function _reshape(x::AbstractVector, set::MOI.PositiveSemidefiniteConeTriangle)
499+
n = MOI.side_dimension(set)
500+
X = zeros(eltype(x), n, n)
501+
k = 1
502+
for i in 1:n
503+
for j in 1:i
504+
X[j, i] = X[i, j] = x[k]
505+
k += 1
506+
end
507+
end
508+
return LinearAlgebra.Symmetric(X)
509+
end
510+
511+
"""
512+
distance_to_set(
513+
::ProjectionUpperBoundDistance,
514+
x::AbstractVector,
515+
set::Union{
516+
MOI.PositiveSemidefiniteConeSquare,
517+
MOI.PositiveSemidefiniteConeTriangle,
518+
},
519+
)
520+
521+
Let ``X`` be `x` reshaped into the appropriate matrix. The returned distance is
522+
``||X - Y||_2^2`` where ``Y`` is the eigen decomposition of ``X`` with negative
523+
eigen values removed.
524+
"""
525+
function distance_to_set(
526+
::ProjectionUpperBoundDistance,
527+
x::AbstractVector{T},
528+
set::Union{
529+
MOI.PositiveSemidefiniteConeSquare,
530+
MOI.PositiveSemidefiniteConeTriangle,
531+
},
532+
) where {T<:Real}
533+
_check_dimension(x, set)
534+
# We should return the norm of `A` defined by:
535+
# ```julia
536+
# λ, U = LinearAlgebra.eigen(_reshape(x, set))
537+
# λ_negative = LinearAlgebra.Diagonal(min.(zero(T), λ))
538+
# A = LinearAlgebra.Symmetric(U * λ_negative * U')
539+
# LinearAlgebra.norm(A, 2)
540+
# ```
541+
# The norm should correspond to `MOI.Utilities.set_dot` so it's the
542+
# Frobenius norm, which is the Euclidean norm of the vector of eigenvalues.
543+
eigvals = LinearAlgebra.eigvals(_reshape(x, set))
544+
eigvals .= min.(zero(T), eigvals)
545+
return LinearAlgebra.norm(eigvals, 2)
546+
end

test/Utilities/distance_to_set.jl

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ function _test_set(set, pairs...; mismatch = nothing)
3030
)
3131
end
3232
for (x, d) in pairs
33-
@test MOI.Utilities.distance_to_set(x, set) d
33+
@test (MOI.Utilities.distance_to_set(x, set), d; atol = 1e-12)
3434
end
3535
return
3636
end
@@ -308,6 +308,30 @@ function test_sos2()
308308
return
309309
end
310310

311+
function test_positivesemidefiniteconesquare()
312+
_test_set(
313+
MOI.PositiveSemidefiniteConeSquare(2),
314+
[1.0, 0.0, 0.0, 1.0] => 0.0,
315+
[1.0, -1.0, -1.0, 1.0] => 0.0,
316+
[1.0, -2.0, -2.0, 1.0] => 1.0,
317+
[1.0, 1.1, 1.1, -2.3] => 2.633053201505194;
318+
mismatch = [1.0],
319+
)
320+
return
321+
end
322+
323+
function test_positivesemidefiniteconetriangle()
324+
_test_set(
325+
MOI.PositiveSemidefiniteConeTriangle(2),
326+
[1.0, 0.0, 1.0] => 0.0,
327+
[1.0, -1.0, 1.0] => 0.0,
328+
[1.0, -2.0, 1.0] => 1.0,
329+
[1.0, 1.1, -2.3] => 2.633053201505194;
330+
mismatch = [1.0],
331+
)
332+
return
333+
end
334+
311335
end
312336

313337
TestFeasibilityChecker.runtests()

0 commit comments

Comments
 (0)