@@ -7,7 +7,7 @@ Compress `A` given a coloring `result` of the sparsity pattern of `A`.
77- If `result` comes from a `:bidirectional` partition, the output is a tuple of matrices `(Br, Bc)`, where `Br` is compressed by row and `Bc` by column.
88
99Compression means summing either the columns or the rows of `A` which share the same color.
10- It is undone by calling [`decompress`](@ref).
10+ It is undone by calling [`decompress`](@ref) or [`decompress!`](@ref) .
1111
1212!!! warning
1313 At the moment, `:bidirectional` partitions are not implemented.
6767 decompress(B::AbstractMatrix, result::AbstractColoringResult)
6868
6969Decompress `B` into a new matrix `A`, given a coloring `result` of the sparsity pattern of `A`.
70+ The in-place alternative is [`decompress!`](@ref).
7071
7172Compression means summing either the columns or the rows of `A` which share the same color.
7273It is done by calling [`compress`](@ref).
@@ -127,10 +128,14 @@ end
127128 )
128129
129130Decompress `B` in-place into `A`, given a coloring `result` of the sparsity pattern of `A`.
131+ The out-of-place alternative is [`decompress`](@ref).
130132
131133Compression means summing either the columns or the rows of `A` which share the same color.
132134It is done by calling [`compress`](@ref).
133135
136+ !!! note
137+ In-place decompression is faster when `A isa SparseMatrixCSC`.
138+
134139# Example
135140
136141```jldoctest
@@ -190,10 +195,10 @@ function decompress!(
190195 return decompress_aux! (A, B, result)
191196end
192197
198+ # # NonSymmetricColoringResult
199+
193200function decompress_aux! (
194- A:: AbstractMatrix{R} ,
195- B:: AbstractMatrix{R} ,
196- result:: AbstractColoringResult{:nonsymmetric,:column,:direct} ,
201+ A:: AbstractMatrix{R} , B:: AbstractMatrix{R} , result:: NonSymmetricColoringResult{:column}
197202) where {R<: Real }
198203 A .= zero (R)
199204 S = get_matrix (result)
@@ -209,9 +214,7 @@ function decompress_aux!(
209214end
210215
211216function decompress_aux! (
212- A:: AbstractMatrix{R} ,
213- B:: AbstractMatrix{R} ,
214- result:: AbstractColoringResult{:nonsymmetric,:row,:direct} ,
217+ A:: AbstractMatrix{R} , B:: AbstractMatrix{R} , result:: NonSymmetricColoringResult{:row}
215218) where {R<: Real }
216219 A .= zero (R)
217220 S = get_matrix (result)
@@ -227,24 +230,31 @@ function decompress_aux!(
227230end
228231
229232function decompress_aux! (
230- A:: AbstractMatrix{R} ,
231- B:: AbstractMatrix{R} ,
232- result:: AbstractColoringResult{:symmetric,:column,:direct} ,
233+ A:: SparseMatrixCSC{R} , B:: AbstractMatrix{R} , result:: NonSymmetricColoringResult{:column}
233234) where {R<: Real }
234- A .= zero (R)
235- S = get_matrix (result)
236- color = column_colors (result)
237- group = column_groups (result)
238- for ij in findall (! iszero, S)
239- i, j = Tuple (ij)
240- k, l = symmetric_coefficient (i, j, color, group, S)
241- A[i, j] = B[k, l]
235+ nzA = nonzeros (A)
236+ ind = result. compressed_indices
237+ for i in eachindex (nzA, ind)
238+ nzA[i] = B[ind[i]]
239+ end
240+ return A
241+ end
242+
243+ function decompress_aux! (
244+ A:: SparseMatrixCSC{R} , B:: AbstractMatrix{R} , result:: NonSymmetricColoringResult{:row}
245+ ) where {R<: Real }
246+ nzA = nonzeros (A)
247+ ind = result. compressed_indices
248+ for i in eachindex (nzA, ind)
249+ nzA[i] = B[ind[i]]
242250 end
243251 return A
244252end
245253
254+ # # StarSetColoringResult
255+
246256function decompress_aux! (
247- A:: AbstractMatrix{R} , B:: AbstractMatrix{R} , result:: StarSetColoringResult{:column}
257+ A:: AbstractMatrix{R} , B:: AbstractMatrix{R} , result:: StarSetColoringResult
248258) where {R<: Real }
249259 A .= zero (R)
250260 S = get_matrix (result)
@@ -258,117 +268,35 @@ function decompress_aux!(
258268end
259269
260270function decompress_aux! (
261- A:: AbstractMatrix{R} ,
262- B:: AbstractMatrix{R} ,
263- result:: AbstractColoringResult{:symmetric,:column,:substitution} ,
271+ A:: SparseMatrixCSC{R} , B:: AbstractMatrix{R} , result:: StarSetColoringResult
264272) where {R<: Real }
265- # build T such that T * strict_upper_nonzeros(A) = B
266- # and solve a linear least-squares problem
267- # only consider the strict upper triangle of A because of symmetry
268- # TODO : make more efficient
269- A .= zero (R)
270- S = sparse (get_matrix (result))
271- color = column_colors (result)
272-
273- n = checksquare (S)
274- strict_upper_nonzero_inds = Tuple{Int,Int}[]
275- I, J, _ = findnz (S)
276- for (i, j) in zip (I, J)
277- (i < j) && push! (strict_upper_nonzero_inds, (i, j))
278- (i == j) && (A[i, i] = B[i, color[i]])
279- end
280-
281- T = spzeros (float (R), length (B), length (strict_upper_nonzero_inds))
282- for (l, (i, j)) in enumerate (strict_upper_nonzero_inds)
283- ci = color[i]
284- cj = color[j]
285- ki = (ci - 1 ) * n + j # A[i, j] appears in B[j, ci]
286- kj = (cj - 1 ) * n + i # A[i, j] appears in B[i, cj]
287- T[ki, l] = one (float (R))
288- T[kj, l] = one (float (R))
289- end
290-
291- strict_upper_nonzeros_A = T \ vec (B)
292- for (l, (i, j)) in enumerate (strict_upper_nonzero_inds)
293- A[i, j] = strict_upper_nonzeros_A[l]
294- A[j, i] = strict_upper_nonzeros_A[l]
273+ nzA = nonzeros (A)
274+ ind = result. compressed_indices
275+ for i in eachindex (nzA, ind)
276+ nzA[i] = B[ind[i]]
295277 end
296278 return A
297279end
298280
281+ # # TreeSetColoringResult
282+
283+ # TODO : add method for A::SparseMatrixCSC
284+
299285function decompress_aux! (
300286 A:: AbstractMatrix{R} , B:: AbstractMatrix{R} , result:: TreeSetColoringResult
301287) where {R<: Real }
302- n = checksquare (A)
303288 A .= zero (R)
304289 S = get_matrix (result)
305290 color = column_colors (result)
306-
307- # forest is a structure DisjointSets from DataStructures.jl
308- # - forest.intmap: a dictionary that maps an edge (i, j) to an integer k
309- # - forest.revmap: a dictionary that does the reverse of intmap, mapping an integer k to an edge (i, j)
310- # - forest.internal.ngroups: the number of trees in the forest
311- forest = result. tree_set. forest
312- ntrees = forest. internal. ngroups
313-
314- # vector of trees where each tree contains the indices of its edges
315- trees = [Int[] for i in 1 : ntrees]
316-
317- # dictionary that maps a tree's root to the index of the tree
318- roots = Dict {Int,Int} ()
319-
320- k = 0
321- for edge in forest. revmap
322- root_edge = find_root! (forest, edge)
323- root = forest. intmap[root_edge]
324- if ! haskey (roots, root)
325- k += 1
326- roots[root] = k
327- end
328- index_tree = roots[root]
329- push! (trees[index_tree], forest. intmap[edge])
330- end
331-
332- # vector of dictionaries where each dictionary stores the degree of each vertex in a tree
333- degrees = [Dict {Int,Int} () for k in 1 : ntrees]
334- for k in 1 : ntrees
335- tree = trees[k]
336- degree = degrees[k]
337- for edge_index in tree
338- i, j = forest. revmap[edge_index]
339- ! haskey (degree, i) && (degree[i] = 0 )
340- ! haskey (degree, j) && (degree[j] = 0 )
341- degree[i] += 1
342- degree[j] += 1
343- end
344- end
345-
346- # depth-first search (DFS) traversal order for each tree in the forest
347- dfs_orders = [Vector {Tuple{Int,Int}} () for k in 1 : ntrees]
348- for k in 1 : ntrees
349- tree = trees[k]
350- degree = degrees[k]
351- while sum (values (degree)) != 0
352- for (t, edge_index) in enumerate (tree)
353- if edge_index != 0
354- i, j = forest. revmap[edge_index]
355- if (degree[i] == 1 ) || (degree[j] == 1 ) # leaf vertex
356- if degree[i] > degree[j] # vertex i is the parent of vertex j
357- i, j = j, i # ensure that i always denotes a leaf vertex
358- end
359- degree[i] -= 1 # decrease the degree of vertex i
360- degree[j] -= 1 # decrease the degree of vertex j
361- tree[t] = 0 # remove the edge (i,j)
362- push! (dfs_orders[k], (i, j))
363- end
364- end
365- end
366- end
367- end
291+ @compat (; degrees, dfs_orders, stored_values) = result
368292
369293 # stored_values holds the sum of edge values for subtrees in a tree.
370294 # For each vertex i, stored_values[i] is the sum of edge values in the subtree rooted at i.
371- stored_values = Vector {R} (undef, n)
295+ stored_values_right_type = if R == Float64
296+ stored_values
297+ else
298+ similar (stored_values, R)
299+ end
372300
373301 # Recover the diagonal coefficients of A
374302 for i in axes (A, 1 )
@@ -378,19 +306,43 @@ function decompress_aux!(
378306 end
379307
380308 # Recover the off-diagonal coefficients of A
381- for k in 1 : ntrees
309+ for k in eachindex (degrees, dfs_orders)
382310 vertices = keys (degrees[k])
383311 for vertex in vertices
384- stored_values [vertex] = zero (R)
312+ stored_values_right_type [vertex] = zero (R)
385313 end
386314
387315 tree = dfs_orders[k]
388316 for (i, j) in tree
389- val = B[i, color[j]] - stored_values [i]
390- stored_values [j] = stored_values [j] + val
317+ val = B[i, color[j]] - stored_values_right_type [i]
318+ stored_values_right_type [j] = stored_values_right_type [j] + val
391319 A[i, j] = val
392320 A[j, i] = val
393321 end
394322 end
395323 return A
396324end
325+
326+ # # MatrixInverseColoringResult
327+
328+ function decompress_aux! (
329+ A:: AbstractMatrix{R} , B:: AbstractMatrix{R} , result:: LinearSystemColoringResult
330+ ) where {R<: Real }
331+ S = get_matrix (result)
332+ color = column_colors (result)
333+ @compat (; strict_upper_nonzero_inds, T_factorization, strict_upper_nonzeros_A) = result
334+
335+ # TODO : for some reason I cannot use ldiv! with a sparse QR
336+ strict_upper_nonzeros_A = T_factorization \ vec (B)
337+ A .= zero (R)
338+ for i in axes (A, 1 )
339+ if ! iszero (S[i, i])
340+ A[i, i] = B[i, color[i]]
341+ end
342+ end
343+ for (l, (i, j)) in enumerate (strict_upper_nonzero_inds)
344+ A[i, j] = strict_upper_nonzeros_A[l]
345+ A[j, i] = strict_upper_nonzeros_A[l]
346+ end
347+ return A
348+ end
0 commit comments