From d1e63a90cc6d764ed1a4e3baef73ca8e802c72e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 16 Oct 2026 14:39:20 +0200 Subject: [PATCH] Support star coloring in decompress_csc! --- src/decompression.jl | 47 ++++++++++++++++---------- test/decompression.jl | 78 +++++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 3 ++ 3 files changed, 110 insertions(+), 18 deletions(-) create mode 100644 test/decompression.jl diff --git a/src/decompression.jl b/src/decompression.jl index 23d51c6c..35e0e71e 100644 --- a/src/decompression.jl +++ b/src/decompression.jl @@ -502,13 +502,30 @@ function decompress_single_color!( return A end -function decompress!( - A::SparseMatrixCSC, B::AbstractMatrix, result::StarSetColoringResult, uplo::Symbol=:F +""" + decompress_csc!( + nzA::AbstractVector, + A_colptr::AbstractVector{<:Integer}, + B::AbstractMatrix, + result::Union{StarSetColoringResult,TreeSetColoringResult}, + uplo::Symbol=:F, + ) + +Decompress the values of `B` into the vector of nonzero entries `nzA` of a +sparse matrix with column pointers `A_colptr`, without constructing the matrix. +The destination pattern must match the full coloring pattern (`uplo=:F`), its +lower triangle (`uplo=:L`), or its upper triangle (`uplo=:U`), with row indices +sorted in increasing order within each column. Return `nothing`. +""" +function decompress_csc!( + nzA::AbstractVector, + A_colptr::AbstractVector{<:Integer}, + B::AbstractMatrix, + result::StarSetColoringResult, + uplo::Symbol=:F, ) (; ag, compressed_indices) = result (; S) = ag - nzA = nonzeros(A) - check_compatible_pattern(A, ag, uplo) if uplo == :F for k in eachindex(nzA, compressed_indices) nzA[k] = B[compressed_indices[k]] @@ -526,6 +543,14 @@ function decompress!( end end end + return nothing +end + +function decompress!( + A::SparseMatrixCSC, B::AbstractMatrix, result::StarSetColoringResult, uplo::Symbol=:F +) + check_compatible_pattern(A, result.ag, uplo) + decompress_csc!(nonzeros(A), A.colptr, B, result, uplo) return A end @@ -585,20 +610,6 @@ function decompress!( return A end -""" - decompress_csc!( - nzA::AbstractVector{R}, - A_colptr::AbstractVector, - B::AbstractMatrix{R}, - result::TreeSetColoringResult, - uplo::Symbol=:F, - ) where {R<:Real} - -Decompress the values of `B` into the vector of nonzero entries `nzA` of a -sparse matrix with column pointers `colptr`. This function assumes that the -row indices are sorted in increasing order and are the same as those of the -sparse matrix given to the `coloring` function that returned `result`. -""" function decompress_csc!( nzA::AbstractVector{R}, A_colptr::AbstractVector{<:Integer}, diff --git a/test/decompression.jl b/test/decompression.jl new file mode 100644 index 00000000..86efa122 --- /dev/null +++ b/test/decompression.jl @@ -0,0 +1,78 @@ +using LinearAlgebra +using SparseArrays +using SparseMatrixColorings +using SparseMatrixColorings: decompress_csc! +using Test + +function test_star_csc_decompression(T, Ti, order, postprocessing) + problem = ColoringProblem(; structure=:symmetric, partition=:column) + algorithm = GreedyColoringAlgorithm(order; decompression=:direct, postprocessing) + @testset "edges = $edges" for edges in ( + [(2, 4), (4, 7), (7, 9)], + [(2, 4), (2, 7), (2, 9), (7, 7)], + [(2, 4), (4, 7), (7, 9), (2, 9), (4, 4), (9, 9)], + [(2, 2), (9, 9)], + Tuple{Int,Int}[], + ) + A = spzeros(T, Ti, 9, 9) + for (i, j) in edges + A[i, j] = A[j, i] = T <: Complex ? T(10i + j, i - j) : T(10i + j) + end + # Keep a structural zero as well as empty rows and columns. + if !isempty(edges) + A[5, 5] = one(T) + A[5, 5] = zero(T) + end + result = coloring(A, problem, algorithm) + B = compress(A, result) + @testset "$uplo" for uplo in (:F, :L, :U) + expected = if uplo == :F + A + elseif uplo == :L + tril(A) + else + triu(A) + end + V = fill(T(NaN), nnz(expected)) + @test decompress_csc!(V, expected.colptr, B, result, uplo) === nothing + @test V == nonzeros(expected) + @test (@allocated decompress_csc!(V, expected.colptr, B, result, uplo)) == 0 + + dest = copy(expected) + fill!(nonzeros(dest), T(NaN)) + @test decompress!(dest, B, result, uplo) === dest + @test dest == expected + @test (@allocated decompress!(dest, B, result, uplo)) == 0 + + # Abstract buffers and a different destination eltype remain supported. + storage = fill(ComplexF64(NaN), 2nnz(expected)) + values = @view storage[1:2:end] + compressed = @view B[:, :] + @test decompress_csc!(values, expected.colptr, compressed, result, uplo) === + nothing + @test values == nonzeros(expected) + @test (@allocated decompress_csc!( + values, expected.colptr, compressed, result, uplo + )) == 0 + mixed = SparseMatrixCSC{ComplexF64,Ti}(expected) + fill!(nonzeros(mixed), ComplexF64(NaN)) + @test decompress!(mixed, compressed, result, uplo) === mixed + @test mixed == expected + end + full_values = similar(nonzeros(A)) + @test decompress_csc!(full_values, A.colptr, B, result) === nothing + @test full_values == nonzeros(A) + end + return nothing +end + +@testset "Star CSC decompression" begin + @testset "$T, $Ti, $order, postprocessing=$postprocessing" for T in ( + Float32, Float64, ComplexF64 + ), + Ti in (Int32, Int64), order in (NaturalOrder(), LargestFirst()), + postprocessing in (false, true) + + test_star_csc_decompression(T, Ti, order, postprocessing) + end +end diff --git a/test/runtests.jl b/test/runtests.jl index 877ebbbe..4df8a982 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -72,6 +72,9 @@ using Colors: Colors end end @testset verbose = true "Correctness" begin + @testset "CSC decompression" begin + include("decompression.jl") + end @testset "Small instances" begin include("small.jl") end