Skip to content

Commit a47d96e

Browse files
amontoisongdalle
andauthored
Add an attribute has_loops in AdjacencyGraph (#186)
* Add an attribute has_loops in AdjacencyGraph * Rename `has_loops` into `has_diagonal`, make it statically inferred * Fixes --------- Co-authored-by: Guillaume Dalle <22795598+gdalle@users.noreply.github.com>
1 parent 11af951 commit a47d96e

5 files changed

Lines changed: 95 additions & 57 deletions

File tree

src/coloring.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -557,9 +557,11 @@ function postprocess!(
557557
color_used = zeros(Bool, maximum(color))
558558

559559
# nonzero diagonal coefficients force the use of their respective color (there can be no neutral colors if the diagonal is fully nonzero)
560-
for i in axes(S, 1)
561-
if !iszero(S[i, i])
562-
color_used[color[i]] = true
560+
if has_diagonal(g)
561+
for i in axes(S, 1)
562+
if !iszero(S[i, i])
563+
color_used[color[i]] = true
564+
end
563565
end
564566
end
565567

src/decompression.jl

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -415,16 +415,22 @@ end
415415
function decompress!(
416416
A::AbstractMatrix, B::AbstractMatrix, result::StarSetColoringResult, uplo::Symbol=:F
417417
)
418-
(; color, star_set) = result
418+
(; ag, color, star_set) = result
419419
(; star, hub, spokes) = star_set
420-
S = result.ag.S
420+
(; S) = ag
421421
uplo == :F && check_same_pattern(A, S)
422422
fill!(A, zero(eltype(A)))
423-
for i in axes(A, 1)
424-
if !iszero(S[i, i])
425-
A[i, i] = B[i, color[i]]
423+
424+
# Recover the diagonal coefficients of A
425+
if has_diagonal(ag)
426+
for i in axes(A, 1)
427+
if !iszero(S[i, i])
428+
A[i, i] = B[i, color[i]]
429+
end
426430
end
427431
end
432+
433+
# Recover the off-diagonal coefficients of A
428434
for s in eachindex(hub, spokes)
429435
j = abs(hub[s])
430436
cj = color[j]
@@ -447,15 +453,21 @@ function decompress_single_color!(
447453
result::StarSetColoringResult,
448454
uplo::Symbol=:F,
449455
)
450-
(; color, group, star_set) = result
456+
(; ag, color, group, star_set) = result
451457
(; hub, spokes) = star_set
452-
S = result.ag.S
458+
(; S) = ag
453459
uplo == :F && check_same_pattern(A, S)
454-
for i in axes(A, 1)
455-
if !iszero(S[i, i]) && color[i] == c
456-
A[i, i] = b[i]
460+
461+
# Recover the diagonal coefficients of A
462+
if has_diagonal(ag)
463+
for i in axes(A, 1)
464+
if !iszero(S[i, i]) && color[i] == c
465+
A[i, i] = b[i]
466+
end
457467
end
458468
end
469+
470+
# Recover the off-diagonal coefficients of A
459471
for s in eachindex(hub, spokes)
460472
j = abs(hub[s])
461473
if color[j] == c
@@ -475,8 +487,8 @@ end
475487
function decompress!(
476488
A::SparseMatrixCSC, B::AbstractMatrix, result::StarSetColoringResult, uplo::Symbol=:F
477489
)
478-
(; compressed_indices) = result
479-
S = result.ag.S
490+
(; ag, compressed_indices) = result
491+
(; S) = ag
480492
nzA = nonzeros(A)
481493
if uplo == :F
482494
check_same_pattern(A, S)
@@ -505,8 +517,8 @@ end
505517
function decompress!(
506518
A::AbstractMatrix, B::AbstractMatrix, result::TreeSetColoringResult, uplo::Symbol=:F
507519
)
508-
(; color, vertices_by_tree, reverse_bfs_orders, buffer) = result
509-
S = result.ag.S
520+
(; ag, color, vertices_by_tree, reverse_bfs_orders, buffer) = result
521+
(; S) = ag
510522
uplo == :F && check_same_pattern(A, S)
511523
R = eltype(A)
512524
fill!(A, zero(R))
@@ -518,9 +530,11 @@ function decompress!(
518530
end
519531

520532
# Recover the diagonal coefficients of A
521-
for i in axes(A, 1)
522-
if !iszero(S[i, i])
523-
A[i, i] = B[i, color[i]]
533+
if has_diagonal(ag)
534+
for i in axes(A, 1)
535+
if !iszero(S[i, i])
536+
A[i, i] = B[i, color[i]]
537+
end
524538
end
525539
end
526540

@@ -551,6 +565,7 @@ function decompress!(
551565
uplo::Symbol=:F,
552566
) where {R<:Real}
553567
(;
568+
ag,
554569
color,
555570
vertices_by_tree,
556571
reverse_bfs_orders,
@@ -560,7 +575,7 @@ function decompress!(
560575
upper_triangle_offsets,
561576
buffer,
562577
) = result
563-
S = result.ag.S
578+
(; S) = ag
564579
A_colptr = A.colptr
565580
nzA = nonzeros(A)
566581
uplo == :F && check_same_pattern(A, S)
@@ -572,22 +587,24 @@ function decompress!(
572587
end
573588

574589
# Recover the diagonal coefficients of A
575-
if uplo == :L
576-
for i in diagonal_indices
577-
# A[i, i] is the first element in column i
578-
nzind = A_colptr[i]
579-
nzA[nzind] = B[i, color[i]]
580-
end
581-
elseif uplo == :U
582-
for i in diagonal_indices
583-
# A[i, i] is the last element in column i
584-
nzind = A_colptr[i + 1] - 1
585-
nzA[nzind] = B[i, color[i]]
586-
end
587-
else # uplo == :F
588-
for (k, i) in enumerate(diagonal_indices)
589-
nzind = diagonal_nzind[k]
590-
nzA[nzind] = B[i, color[i]]
590+
if has_diagonal(ag)
591+
if uplo == :L
592+
for i in diagonal_indices
593+
# A[i, i] is the first element in column i
594+
nzind = A_colptr[i]
595+
nzA[nzind] = B[i, color[i]]
596+
end
597+
elseif uplo == :U
598+
for i in diagonal_indices
599+
# A[i, i] is the last element in column i
600+
nzind = A_colptr[i + 1] - 1
601+
nzA[nzind] = B[i, color[i]]
602+
end
603+
else # uplo == :F
604+
for (k, i) in enumerate(diagonal_indices)
605+
nzind = diagonal_nzind[k]
606+
nzA[nzind] = B[i, color[i]]
607+
end
591608
end
592609
end
593610

src/graph.jl

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ end
9696
## Adjacency graph
9797

9898
"""
99-
AdjacencyGraph{T}
99+
AdjacencyGraph{T,has_diagonal}
100100
101101
Undirected graph without self-loops representing the nonzeros of a symmetric matrix (typically a Hessian matrix).
102102
@@ -111,27 +111,44 @@ The adjacency graph of a symmetric matrix `A ∈ ℝ^{n × n}` is `G(A) = (V, E)
111111
112112
# Fields
113113
114-
- `S::SparsityPatternCSC{T}`
114+
- `S::SparsityPatternCSC{T}`: Underlying sparsity pattern, whose diagonal is empty whenever `has_diagonal` is `false`
115115
116116
# References
117117
118118
> [_What Color Is Your Jacobian? SparsityPatternCSC Coloring for Computing Derivatives_](https://epubs.siam.org/doi/10.1137/S0036144504444711), Gebremedhin et al. (2005)
119119
"""
120-
struct AdjacencyGraph{T}
120+
struct AdjacencyGraph{T,has_diagonal}
121121
S::SparsityPatternCSC{T}
122122
end
123123

124-
AdjacencyGraph(A::AbstractMatrix) = AdjacencyGraph(SparseMatrixCSC(A))
125-
AdjacencyGraph(A::SparseMatrixCSC) = AdjacencyGraph(SparsityPatternCSC(A))
124+
function AdjacencyGraph(S::SparsityPatternCSC{T}; has_diagonal::Bool=true) where {T}
125+
return AdjacencyGraph{T,has_diagonal}(S)
126+
end
127+
128+
function AdjacencyGraph(A::SparseMatrixCSC; has_diagonal::Bool=true)
129+
return AdjacencyGraph(SparsityPatternCSC(A); has_diagonal)
130+
end
131+
132+
function AdjacencyGraph(A::AbstractMatrix; has_diagonal::Bool=true)
133+
return AdjacencyGraph(SparseMatrixCSC(A); has_diagonal)
134+
end
126135

127136
pattern(g::AdjacencyGraph) = g.S
128137
nb_vertices(g::AdjacencyGraph) = pattern(g).n
129138
vertices(g::AdjacencyGraph) = 1:nb_vertices(g)
130139

131-
function neighbors(g::AdjacencyGraph, v::Integer)
140+
has_diagonal(::AdjacencyGraph{T,hl}) where {T,hl} = hl
141+
142+
function neighbors(g::AdjacencyGraph{T,true}, v::Integer) where {T}
143+
S = pattern(g)
144+
neighbors_v = view(rowvals(S), nzrange(S, v))
145+
return Iterators.filter(!=(v), neighbors_v) # TODO: optimize
146+
end
147+
148+
function neighbors(g::AdjacencyGraph{T,false}, v::Integer) where {T}
132149
S = pattern(g)
133-
neighbors_with_loops = view(rowvals(S), nzrange(S, v))
134-
return Iterators.filter(!=(v), neighbors_with_loops) # TODO: optimize
150+
neighbors_v = view(rowvals(S), nzrange(S, v))
151+
return neighbors_v
135152
end
136153

137154
function degree(g::AdjacencyGraph, v::Integer)

src/interface.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ function coloring(
245245
spzeros(T, n, n) SparseMatrixCSC(Aᵀ)
246246
SparseMatrixCSC(A) spzeros(T, m, m)
247247
] # TODO: slow
248-
ag = AdjacencyGraph(A_and_Aᵀ)
248+
ag = AdjacencyGraph(A_and_Aᵀ; has_diagonal=false)
249249
if decompression == :direct
250250
color, star_set = star_coloring(ag, algo.order; postprocessing=algo.postprocessing)
251251
symmetric_result = StarSetColoringResult(A_and_Aᵀ, ag, color, star_set)

src/result.jl

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -295,23 +295,25 @@ function TreeSetColoringResult(
295295
decompression_eltype::Type{R},
296296
) where {R}
297297
(; vertices_by_tree, reverse_bfs_orders) = tree_set
298-
S = ag.S
298+
(; S) = ag
299299
nvertices = length(color)
300300
group = group_by_color(color)
301+
rv = rowvals(S)
301302

302303
# Vector for the decompression of the diagonal coefficients
303304
diagonal_indices = Int[]
304305
diagonal_nzind = Int[]
305306
ndiag = 0
306307

307-
rv = rowvals(S)
308-
for j in axes(S, 2)
309-
for k in nzrange(S, j)
310-
i = rv[k]
311-
if i == j
312-
push!(diagonal_indices, i)
313-
push!(diagonal_nzind, k)
314-
ndiag += 1
308+
if has_diagonal(ag)
309+
for j in axes(S, 2)
310+
for k in nzrange(S, j)
311+
i = rv[k]
312+
if i == j
313+
push!(diagonal_indices, i)
314+
push!(diagonal_nzind, k)
315+
ndiag += 1
316+
end
315317
end
316318
end
317319
end
@@ -497,7 +499,7 @@ struct BicoloringResult{
497499
} <: AbstractColoringResult{:nonsymmetric,:bidirectional,decompression}
498500
"matrix that was colored"
499501
A::M
500-
"adjacency graph that was used for coloring (constructed from the bipartite graph)"
502+
"augmented adjacency graph that was used for bicoloring"
501503
abg::G
502504
"one integer color for each column"
503505
column_color::Vector{Int}
@@ -507,7 +509,7 @@ struct BicoloringResult{
507509
column_group::V
508510
"color groups for rows"
509511
row_group::V
510-
"result for the coloring of the symmetric 2x2 block matrix"
512+
"result for the coloring of the symmetric 2 x 2 block matrix"
511513
symmetric_result::SR
512514
"column color to index"
513515
col_color_ind::Dict{Int,Int}

0 commit comments

Comments
 (0)