Skip to content

Commit 759ed65

Browse files
authored
Working star coloring (#11)
* Working star coloring * Fix verbosity * More efficient recovery
1 parent c1b2cf4 commit 759ed65

16 files changed

+480
-312
lines changed

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ pkg> add https://github.com/gdalle/SparseMatrixColorings.jl
1818

1919
## Background
2020

21-
The algorithms implemented in this package are mainly taken from the following articles:
21+
The algorithms implemented in this package are taken from the following articles:
2222

23-
> [_What Color Is Your Jacobian? Graph Coloring for Computing Derivatives_](https://epubs.siam.org/doi/10.1137/S0036144504444711), Gebremedhin et al. (2005)
24-
25-
> [_ColPack: Software for graph coloring and related problems in scientific computing_](https://dl.acm.org/doi/10.1145/2513109.2513110), Gebremedhin et al. (2013)
23+
- [_What Color Is Your Jacobian? Graph Coloring for Computing Derivatives_](https://epubs.siam.org/doi/10.1137/S0036144504444711), Gebremedhin et al. (2005)
24+
- [_New Acyclic and Star Coloring Algorithms with Application to Computing Hessians_](https://epubs.siam.org/doi/abs/10.1137/050639879), Gebremedhin et al. (2007)
25+
- [_Efficient Computation of Sparse Hessians Using Coloring and Automatic Differentiation_](https://pubsonline.informs.org/doi/abs/10.1287/ijoc.1080.0286), Gebremedhin et al. (2009)
26+
- [_ColPack: Software for graph coloring and related problems in scientific computing_](https://dl.acm.org/doi/10.1145/2513109.2513110), Gebremedhin et al. (2013)
2627

2728
Some parts of the articles (like definitions) are thus copied verbatim in the documentation.
2829

docs/src/api.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ CurrentModule = SparseMatrixColorings
1010
```@docs
1111
SparseMatrixColorings
1212
GreedyColoringAlgorithm
13+
column_coloring
14+
row_coloring
15+
symmetric_coloring
1316
```
1417

1518
## Public, not exported
@@ -60,13 +63,12 @@ vertices
6063

6164
```@docs
6265
partial_distance2_coloring
63-
star_coloring1
66+
star_coloring
6467
```
6568

6669
### Testing
6770

6871
```@docs
6972
check_structurally_orthogonal_columns
70-
check_structurally_orthogonal_rows
71-
check_symmetrically_orthogonal
73+
check_symmetrically_orthogonal_columns
7274
```

src/SparseMatrixColorings.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ $README
55
"""
66
module SparseMatrixColorings
77

8-
using ADTypes: ADTypes, AbstractColoringAlgorithm
8+
using ADTypes:
9+
ADTypes, AbstractColoringAlgorithm, column_coloring, row_coloring, symmetric_coloring
910
using Compat: @compat
1011
using DocStringExtensions: README
1112
using LinearAlgebra:
@@ -46,7 +47,9 @@ include("check.jl")
4647
@compat public decompress_columns, decompress_columns!
4748
@compat public decompress_rows, decompress_rows!
4849
@compat public decompress_symmetric, decompress_symmetric!
50+
@compat public column_coloring, row_coloring, symmetric_coloring
4951

5052
export GreedyColoringAlgorithm
53+
export column_coloring, row_coloring, symmetric_coloring
5154

5255
end

src/adtypes.jl

Lines changed: 69 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,42 @@ Compatible with the [ADTypes.jl coloring framework](https://sciml.github.io/ADTy
99
1010
GreedyColoringAlgorithm(order::AbstractOrder=NaturalOrder())
1111
12-
# Implements
12+
# See also
13+
14+
- [`AbstractOrder`](@ref)
15+
"""
16+
struct GreedyColoringAlgorithm{O<:AbstractOrder} <: ADTypes.AbstractColoringAlgorithm
17+
order::O
18+
end
19+
20+
GreedyColoringAlgorithm() = GreedyColoringAlgorithm(NaturalOrder())
21+
22+
function Base.show(io::IO, algo::GreedyColoringAlgorithm)
23+
return print(io, "GreedyColoringAlgorithm($(algo.order))")
24+
end
25+
26+
"""
27+
column_coloring(A::AbstractMatrix, algo::GreedyColoringAlgorithm)
28+
29+
Compute a partial distance-2 coloring of the columns in the bipartite graph of the matrix `A`.
1330
14-
- [`ADTypes.column_coloring`](@extref ADTypes) and [`ADTypes.row_coloring`](@extref ADTypes) with a partial distance-2 coloring of the bipartite graph
15-
- [`ADTypes.symmetric_coloring`](@extref ADTypes) with a star coloring of the adjacency graph
31+
Function defined by ADTypes, re-exported by SparseMatrixColorings.
1632
17-
# Example use
33+
# Example
1834
1935
```jldoctest
20-
using ADTypes, SparseMatrixColorings, SparseArrays
36+
using SparseMatrixColorings, SparseArrays
2137
2238
algo = GreedyColoringAlgorithm(SparseMatrixColorings.LargestFirst())
39+
2340
A = sparse([
2441
0 0 1 1 0
2542
1 0 0 0 1
2643
0 1 1 0 0
2744
0 1 1 0 1
2845
])
29-
ADTypes.column_coloring(A, algo)
46+
47+
column_coloring(A, algo)
3048
3149
# output
3250
@@ -37,32 +55,62 @@ ADTypes.column_coloring(A, algo)
3755
2
3856
3
3957
```
40-
41-
# See also
42-
43-
- [`AbstractOrder`](@ref)
4458
"""
45-
struct GreedyColoringAlgorithm{O<:AbstractOrder} <: ADTypes.AbstractColoringAlgorithm
46-
order::O
47-
end
48-
49-
GreedyColoringAlgorithm() = GreedyColoringAlgorithm(NaturalOrder())
50-
51-
function Base.show(io::IO, algo::GreedyColoringAlgorithm)
52-
return print(io, "GreedyColoringAlgorithm($(algo.order))")
53-
end
54-
5559
function ADTypes.column_coloring(A::AbstractMatrix, algo::GreedyColoringAlgorithm)
5660
bg = bipartite_graph(A)
5761
return partial_distance2_coloring(bg, Val(2), algo.order)
5862
end
5963

64+
"""
65+
row_coloring(A::AbstractMatrix, algo::GreedyColoringAlgorithm)
66+
67+
Compute a partial distance-2 coloring of the rows in the bipartite graph of the matrix `A`.
68+
69+
Function defined by ADTypes, re-exported by SparseMatrixColorings.
70+
71+
# Example
72+
73+
```jldoctest
74+
using SparseMatrixColorings, SparseArrays
75+
76+
algo = GreedyColoringAlgorithm(SparseMatrixColorings.LargestFirst())
77+
78+
A = sparse([
79+
0 0 1 1 0
80+
1 0 0 0 1
81+
0 1 1 0 0
82+
0 1 1 0 1
83+
])
84+
85+
row_coloring(A, algo)
86+
87+
# output
88+
89+
4-element Vector{Int64}:
90+
2
91+
2
92+
3
93+
1
94+
```
95+
"""
6096
function ADTypes.row_coloring(A::AbstractMatrix, algo::GreedyColoringAlgorithm)
6197
bg = bipartite_graph(A)
6298
return partial_distance2_coloring(bg, Val(1), algo.order)
6399
end
64100

101+
"""
102+
symmetric_coloring(A::AbstractMatrix, algo::GreedyColoringAlgorithm)
103+
104+
Compute a star coloring of the columns in the adjacency graph of the symmetric matrix `A`.
105+
106+
Function defined by ADTypes, re-exported by SparseMatrixColorings.
107+
108+
# Example
109+
110+
!!! warning
111+
Work in progress.
112+
"""
65113
function ADTypes.symmetric_coloring(A::AbstractMatrix, algo::GreedyColoringAlgorithm)
66114
ag = adjacency_graph(A)
67-
return star_coloring1(ag, algo.order)
115+
return star_coloring(ag, algo.order)
68116
end

src/check.jl

Lines changed: 48 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,95 @@
11
"""
22
check_structurally_orthogonal_columns(
3-
A::AbstractMatrix, colors::AbstractVector{<:Integer}
3+
A::AbstractMatrix, color::AbstractVector{<:Integer}
44
verbose=false
55
)
66
7-
Return `true` if coloring the columns of the matrix `A` with the vector `colors` results in a partition that is structurally orthogonal, and `false` otherwise.
7+
Return `true` if coloring the columns of the matrix `A` with the vector `color` results in a partition that is structurally orthogonal, and `false` otherwise.
88
99
A partition of the columns of a matrix `A` is _structurally orthogonal_ if, for every nonzero element `A[i, j]`, the group containing column `A[:, j]` has no other column with a nonzero in row `i`.
1010
1111
!!! warning
1212
This function is not coded with efficiency in mind, it is designed for small-scale tests.
13+
14+
# References
15+
16+
> [_What Color Is Your Jacobian? Graph Coloring for Computing Derivatives_](https://epubs.siam.org/doi/10.1137/S0036144504444711), Gebremedhin et al. (2005)
1317
"""
1418
function check_structurally_orthogonal_columns(
15-
A::AbstractMatrix, colors::AbstractVector{<:Integer}; verbose::Bool=true
19+
A::AbstractMatrix, color::AbstractVector{<:Integer}; verbose::Bool=false
1620
)
17-
groups = color_groups(colors)
18-
for (c, g) in enumerate(groups)
19-
Ag = @view A[:, g]
21+
if length(color) != size(A, 2)
22+
if verbose
23+
@warn "$(length(color)) colors provided for $(size(A, 2)) columns"
24+
end
25+
return false
26+
end
27+
group = color_groups(color)
28+
for (c, g) in enumerate(group)
29+
Ag = view(A, :, g)
2030
nonzeros_per_row = dropdims(count(!iszero, Ag; dims=2); dims=2)
2131
max_nonzeros_per_row, i = findmax(nonzeros_per_row)
2232
if max_nonzeros_per_row > 1
23-
verbose && @warn "Columns $g (with color $c) share nonzeros in row $i"
24-
return false
25-
end
26-
end
27-
return true
28-
end
29-
30-
"""
31-
check_structurally_orthogonal_rows(
32-
A::AbstractMatrix, colors::AbstractVector{<:Integer};
33-
verbose=false
34-
)
35-
36-
Return `true` if coloring the rows of the matrix `A` with the vector `colors` results in a partition that is structurally orthogonal, and `false` otherwise.
37-
38-
A partition of the rows of a matrix `A` is _structurally orthogonal_ if, for every nonzero element `A[i, j]`, the group containing row `A[i, :]` has no other row with a nonzero in column `j`.
39-
40-
!!! warning
41-
This function is not coded with efficiency in mind, it is designed for small-scale tests.
42-
"""
43-
function check_structurally_orthogonal_rows(
44-
A::AbstractMatrix, colors::AbstractVector{<:Integer}; verbose::Bool=true
45-
)
46-
groups = color_groups(colors)
47-
for (c, g) in enumerate(groups)
48-
Ag = @view A[g, :]
49-
nonzeros_per_col = dropdims(count(!iszero, Ag; dims=1); dims=1)
50-
max_nonzeros_per_col, j = findmax(nonzeros_per_col)
51-
if max_nonzeros_per_col > 1
52-
verbose && @warn "Rows $g (with color $c) share nonzeros in column $j"
33+
if verbose
34+
incompatible_columns = g[findall(!iszero, view(Ag, i, :))]
35+
@warn "In color $c, columns $incompatible_columns all have nonzeros in row $i"
36+
end
5337
return false
5438
end
5539
end
5640
return true
5741
end
5842

5943
"""
60-
check_symmetrically_orthogonal(
61-
A::AbstractMatrix, colors::AbstractVector{<:Integer};
44+
check_symmetrically_orthogonal_columns(
45+
A::AbstractMatrix, color::AbstractVector{<:Integer};
6246
verbose=false
6347
)
6448
65-
Return `true` if coloring the columns of the symmetric matrix `A` with the vector `colors` results in a partition that is symmetrically orthogonal, and `false` otherwise.
49+
Return `true` if coloring the columns of the symmetric matrix `A` with the vector `color` results in a partition that is symmetrically orthogonal, and `false` otherwise.
6650
67-
A partition of the columns of a symmetrix matrix `A` is _symmetrically orthogonal_ if, for every nonzero element `A[i, j]`, either
51+
A partition of the columns of a symmetrix matrix `A` is _symmetrically orthogonal_ if, for every nonzero element `A[i, j]`, either of the following statements holds:
6852
6953
1. the group containing the column `A[:, j]` has no other column with a nonzero in row `i`
7054
2. the group containing the column `A[:, i]` has no other column with a nonzero in row `j`
7155
7256
!!! warning
7357
This function is not coded with efficiency in mind, it is designed for small-scale tests.
58+
59+
# References
60+
61+
> [_What Color Is Your Jacobian? Graph Coloring for Computing Derivatives_](https://epubs.siam.org/doi/10.1137/S0036144504444711), Gebremedhin et al. (2005)
7462
"""
75-
function check_symmetrically_orthogonal(
76-
A::AbstractMatrix, colors::AbstractVector{<:Integer}; verbose::Bool=true
63+
function check_symmetrically_orthogonal_columns(
64+
A::AbstractMatrix, color::AbstractVector{<:Integer}; verbose::Bool=false
7765
)
7866
checksquare(A)
67+
if length(color) != size(A, 2)
68+
if verbose
69+
@warn "$(length(color)) colors provided for $(size(A, 2)) columns"
70+
end
71+
return false
72+
end
7973
issymmetric(A) || return false
80-
groups = color_groups(colors)
74+
group = color_groups(color)
8175
for i in axes(A, 2), j in axes(A, 2)
8276
iszero(A[i, j]) && continue
83-
ki, kj = colors[i], colors[j]
84-
gi, gj = groups[ki], groups[kj]
77+
ci, cj = color[i], color[j]
78+
gi, gj = group[ci], group[cj]
8579
A_gj_rowi = view(A, i, gj)
8680
A_gi_rowj = view(A, j, gi)
8781
nonzeros_gj_rowi = count(!iszero, A_gj_rowi)
8882
nonzeros_gi_rowj = count(!iszero, A_gi_rowj)
8983
if nonzeros_gj_rowi > 1 && nonzeros_gi_rowj > 1
90-
verbose && @warn """
91-
For coefficient $((i, j)):
92-
- columns $gj (with color $kj) share nonzeros in row $i
93-
- columns $gi (with color $ki) share nonzeros in row $j
94-
"""
84+
if verbose
85+
gj_incompatible_columns = gj[findall(!iszero, A_gj_rowi)]
86+
gi_incompatible_columns = gi[findall(!iszero, A_gi_rowj)]
87+
@warn """
88+
For coefficient (i=$i, j=$j) with column colors (ci=$ci, cj=$cj):
89+
- in color ci=$ci, columns $gi_incompatible_columns all have nonzeros in row j=$j
90+
- in color cj=$cj, columns $gj_incompatible_columns all have nonzeros in row i=$i
91+
"""
92+
end
9593
return false
9694
end
9795
end

0 commit comments

Comments
 (0)