Skip to content

Commit 0f00909

Browse files
authored
Add option to decompress into a triangle only (#83)
* Add option to decompress into a triangle only * Add tests
1 parent 87508b9 commit 0f00909

4 files changed

Lines changed: 174 additions & 45 deletions

File tree

src/decompression.jl

Lines changed: 89 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ end
124124
"""
125125
decompress!(
126126
A::AbstractMatrix, B::AbstractMatrix,
127-
result::AbstractColoringResult,
127+
result::AbstractColoringResult, [uplo=:UL]
128128
)
129129
130130
Decompress `B` in-place into `A`, given a coloring `result` of the sparsity pattern of `A`.
@@ -133,6 +133,8 @@ The out-of-place alternative is [`decompress`](@ref).
133133
Compression means summing either the columns or the rows of `A` which share the same color.
134134
It is done by calling [`compress`](@ref).
135135
136+
For `:symmetric` coloring results (and for those only), an optional positional argument `uplo in (:U, :L, :UL)` can be passed to specify which triangle of the matrix `A` should be updated: the upper one, the lower one, or both.
137+
136138
!!! note
137139
In-place decompression is faster when `A isa SparseMatrixCSC`.
138140
@@ -186,7 +188,7 @@ function decompress! end
186188
"""
187189
decompress_single_color!(
188190
A::AbstractMatrix, b::AbstractVector, c::Integer,
189-
result::AbstractColoringResult,
191+
result::AbstractColoringResult, [uplo=:UL]
190192
)
191193
192194
Decompress the vector `b` corresponding to color `c` in-place into `A`, given a coloring `result` of the sparsity pattern of `A`.
@@ -195,6 +197,8 @@ Decompress the vector `b` corresponding to color `c` in-place into `A`, given a
195197
- If `result` comes from a `:nonsymmetric` structure with `:row` partition, this will update the rows of `A` that share color `c` (whose sum makes up `b`).
196198
- If `result` comes from a `:symmetric` structure with `:column` partition, this will update the coefficients of `A` whose value is deduced from color `c`.
197199
200+
For `:symmetric` coloring results (and for those only), an optional positional argument `uplo in (:U, :L, :UL)` can be passed to specify which triangle of the matrix `A` should be updated: the upper one, the lower one, or both.
201+
198202
!!! warning
199203
This function will only update some coefficients of `A`, without resetting the rest to zero.
200204
@@ -246,6 +250,16 @@ true
246250
"""
247251
function decompress_single_color! end
248252

253+
function in_triangle(i::Integer, j::Integer, uplo::Symbol)
254+
if uplo == :UL
255+
return true
256+
elseif uplo == :U
257+
return i <= j
258+
else
259+
return i >= j
260+
end
261+
end
262+
249263
## ColumnColoringResult
250264

251265
function decompress!(
@@ -293,6 +307,22 @@ function decompress!(
293307
return A
294308
end
295309

310+
function decompress_single_color!(
311+
A::SparseMatrixCSC{R}, b::AbstractVector{R}, c::Integer, result::ColumnColoringResult
312+
) where {R<:Real}
313+
@compat (; S, group) = result
314+
check_same_pattern(A, S)
315+
rvS = rowvals(S)
316+
nzA = nonzeros(A)
317+
for j in group[c]
318+
for k in nzrange(S, j)
319+
i = rvS[k]
320+
nzA[k] = b[i]
321+
end
322+
end
323+
return A
324+
end
325+
296326
## RowColoringResult
297327

298328
function decompress!(
@@ -343,7 +373,10 @@ end
343373
## StarSetColoringResult
344374

345375
function decompress!(
346-
A::AbstractMatrix{R}, B::AbstractMatrix{R}, result::StarSetColoringResult
376+
A::AbstractMatrix{R},
377+
B::AbstractMatrix{R},
378+
result::StarSetColoringResult,
379+
uplo::Symbol=:UL,
347380
) where {R<:Real}
348381
@compat (; S, color, star_set) = result
349382
@compat (; star, hub, spokes) = star_set
@@ -356,16 +389,25 @@ function decompress!(
356389
end
357390
for s in eachindex(hub, spokes)
358391
j = abs(hub[s])
392+
cj = color[j]
359393
for i in spokes[s]
360-
A[i, j] = B[i, color[j]]
361-
A[j, i] = B[i, color[j]]
394+
if in_triangle(i, j, uplo)
395+
A[i, j] = B[i, cj]
396+
end
397+
if in_triangle(j, i, uplo)
398+
A[j, i] = B[i, cj]
399+
end
362400
end
363401
end
364402
return A
365403
end
366404

367405
function decompress_single_color!(
368-
A::AbstractMatrix{R}, b::AbstractVector{R}, c::Integer, result::StarSetColoringResult
406+
A::AbstractMatrix{R},
407+
b::AbstractVector{R},
408+
c::Integer,
409+
result::StarSetColoringResult,
410+
uplo::Symbol=:UL,
369411
) where {R<:Real}
370412
@compat (; S, color, group, star_set) = result
371413
@compat (; hub, spokes) = star_set
@@ -379,8 +421,12 @@ function decompress_single_color!(
379421
j = abs(hub[s])
380422
if color[j] == c
381423
for i in spokes[s]
382-
A[i, j] = b[i]
383-
A[j, i] = b[i]
424+
if in_triangle(i, j, uplo)
425+
A[i, j] = b[i]
426+
end
427+
if in_triangle(j, i, uplo)
428+
A[j, i] = b[i]
429+
end
384430
end
385431
end
386432
end
@@ -399,12 +445,33 @@ function decompress!(
399445
return A
400446
end
401447

448+
function decompress!(
449+
A::SparseMatrixCSC{R}, B::AbstractMatrix{R}, result::StarSetColoringResult, uplo::Symbol
450+
) where {R<:Real}
451+
@compat (; S, compressed_indices) = result
452+
check_same_pattern(A, S)
453+
rvA = rowvals(A)
454+
nzA = nonzeros(A)
455+
for j in axes(S, 2)
456+
for k in nzrange(S, j)
457+
i = rvA[k]
458+
if in_triangle(i, j, uplo)
459+
nzA[k] = B[compressed_indices[k]]
460+
end
461+
end
462+
end
463+
return A
464+
end
465+
402466
## TreeSetColoringResult
403467

404468
# TODO: add method for A::SparseMatrixCSC
405469

406470
function decompress!(
407-
A::AbstractMatrix{R}, B::AbstractMatrix{R}, result::TreeSetColoringResult
471+
A::AbstractMatrix{R},
472+
B::AbstractMatrix{R},
473+
result::TreeSetColoringResult,
474+
uplo::Symbol=:UL,
408475
) where {R<:Real}
409476
@compat (; S, color, vertices_by_tree, reverse_bfs_orders, buffer) = result
410477
check_same_pattern(A, S)
@@ -432,8 +499,12 @@ function decompress!(
432499
for (i, j) in reverse_bfs_orders[k]
433500
val = B[i, color[j]] - buffer_right_type[i]
434501
buffer_right_type[j] = buffer_right_type[j] + val
435-
A[i, j] = val
436-
A[j, i] = val
502+
if in_triangle(i, j, uplo)
503+
A[i, j] = val
504+
end
505+
if in_triangle(j, i, uplo)
506+
A[j, i] = val
507+
end
437508
end
438509
end
439510
return A
@@ -442,7 +513,7 @@ end
442513
## MatrixInverseColoringResult
443514

444515
function decompress!(
445-
A::AbstractMatrix{R}, B::AbstractMatrix{R}, result::LinearSystemColoringResult
516+
A::AbstractMatrix{R}, B::AbstractMatrix{R}, result::LinearSystemColoringResult; uplo=:UL
446517
) where {R<:Real}
447518
@compat (;
448519
S, color, strict_upper_nonzero_inds, T_factorization, strict_upper_nonzeros_A
@@ -458,8 +529,12 @@ function decompress!(
458529
end
459530
end
460531
for (l, (i, j)) in enumerate(strict_upper_nonzero_inds)
461-
A[i, j] = strict_upper_nonzeros_A[l]
462-
A[j, i] = strict_upper_nonzeros_A[l]
532+
if in_triangle(i, j, uplo)
533+
A[i, j] = strict_upper_nonzeros_A[l]
534+
end
535+
if in_triangle(j, i, uplo)
536+
A[j, i] = strict_upper_nonzeros_A[l]
537+
end
463538
end
464539
return A
465540
end

test/random.jl

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using ADTypes: column_coloring, row_coloring, symmetric_coloring
2-
using Compat
3-
using LinearAlgebra: I, Symmetric
2+
using LinearAlgebra
43
using SparseArrays
54
using SparseMatrixColorings
65
using SparseMatrixColorings:
@@ -27,7 +26,7 @@ symmetric_params = vcat(
2726
@testset "Column coloring & decompression" begin
2827
problem = ColoringProblem(; structure=:nonsymmetric, partition=:column)
2928
algo = GreedyColoringAlgorithm(; decompression=:direct)
30-
for (m, n, p) in asymmetric_params
29+
@testset "$((; m, n, p))" for (m, n, p) in asymmetric_params
3130
A0 = sprand(rng, m, n, p)
3231
color0 = column_coloring(A0, algo)
3332
@test structurally_orthogonal_columns(A0, color0)
@@ -39,7 +38,7 @@ end;
3938
@testset "Row coloring & decompression" begin
4039
problem = ColoringProblem(; structure=:nonsymmetric, partition=:row)
4140
algo = GreedyColoringAlgorithm(; decompression=:direct)
42-
for (m, n, p) in asymmetric_params
41+
@testset "$((; m, n, p))" for (m, n, p) in asymmetric_params
4342
A0 = sprand(rng, m, n, p)
4443
color0 = row_coloring(A0, algo)
4544
@test structurally_orthogonal_columns(transpose(A0), color0)
@@ -51,8 +50,8 @@ end;
5150
@testset "Symmetric coloring & direct decompression" begin
5251
problem = ColoringProblem(; structure=:symmetric, partition=:column)
5352
algo = GreedyColoringAlgorithm(; decompression=:direct)
54-
for (n, p) in symmetric_params
55-
A0 = Symmetric(sprand(rng, n, n, p))
53+
@testset "$((; n, p))" for (n, p) in symmetric_params
54+
A0 = sparse(Symmetric(sprand(rng, n, n, p)))
5655
color0 = symmetric_coloring(A0, algo)
5756
@test symmetrically_orthogonal_columns(A0, color0)
5857
@test directly_recoverable_columns(A0, color0)
@@ -63,8 +62,8 @@ end;
6362
@testset "Symmetric coloring & substitution decompression" begin
6463
problem = ColoringProblem(; structure=:symmetric, partition=:column)
6564
algo = GreedyColoringAlgorithm(; decompression=:substitution)
66-
for (n, p) in symmetric_params
67-
A0 = Symmetric(sprand(rng, n, n, p))
65+
@testset "$((; n, p))" for (n, p) in symmetric_params
66+
A0 = sparse(Symmetric(sprand(rng, n, n, p)))
6867
# TODO: find tests for recoverability
6968
test_coloring_decompression(A0, problem, algo)
7069
end

test/small.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
using Compat
2-
using LinearAlgebra
31
using SparseArrays
42
using SparseMatrixColorings
53
using SparseMatrixColorings:

test/utils.jl

Lines changed: 78 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using LinearAlgebra
12
using SparseMatrixColorings
23
using SparseMatrixColorings: LinearSystemColoringResult, matrix_versions, respectful_similar
34
using Test
@@ -10,38 +11,94 @@ function test_coloring_decompression(
1011
color0=nothing,
1112
) where {structure,partition,decompression}
1213
color_vec = Vector{Int}[]
13-
for A in matrix_versions(A0)
14+
@testset "$(typeof(A))" for A in matrix_versions(A0)
1415
result = coloring(A, problem, algo; decompression_eltype=eltype(A))
1516
color = if partition == :column
1617
column_colors(result)
1718
elseif partition == :row
1819
row_colors(result)
1920
end
2021
push!(color_vec, color)
22+
2123
B = compress(A, result)
22-
!isnothing(color0) && @test color == color0
23-
!isnothing(B0) && @test B == B0
24-
@test decompress(B, result) A0
25-
@test decompress(B, result) A0 # check result wasn't modified
26-
@test decompress!(respectful_similar(A), B, result) A0
27-
@test decompress!(respectful_similar(A), B, result) A0
28-
if decompression == :direct
29-
A2 = respectful_similar(A)
30-
A2 .= zero(eltype(A2))
31-
for c in unique(color)
32-
if partition == :column
33-
decompress_single_color!(A2, B[:, c], c, result)
34-
elseif partition == :row
35-
decompress_single_color!(A2, B[c, :], c, result)
24+
25+
@testset "Reference" begin
26+
!isnothing(color0) && @test color == color0
27+
!isnothing(B0) && @test B == B0
28+
end
29+
30+
@testset "Full decompression" begin
31+
@test decompress(B, result) A0
32+
@test decompress(B, result) A0 # check result wasn't modified
33+
@test decompress!(respectful_similar(A), B, result) A0
34+
@test decompress!(respectful_similar(A), B, result) A0
35+
end
36+
37+
@testset "Single-color decompression" begin
38+
if decompression == :direct # TODO: implement for :substitution too
39+
A2 = respectful_similar(A)
40+
A2 .= zero(eltype(A2))
41+
for c in unique(color)
42+
if partition == :column
43+
decompress_single_color!(A2, B[:, c], c, result)
44+
elseif partition == :row
45+
decompress_single_color!(A2, B[c, :], c, result)
46+
end
3647
end
48+
@test A2 A0
3749
end
38-
@test A2 A0
3950
end
40-
if structure == :symmetric
41-
linresult = LinearSystemColoringResult(sparse(A), color, eltype(A))
42-
@test decompress(B, linresult) A0
43-
@test decompress!(respectful_similar(A), B, linresult) A0
51+
52+
@testset "Triangle decompression" begin
53+
if structure == :symmetric
54+
A3upper = respectful_similar(A)
55+
A3lower = respectful_similar(A)
56+
A3both = respectful_similar(A)
57+
A3upper .= zero(eltype(A))
58+
A3lower .= zero(eltype(A))
59+
A3both .= zero(eltype(A))
60+
61+
decompress!(A3upper, B, result, :U)
62+
decompress!(A3lower, B, result, :L)
63+
decompress!(A3both, B, result, :UL)
64+
65+
@test A3upper triu(A0)
66+
@test A3lower tril(A0)
67+
@test A3both A0
68+
end
4469
end
70+
71+
@testset "Single-color triangle decompression" begin
72+
if structure == :symmetric && decompression == :direct
73+
A4upper = respectful_similar(A)
74+
A4lower = respectful_similar(A)
75+
A4both = respectful_similar(A)
76+
A4upper .= zero(eltype(A))
77+
A4lower .= zero(eltype(A))
78+
A4both .= zero(eltype(A))
79+
80+
for c in unique(color)
81+
decompress_single_color!(A4upper, B[:, c], c, result, :U)
82+
decompress_single_color!(A4lower, B[:, c], c, result, :L)
83+
decompress_single_color!(A4both, B[:, c], c, result, :UL)
84+
end
85+
86+
@test A4upper triu(A0)
87+
@test A4lower tril(A0)
88+
@test A4both A0
89+
end
90+
end
91+
92+
@testset "Linear system decompression" begin
93+
if structure == :symmetric
94+
linresult = LinearSystemColoringResult(sparse(A), color, eltype(A))
95+
@test decompress(B, linresult) A0
96+
@test decompress!(respectful_similar(A), B, linresult) A0
97+
end
98+
end
99+
end
100+
101+
@testset "Coherence between all colorings" begin
102+
@test all(color_vec .== Ref(color_vec[1]))
45103
end
46-
@test all(color_vec .== Ref(color_vec[1]))
47104
end

0 commit comments

Comments
 (0)