diff --git a/Project.toml b/Project.toml index 6c3eb1cb..2e71e968 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.18" +version = "0.4.19" [deps] ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b" diff --git a/src/coloring.jl b/src/coloring.jl index d5214610..c38ebd26 100644 --- a/src/coloring.jl +++ b/src/coloring.jl @@ -1,11 +1,11 @@ """ - partial_distance2_coloring(bg::BipartiteGraph, ::Val{side}, order::AbstractOrder) + partial_distance2_coloring(bg::BipartiteGraph, ::Val{side}, vertices_in_order::AbstractVector) Compute a distance-2 coloring of the given `side` (`1` or `2`) in the bipartite graph `bg` and return a vector of integer colors. A _distance-2 coloring_ is such that two vertices have different colors if they are at distance at most 2. -The vertices are colored in a greedy fashion, following the `order` supplied. +The vertices are colored in a greedy fashion, following the order supplied. # See also @@ -17,11 +17,10 @@ The vertices are colored in a greedy fashion, following the `order` supplied. > [_What Color Is Your Jacobian? Graph Coloring for Computing Derivatives_](https://epubs.siam.org/doi/10.1137/S0036144504444711), Gebremedhin et al. (2005), Algorithm 3.2 """ function partial_distance2_coloring( - bg::BipartiteGraph{T}, ::Val{side}, order::AbstractOrder + bg::BipartiteGraph{T}, ::Val{side}, vertices_in_order::AbstractVector{<:Integer} ) where {T,side} color = Vector{T}(undef, nb_vertices(bg, Val(side))) forbidden_colors = Vector{T}(undef, nb_vertices(bg, Val(side))) - vertices_in_order = vertices(bg, Val(side), order) partial_distance2_coloring!(color, forbidden_colors, bg, Val(side), vertices_in_order) return color end @@ -54,7 +53,7 @@ function partial_distance2_coloring!( end """ - star_coloring(g::AdjacencyGraph, order::AbstractOrder, postprocessing::Bool) + star_coloring(g::AdjacencyGraph, vertices_in_order::AbstractVector, postprocessing::Bool) Compute a star coloring of all vertices in the adjacency graph `g` and return a tuple `(color, star_set)`, where @@ -63,7 +62,7 @@ Compute a star coloring of all vertices in the adjacency graph `g` and return a A _star coloring_ is a distance-1 coloring such that every path on 4 vertices uses at least 3 colors. -The vertices are colored in a greedy fashion, following the `order` supplied. +The vertices are colored in a greedy fashion, following the order supplied. If `postprocessing=true`, some colors might be replaced with `0` (the "neutral" color) as long as they are not needed during decompression. @@ -77,7 +76,7 @@ If `postprocessing=true`, some colors might be replaced with `0` (the "neutral" > [_New Acyclic and Star Coloring Algorithms with Application to Computing Hessians_](https://epubs.siam.org/doi/abs/10.1137/050639879), Gebremedhin et al. (2007), Algorithm 4.1 """ function star_coloring( - g::AdjacencyGraph{T}, order::AbstractOrder, postprocessing::Bool + g::AdjacencyGraph{T}, vertices_in_order::AbstractVector{<:Integer}, postprocessing::Bool ) where {T<:Integer} # Initialize data structures nv = nb_vertices(g) @@ -88,7 +87,6 @@ function star_coloring( treated = zeros(T, nv) star = Vector{T}(undef, ne) hub = T[] # one hub for each star, including the trivial ones - vertices_in_order = vertices(g, order) for v in vertices_in_order for (w, index_vw) in neighbors_with_edge_indices(g, v) @@ -206,7 +204,7 @@ struct StarSet{T} end """ - acyclic_coloring(g::AdjacencyGraph, order::AbstractOrder, postprocessing::Bool) + acyclic_coloring(g::AdjacencyGraph, vertices_in_order::AbstractVector, postprocessing::Bool) Compute an acyclic coloring of all vertices in the adjacency graph `g` and return a tuple `(color, tree_set)`, where @@ -215,7 +213,7 @@ Compute an acyclic coloring of all vertices in the adjacency graph `g` and retur An _acyclic coloring_ is a distance-1 coloring with the further restriction that every cycle uses at least 3 colors. -The vertices are colored in a greedy fashion, following the `order` supplied. +The vertices are colored in a greedy fashion, following the order supplied. If `postprocessing=true`, some colors might be replaced with `0` (the "neutral" color) as long as they are not needed during decompression. @@ -229,7 +227,7 @@ If `postprocessing=true`, some colors might be replaced with `0` (the "neutral" > [_New Acyclic and Star Coloring Algorithms with Application to Computing Hessians_](https://epubs.siam.org/doi/abs/10.1137/050639879), Gebremedhin et al. (2007), Algorithm 3.1 """ function acyclic_coloring( - g::AdjacencyGraph{T}, order::AbstractOrder, postprocessing::Bool + g::AdjacencyGraph{T}, vertices_in_order::AbstractVector{<:Integer}, postprocessing::Bool ) where {T<:Integer} # Initialize data structures nv = nb_vertices(g) @@ -239,7 +237,6 @@ function acyclic_coloring( first_neighbor = fill((zero(T), zero(T), zero(T)), nv) # at first no neighbors have been encountered first_visit_to_tree = fill((zero(T), zero(T)), ne) forest = Forest{T}(ne) - vertices_in_order = vertices(g, order) for v in vertices_in_order for w in neighbors(g, v) @@ -527,8 +524,8 @@ function TreeSet( # Positions of the first and last vertices in the current tree # Note: tree_edge_indices contains the positions of the first and last edges, # so we add to add an offset k-1 between edge indices and vertex indices - first_vertex = tree_edge_indices[k] + (k-1) - last_vertex = tree_edge_indices[k + 1] + (k-1) + first_vertex = tree_edge_indices[k] + (k - 1) + last_vertex = tree_edge_indices[k + 1] + (k - 1) # compute the degree of each vertex in the tree for index_vertex in first_vertex:last_vertex diff --git a/src/interface.jl b/src/interface.jl index f54b5f73..79fe71e9 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -227,10 +227,10 @@ function _coloring( decompression_eltype::Type, symmetric_pattern::Bool, ) - bg = BipartiteGraph( - A; symmetric_pattern=symmetric_pattern || A isa Union{Symmetric,Hermitian} - ) - color = partial_distance2_coloring(bg, Val(2), algo.order) + symmetric_pattern = symmetric_pattern || A isa Union{Symmetric,Hermitian} + bg = BipartiteGraph(A; symmetric_pattern) + vertices_in_order = vertices(bg, Val(2), algo.order) + color = partial_distance2_coloring(bg, Val(2), vertices_in_order) if speed_setting isa WithResult return ColumnColoringResult(A, bg, color) else @@ -246,10 +246,10 @@ function _coloring( decompression_eltype::Type, symmetric_pattern::Bool, ) - bg = BipartiteGraph( - A; symmetric_pattern=symmetric_pattern || A isa Union{Symmetric,Hermitian} - ) - color = partial_distance2_coloring(bg, Val(1), algo.order) + symmetric_pattern = symmetric_pattern || A isa Union{Symmetric,Hermitian} + bg = BipartiteGraph(A; symmetric_pattern) + vertices_in_order = vertices(bg, Val(1), algo.order) + color = partial_distance2_coloring(bg, Val(1), vertices_in_order) if speed_setting isa WithResult return RowColoringResult(A, bg, color) else @@ -266,7 +266,8 @@ function _coloring( symmetric_pattern::Bool, ) ag = AdjacencyGraph(A; has_diagonal=true) - color, star_set = star_coloring(ag, algo.order, algo.postprocessing) + vertices_in_order = vertices(ag, algo.order) + color, star_set = star_coloring(ag, vertices_in_order, algo.postprocessing) if speed_setting isa WithResult return StarSetColoringResult(A, ag, color, star_set) else @@ -283,7 +284,8 @@ function _coloring( symmetric_pattern::Bool, ) where {R} ag = AdjacencyGraph(A; has_diagonal=true) - color, tree_set = acyclic_coloring(ag, algo.order, algo.postprocessing) + vertices_in_order = vertices(ag, algo.order) + color, tree_set = acyclic_coloring(ag, vertices_in_order, algo.postprocessing) if speed_setting isa WithResult return TreeSetColoringResult(A, ag, color, tree_set, R) else @@ -301,7 +303,8 @@ function _coloring( ) where {R} A_and_Aᵀ, edge_to_index = bidirectional_pattern(A; symmetric_pattern) ag = AdjacencyGraph(A_and_Aᵀ, edge_to_index; has_diagonal=false) - color, star_set = star_coloring(ag, algo.order, algo.postprocessing) + vertices_in_order = vertices(ag, algo.order) + color, star_set = star_coloring(ag, vertices_in_order, algo.postprocessing) if speed_setting isa WithResult symmetric_result = StarSetColoringResult(A_and_Aᵀ, ag, color, star_set) return BicoloringResult(A, ag, symmetric_result, R) @@ -323,7 +326,8 @@ function _coloring( ) where {R} A_and_Aᵀ, edge_to_index = bidirectional_pattern(A; symmetric_pattern) ag = AdjacencyGraph(A_and_Aᵀ, edge_to_index; has_diagonal=false) - color, tree_set = acyclic_coloring(ag, algo.order, algo.postprocessing) + vertices_in_order = vertices(ag, algo.order) + color, tree_set = acyclic_coloring(ag, vertices_in_order, algo.postprocessing) if speed_setting isa WithResult symmetric_result = TreeSetColoringResult(A_and_Aᵀ, ag, color, tree_set, R) return BicoloringResult(A, ag, symmetric_result, R) diff --git a/test/suitesparse.jl b/test/suitesparse.jl index bf3bea04..d69d8a9f 100644 --- a/test/suitesparse.jl +++ b/test/suitesparse.jl @@ -20,11 +20,11 @@ using Test nbunique(x) = length(unique(x)) -_N() = NaturalOrder() -_LF() = LargestFirst() -_SL() = SmallestLast(; reproduce_colpack=true) -_ID() = IncidenceDegree(; reproduce_colpack=true) -_DLF() = DynamicLargestFirst(; reproduce_colpack=true) +_N(args...) = vertices(args..., NaturalOrder()) +_LF(args...) = vertices(args..., LargestFirst()) +_SL(args...) = vertices(args..., SmallestLast(; reproduce_colpack=true)) +_ID(args...) = vertices(args..., IncidenceDegree(; reproduce_colpack=true)) +_DLF(args...) = vertices(args..., DynamicLargestFirst(; reproduce_colpack=true)) ## Distance-2 coloring @@ -49,13 +49,17 @@ colpack_table_6_7 = CSV.read( @test maximum_degree(bg, Val(2)) == row[:Δ2] end @testset "Natural" begin - @test nbunique(partial_distance2_coloring(bg, Val(1), _N())) == row[:N1] - @test nbunique(partial_distance2_coloring(bg, Val(2), _N())) == row[:N2] + @test nbunique(partial_distance2_coloring(bg, Val(1), _N(bg, Val(1)))) == + row[:N1] + @test nbunique(partial_distance2_coloring(bg, Val(2), _N(bg, Val(2)))) == + row[:N2] end yield() @testset "LargestFirst" begin - @test nbunique(partial_distance2_coloring(bg, Val(1), _LF())) == row[:LF1] - @test nbunique(partial_distance2_coloring(bg, Val(2), _LF())) == row[:LF2] + @test nbunique(partial_distance2_coloring(bg, Val(1), _LF(bg, Val(1)))) == + row[:LF1] + @test nbunique(partial_distance2_coloring(bg, Val(2), _LF(bg, Val(2)))) == + row[:LF2] end yield() if row[:name] == "af23560" @@ -67,18 +71,24 @@ colpack_table_6_7 = CSV.read( continue end @testset "SmallestLast" begin - @test nbunique(partial_distance2_coloring(bg, Val(1), _SL())) == row[:SL1] - @test nbunique(partial_distance2_coloring(bg, Val(2), _SL())) == row[:SL2] + @test nbunique(partial_distance2_coloring(bg, Val(1), _SL(bg, Val(1)))) == + row[:SL1] + @test nbunique(partial_distance2_coloring(bg, Val(2), _SL(bg, Val(2)))) == + row[:SL2] end yield() @testset "IncidenceDegree" begin - @test nbunique(partial_distance2_coloring(bg, Val(1), _ID())) == row[:ID1] - @test nbunique(partial_distance2_coloring(bg, Val(2), _ID())) == row[:ID2] + @test nbunique(partial_distance2_coloring(bg, Val(1), _ID(bg, Val(1)))) == + row[:ID1] + @test nbunique(partial_distance2_coloring(bg, Val(2), _ID(bg, Val(2)))) == + row[:ID2] end yield() @testset "DynamicLargestFirst" begin - @test nbunique(partial_distance2_coloring(bg, Val(1), _DLF())) == row[:DLF1] - @test nbunique(partial_distance2_coloring(bg, Val(2), _DLF())) == row[:DLF2] + @test nbunique(partial_distance2_coloring(bg, Val(1), _DLF(bg, Val(1)))) == + row[:DLF1] + @test nbunique(partial_distance2_coloring(bg, Val(2), _DLF(bg, Val(2)))) == + row[:DLF2] end yield() end @@ -105,7 +115,8 @@ what_table_31_32 = CSV.read( @test maximum_degree(bg, Val(1)) == row[:ρmax] @test minimum_degree(bg, Val(2)) == row[:κmin] @test maximum_degree(bg, Val(2)) == row[:κmax] - color_Nb = partial_distance2_coloring(bg, Val(2), NaturalOrder()) + vertices_in_order = vertices(bg, Val(2), NaturalOrder()) + color_Nb = partial_distance2_coloring(bg, Val(2), vertices_in_order) if length(unique(color_Nb)) == row[:K] @test length(unique(color_Nb)) == row[:K] else @@ -133,7 +144,8 @@ what_table_41_42 = CSV.read( @test maximum_degree(ag) == row[:Δ] @test minimum_degree(ag) == row[:δ] postprocessing = false - color_N, _ = star_coloring(ag, NaturalOrder(), postprocessing) + vertices_in_order = vertices(ag, NaturalOrder()) + color_N, _ = star_coloring(ag, vertices_in_order, postprocessing) @test_skip row[:KS1] <= length(unique(color_N)) <= row[:KS2] # TODO: find better yield() end