From 2d2eafab8f8ef6e32d156b5452170eac3435bca2 Mon Sep 17 00:00:00 2001 From: Alexis Montoison Date: Wed, 9 Sep 2026 11:23:01 -0600 Subject: [PATCH 1/3] Update type_stability.jl --- test/type_stability.jl | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/test/type_stability.jl b/test/type_stability.jl index 3c6f9f02..d063cfc9 100644 --- a/test/type_stability.jl +++ b/test/type_stability.jl @@ -12,6 +12,7 @@ rng = StableRNG(63) @testset "Sparse coloring" begin n = 10 A = sparse(Symmetric(sprand(rng, n, n, 5 / n))) + decompression_eltype = Float64 # ADTypes @testset "ADTypes" begin @@ -38,22 +39,26 @@ rng = StableRNG(63) @test_opt coloring( A, ColoringProblem(; structure, partition), - GreedyColoringAlgorithm(order; decompression), + GreedyColoringAlgorithm(order; decompression); + decompression_eltype, ) @test_opt coloring( A, ColoringProblem(; structure, partition), - GreedyColoringAlgorithm((NaturalOrder(), order); decompression), + GreedyColoringAlgorithm((NaturalOrder(), order); decompression); + decompression_eltype, ) @inferred coloring( A, ColoringProblem(; structure, partition), - GreedyColoringAlgorithm(order; decompression), + GreedyColoringAlgorithm(order; decompression); + decompression_eltype, ) @inferred coloring( A, ColoringProblem(; structure, partition), - GreedyColoringAlgorithm((NaturalOrder(), order); decompression), + GreedyColoringAlgorithm((NaturalOrder(), order); decompression); + decompression_eltype, ) end end From 87db0e2f50e92a0c32e440eb86cdf9ee1cbdd54a Mon Sep 17 00:00:00 2001 From: Alexis Montoison Date: Sat, 5 Sep 2026 21:03:00 -0600 Subject: [PATCH 2/3] Fix the inference of the function coloring --- src/interface.jl | 8 ++++--- test/runtests.jl | 3 +++ test/static_compilation.jl | 47 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 test/static_compilation.jl diff --git a/src/interface.jl b/src/interface.jl index 9480e07c..a219186d 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -198,10 +198,12 @@ function coloring( A::AbstractMatrix, problem::ColoringProblem, algo::GreedyColoringAlgorithm; - decompression_eltype::Type{R}=Float64, + decompression_eltype::Type=Float64, symmetric_pattern::Bool=false, -) where {R} - return _coloring(WithResult(), A, problem, algo, R, symmetric_pattern) +) + return _coloring( + WithResult(), A, problem, algo, decompression_eltype, symmetric_pattern + ) end """ diff --git a/test/runtests.jl b/test/runtests.jl index 877ebbbe..11d98704 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -95,6 +95,9 @@ using Colors: Colors include("type_stability.jl") end end + @testset "Static compilation" begin + include("static_compilation.jl") + end @testset "Allocations" begin include("allocations.jl") end diff --git a/test/static_compilation.jl b/test/static_compilation.jl new file mode 100644 index 00000000..dbc6005b --- /dev/null +++ b/test/static_compilation.jl @@ -0,0 +1,47 @@ +# Guard for static compilation (`juliac --trim=safe`). +# +# The C and Fortran interfaces compile SMC.jl into a standalone shared library. +# The trimming verifier rejects any call whose result type is not fully inferred, +# so `coloring` must return a concrete type. + +using SparseArrays +using SparseMatrixColorings +using Test + +const STATIC_COMBOS = [ + (ColoringProblem{:nonsymmetric,:column}(), GreedyColoringAlgorithm{:direct}()), + (ColoringProblem{:nonsymmetric,:row}(), GreedyColoringAlgorithm{:direct}()), + (ColoringProblem{:symmetric,:column}(), GreedyColoringAlgorithm{:direct}()), + (ColoringProblem{:symmetric,:column}(), GreedyColoringAlgorithm{:substitution}()), + (ColoringProblem{:nonsymmetric,:bidirectional}(), GreedyColoringAlgorithm{:direct}()), + ( + ColoringProblem{:nonsymmetric,:bidirectional}(), + GreedyColoringAlgorithm{:substitution}(), + ), +] + +# Symmetric, so that it is a valid input for every combination above. +const STATIC_MATRIX = sparse( + [1, 2, 1, 2, 3, 2, 3, 4, 3, 4], [1, 1, 2, 2, 2, 3, 3, 3, 4, 4], ones(10), 4, 4 +) + +@testset "coloring infers a concrete result type" begin + @testset "$(typeof(problem)) / $(typeof(algo))" for (problem, algo) in STATIC_COMBOS + for decompression_eltype in (Float32, Float64) + for symmetric_pattern in (false, true) + @test (@inferred( + ( + (A, p, a, sp, R) -> + coloring(A, p, a; decompression_eltype=R, symmetric_pattern=sp) + )( + STATIC_MATRIX, + problem, + algo, + symmetric_pattern, + decompression_eltype, + ) + )) isa AbstractColoringResult + end + end + end +end From d6670439942c7f27cc8e7cc7b9b45d2cf13503d5 Mon Sep 17 00:00:00 2001 From: Alexis Montoison Date: Wed, 9 Sep 2026 21:39:43 -0600 Subject: [PATCH 3/3] Update test/type_stability.jl --- test/runtests.jl | 3 --- test/static_compilation.jl | 47 ---------------------------------- test/type_stability.jl | 52 +++++++++++++++++++++++++++++++------- 3 files changed, 43 insertions(+), 59 deletions(-) delete mode 100644 test/static_compilation.jl diff --git a/test/runtests.jl b/test/runtests.jl index 11d98704..877ebbbe 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -95,9 +95,6 @@ using Colors: Colors include("type_stability.jl") end end - @testset "Static compilation" begin - include("static_compilation.jl") - end @testset "Allocations" begin include("allocations.jl") end diff --git a/test/static_compilation.jl b/test/static_compilation.jl deleted file mode 100644 index dbc6005b..00000000 --- a/test/static_compilation.jl +++ /dev/null @@ -1,47 +0,0 @@ -# Guard for static compilation (`juliac --trim=safe`). -# -# The C and Fortran interfaces compile SMC.jl into a standalone shared library. -# The trimming verifier rejects any call whose result type is not fully inferred, -# so `coloring` must return a concrete type. - -using SparseArrays -using SparseMatrixColorings -using Test - -const STATIC_COMBOS = [ - (ColoringProblem{:nonsymmetric,:column}(), GreedyColoringAlgorithm{:direct}()), - (ColoringProblem{:nonsymmetric,:row}(), GreedyColoringAlgorithm{:direct}()), - (ColoringProblem{:symmetric,:column}(), GreedyColoringAlgorithm{:direct}()), - (ColoringProblem{:symmetric,:column}(), GreedyColoringAlgorithm{:substitution}()), - (ColoringProblem{:nonsymmetric,:bidirectional}(), GreedyColoringAlgorithm{:direct}()), - ( - ColoringProblem{:nonsymmetric,:bidirectional}(), - GreedyColoringAlgorithm{:substitution}(), - ), -] - -# Symmetric, so that it is a valid input for every combination above. -const STATIC_MATRIX = sparse( - [1, 2, 1, 2, 3, 2, 3, 4, 3, 4], [1, 1, 2, 2, 2, 3, 3, 3, 4, 4], ones(10), 4, 4 -) - -@testset "coloring infers a concrete result type" begin - @testset "$(typeof(problem)) / $(typeof(algo))" for (problem, algo) in STATIC_COMBOS - for decompression_eltype in (Float32, Float64) - for symmetric_pattern in (false, true) - @test (@inferred( - ( - (A, p, a, sp, R) -> - coloring(A, p, a; decompression_eltype=R, symmetric_pattern=sp) - )( - STATIC_MATRIX, - problem, - algo, - symmetric_pattern, - decompression_eltype, - ) - )) isa AbstractColoringResult - end - end - end -end diff --git a/test/type_stability.jl b/test/type_stability.jl index d063cfc9..c9ea92ae 100644 --- a/test/type_stability.jl +++ b/test/type_stability.jl @@ -9,10 +9,18 @@ using Test rng = StableRNG(63) +# `@inferred` and `@test_opt` build their keyword arguments with `typeof`, which erases +# `Type{Float64}` down to `DataType`. Passing `decompression_eltype` straight to them would +# therefore hide the eltype from inference and make every result type unresolvable. Positional +# arguments go through `Core.Typeof` instead, which keeps `Type{R}` intact, so this wrapper is +# what lets us check that `coloring` stays inferrable when the eltype is given explicitly. +function coloring_with_eltype(A, problem, algo, ::Type{R}) where {R} + return coloring(A, problem, algo; decompression_eltype=R) +end + @testset "Sparse coloring" begin n = 10 A = sparse(Symmetric(sprand(rng, n, n, 5 / n))) - decompression_eltype = Float64 # ADTypes @testset "ADTypes" begin @@ -39,29 +47,55 @@ rng = StableRNG(63) @test_opt coloring( A, ColoringProblem(; structure, partition), - GreedyColoringAlgorithm(order; decompression); - decompression_eltype, + GreedyColoringAlgorithm(order; decompression), ) @test_opt coloring( A, ColoringProblem(; structure, partition), - GreedyColoringAlgorithm((NaturalOrder(), order); decompression); - decompression_eltype, + GreedyColoringAlgorithm((NaturalOrder(), order); decompression), ) @inferred coloring( A, ColoringProblem(; structure, partition), - GreedyColoringAlgorithm(order; decompression); - decompression_eltype, + GreedyColoringAlgorithm(order; decompression), ) @inferred coloring( A, ColoringProblem(; structure, partition), - GreedyColoringAlgorithm((NaturalOrder(), order); decompression); - decompression_eltype, + GreedyColoringAlgorithm((NaturalOrder(), order); decompression), ) end end + + @testset "Explicit decompression_eltype" begin + @testset "$structure - $partition - $decompression - $R" for ( + structure, partition, decompression + ) in [ + (:nonsymmetric, :column, :direct), + (:nonsymmetric, :row, :direct), + (:symmetric, :column, :direct), + (:symmetric, :column, :substitution), + (:nonsymmetric, :bidirectional, :direct), + (:nonsymmetric, :bidirectional, :substitution), + ], + R in (Float64, Float32) + + @testset for order in all_orders() + @test_opt coloring_with_eltype( + A, + ColoringProblem(; structure, partition), + GreedyColoringAlgorithm(order; decompression), + R, + ) + @inferred coloring_with_eltype( + A, + ColoringProblem(; structure, partition), + GreedyColoringAlgorithm(order; decompression), + R, + ) + end + end + end end; @testset "Structured coloring" begin