From f93716681836499f34dcdf6a9012c84b95183532 Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Sat, 8 Mar 2025 10:34:54 -0500 Subject: [PATCH 01/13] Add chordal coloring. --- Project.toml | 2 ++ src/SparseMatrixColorings.jl | 4 +++- src/chordal.jl | 29 +++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/chordal.jl diff --git a/Project.toml b/Project.toml index 6e4c40b0..44553652 100644 --- a/Project.toml +++ b/Project.toml @@ -5,6 +5,7 @@ version = "0.4.14" [deps] ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b" +CliqueTrees = "60701a23-6482-424a-84db-faee86b9b1f8" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" @@ -19,6 +20,7 @@ SparseMatrixColoringsColorsExt = "Colors" [compat] ADTypes = "1.2.1" +CliqueTrees = "0.5.2" Colors = "0.12.11, 0.13" DataStructures = "0.18" DocStringExtensions = "0.8,0.9" diff --git a/src/SparseMatrixColorings.jl b/src/SparseMatrixColorings.jl index 7d879c9a..dc95213d 100644 --- a/src/SparseMatrixColorings.jl +++ b/src/SparseMatrixColorings.jl @@ -11,6 +11,7 @@ module SparseMatrixColorings using ADTypes: ADTypes using Base.Iterators: Iterators +using CliqueTrees: CliqueTrees using DataStructures: DisjointSets, find_root!, root_union!, num_groups using DocStringExtensions: README, EXPORTS, SIGNATURES, TYPEDEF, TYPEDFIELDS using LinearAlgebra: @@ -49,6 +50,7 @@ include("result.jl") include("matrices.jl") include("interface.jl") include("constant.jl") +include("chordal.jl") include("adtypes.jl") include("decompression.jl") include("check.jl") @@ -58,7 +60,7 @@ include("show_colors.jl") export NaturalOrder, RandomOrder, LargestFirst export DynamicDegreeBasedOrder, SmallestLast, IncidenceDegree, DynamicLargestFirst export ColoringProblem, GreedyColoringAlgorithm, AbstractColoringResult -export ConstantColoringAlgorithm +export ConstantColoringAlgorithm, ChordalColoringAlgorithm export coloring, fast_coloring export column_colors, row_colors, ncolors export column_groups, row_groups diff --git a/src/chordal.jl b/src/chordal.jl new file mode 100644 index 00000000..e1527ace --- /dev/null +++ b/src/chordal.jl @@ -0,0 +1,29 @@ +struct ChordalColoringAlgorithm <: ADTypes.AbstractColoringAlgorithm end + +struct ChordalColoringResult{M <: AbstractMatrix, V} <: AbstractColoringResult{:symmetric, :column, :direct} + A::M + color::Vector{Int} + group::V +end + +function coloring( + A::AbstractMatrix, + ::ColoringProblem{:symmetric,:column}, + algo::ChordalColoringAlgorithm, +) + # compute an elimination ordering using + # the maximum cardinality search algorithm + perm, invp = CliqueTrees.permutation(A; alg=CliqueTrees.MCS()) + + # if the ordering is not perfect, then the graph is not chordal + if !CliqueTrees.isperfect(A, perm, invp) + error("Matrix does not have a chordal sparsity pattern.") + end + + # if the graph is chordal, then find a minimal vertex coloring + color = CliqueTrees.color(A, invp).colors + + # compute groups and return result + group = group_by_color(color) + return ChordalColoringResult(A, color, group) +end From 003360b613f95ef6e793b9617ff24116c8db80c7 Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Sat, 8 Mar 2025 11:11:45 -0500 Subject: [PATCH 02/13] fix bug --- src/chordal.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chordal.jl b/src/chordal.jl index e1527ace..47e0beb2 100644 --- a/src/chordal.jl +++ b/src/chordal.jl @@ -21,7 +21,7 @@ function coloring( end # if the graph is chordal, then find a minimal vertex coloring - color = CliqueTrees.color(A, invp).colors + color = CliqueTrees.color(A, perm, invp).colors # compute groups and return result group = group_by_color(color) From ef9f44405443240a9099723128fed99f6e6423ae Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Sat, 8 Mar 2025 13:30:22 -0500 Subject: [PATCH 03/13] Add ordering algorithm: `PerfectEliminationOrder` --- Project.toml | 3 +- ext/SparseMatrixColoringsCliqueTreesExt.jl | 32 ++++++++++++++++++++++ src/SparseMatrixColorings.jl | 5 ++-- src/chordal.jl | 29 -------------------- src/order.jl | 14 ++++++++++ 5 files changed, 50 insertions(+), 33 deletions(-) create mode 100644 ext/SparseMatrixColoringsCliqueTreesExt.jl delete mode 100644 src/chordal.jl diff --git a/Project.toml b/Project.toml index 44553652..88b746df 100644 --- a/Project.toml +++ b/Project.toml @@ -5,7 +5,6 @@ version = "0.4.14" [deps] ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b" -CliqueTrees = "60701a23-6482-424a-84db-faee86b9b1f8" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" @@ -13,9 +12,11 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [weakdeps] +CliqueTrees = "60701a23-6482-424a-84db-faee86b9b1f8" Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" [extensions] +SparseMatrixColoringsCliqueTreesExt = "CliqueTrees" SparseMatrixColoringsColorsExt = "Colors" [compat] diff --git a/ext/SparseMatrixColoringsCliqueTreesExt.jl b/ext/SparseMatrixColoringsCliqueTreesExt.jl new file mode 100644 index 00000000..2df9573d --- /dev/null +++ b/ext/SparseMatrixColoringsCliqueTreesExt.jl @@ -0,0 +1,32 @@ +module SparseMatrixColoringsCliqueTreesExt + +using CliqueTrees: CliqueTrees +using SparseArrays +using SparseMatrixColorings: SparseMatrixColorings, AdjacencyGraph, BipartiteGraph, PerfectEliminationOrder, pattern + +function SparseMatrixColorings.vertices(g::AdjacencyGraph{T}, order::PerfectEliminationOrder) where T + S = pattern(g) + + # construct matrix with sparsity pattern S + M = SparseMatrixCSC{Bool, T}(size(S)..., S.colptr, rowvals(S), ones(Bool, nnz(S))) + + # can also use alg=CliqueTrees.LexBFS() + order, _ = CliqueTrees.permutation(M; alg=CliqueTrees.MCS()) + + return reverse!(order) +end + +function SparseMatrixColorings.vertices(bg::BipartiteGraph{T}, ::Val{side}, order::PerfectEliminationOrder) where {T, side} + S = pattern(g, Val(side)) + + # construct matrix with sparsity pattern S + M = SparseMatrixCSC{Bool, T}(size(S)..., S.colptr, rowvals(S), ones(Bool, nnz(S))) + + # can also use alg=CliqueTrees.LexBFS() + order, _ = CliqueTrees.permutation(M; alg=CliqueTrees.MCS()) + + return reverse!(order) + +end + +end # module diff --git a/src/SparseMatrixColorings.jl b/src/SparseMatrixColorings.jl index dc95213d..35b9857c 100644 --- a/src/SparseMatrixColorings.jl +++ b/src/SparseMatrixColorings.jl @@ -11,7 +11,6 @@ module SparseMatrixColorings using ADTypes: ADTypes using Base.Iterators: Iterators -using CliqueTrees: CliqueTrees using DataStructures: DisjointSets, find_root!, root_union!, num_groups using DocStringExtensions: README, EXPORTS, SIGNATURES, TYPEDEF, TYPEDFIELDS using LinearAlgebra: @@ -50,7 +49,6 @@ include("result.jl") include("matrices.jl") include("interface.jl") include("constant.jl") -include("chordal.jl") include("adtypes.jl") include("decompression.jl") include("check.jl") @@ -59,8 +57,9 @@ include("show_colors.jl") export NaturalOrder, RandomOrder, LargestFirst export DynamicDegreeBasedOrder, SmallestLast, IncidenceDegree, DynamicLargestFirst +export PerfectEliminationOrder export ColoringProblem, GreedyColoringAlgorithm, AbstractColoringResult -export ConstantColoringAlgorithm, ChordalColoringAlgorithm +export ConstantColoringAlgorithm export coloring, fast_coloring export column_colors, row_colors, ncolors export column_groups, row_groups diff --git a/src/chordal.jl b/src/chordal.jl deleted file mode 100644 index 47e0beb2..00000000 --- a/src/chordal.jl +++ /dev/null @@ -1,29 +0,0 @@ -struct ChordalColoringAlgorithm <: ADTypes.AbstractColoringAlgorithm end - -struct ChordalColoringResult{M <: AbstractMatrix, V} <: AbstractColoringResult{:symmetric, :column, :direct} - A::M - color::Vector{Int} - group::V -end - -function coloring( - A::AbstractMatrix, - ::ColoringProblem{:symmetric,:column}, - algo::ChordalColoringAlgorithm, -) - # compute an elimination ordering using - # the maximum cardinality search algorithm - perm, invp = CliqueTrees.permutation(A; alg=CliqueTrees.MCS()) - - # if the ordering is not perfect, then the graph is not chordal - if !CliqueTrees.isperfect(A, perm, invp) - error("Matrix does not have a chordal sparsity pattern.") - end - - # if the graph is chordal, then find a minimal vertex coloring - color = CliqueTrees.color(A, perm, invp).colors - - # compute groups and return result - group = group_by_color(color) - return ChordalColoringResult(A, color, group) -end diff --git a/src/order.jl b/src/order.jl index 0635486b..6be029c8 100644 --- a/src/order.jl +++ b/src/order.jl @@ -301,3 +301,17 @@ Instance of [`AbstractOrder`](@ref) which sorts vertices from lowest to highest - [`DynamicDegreeBasedOrder`](@ref) """ const DynamicLargestFirst = DynamicDegreeBasedOrder{:forward,:low2high} + +""" + PerfectEliminationOrder + +Instance of [`AbstractOrder`](@ref) which sorts the vertices of a chordal graph in a perfect elimination order. + +!!! danger + This order is implemented as a package extension and requires loading CliqueTrees.jl. + +# References + +- [Simple Linear-Time Algorithms to Test Chordality of Graphs, Test Acyclicity of Hypergraphs, and Selectively Reduce Acyclic Hypergraphs](https://epubs.siam.org/doi/10.1137/0213035), Tarjan and Yannakakis (1984) +""" +struct PerfectEliminationOrder <: AbstractOrder end From 0279098ac060eb0d7f4235449fd180d363b52ced Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Sat, 8 Mar 2025 14:53:43 -0500 Subject: [PATCH 04/13] Add tests for ordering algorithm `PerfectEliminationOrder`. --- ext/SparseMatrixColoringsCliqueTreesExt.jl | 18 +++++++++++------- test/Project.toml | 1 + test/order.jl | 18 ++++++++++++++++++ 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/ext/SparseMatrixColoringsCliqueTreesExt.jl b/ext/SparseMatrixColoringsCliqueTreesExt.jl index 2df9573d..42e4a87e 100644 --- a/ext/SparseMatrixColoringsCliqueTreesExt.jl +++ b/ext/SparseMatrixColoringsCliqueTreesExt.jl @@ -2,13 +2,16 @@ module SparseMatrixColoringsCliqueTreesExt using CliqueTrees: CliqueTrees using SparseArrays -using SparseMatrixColorings: SparseMatrixColorings, AdjacencyGraph, BipartiteGraph, PerfectEliminationOrder, pattern +using SparseMatrixColorings: + SparseMatrixColorings, AdjacencyGraph, BipartiteGraph, PerfectEliminationOrder, pattern -function SparseMatrixColorings.vertices(g::AdjacencyGraph{T}, order::PerfectEliminationOrder) where T +function SparseMatrixColorings.vertices( + g::AdjacencyGraph{T}, order::PerfectEliminationOrder +) where {T} S = pattern(g) # construct matrix with sparsity pattern S - M = SparseMatrixCSC{Bool, T}(size(S)..., S.colptr, rowvals(S), ones(Bool, nnz(S))) + M = SparseMatrixCSC{Bool,T}(size(S)..., S.colptr, rowvals(S), ones(Bool, nnz(S))) # can also use alg=CliqueTrees.LexBFS() order, _ = CliqueTrees.permutation(M; alg=CliqueTrees.MCS()) @@ -16,17 +19,18 @@ function SparseMatrixColorings.vertices(g::AdjacencyGraph{T}, order::PerfectElim return reverse!(order) end -function SparseMatrixColorings.vertices(bg::BipartiteGraph{T}, ::Val{side}, order::PerfectEliminationOrder) where {T, side} - S = pattern(g, Val(side)) +function SparseMatrixColorings.vertices( + bg::BipartiteGraph{T}, ::Val{side}, order::PerfectEliminationOrder +) where {T,side} + S = pattern(bg, Val(side)) # construct matrix with sparsity pattern S - M = SparseMatrixCSC{Bool, T}(size(S)..., S.colptr, rowvals(S), ones(Bool, nnz(S))) + M = SparseMatrixCSC{Bool,T}(size(S)..., S.colptr, rowvals(S), ones(Bool, nnz(S))) # can also use alg=CliqueTrees.LexBFS() order, _ = CliqueTrees.permutation(M; alg=CliqueTrees.MCS()) return reverse!(order) - end end # module diff --git a/test/Project.toml b/test/Project.toml index 05d0dedf..00ee7348 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -7,6 +7,7 @@ BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" Chairmarks = "0ca39b1e-fe0b-4e98-acfc-b1656634c4de" +CliqueTrees = "60701a23-6482-424a-84db-faee86b9b1f8" Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" diff --git a/test/order.jl b/test/order.jl index 8071603f..cc294f66 100644 --- a/test/order.jl +++ b/test/order.jl @@ -1,3 +1,5 @@ +using CliqueTrees: CliqueTrees +using BandedMatrices using LinearAlgebra using SparseArrays using SparseMatrixColorings @@ -7,6 +9,7 @@ using SparseMatrixColorings: LargestFirst, NaturalOrder, RandomOrder, + PerfectEliminationOrder, degree_dist2, nb_vertices, valid_dynamic_order, @@ -110,3 +113,18 @@ end; end end end; + +@testset "PerfectEliminationOrder" begin + problem1 = ColoringProblem(; structure=:symmetric, partition=:column) + problem2 = ColoringProblem(; structure=:nonsymmetric, partition=:column) + algorithm = GreedyColoringAlgorithm( + PerfectEliminationOrder(); decompression=:substitution + ) + + for (n, m) in ((800, 80), (400, 40), (200, 20), (100, 10)) + perm = randperm(n) + matrix = permute!(sparse(Symmetric(brand(n, n, m, 0), :L)), perm, perm) + @test ncolors(coloring(matrix, problem1, algorithm)) == 1m + 1 + @test ncolors(coloring(matrix, problem2, algorithm)) == 2m + 1 + end +end From 7d5e1a59580254cf3906e5568d6b1023d55bc1fd Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Sat, 8 Mar 2025 15:56:38 -0500 Subject: [PATCH 05/13] Respond to PR comments. --- ext/SparseMatrixColoringsCliqueTreesExt.jl | 34 +++++++--------------- src/order.jl | 4 +-- test/order.jl | 18 ++++++++---- 3 files changed, 26 insertions(+), 30 deletions(-) diff --git a/ext/SparseMatrixColoringsCliqueTreesExt.jl b/ext/SparseMatrixColoringsCliqueTreesExt.jl index 42e4a87e..ebf6507c 100644 --- a/ext/SparseMatrixColoringsCliqueTreesExt.jl +++ b/ext/SparseMatrixColoringsCliqueTreesExt.jl @@ -1,34 +1,22 @@ module SparseMatrixColoringsCliqueTreesExt -using CliqueTrees: CliqueTrees -using SparseArrays -using SparseMatrixColorings: - SparseMatrixColorings, AdjacencyGraph, BipartiteGraph, PerfectEliminationOrder, pattern +import CliqueTrees: permutation, LexBFS, MCS +import SparseArrays: SparseMatrixCSC, rowvals, nnz +import SparseMatrixColorings: + AdjacencyGraph, BipartiteGraph, PerfectEliminationOrder, pattern, vertices -function SparseMatrixColorings.vertices( - g::AdjacencyGraph{T}, order::PerfectEliminationOrder -) where {T} +function vertices(g::AdjacencyGraph{T}, order::PerfectEliminationOrder) where {T} S = pattern(g) # construct matrix with sparsity pattern S M = SparseMatrixCSC{Bool,T}(size(S)..., S.colptr, rowvals(S), ones(Bool, nnz(S))) - # can also use alg=CliqueTrees.LexBFS() - order, _ = CliqueTrees.permutation(M; alg=CliqueTrees.MCS()) - - return reverse!(order) -end - -function SparseMatrixColorings.vertices( - bg::BipartiteGraph{T}, ::Val{side}, order::PerfectEliminationOrder -) where {T,side} - S = pattern(bg, Val(side)) - - # construct matrix with sparsity pattern S - M = SparseMatrixCSC{Bool,T}(size(S)..., S.colptr, rowvals(S), ones(Bool, nnz(S))) - - # can also use alg=CliqueTrees.LexBFS() - order, _ = CliqueTrees.permutation(M; alg=CliqueTrees.MCS()) + # construct a perfect elimination order + # self-loops are ignored + # we can also use alg=LexBFS() + # - time complexity: O(|V| + |E|) + # - space complexity: O(|V| + |E|) + order, _ = permutation(M; alg=MCS()) return reverse!(order) end diff --git a/src/order.jl b/src/order.jl index 6be029c8..355a7f5d 100644 --- a/src/order.jl +++ b/src/order.jl @@ -305,10 +305,10 @@ const DynamicLargestFirst = DynamicDegreeBasedOrder{:forward,:low2high} """ PerfectEliminationOrder -Instance of [`AbstractOrder`](@ref) which sorts the vertices of a chordal graph in a perfect elimination order. +A linear-time ordering code for symmetric graphs. On [chordal graphs](https://en.wikipedia.org/wiki/Chordal_graph), the code computes a perfect elimination ordering. Otherwise, it computes a suboptimal ordering. !!! danger - This order is implemented as a package extension and requires loading CliqueTrees.jl. + This order is implemented as a package extension and requires loading [CliqueTrees.jl](https://github.com/AlgebraicJulia/CliqueTrees.jl). # References diff --git a/test/order.jl b/test/order.jl index cc294f66..20ab816a 100644 --- a/test/order.jl +++ b/test/order.jl @@ -115,16 +115,24 @@ end; end; @testset "PerfectEliminationOrder" begin - problem1 = ColoringProblem(; structure=:symmetric, partition=:column) - problem2 = ColoringProblem(; structure=:nonsymmetric, partition=:column) + problem = ColoringProblem(; structure=:symmetric, partition=:column) algorithm = GreedyColoringAlgorithm( PerfectEliminationOrder(); decompression=:substitution ) + # band graphs for (n, m) in ((800, 80), (400, 40), (200, 20), (100, 10)) - perm = randperm(n) + perm = randperm(rng, n) matrix = permute!(sparse(Symmetric(brand(n, n, m, 0), :L)), perm, perm) - @test ncolors(coloring(matrix, problem1, algorithm)) == 1m + 1 - @test ncolors(coloring(matrix, problem2, algorithm)) == 2m + 1 + π = vertices(AdjacencyGraph(matrix), PerfectEliminationOrder()) + @test isperm(π) + @test ncolors(coloring(matrix, problem, algorithm)) == m + 1 + end + + # random graphs + for (n, p) in Iterators.product(20:20:100, 0.0:0.1:0.2) + matrix = sparse(Symmetric(sprand(rng, Bool, n, n, p))) + π = vertices(AdjacencyGraph(matrix), PerfectEliminationOrder()) + @test isperm(π) end end From 8798bc63dacf954b523e2b46fbaaac82ee2eb9f9 Mon Sep 17 00:00:00 2001 From: Richard Samuelson Date: Sat, 8 Mar 2025 16:58:58 -0500 Subject: [PATCH 06/13] Add `PerfectEliminationOrder` to api. --- docs/Project.toml | 3 +++ docs/src/api.md | 1 + 2 files changed, 4 insertions(+) diff --git a/docs/Project.toml b/docs/Project.toml index fc5d3c54..61f164de 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -6,3 +6,6 @@ DocumenterInterLinks = "d12716ef-a0f6-4df4-a9f1-a5a34e75c656" Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0" SparseMatrixColorings = "0a514795-09f3-496d-8182-132a7b665d35" StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" + +[sources] +SparseMatrixColorings = {path=".."} diff --git a/docs/src/api.md b/docs/src/api.md index d2ca870e..c121d1e3 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -53,4 +53,5 @@ SmallestLast IncidenceDegree DynamicLargestFirst DynamicDegreeBasedOrder +PerfectEliminationOrder ``` From 52b0a70d0a6125f79356310cbf6913e367009954 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Sun, 9 Mar 2025 10:51:38 +0100 Subject: [PATCH 07/13] Update src/order.jl --- src/order.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/order.jl b/src/order.jl index 355a7f5d..b599006a 100644 --- a/src/order.jl +++ b/src/order.jl @@ -305,7 +305,10 @@ const DynamicLargestFirst = DynamicDegreeBasedOrder{:forward,:low2high} """ PerfectEliminationOrder -A linear-time ordering code for symmetric graphs. On [chordal graphs](https://en.wikipedia.org/wiki/Chordal_graph), the code computes a perfect elimination ordering. Otherwise, it computes a suboptimal ordering. +Instance of [`AbstractOrder`](@ref) which computes a perfect elimination ordering when the underlying graph is [chordal](https://en.wikipedia.org/wiki/Chordal_graph). For generic graphs, it computes a suboptimal ordering. + +!!! warning + This order can only be applied for symmetric or bidirectional coloring problems, not unidirectional ones. !!! danger This order is implemented as a package extension and requires loading [CliqueTrees.jl](https://github.com/AlgebraicJulia/CliqueTrees.jl). From d9faa7a686b893a39282f5eedb349ea2c99833e1 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Sun, 9 Mar 2025 11:29:23 +0100 Subject: [PATCH 08/13] Update src/order.jl --- src/order.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/order.jl b/src/order.jl index b599006a..6160a8f9 100644 --- a/src/order.jl +++ b/src/order.jl @@ -308,7 +308,7 @@ const DynamicLargestFirst = DynamicDegreeBasedOrder{:forward,:low2high} Instance of [`AbstractOrder`](@ref) which computes a perfect elimination ordering when the underlying graph is [chordal](https://en.wikipedia.org/wiki/Chordal_graph). For generic graphs, it computes a suboptimal ordering. !!! warning - This order can only be applied for symmetric or bidirectional coloring problems, not unidirectional ones. + This order can only be applied for symmetric or bidirectional coloring problems, not unidirectional ones. Furthermore, its theoretical guarantees only hold for decompression by substitution. !!! danger This order is implemented as a package extension and requires loading [CliqueTrees.jl](https://github.com/AlgebraicJulia/CliqueTrees.jl). From 99a02d0ee935188f7ac5362dbad05fd3acbd7c15 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Sun, 9 Mar 2025 11:30:08 +0100 Subject: [PATCH 09/13] Update src/order.jl --- src/order.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/order.jl b/src/order.jl index 6160a8f9..5596db2e 100644 --- a/src/order.jl +++ b/src/order.jl @@ -308,7 +308,7 @@ const DynamicLargestFirst = DynamicDegreeBasedOrder{:forward,:low2high} Instance of [`AbstractOrder`](@ref) which computes a perfect elimination ordering when the underlying graph is [chordal](https://en.wikipedia.org/wiki/Chordal_graph). For generic graphs, it computes a suboptimal ordering. !!! warning - This order can only be applied for symmetric or bidirectional coloring problems, not unidirectional ones. Furthermore, its theoretical guarantees only hold for decompression by substitution. + This order can only be applied for symmetric or bidirectional coloring problems. Furthermore, its optimality guarantees only hold for decompression by substitution, not for direct decompression. !!! danger This order is implemented as a package extension and requires loading [CliqueTrees.jl](https://github.com/AlgebraicJulia/CliqueTrees.jl). From 1642fc83c2df440a5aea2a998b947106d7858e8e Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Sun, 9 Mar 2025 13:41:50 +0100 Subject: [PATCH 10/13] Fix docstring --- src/order.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/order.jl b/src/order.jl index 5596db2e..d5a5ddb9 100644 --- a/src/order.jl +++ b/src/order.jl @@ -305,10 +305,10 @@ const DynamicLargestFirst = DynamicDegreeBasedOrder{:forward,:low2high} """ PerfectEliminationOrder -Instance of [`AbstractOrder`](@ref) which computes a perfect elimination ordering when the underlying graph is [chordal](https://en.wikipedia.org/wiki/Chordal_graph). For generic graphs, it computes a suboptimal ordering. +Instance of [`AbstractOrder`](@ref) which computes a perfect elimination ordering when the underlying graph is [chordal](https://en.wikipedia.org/wiki/Chordal_graph). For non-chordal graphs, it computes a suboptimal ordering. !!! warning - This order can only be applied for symmetric or bidirectional coloring problems. Furthermore, its optimality guarantees only hold for decompression by substitution, not for direct decompression. + This order can only be applied for symmetric or bidirectional coloring problems. !!! danger This order is implemented as a package extension and requires loading [CliqueTrees.jl](https://github.com/AlgebraicJulia/CliqueTrees.jl). From 226d8d808e05a8a463e352f72ce5055f9f58a05f Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Sun, 9 Mar 2025 13:56:26 +0100 Subject: [PATCH 11/13] Bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 88b746df..157b96e6 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.14" +version = "0.4.15" [deps] ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b" From 13300d20acffcc7249ab1cf1cc13237257945fc6 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Sun, 9 Mar 2025 14:50:04 +0100 Subject: [PATCH 12/13] Make elimination algorithm parametric --- ext/SparseMatrixColoringsCliqueTreesExt.jl | 9 ++++----- src/order.jl | 9 +++++++-- test/order.jl | 8 +++++--- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/ext/SparseMatrixColoringsCliqueTreesExt.jl b/ext/SparseMatrixColoringsCliqueTreesExt.jl index ebf6507c..5dde067f 100644 --- a/ext/SparseMatrixColoringsCliqueTreesExt.jl +++ b/ext/SparseMatrixColoringsCliqueTreesExt.jl @@ -1,10 +1,12 @@ module SparseMatrixColoringsCliqueTreesExt -import CliqueTrees: permutation, LexBFS, MCS +import CliqueTrees: permutation, EliminationAlgorithm, MCS import SparseArrays: SparseMatrixCSC, rowvals, nnz import SparseMatrixColorings: AdjacencyGraph, BipartiteGraph, PerfectEliminationOrder, pattern, vertices +PerfectEliminationOrder() = PerfectEliminationOrder(MCS()) + function vertices(g::AdjacencyGraph{T}, order::PerfectEliminationOrder) where {T} S = pattern(g) @@ -13,10 +15,7 @@ function vertices(g::AdjacencyGraph{T}, order::PerfectEliminationOrder) where {T # construct a perfect elimination order # self-loops are ignored - # we can also use alg=LexBFS() - # - time complexity: O(|V| + |E|) - # - space complexity: O(|V| + |E|) - order, _ = permutation(M; alg=MCS()) + order, _ = permutation(M; alg=order.elimination_algorithm) return reverse!(order) end diff --git a/src/order.jl b/src/order.jl index d5a5ddb9..237d542b 100644 --- a/src/order.jl +++ b/src/order.jl @@ -14,6 +14,7 @@ Depending on how the vertices are ordered, the number of colors necessary may va - [`IncidenceDegree`](@ref) (experimental) - [`SmallestLast`](@ref) (experimental) - [`DynamicLargestFirst`](@ref) (experimental) +- [`PerfectEliminationOrder`](@ref) """ abstract type AbstractOrder end @@ -303,10 +304,12 @@ Instance of [`AbstractOrder`](@ref) which sorts vertices from lowest to highest const DynamicLargestFirst = DynamicDegreeBasedOrder{:forward,:low2high} """ - PerfectEliminationOrder + PerfectEliminationOrder(elimination_algorithm=CliqueTrees.MCS()) Instance of [`AbstractOrder`](@ref) which computes a perfect elimination ordering when the underlying graph is [chordal](https://en.wikipedia.org/wiki/Chordal_graph). For non-chordal graphs, it computes a suboptimal ordering. +The `elimination_algorithm` must be an instance of `CliqueTrees.EliminationAlgorithm`. + !!! warning This order can only be applied for symmetric or bidirectional coloring problems. @@ -317,4 +320,6 @@ Instance of [`AbstractOrder`](@ref) which computes a perfect elimination orderin - [Simple Linear-Time Algorithms to Test Chordality of Graphs, Test Acyclicity of Hypergraphs, and Selectively Reduce Acyclic Hypergraphs](https://epubs.siam.org/doi/10.1137/0213035), Tarjan and Yannakakis (1984) """ -struct PerfectEliminationOrder <: AbstractOrder end +struct PerfectEliminationOrder{E} <: AbstractOrder + elimination_algorithm::E +end diff --git a/test/order.jl b/test/order.jl index 20ab816a..96abb0ac 100644 --- a/test/order.jl +++ b/test/order.jl @@ -116,8 +116,9 @@ end; @testset "PerfectEliminationOrder" begin problem = ColoringProblem(; structure=:symmetric, partition=:column) - algorithm = GreedyColoringAlgorithm( - PerfectEliminationOrder(); decompression=:substitution + direct_algo = GreedyColoringAlgorithm(PerfectEliminationOrder(); decompression=:direct) + substitution_algo = GreedyColoringAlgorithm( + PerfectEliminationOrder(CliqueTrees.LexBFS()); decompression=:substitution ) # band graphs @@ -126,7 +127,8 @@ end; matrix = permute!(sparse(Symmetric(brand(n, n, m, 0), :L)), perm, perm) π = vertices(AdjacencyGraph(matrix), PerfectEliminationOrder()) @test isperm(π) - @test ncolors(coloring(matrix, problem, algorithm)) == m + 1 + @test ncolors(coloring(matrix, problem, direct_algo)) == 2m + 1 + @test ncolors(coloring(matrix, problem, substitution_algo)) == m + 1 end # random graphs From 38ba304f61abb3a37d6d56fb82ba23168a53b17e Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Sun, 9 Mar 2025 14:57:13 +0100 Subject: [PATCH 13/13] Only substitution --- src/order.jl | 2 +- test/order.jl | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/order.jl b/src/order.jl index 237d542b..319279fb 100644 --- a/src/order.jl +++ b/src/order.jl @@ -311,7 +311,7 @@ Instance of [`AbstractOrder`](@ref) which computes a perfect elimination orderin The `elimination_algorithm` must be an instance of `CliqueTrees.EliminationAlgorithm`. !!! warning - This order can only be applied for symmetric or bidirectional coloring problems. + This order can only be applied for symmetric or bidirectional coloring problems. Furthermore, its theoretical guarantees only hold for decompression by substitution. !!! danger This order is implemented as a package extension and requires loading [CliqueTrees.jl](https://github.com/AlgebraicJulia/CliqueTrees.jl). diff --git a/test/order.jl b/test/order.jl index 96abb0ac..347e4b28 100644 --- a/test/order.jl +++ b/test/order.jl @@ -116,9 +116,8 @@ end; @testset "PerfectEliminationOrder" begin problem = ColoringProblem(; structure=:symmetric, partition=:column) - direct_algo = GreedyColoringAlgorithm(PerfectEliminationOrder(); decompression=:direct) substitution_algo = GreedyColoringAlgorithm( - PerfectEliminationOrder(CliqueTrees.LexBFS()); decompression=:substitution + PerfectEliminationOrder(); decompression=:substitution ) # band graphs @@ -127,7 +126,6 @@ end; matrix = permute!(sparse(Symmetric(brand(n, n, m, 0), :L)), perm, perm) π = vertices(AdjacencyGraph(matrix), PerfectEliminationOrder()) @test isperm(π) - @test ncolors(coloring(matrix, problem, direct_algo)) == 2m + 1 @test ncolors(coloring(matrix, problem, substitution_algo)) == m + 1 end