From ee20e19887a506bff82118c2351a5bfbfc885354 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Wed, 2 Apr 2025 19:20:37 +0200 Subject: [PATCH 1/3] Fix type conversion for weird orders --- Project.toml | 2 +- src/order.jl | 4 ++-- test/type_stability.jl | 40 +++++++++++++++++++++------------------- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/Project.toml b/Project.toml index 6fb2a43e..690cfb50 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "SparseMatrixColorings" uuid = "0a514795-09f3-496d-8182-132a7b665d35" authors = ["Guillaume Dalle", "Alexis Montoison"] -version = "0.4.15" +version = "0.4.16" [deps] ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b" diff --git a/src/order.jl b/src/order.jl index 11aeee3f..b97e0611 100644 --- a/src/order.jl +++ b/src/order.jl @@ -144,7 +144,7 @@ function DegreeBuckets(::Type{T}, degrees::Vector{<:Integer}, dmax::Integer) whe deg_count[d + 1] += 1 end # bucket limits - bucket_high = cumsum(deg_count) + bucket_high = convert(Vector{T}, cumsum(deg_count)) bucket_low = vcat(zero(T), @view(bucket_high[1:(end - 1)])) bucket_low .+= 1 # assign each vertex to the correct position inside its degree class @@ -253,7 +253,7 @@ function vertices( if degree_increasing(; degtype, direction) degrees = zeros(T, nb_vertices(g)) else - degrees = [degree(g, v) for v in vertices(g)] + degrees = T[degree(g, v) for v in vertices(g)] end db = DegreeBuckets(T, degrees, maximum_degree(g)) π = T[] diff --git a/test/type_stability.jl b/test/type_stability.jl index cd01df8c..455e5cf8 100644 --- a/test/type_stability.jl +++ b/test/type_stability.jl @@ -178,25 +178,27 @@ end; (:nonsymmetric, :bidirectional, :direct), (:nonsymmetric, :bidirectional, :substitution), ] - result = coloring( - A, - ColoringProblem(; structure, partition), - GreedyColoringAlgorithm(; decompression); - ) - if partition in (:column, :bidirectional) - @test eltype(column_colors(result)) == Int32 - @test eltype(column_groups(result)[1]) == Int32 - end - if partition in (:row, :bidirectional) - @test eltype(row_colors(result)) == Int32 - @test eltype(row_groups(result)[1]) == Int32 - end - if partition == :bidirectional - Br, Bc = compress(A, result) - @test decompress(Br, Bc, result) isa SparseMatrixCSC{Float32,Int32} - else - B = compress(A, result) - @test decompress(B, result) isa SparseMatrixCSC{Float32,Int32} + for order in (NaturalOrder(), RandomOrder(), LargestFirst(), DynamicLargestFirst()) + result = coloring( + A, + ColoringProblem(; structure, partition), + GreedyColoringAlgorithm(order; decompression); + ) + if partition in (:column, :bidirectional) + @test eltype(column_colors(result)) == Int32 + @test eltype(column_groups(result)[1]) == Int32 + end + if partition in (:row, :bidirectional) + @test eltype(row_colors(result)) == Int32 + @test eltype(row_groups(result)[1]) == Int32 + end + if partition == :bidirectional + Br, Bc = compress(A, result) + @test decompress(Br, Bc, result) isa SparseMatrixCSC{Float32,Int32} + else + B = compress(A, result) + @test decompress(B, result) isa SparseMatrixCSC{Float32,Int32} + end end end end From 9fa323527ff47455eedda6c25139c4d6fa9e1a76 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Wed, 2 Apr 2025 19:29:01 +0200 Subject: [PATCH 2/3] Fix --- src/order.jl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/order.jl b/src/order.jl index b97e0611..d71bfab1 100644 --- a/src/order.jl +++ b/src/order.jl @@ -137,15 +137,17 @@ struct DegreeBuckets{T} positions::Vector{T} end -function DegreeBuckets(::Type{T}, degrees::Vector{<:Integer}, dmax::Integer) where {T} +function DegreeBuckets(::Type{T}, degrees::Vector{T}, dmax::Integer) where {T} # number of vertices per degree class deg_count = zeros(T, dmax + 1) for d in degrees deg_count[d + 1] += 1 end # bucket limits - bucket_high = convert(Vector{T}, cumsum(deg_count)) - bucket_low = vcat(zero(T), @view(bucket_high[1:(end - 1)])) + bucket_high = accumulate(+, deg_count) + bucket_low = similar(bucket_high) + bucket_low[1] = 0 + bucket_low[2:end] .= bucket_high[1:(end - 1)] bucket_low .+= 1 # assign each vertex to the correct position inside its degree class bucket_storage = similar(degrees, T) From 314a5639cb4c6661799497a0555febd1f9d6db65 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Wed, 2 Apr 2025 20:30:36 +0200 Subject: [PATCH 3/3] Add tests --- src/order.jl | 16 +++++++++++++--- test/allocations.jl | 30 ++++++++++++++++++++++++++++++ test/type_stability.jl | 4 ++-- 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/order.jl b/src/order.jl index d71bfab1..c2ab3a8e 100644 --- a/src/order.jl +++ b/src/order.jl @@ -146,9 +146,8 @@ function DegreeBuckets(::Type{T}, degrees::Vector{T}, dmax::Integer) where {T} # bucket limits bucket_high = accumulate(+, deg_count) bucket_low = similar(bucket_high) - bucket_low[1] = 0 - bucket_low[2:end] .= bucket_high[1:(end - 1)] - bucket_low .+= 1 + bucket_low[1] = 1 + bucket_low[2:end] .= @view(bucket_high[1:(end - 1)]) .+ 1 # assign each vertex to the correct position inside its degree class bucket_storage = similar(degrees, T) positions = similar(degrees, T) @@ -380,3 +379,14 @@ The `elimination_algorithm` must be an instance of `CliqueTrees.EliminationAlgor struct PerfectEliminationOrder{E} <: AbstractOrder elimination_algorithm::E end + +function all_orders() + return [ + NaturalOrder(), + RandomOrder(), + LargestFirst(), + SmallestLast(), + IncidenceDegree(), + DynamicLargestFirst(), + ] +end diff --git a/test/allocations.jl b/test/allocations.jl index 8eb05b7f..5b3a2824 100644 --- a/test/allocations.jl +++ b/test/allocations.jl @@ -132,3 +132,33 @@ end test_noallocs_structured_decompression(1000; structure, partition, decompression) end end + +@testset "Single precision" begin + n, p = 10_000, 0.001 + A64 = sparse(Symmetric(sprand(rng, Float32, 100, 100, 0.1))) + A32 = convert(SparseMatrixCSC{Float32,Int32}, A64) + @testset "$structure - $partition - $decompression" for ( + structure, partition, decompression + ) in [ + (:nonsymmetric, :column, :direct), + (:nonsymmetric, :row, :direct), + (:symmetric, :column, :direct), + (:symmetric, :column, :substitution), + (:nonsymmetric, :bidirectional, :direct), + (:nonsymmetric, :bidirectional, :substitution), + ] + @testset for order in + (NaturalOrder(), RandomOrder(), LargestFirst(), DynamicLargestFirst()) + problem = ColoringProblem(; structure, partition) + algo = GreedyColoringAlgorithm(order; decompression) + b64 = @b fast_coloring(A64, problem, algo) + b32 = @b fast_coloring(A32, problem, algo) + # check that we allocate no more than 50% + epsilon with Int32 + if decompression == :direct + @test b32.bytes < 0.6 * b64.bytes + else + @test_broken b32.bytes < 0.6 * b64.bytes + end + end + end +end; diff --git a/test/type_stability.jl b/test/type_stability.jl index 455e5cf8..8297c1e3 100644 --- a/test/type_stability.jl +++ b/test/type_stability.jl @@ -3,7 +3,7 @@ using JET using LinearAlgebra using SparseArrays using SparseMatrixColorings -using SparseMatrixColorings: matrix_versions, respectful_similar +using SparseMatrixColorings: matrix_versions, respectful_similar, all_orders using StableRNGs using Test @@ -178,7 +178,7 @@ end; (:nonsymmetric, :bidirectional, :direct), (:nonsymmetric, :bidirectional, :substitution), ] - for order in (NaturalOrder(), RandomOrder(), LargestFirst(), DynamicLargestFirst()) + @testset for order in all_orders() result = coloring( A, ColoringProblem(; structure, partition),