API reference
The docstrings on this page define the public API of the package.
SparseMatrixColorings.SparseMatrixColorings — ModuleSparseMatrixColoringsSparseMatrixColorings.jl
Coloring algorithms for sparse Jacobian and Hessian matrices.
Getting started
To install this package, run the following in a Julia Pkg REPL:
pkg> add SparseMatrixColoringsBackground
The algorithms implemented in this package are described in the following preprint:
- Revisiting Sparse Matrix Coloring and Bicoloring, Montoison et al. (2025)
and inspired by previous works:
- What Color Is Your Jacobian? Graph Coloring for Computing Derivatives, Gebremedhin et al. (2005)
- New Acyclic and Star Coloring Algorithms with Application to Computing Hessians, Gebremedhin et al. (2007)
- Efficient Computation of Sparse Hessians Using Coloring and Automatic Differentiation, Gebremedhin et al. (2009)
- ColPack: Software for graph coloring and related problems in scientific computing, Gebremedhin et al. (2013)
Some parts of the articles (like definitions) are thus copied verbatim in the documentation.
Alternatives
- ColPack.jl: a Julia interface to the C++ library ColPack
- SparseDiffTools.jl: contains Julia implementations of some coloring algorithms
Citing
Please cite this software using the provided CITATION.cff file or the .bib entry below:
@unpublished{montoison2025revisitingsparsematrixcoloring,
title={Revisiting Sparse Matrix Coloring and Bicoloring},
author={Alexis Montoison and Guillaume Dalle and Assefaw Gebremedhin},
year={2025},
eprint={2505.07308},
archivePrefix={arXiv},
primaryClass={math.NA},
url={https://arxiv.org/abs/2505.07308},
}The link https://zenodo.org/doi/10.5281/zenodo.11314275 resolves to the latest version on Zenodo.
Exports
AbstractColoringResultColoringProblemConstantColoringAlgorithmDynamicDegreeBasedOrderDynamicLargestFirstGreedyColoringAlgorithmIncidenceDegreeLargestFirstNaturalOrderPerfectEliminationOrderRandomOrderSmallestLastcoloringcolumn_colorscolumn_groupscompressdecompressdecompress!decompress_single_color!fast_coloringncolorsrow_colorsrow_groupssparsity_pattern
Main function
SparseMatrixColorings.coloring — Functioncoloring(
S::AbstractMatrix,
problem::ColoringProblem,
algo::GreedyColoringAlgorithm;
[decompression_eltype=Float64, symmetric_pattern=false]
)Solve a ColoringProblem on the matrix S with a GreedyColoringAlgorithm and return an AbstractColoringResult.
The result can be used to compress and decompress a matrix A with the same sparsity pattern as S. If eltype(A) == decompression_eltype, decompression might be faster.
For a :nonsymmetric problem (and only then), setting symmetric_pattern=true indicates that the pattern of nonzeros is symmetric. This condition is weaker than the symmetry of actual values, so it can happen for some Jacobians. Specifying it allows faster construction of the bipartite graph.
Example
julia> using SparseMatrixColorings, SparseArrays
julia> S = sparse([
0 0 1 1 0 1
1 0 0 0 1 0
0 1 0 0 1 0
0 1 1 0 0 0
]);
julia> problem = ColoringProblem(; structure=:nonsymmetric, partition=:column);
julia> algo = GreedyColoringAlgorithm(; decompression=:direct);
julia> result = coloring(S, problem, algo);
julia> column_colors(result)
6-element Vector{Int64}:
1
1
2
1
2
3
julia> collect.(column_groups(result))
3-element Vector{Vector{Int64}}:
[1, 2, 4]
[3, 5]
[6]See also
SparseMatrixColorings.fast_coloring — Functionfast_coloring(
S::AbstractMatrix,
problem::ColoringProblem,
algo::GreedyColoringAlgorithm;
[symmetric_pattern=false]
)Solve a ColoringProblem on the matrix S with a GreedyColoringAlgorithm and return
- a single color vector for
:columnand:rowproblems - a tuple of color vectors for
:bidirectionalproblems
This function is very similar to coloring, but it skips the computation of an AbstractColoringResult to speed things up.
See also
SparseMatrixColorings.ColoringProblem — TypeColoringProblem{structure,partition}Selector type for the coloring problem to solve, enabling multiple dispatch.
It is passed as an argument to the main function coloring.
Constructors
ColoringProblem{structure,partition}()
ColoringProblem(; structure=:nonsymmetric, partition=:column)structure::Symbol: either:nonsymmetricor:symmetricpartition::Symbol: either:column,:rowor:bidirectional
Link to automatic differentiation
Matrix coloring is often used in automatic differentiation, and here is the translation guide:
| matrix | mode | structure | partition | implemented |
|---|---|---|---|---|
| Jacobian | forward | :nonsymmetric | :column | yes |
| Jacobian | reverse | :nonsymmetric | :row | yes |
| Jacobian | mixed | :nonsymmetric | :bidirectional | yes |
| Hessian | - | :symmetric | :column | yes |
| Hessian | - | :symmetric | :row | no |
SparseMatrixColorings.GreedyColoringAlgorithm — TypeGreedyColoringAlgorithm{decompression} <: ADTypes.AbstractColoringAlgorithmGreedy coloring algorithm for sparse matrices which colors columns or rows one after the other, following a configurable order.
It is passed as an argument to the main function coloring.
Constructors
GreedyColoringAlgorithm{decompression}(order=NaturalOrder(); postprocessing=false)
GreedyColoringAlgorithm(order=NaturalOrder(); postprocessing=false, decompression=:direct)order::Union{AbstractOrder,Tuple}: the order in which the columns or rows are colored, which can impact the number of colors. Can also be a tuple of different orders to try out, from which the best order (the one with the lowest total number of colors) will be used.postprocessing::Bool: whether or not the coloring will be refined by assigning the neutral color0to some vertices.decompression::Symbol: either:director:substitution. Usually:substitutionleads to fewer colors, at the cost of a more expensive coloring (and decompression). When:substitutionis not applicable, it falls back on:directdecompression.
ADTypes coloring interface
GreedyColoringAlgorithm is a subtype of ADTypes.AbstractColoringAlgorithm, which means the following methods are also applicable:
See their respective docstrings for details.
See also
SparseMatrixColorings.ConstantColoringAlgorithm — TypeConstantColoringAlgorithm{partition} <: ADTypes.AbstractColoringAlgorithmColoring algorithm which always returns the same precomputed vector of colors. Useful when the optimal coloring of a matrix can be determined a priori due to its specific structure (e.g. banded).
It is passed as an argument to the main function coloring, but will only work if the associated problem has :nonsymmetric structure. Indeed, for symmetric coloring problems, we need more than just the vector of colors to allow fast decompression.
Constructors
ConstantColoringAlgorithm{partition}(matrix_template, color)
ConstantColoringAlgorithm(matrix_template, color; partition=:column)partition::Symbol: either:rowor:column.matrix_template::AbstractMatrix: matrix for which the vector of colors was precomputed (the algorithm will only accept matrices of the exact same size).color::Vector{<:Integer}: vector of integer colors, one for each row or column (depending onpartition).
We do not necessarily verify consistency between the matrix template and the vector of colors, this is the responsibility of the user.
Example
julia> using SparseMatrixColorings, LinearAlgebra
julia> matrix_template = Diagonal(ones(Bool, 5))
5×5 Diagonal{Bool, Vector{Bool}}:
1 ⋅ ⋅ ⋅ ⋅
⋅ 1 ⋅ ⋅ ⋅
⋅ ⋅ 1 ⋅ ⋅
⋅ ⋅ ⋅ 1 ⋅
⋅ ⋅ ⋅ ⋅ 1
julia> color = ones(Int, 5) # coloring a Diagonal is trivial
5-element Vector{Int64}:
1
1
1
1
1
julia> problem = ColoringProblem(; structure=:nonsymmetric, partition=:column);
julia> algo = ConstantColoringAlgorithm(matrix_template, color; partition=:column);
julia> result = coloring(similar(matrix_template), problem, algo);
julia> column_colors(result)
5-element Vector{Int64}:
1
1
1
1
1ADTypes coloring interface
ConstantColoringAlgorithm is a subtype of ADTypes.AbstractColoringAlgorithm, which means the following methods are also applicable (although they will error if the kind of coloring demanded not consistent):
Result analysis
SparseMatrixColorings.AbstractColoringResult — TypeAbstractColoringResult{structure,partition,decompression}Abstract type for the result of a coloring algorithm.
It is the supertype of the object returned by the main function coloring.
Type parameters
Combination between the type parameters of ColoringProblem and GreedyColoringAlgorithm:
structure::Symbol: either:nonsymmetricor:symmetricpartition::Symbol: either:column,:rowor:bidirectionaldecompression::Symbol: either:director:substitution
Applicable methods
column_colorsandcolumn_groups(for a:columnor:bidirectionalpartition)row_colorsandrow_groups(for a:rowor:bidirectionalpartition)sparsity_patterncompress,decompress,decompress!,decompress_single_color!
SparseMatrixColorings.column_colors — Functioncolumn_colors(result::AbstractColoringResult)Return a vector color of integer colors, one for each column of the colored matrix.
SparseMatrixColorings.row_colors — Functionrow_colors(result::AbstractColoringResult)Return a vector color of integer colors, one for each row of the colored matrix.
SparseMatrixColorings.ncolors — Functionncolors(result::AbstractColoringResult)Return the number of different non-zero colors used to color the matrix.
For bidirectional partitions, this number is the sum of the number of non-zero row colors and the number of non-zero column colors.
SparseMatrixColorings.column_groups — Functioncolumn_groups(result::AbstractColoringResult)Return a vector group such that for every non-zero color c, group[c] contains the indices of all columns that are colored with c.
SparseMatrixColorings.row_groups — Functionrow_groups(result::AbstractColoringResult)Return a vector group such that for every non-zero color c, group[c] contains the indices of all rows that are colored with c.
SparseMatrixColorings.sparsity_pattern — Functionsparsity_pattern(result::AbstractColoringResult)Return the matrix that was initially passed to coloring, without any modifications.
Decompression
SparseMatrixColorings.compress — Functioncompress(A, result::AbstractColoringResult)Compress A given a coloring result of the sparsity pattern of A.
- If
resultcomes from a:column(resp.:row) partition, the output is a single matrixBcompressed by column (resp. by row). - If
resultcomes from a:bidirectionalpartition, the output is a tuple of matrices(Br, Bc), whereBris compressed by row andBcby column.
Compression means summing either the columns or the rows of A which share the same color. It is undone by calling decompress or decompress!.
Example
julia> using SparseMatrixColorings, SparseArrays
julia> A = sparse([
0 0 4 6 0 9
1 0 0 0 7 0
0 2 0 0 8 0
0 3 5 0 0 0
]);
julia> result = coloring(A, ColoringProblem(), GreedyColoringAlgorithm());
julia> collect.(column_groups(result))
3-element Vector{Vector{Int64}}:
[1, 2, 4]
[3, 5]
[6]
julia> B = compress(A, result)
4×3 Matrix{Int64}:
6 4 9
1 7 0
2 8 0
3 5 0See also
SparseMatrixColorings.decompress — Functiondecompress(B::AbstractMatrix, result::AbstractColoringResult{_,:column/:row})
decompress(Br::AbstractMatrix, Bc::AbstractMatrix, result::AbstractColoringResult{_,:bidirectional})Decompress B (or the tuple (Br,Bc)) into a new matrix A, given a coloring result of the sparsity pattern of A. The in-place alternative is decompress!.
Compression means summing either the columns or the rows of A which share the same color. It is done by calling compress.
Example
julia> using SparseMatrixColorings, SparseArrays
julia> A = sparse([
0 0 4 6 0 9
1 0 0 0 7 0
0 2 0 0 8 0
0 3 5 0 0 0
]);
julia> result = coloring(A, ColoringProblem(), GreedyColoringAlgorithm());
julia> collect.(column_groups(result))
3-element Vector{Vector{Int64}}:
[1, 2, 4]
[3, 5]
[6]
julia> B = compress(A, result)
4×3 Matrix{Int64}:
6 4 9
1 7 0
2 8 0
3 5 0
julia> decompress(B, result)
4×6 SparseMatrixCSC{Int64, Int64} with 9 stored entries:
⋅ ⋅ 4 6 ⋅ 9
1 ⋅ ⋅ ⋅ 7 ⋅
⋅ 2 ⋅ ⋅ 8 ⋅
⋅ 3 5 ⋅ ⋅ ⋅
julia> decompress(B, result) == A
trueSee also
SparseMatrixColorings.decompress! — Functiondecompress!(
A::AbstractMatrix, B::AbstractMatrix,
result::AbstractColoringResult{_,:column/:row}, [uplo=:F]
)
decompress!(
A::AbstractMatrix, Br::AbstractMatrix, Bc::AbstractMatrix
result::AbstractColoringResult{_,:bidirectional}
)Decompress B (or the tuple (Br,Bc)) in-place into A, given a coloring result of the sparsity pattern of A. The out-of-place alternative is decompress.
Compression means summing either the columns or the rows of A which share the same color. It is done by calling compress.
For :symmetric coloring results (and for those only), an optional positional argument uplo in (:U, :L, :F) can be passed to specify which part of the matrix A should be updated: the Upper triangle, the Lower triangle, or the Full matrix. When A isa SparseMatrixCSC, using the uplo argument requires a target matrix which only stores the relevant triangle(s).
Example
julia> using SparseMatrixColorings, SparseArrays
julia> A = sparse([
0 0 4 6 0 9
1 0 0 0 7 0
0 2 0 0 8 0
0 3 5 0 0 0
]);
julia> result = coloring(A, ColoringProblem(), GreedyColoringAlgorithm());
julia> collect.(column_groups(result))
3-element Vector{Vector{Int64}}:
[1, 2, 4]
[3, 5]
[6]
julia> B = compress(A, result)
4×3 Matrix{Int64}:
6 4 9
1 7 0
2 8 0
3 5 0
julia> A2 = similar(A);
julia> decompress!(A2, B, result)
4×6 SparseMatrixCSC{Int64, Int64} with 9 stored entries:
⋅ ⋅ 4 6 ⋅ 9
1 ⋅ ⋅ ⋅ 7 ⋅
⋅ 2 ⋅ ⋅ 8 ⋅
⋅ 3 5 ⋅ ⋅ ⋅
julia> A2 == A
trueSee also
SparseMatrixColorings.decompress_single_color! — Functiondecompress_single_color!(
A::AbstractMatrix, b::AbstractVector, c::Integer,
result::AbstractColoringResult, [uplo=:F]
)Decompress the vector b corresponding to color c in-place into A, given a :direct coloring result of the sparsity pattern of A (it will not work with a :substitution coloring).
- If
resultcomes from a:nonsymmetricstructure with:columnpartition, this will update the columns ofAthat share colorc(whose sum makes upb). - If
resultcomes from a:nonsymmetricstructure with:rowpartition, this will update the rows ofAthat share colorc(whose sum makes upb). - If
resultcomes from a:symmetricstructure with:columnpartition, this will update the coefficients ofAwhose value is deduced from colorc.
For :symmetric coloring results (and for those only), an optional positional argument uplo in (:U, :L, :F) can be passed to specify which part of the matrix A should be updated: the Upper triangle, the Lower triangle, or the Full matrix. When A isa SparseMatrixCSC, using the uplo argument requires a target matrix which only stores the relevant triangle(s).
Example
julia> using SparseMatrixColorings, SparseArrays
julia> A = sparse([
0 0 4 6 0 9
1 0 0 0 7 0
0 2 0 0 8 0
0 3 5 0 0 0
]);
julia> result = coloring(A, ColoringProblem(), GreedyColoringAlgorithm());
julia> collect.(column_groups(result))
3-element Vector{Vector{Int64}}:
[1, 2, 4]
[3, 5]
[6]
julia> B = compress(A, result)
4×3 Matrix{Int64}:
6 4 9
1 7 0
2 8 0
3 5 0
julia> A2 = similar(A); A2 .= 0;
julia> decompress_single_color!(A2, B[:, 2], 2, result)
4×6 SparseMatrixCSC{Int64, Int64} with 9 stored entries:
⋅ ⋅ 4 0 ⋅ 0
0 ⋅ ⋅ ⋅ 7 ⋅
⋅ 0 ⋅ ⋅ 8 ⋅
⋅ 0 5 ⋅ ⋅ ⋅
julia> A2[:, [3, 5]] == A[:, [3, 5]]
trueSee also
Orders
SparseMatrixColorings.AbstractOrder — TypeAbstractOrderAbstract supertype for the vertex order used inside GreedyColoringAlgorithm.
In this algorithm, the rows and columns of a matrix form a graph, and the vertices are colored one after the other in a greedy fashion. Depending on how the vertices are ordered, the number of colors necessary may vary.
Options
NaturalOrderRandomOrderLargestFirstIncidenceDegree(experimental)SmallestLast(experimental)DynamicLargestFirst(experimental)PerfectEliminationOrder
SparseMatrixColorings.NaturalOrder — TypeNaturalOrder()Instance of AbstractOrder which sorts vertices using their index in the provided graph.
SparseMatrixColorings.RandomOrder — TypeRandomOrder(rng=default_rng(), seed=nothing)Instance of AbstractOrder which sorts vertices using a random permutation, generated from rng with a given seed.
- If
seed = nothing, therngwill never be re-seeded. Therefore, two consecutive calls tovertices(g, order)will give different results. - Otherwise, the
rngwill be re-seeded before each sample. Therefore, two consecutive calls tovertices(g, order)will give the same result.
Do not use a seed with the default_rng(), otherwise you will affect the global state of your program. If you need reproducibility, create a new rng specifically for your RandomOrder. The package StableRNGs.jl offers random number generators whose behavior is stable across Julia versions.
SparseMatrixColorings.LargestFirst — TypeLargestFirst()Instance of AbstractOrder which sorts vertices using their degree in the provided graph: the largest degree comes first.
SparseMatrixColorings.SmallestLast — FunctionSmallestLast(; reproduce_colpack=false)Instance of AbstractOrder which sorts vertices from highest to lowest using the dynamic back degree.
See also
SparseMatrixColorings.IncidenceDegree — FunctionIncidenceDegree(; reproduce_colpack=false)Instance of AbstractOrder which sorts vertices from lowest to highest using the dynamic back degree.
See also
SparseMatrixColorings.DynamicLargestFirst — FunctionDynamicLargestFirst(; reproduce_colpack=false)Instance of AbstractOrder which sorts vertices from lowest to highest using the dynamic forward degree.
See also
SparseMatrixColorings.DynamicDegreeBasedOrder — TypeDynamicDegreeBasedOrder{degtype,direction}(; reproduce_colpack=false)Instance of AbstractOrder which sorts vertices using a dynamically computed degree.
This order works by assigning vertices to buckets based on their dynamic degree, and then updating buckets iteratively by transfering vertices between them.
Type parameters
degtype::Symbol: can be:forward(for the forward degree) or:back(for the back degree)direction::Symbol: can be:low2high(if the order is defined from lowest to highest, i.e.1ton) or:high2low(if the order is defined from highest to lowest, i.e.nto1)
Concrete variants
Settings
reproduce_colpack::Bool: whether to manage the buckets in the exact same way as the original ColPack implementation.- When
reproduce_colpack=true, we always append and remove vertices at the end of a bucket (unilateral). - When
reproduce_colpack=false(the default), we can append and remove vertices either at the start or at the end of a bucket (bilateral).
- When
Allowing modifications on both sides of a bucket enables storage optimization, with a single fixed-size vector for all buckets instead of one dynamically-sized vector per bucket. As a result, the default setting reproduce_colpack=false is slightly more memory-efficient.
References
- ColPack: Software for graph coloring and related problems in scientific computing, Gebremedhin et al. (2013), Section 5
SparseMatrixColorings.PerfectEliminationOrder — TypePerfectEliminationOrder(elimination_algorithm=CliqueTrees.MCS())Instance of AbstractOrder which computes a perfect elimination ordering when the underlying graph is chordal. For non-chordal graphs, it computes a suboptimal ordering.
The elimination_algorithm must be an instance of CliqueTrees.EliminationAlgorithm.
This order can only be applied for symmetric or bidirectional coloring problems. Furthermore, its theoretical guarantees only hold for decompression by substitution.
This order is implemented as a package extension and requires loading CliqueTrees.jl.
References