Skip to content

Commit 54b5bb2

Browse files
committed
[bicoloring] Use Br and Bc directly for the decompression
1 parent d9ec840 commit 54b5bb2

File tree

7 files changed

+385
-104
lines changed

7 files changed

+385
-104
lines changed

docs/src/dev.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ SparseMatrixColorings.partial_distance2_coloring
2626
SparseMatrixColorings.star_coloring
2727
SparseMatrixColorings.acyclic_coloring
2828
SparseMatrixColorings.group_by_color
29+
SparseMatrixColorings.remap_colors
2930
SparseMatrixColorings.Forest
3031
SparseMatrixColorings.StarSet
3132
SparseMatrixColorings.TreeSet
@@ -39,8 +40,8 @@ SparseMatrixColorings.RowColoringResult
3940
SparseMatrixColorings.StarSetColoringResult
4041
SparseMatrixColorings.TreeSetColoringResult
4142
SparseMatrixColorings.LinearSystemColoringResult
42-
SparseMatrixColorings.BicoloringResult
43-
SparseMatrixColorings.remap_colors
43+
SparseMatrixColorings.StarSetBicoloringResult
44+
SparseMatrixColorings.TreeSetBicoloringResult
4445
```
4546

4647
## Testing

src/decompression.jl

Lines changed: 172 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -515,12 +515,14 @@ end
515515
## TreeSetColoringResult
516516

517517
function decompress!(
518-
A::AbstractMatrix, B::AbstractMatrix, result::TreeSetColoringResult, uplo::Symbol=:F
519-
)
518+
A::AbstractMatrix{R},
519+
B::AbstractMatrix{R},
520+
result::TreeSetColoringResult,
521+
uplo::Symbol=:F,
522+
) where {R<:Real}
520523
(; ag, color, reverse_bfs_orders, tree_edge_indices, nt, buffer) = result
521524
(; S) = ag
522525
uplo == :F && check_same_pattern(A, S)
523-
R = eltype(A)
524526
fill!(A, zero(R))
525527

526528
if eltype(buffer) == R
@@ -714,61 +716,184 @@ function decompress!(
714716
return A
715717
end
716718

717-
## BicoloringResult
718-
719-
function _join_compressed!(result::BicoloringResult, Br::AbstractMatrix, Bc::AbstractMatrix)
720-
#=
721-
Say we have an original matrix `A` of size `(n, m)` and we build an augmented matrix `A_and_Aᵀ = [zeros(n, n) Aᵀ; A zeros(m, m)]`.
722-
Its first `1:n` columns have the form `[zeros(n); A[:, j]]` and its following `n+1:n+m` columns have the form `[A[i, :]; zeros(m)]`.
723-
The symmetric column coloring is performed on `A_and_Aᵀ` and the column-wise compression of `A_and_Aᵀ` should return a matrix `Br_and_Bc`.
724-
But in reality, `Br_and_Bc` is computed as two partial compressions: the row-wise compression `Br` (corresponding to `Aᵀ`) and the columnwise compression `Bc` (corresponding to `A`).
725-
Before symmetric decompression, we must reconstruct `Br_and_Bc` from `Br` and `Bc`, knowing that the symmetric colors (those making up `Br_and_Bc`) are present in either a row of `Br`, a column of `Bc`, or both.
726-
Therefore, the column indices in `Br_and_Bc` don't necessarily match with the row indices in `Br` or the column indices in `Bc` since some colors may be missing in the partial compressions.
727-
The columns of the top part of `Br_and_Bc` (rows `1:n`) are the rows of `Br`, interlaced with zero columns whenever the current color hasn't been used to color any row.
728-
The columns of the bottom part of `Br_and_Bc` (rows `n+1:n+m`) are the columns of `Bc`, interlaced with zero columns whenever the current color hasn't been used to color any column.
729-
We use the vectors `symmetric_to_row` and `symmetric_to_column` to map from symmetric colors to row and column colors.
730-
=#
731-
(; A, symmetric_to_column, symmetric_to_row) = result
732-
m, n = size(A)
733-
R = Base.promote_eltype(Br, Bc)
734-
if eltype(result.Br_and_Bc) == R
735-
Br_and_Bc = result.Br_and_Bc
736-
else
737-
Br_and_Bc = similar(result.Br_and_Bc, R)
738-
end
739-
fill!(Br_and_Bc, zero(R))
740-
for c in axes(Br_and_Bc, 2)
741-
if symmetric_to_row[c] > 0 # some rows were colored with the symmetric color c
742-
copyto!(view(Br_and_Bc, 1:n, c), view(Br, symmetric_to_row[c], :))
743-
end
744-
if symmetric_to_column[c] > 0 # some columns were colored with the symmetric color c
745-
copyto!(
746-
view(Br_and_Bc, (n + 1):(n + m), c), view(Bc, :, symmetric_to_column[c])
747-
)
719+
## StarSetBicoloringResult
720+
721+
function decompress!(
722+
A::AbstractMatrix,
723+
Br::AbstractMatrix,
724+
Bc::AbstractMatrix,
725+
result::StarSetBicoloringResult,
726+
)
727+
(; S, A_indices, compressed_indices, pos_Bc) = result
728+
fill!(A, zero(eltype(A)))
729+
730+
ind_Bc = 1
731+
ind_Br = nnz(S)
732+
rvS = rowvals(S)
733+
for j in axes(S, 2)
734+
for k in nzrange(S, j)
735+
i = rvS[k]
736+
index_Bc = compressed_indices[ind_Bc]
737+
if A_indices[ind_Bc] == k
738+
A[i, j] = Bc[index_Bc]
739+
if ind_Bc < pos_Bc
740+
ind_Bc += 1
741+
end
742+
else
743+
index_Br = compressed_indices[ind_Br]
744+
A[i, j] = Br[index_Br]
745+
ind_Br -= 1
746+
end
748747
end
749748
end
750-
return Br_and_Bc
749+
return A
751750
end
752751

753752
function decompress!(
754-
A::AbstractMatrix, Br::AbstractMatrix, Bc::AbstractMatrix, result::BicoloringResult
753+
A::SparseMatrixCSC,
754+
Br::AbstractMatrix,
755+
Bc::AbstractMatrix,
756+
result::StarSetBicoloringResult,
755757
)
758+
(; A_indices, compressed_indices, pos_Bc) = result
759+
nzA = nonzeros(A)
760+
for k in 1:pos_Bc
761+
nzA[A_indices[k]] = Bc[compressed_indices[k]]
762+
end
763+
for k in (pos_Bc + 1):length(nzA)
764+
nzA[A_indices[k]] = Br[compressed_indices[k]]
765+
end
766+
return A
767+
end
768+
769+
## TreeSetBicoloringResult
770+
771+
function decompress!(
772+
A::AbstractMatrix{R},
773+
Br::AbstractMatrix{R},
774+
Bc::AbstractMatrix{R},
775+
result::TreeSetBicoloringResult,
776+
) where {R<:Real}
777+
(;
778+
symmetric_color,
779+
symmetric_to_row,
780+
symmetric_to_column,
781+
reverse_bfs_orders,
782+
tree_edge_indices,
783+
nt,
784+
buffer,
785+
) = result
786+
756787
m, n = size(A)
757-
Br_and_Bc = _join_compressed!(result, Br, Bc)
758-
A_and_Aᵀ = decompress(Br_and_Bc, result.symmetric_result)
759-
copyto!(A, A_and_Aᵀ[(n + 1):(n + m), 1:n]) # original matrix in bottom left corner
788+
fill!(A, zero(R))
789+
790+
if eltype(buffer) == R
791+
buffer_right_type = buffer
792+
else
793+
buffer_right_type = similar(buffer, R)
794+
end
795+
796+
for k in 1:nt
797+
# Positions of the first and last edges of the tree
798+
first = tree_edge_indices[k]
799+
last = tree_edge_indices[k + 1] - 1
800+
801+
# Reset the buffer to zero for all vertices in the tree (except the root)
802+
for pos in first:last
803+
(vertex, _) = reverse_bfs_orders[pos]
804+
buffer_right_type[vertex] = zero(R)
805+
end
806+
# Reset the buffer to zero for the root vertex
807+
(_, root) = reverse_bfs_orders[last]
808+
buffer_right_type[root] = zero(R)
809+
810+
for pos in first:last
811+
(i, j) = reverse_bfs_orders[pos]
812+
cj = symmetric_color[j]
813+
if in_triangle(i, j, :L)
814+
val = Bc[i - n, symmetric_to_column[cj]] - buffer_right_type[i]
815+
buffer_right_type[j] = buffer_right_type[j] + val
816+
A[i - n, j] = val
817+
else
818+
val = Br[symmetric_to_row[cj], i] - buffer_right_type[i]
819+
buffer_right_type[j] = buffer_right_type[j] + val
820+
A[j - n, i] = val
821+
end
822+
end
823+
end
760824
return A
761825
end
762826

763827
function decompress!(
764-
A::SparseMatrixCSC, Br::AbstractMatrix, Bc::AbstractMatrix, result::BicoloringResult
765-
)
766-
(; large_colptr, large_rowval, symmetric_result) = result
828+
A::SparseMatrixCSC{R},
829+
Br::AbstractMatrix{R},
830+
Bc::AbstractMatrix{R},
831+
result::TreeSetBicoloringResult,
832+
) where {R<:Real}
833+
(;
834+
symmetric_color,
835+
symmetric_to_column,
836+
symmetric_to_row,
837+
reverse_bfs_orders,
838+
tree_edge_indices,
839+
nt,
840+
A_indices,
841+
buffer,
842+
) = result
843+
767844
m, n = size(A)
768-
Br_and_Bc = _join_compressed!(result, Br, Bc)
769-
# pretend A is larger
770-
A_and_noAᵀ = SparseMatrixCSC(m + n, m + n, large_colptr, large_rowval, A.nzval)
771-
# decompress lower triangle only
772-
decompress!(A_and_noAᵀ, Br_and_Bc, symmetric_result, :L)
845+
nzA = nonzeros(A)
846+
847+
if eltype(buffer) == R
848+
buffer_right_type = buffer
849+
else
850+
buffer_right_type = similar(buffer, R)
851+
end
852+
853+
# Recover the coefficients of A
854+
counter = 0
855+
856+
# Recover the off-diagonal coefficients of A
857+
for k in 1:nt
858+
# Positions of the first and last edges of the tree
859+
first = tree_edge_indices[k]
860+
last = tree_edge_indices[k + 1] - 1
861+
862+
# Reset the buffer to zero for all vertices in the tree (except the root)
863+
for pos in first:last
864+
(vertex, _) = reverse_bfs_orders[pos]
865+
buffer_right_type[vertex] = zero(R)
866+
end
867+
# Reset the buffer to zero for the root vertex
868+
(_, root) = reverse_bfs_orders[last]
869+
buffer_right_type[root] = zero(R)
870+
871+
for pos in first:last
872+
(i, j) = reverse_bfs_orders[pos]
873+
cj = symmetric_color[j]
874+
counter += 1
875+
876+
#! format: off
877+
# A[i,j] is in the lower triangular part of A
878+
if in_triangle(i, j, :L)
879+
val = Bc[i - n, symmetric_to_column[cj]] - buffer_right_type[i]
880+
buffer_right_type[j] = buffer_right_type[j] + val
881+
882+
# A[i,j] is stored at index_ij = A_indices[counter] in A.nzval
883+
nzind = A_indices[counter]
884+
nzA[nzind] = val
885+
886+
# A[i,j] is in the upper triangular part of A
887+
else
888+
val = Br[symmetric_to_row[cj], i] - buffer_right_type[i]
889+
buffer_right_type[j] = buffer_right_type[j] + val
890+
891+
# A[j,i] is stored at index_ji = A_indices[counter] in A.nzval
892+
nzind = A_indices[counter]
893+
nzA[nzind] = val
894+
end
895+
#! format: on
896+
end
897+
end
773898
return A
774899
end

src/graph.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ end
100100
Return a [`SparsityPatternCSC`](@ref) corresponding to the matrix `[0 Aᵀ; A 0]`, with a minimum of allocations.
101101
"""
102102
function bidirectional_pattern(A::AbstractMatrix; symmetric_pattern::Bool)
103-
bidirectional_pattern(SparsityPatternCSC(SparseMatrixCSC(A)); symmetric_pattern)
103+
S = SparsityPatternCSC(SparseMatrixCSC(A))
104+
bidirectional_pattern(S; symmetric_pattern)
104105
end
105106

106107
function bidirectional_pattern(S::SparsityPatternCSC{T}; symmetric_pattern::Bool) where {T}
@@ -172,7 +173,7 @@ function bidirectional_pattern(S::SparsityPatternCSC{T}; symmetric_pattern::Bool
172173

173174
# Create the SparsityPatternCSC of the augmented adjacency matrix
174175
S_and_Sᵀ = SparsityPatternCSC{T}(p, p, colptr, rowval)
175-
return S_and_Sᵀ, edge_to_index
176+
return S, S_and_Sᵀ, edge_to_index
176177
end
177178

178179
function build_edge_to_index(S::SparsityPatternCSC{T}) where {T}

src/interface.jl

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ function _coloring(
282282
algo::GreedyColoringAlgorithm{:substitution},
283283
decompression_eltype::Type{R},
284284
symmetric_pattern::Bool,
285-
) where {R}
285+
) where {R<:Real}
286286
ag = AdjacencyGraph(A; has_diagonal=true)
287287
vertices_in_order = vertices(ag, algo.order)
288288
color, tree_set = acyclic_coloring(ag, vertices_in_order, algo.postprocessing)
@@ -298,19 +298,18 @@ function _coloring(
298298
A::AbstractMatrix,
299299
::ColoringProblem{:nonsymmetric,:bidirectional},
300300
algo::GreedyColoringAlgorithm{:direct},
301-
decompression_eltype::Type{R},
301+
decompression_eltype::Type,
302302
symmetric_pattern::Bool,
303303
) where {R}
304-
A_and_Aᵀ, edge_to_index = bidirectional_pattern(A; symmetric_pattern)
305-
ag = AdjacencyGraph(A_and_Aᵀ, edge_to_index; has_diagonal=false)
304+
S, S_and_Sᵀ, edge_to_index = bidirectional_pattern(A; symmetric_pattern)
305+
ag = AdjacencyGraph(S_and_Sᵀ, edge_to_index; has_diagonal=false)
306306
vertices_in_order = vertices(ag, algo.order)
307-
color, star_set = star_coloring(ag, vertices_in_order, algo.postprocessing)
307+
symmetric_color, star_set = star_coloring(ag, vertices_in_order, algo.postprocessing)
308308
if speed_setting isa WithResult
309-
symmetric_result = StarSetColoringResult(A_and_Aᵀ, ag, color, star_set)
310-
return BicoloringResult(A, ag, symmetric_result, R)
309+
return StarSetBicoloringResult(A, S, ag, symmetric_color, star_set)
311310
else
312311
row_color, column_color, _ = remap_colors(
313-
eltype(ag), color, maximum(color), size(A)...
312+
eltype(ag), symmetric_color, maximum(symmetric_color), size(A)...
314313
)
315314
return row_color, column_color
316315
end
@@ -324,16 +323,15 @@ function _coloring(
324323
decompression_eltype::Type{R},
325324
symmetric_pattern::Bool,
326325
) where {R}
327-
A_and_Aᵀ, edge_to_index = bidirectional_pattern(A; symmetric_pattern)
328-
ag = AdjacencyGraph(A_and_Aᵀ, edge_to_index; has_diagonal=false)
326+
S, S_and_Sᵀ, edge_to_index = bidirectional_pattern(A; symmetric_pattern)
327+
ag = AdjacencyGraph(S_and_Sᵀ, edge_to_index; has_diagonal=false)
329328
vertices_in_order = vertices(ag, algo.order)
330-
color, tree_set = acyclic_coloring(ag, vertices_in_order, algo.postprocessing)
329+
symmetric_color, tree_set = acyclic_coloring(ag, vertices_in_order, algo.postprocessing)
331330
if speed_setting isa WithResult
332-
symmetric_result = TreeSetColoringResult(A_and_Aᵀ, ag, color, tree_set, R)
333-
return BicoloringResult(A, ag, symmetric_result, R)
331+
return TreeSetBicoloringResult(A, S, ag, symmetric_color, tree_set, R)
334332
else
335333
row_color, column_color, _ = remap_colors(
336-
eltype(ag), color, maximum(color), size(A)...
334+
eltype(ag), symmetric_color, maximum(symmetric_color), size(A)...
337335
)
338336
return row_color, column_color
339337
end

0 commit comments

Comments
 (0)