Skip to content

Commit 8567b7f

Browse files
authored
Implement clever symmetric decompression with star set (#25)
* Implement clever symmetric decompression with star set * Field typo * Typo * Spoke handling * Fix
1 parent 12f3d04 commit 8567b7f

File tree

7 files changed

+175
-22
lines changed

7 files changed

+175
-22
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "SparseMatrixColorings"
22
uuid = "0a514795-09f3-496d-8182-132a7b665d35"
33
authors = ["Guillaume Dalle <22795598+gdalle@users.noreply.github.com>"]
4-
version = "0.3.3"
4+
version = "0.3.4"
55

66
[deps]
77
ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b"

docs/src/api.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ GreedyColoringAlgorithm
1313
column_coloring
1414
row_coloring
1515
symmetric_coloring
16+
symmetric_coloring_detailed
1617
```
1718

1819
## Public, not exported
@@ -36,6 +37,7 @@ decompress_rows
3637
decompress_rows!
3738
decompress_symmetric
3839
decompress_symmetric!
40+
StarSet
3941
```
4042

4143
## Private
@@ -64,6 +66,7 @@ vertices
6466
```@docs
6567
partial_distance2_coloring
6668
star_coloring
69+
star_coloring_detailed
6770
```
6871

6972
### Testing

src/SparseMatrixColorings.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ include("check.jl")
4646
@compat public decompress_columns, decompress_columns!
4747
@compat public decompress_rows, decompress_rows!
4848
@compat public decompress_symmetric, decompress_symmetric!
49+
@compat public StarSet
4950

5051
export GreedyColoringAlgorithm
51-
export column_coloring, row_coloring, symmetric_coloring
52+
export column_coloring, row_coloring, symmetric_coloring, symmetric_coloring_detailed
5253

5354
end

src/adtypes.jl

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ end
2626
"""
2727
column_coloring(A::AbstractMatrix, algo::GreedyColoringAlgorithm)
2828
29-
Compute a partial distance-2 coloring of the columns in the bipartite graph of the matrix `A`.
29+
Compute a partial distance-2 coloring of the columns in the bipartite graph of the matrix `A`, return a vector of integer colors.
3030
3131
Function defined by ADTypes, re-exported by SparseMatrixColorings.
3232
@@ -64,7 +64,7 @@ end
6464
"""
6565
row_coloring(A::AbstractMatrix, algo::GreedyColoringAlgorithm)
6666
67-
Compute a partial distance-2 coloring of the rows in the bipartite graph of the matrix `A`.
67+
Compute a partial distance-2 coloring of the rows in the bipartite graph of the matrix `A`, return a vector of integer colors.
6868
6969
Function defined by ADTypes, re-exported by SparseMatrixColorings.
7070
@@ -101,16 +101,52 @@ end
101101
"""
102102
symmetric_coloring(A::AbstractMatrix, algo::GreedyColoringAlgorithm)
103103
104-
Compute a star coloring of the columns in the adjacency graph of the symmetric matrix `A`.
104+
Compute a star coloring of the columns in the adjacency graph of the symmetric matrix `A`, return a vector of integer colors.
105105
106106
Function defined by ADTypes, re-exported by SparseMatrixColorings.
107107
108108
# Example
109109
110-
!!! warning
111-
Work in progress.
110+
```jldoctest
111+
using SparseMatrixColorings, SparseArrays
112+
113+
algo = GreedyColoringAlgorithm(SparseMatrixColorings.LargestFirst())
114+
115+
A = sparse([
116+
1 1 1 1
117+
1 1 0 0
118+
1 0 1 0
119+
1 0 0 1
120+
])
121+
122+
symmetric_coloring(A, algo)
123+
124+
# output
125+
126+
4-element Vector{Int64}:
127+
1
128+
2
129+
2
130+
2
131+
```
112132
"""
113133
function ADTypes.symmetric_coloring(A::AbstractMatrix, algo::GreedyColoringAlgorithm)
114134
ag = adjacency_graph(A)
115135
return star_coloring(ag, algo.order)
116136
end
137+
138+
"""
139+
symmetric_coloring_detailed(A::AbstractMatrix, algo::GreedyColoringAlgorithm)
140+
141+
Do the same as [`symmetric_coloring`](@ref) but return a tuple `(color, star_set)`, where
142+
143+
- `color` is the vector of integer colors
144+
- `star_set` is a [`StarSet`](@ref) encoding the set of 2-colored stars, which can be used to speed up decompression
145+
146+
!!! warning
147+
This function is not defined by ADTypes.
148+
"""
149+
function symmetric_coloring_detailed(A::AbstractMatrix, algo::GreedyColoringAlgorithm)
150+
ag = adjacency_graph(A)
151+
return star_coloring_detailed(ag, algo.order)
152+
end

src/coloring.jl

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,51 @@ The vertices are colored in a greedy fashion, following the `order` supplied.
7272
> [_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
7373
"""
7474
function star_coloring(g::Graph, order::AbstractOrder)
75+
color, _ = star_coloring_detailed(g, order)
76+
return color
77+
end
78+
79+
"""
80+
StarSet
81+
82+
Encode a set of 2-colored stars resulting from the star coloring algorithm.
83+
84+
# Fields
85+
86+
The fields are not part of the public API, even though the type is.
87+
88+
- `star::Dict{Tuple{Int,Int},Int}`: a mapping from edges (pair of vertices) their to star index
89+
- `hub::Vector{Int}`: a mapping from star indices to their hub (the hub is `0` if the star only contains one edge)
90+
91+
# References
92+
93+
> [_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
94+
"""
95+
struct StarSet
96+
star::Dict{Tuple{Int,Int},Int}
97+
hub::Vector{Int}
98+
end
99+
100+
"""
101+
star_coloring_detailed(g::Graph, order::AbstractOrder)
102+
103+
Do the same as [`star_coloring`](@ref) but return a tuple `(color, star_set)`, where
104+
105+
- `color` is the vector of integer colors
106+
- `star_set` is a [`StarSet`](@ref) encoding the set of 2-colored stars, which can be used to speed up decompression
107+
108+
# See also
109+
110+
- [`star_coloring`](@ref)
111+
- [`StarSet`](@ref)
112+
113+
# References
114+
115+
> [_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
116+
117+
> [_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), Figure 2
118+
"""
119+
function star_coloring_detailed(g::Graph, order::AbstractOrder)
75120
# Initialize data structures
76121
color = zeros(Int, length(g))
77122
forbidden_colors = zeros(Int, length(g))
@@ -112,7 +157,7 @@ function star_coloring(g::Graph, order::AbstractOrder)
112157
end
113158
_update_stars!(star, hub, g, v, color, first_neighbor)
114159
end
115-
return color
160+
return color, StarSet(star, hub)
116161
end
117162

118163
_sort(u, v) = (min(u, v), max(u, v))

src/decompression.jl

Lines changed: 77 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
color::AbstractVector{<:Integer}
99
) where {R<:Real}
1010
11-
Decompress the thin matrix `B` into the fat matrix `A` which must have the same sparsity pattern as `S`.
11+
Decompress the narrow matrix `B` into the wide matrix `A` which must have the same sparsity pattern as `S`.
1212
1313
Here, `color` is a column coloring of `S`, while `B` is a compressed representation of matrix `A` obtained by summing the columns that share the same color.
1414
"""
@@ -63,7 +63,7 @@ end
6363
color::AbstractVector{<:Integer}
6464
) where {R<:Real}
6565
66-
Decompress the thin matrix `B` into a new fat matrix `A` with the same sparsity pattern as `S`.
66+
Decompress the narrow matrix `B` into a new wide matrix `A` with the same sparsity pattern as `S`.
6767
6868
Here, `color` is a column coloring of `S`, while `B` is a compressed representation of matrix `A` obtained by summing the columns that share the same color.
6969
"""
@@ -158,16 +158,19 @@ end
158158
A::AbstractMatrix{R},
159159
S::AbstractMatrix{Bool},
160160
B::AbstractMatrix{R},
161-
color::AbstractVector{<:Integer}
161+
color::AbstractVector{<:Integer},
162+
[star_set::StarSet],
162163
) where {R<:Real}
163164
164-
Decompress the thin matrix `B` into the symmetric matrix `A` which must have the same sparsity pattern as `S`.
165+
Decompress the narrow matrix `B` into the symmetric matrix `A` which must have the same sparsity pattern as `S`.
165166
166167
Here, `color` is a symmetric coloring of `S`, while `B` is a compressed representation of matrix `A` obtained by summing the columns that share the same color.
167168
169+
Decompression is faster when a [`StarSet`](@ref) is also provided.
170+
168171
# References
169172
170-
> [_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), Figure 2
173+
> [_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), Figures 2 and 3
171174
"""
172175
function decompress_symmetric! end
173176

@@ -201,35 +204,96 @@ function decompress_symmetric!(
201204
return A
202205
end
203206

207+
function decompress_symmetric!(
208+
A::AbstractMatrix{R},
209+
S::AbstractMatrix{Bool},
210+
B::AbstractMatrix{R},
211+
color::AbstractVector{<:Integer},
212+
star_set::StarSet,
213+
) where {R<:Real}
214+
@compat (; star, hub) = star_set
215+
checksquare(A)
216+
if !same_sparsity_pattern(A, S)
217+
throw(DimensionMismatch("`A` and `S` must have the same sparsity pattern."))
218+
end
219+
A .= zero(R)
220+
for i in axes(A, 1)
221+
if !iszero(S[i, i])
222+
A[i, i] = B[i, color[i]]
223+
end
224+
end
225+
for ((i, j), star_id) in pairs(star)
226+
i == j && continue
227+
h = hub[star_id]
228+
if h == 0
229+
# pick arbitrary hub
230+
h = i
231+
end
232+
if h == j
233+
# i is the spoke
234+
A[i, j] = A[j, i] = B[i, color[h]]
235+
elseif h == i
236+
# j is the spoke
237+
A[i, j] = A[j, i] = B[j, color[h]]
238+
end
239+
end
240+
return A
241+
end
242+
204243
function decompress_symmetric!(
205244
A::Symmetric{R},
206245
S::AbstractMatrix{Bool},
207246
B::AbstractMatrix{R},
208-
colors::AbstractVector{<:Integer},
247+
color::AbstractVector{<:Integer},
248+
) where {R<:Real}
249+
# requires parent decompression to handle both upper and lower triangles
250+
decompress_symmetric!(parent(A), S, B, color)
251+
return A
252+
end
253+
254+
function decompress_symmetric!(
255+
A::Symmetric{R},
256+
S::AbstractMatrix{Bool},
257+
B::AbstractMatrix{R},
258+
color::AbstractVector{<:Integer},
259+
star_set::StarSet,
209260
) where {R<:Real}
210261
# requires parent decompression to handle both upper and lower triangles
211-
decompress_symmetric!(parent(A), S, B, colors)
262+
decompress_symmetric!(parent(A), S, B, color, star_set)
212263
return A
213264
end
214265

215266
"""
216267
decompress_symmetric(
217268
S::AbstractMatrix{Bool},
218269
B::AbstractMatrix{R},
219-
colors::AbstractVector{<:Integer}
270+
color::AbstractVector{<:Integer},
271+
[star_set::StarSet],
220272
) where {R<:Real}
221273
222-
Decompress the thin matrix `B` into a new symmetric matrix `A` with the same sparsity pattern as `S`.
274+
Decompress the narrow matrix `B` into a new symmetric matrix `A` with the same sparsity pattern as `S`.
223275
224-
Here, `colors` is a symmetric coloring of `S`, while `B` is a compressed representation of matrix `A` obtained by summing the columns that share the same color.
276+
Here, `color` is a symmetric coloring of `S`, while `B` is a compressed representation of matrix `A` obtained by summing the columns that share the same color.
277+
278+
Decompression is faster when a [`StarSet`](@ref) is also provided.
225279
226280
# References
227281
228-
> [_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)
282+
> [_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), Figures 2 and 3
229283
"""
230284
function decompress_symmetric(
231-
S::AbstractMatrix{Bool}, B::AbstractMatrix{R}, colors::AbstractVector{<:Integer}
285+
S::AbstractMatrix{Bool}, B::AbstractMatrix{R}, color::AbstractVector{<:Integer}
286+
) where {R<:Real}
287+
A = respectful_similar(S, R)
288+
return decompress_symmetric!(A, S, B, color)
289+
end
290+
291+
function decompress_symmetric(
292+
S::AbstractMatrix{Bool},
293+
B::AbstractMatrix{R},
294+
color::AbstractVector{<:Integer},
295+
star_set::StarSet,
232296
) where {R<:Real}
233297
A = respectful_similar(S, R)
234-
return decompress_symmetric!(A, S, B, colors)
298+
return decompress_symmetric!(A, S, B, color, star_set)
235299
end

test/random.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ using SparseMatrixColorings:
77
GreedyColoringAlgorithm,
88
structurally_orthogonal_columns,
99
symmetrically_orthogonal_columns,
10+
directly_recoverable_columns,
1011
matrix_versions
1112
using StableRNGs
1213
using Test
@@ -72,7 +73,9 @@ end;
7273
A0 = Symmetric(sprand(rng, Bool, n, n, p))
7374
S0 = map(!iszero, A0)
7475
@testset "A::$(typeof(A))" for A in matrix_versions(A0)
75-
color = symmetric_coloring(A, algo)
76+
# Naive decompression
77+
color, star_set = symmetric_coloring_detailed(A, algo)
78+
@test color == symmetric_coloring(A, algo)
7679
@test symmetrically_orthogonal_columns(A, color)
7780
@test directly_recoverable_columns(A, color)
7881
group = color_groups(color)
@@ -81,6 +84,7 @@ end;
8184
end
8285
@testset "S::$(typeof(S))" for S in matrix_versions(S0)
8386
@test decompress_symmetric(S, B, color) == A
87+
@test decompress_symmetric(S, B, color, star_set) == A
8488
end
8589
end
8690
end

0 commit comments

Comments
 (0)