Skip to content

Commit 7505876

Browse files
committed
Backport the modifications of Guillaume
1 parent 8738fdd commit 7505876

5 files changed

Lines changed: 77 additions & 81 deletions

File tree

src/coloring.jl

Lines changed: 40 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -82,18 +82,18 @@ function star_coloring(g::AdjacencyGraph, order::AbstractOrder, postprocessing::
8282
nv = nb_vertices(g)
8383
ne = nb_edges(g)
8484
color = zeros(Int, nv)
85-
forbidden_colors = zeros(Int, nv)
86-
edge_to_index = build_edge_to_index(S, forbidden_colors)
85+
forbidden_colors = g.vertex_buffer # Vector of length nv
86+
fill!(forbidden_colors, 0)
8787
first_neighbor = fill((0, 0, 0), nv) # at first no neighbors have been encountered
8888
treated = zeros(Int, nv)
8989
star = Vector{Int}(undef, ne)
9090
hub = Int[] # one hub for each star, including the trivial ones
9191
vertices_in_order = vertices(g, order)
9292

9393
for v in vertices_in_order
94-
for (iw, w) in enumerate(neighbors2(g, v))
95-
(v == w || iszero(color[w])) && continue
96-
index_vw = edge_to_index[S.colptr[v] + iw - 1]
94+
for (w, index_vw) in neighbors_with_edge_indices(g, v)
95+
!has_diagonal(g) || (v == w && continue)
96+
iszero(color[w]) && continue
9797
forbidden_colors[color[w]] = v
9898
(p, q, _) = first_neighbor[color[w]]
9999
if p == v # Case 1
@@ -105,9 +105,9 @@ function star_coloring(g::AdjacencyGraph, order::AbstractOrder, postprocessing::
105105
_treat!(treated, forbidden_colors, g, v, w, color)
106106
else
107107
first_neighbor[color[w]] = (v, w, index_vw)
108-
for (ix, x) in enumerate(neighbors2(g, w))
109-
(w == x || x == v || iszero(color[x])) && continue
110-
index_wx = edge_to_index[S.colptr[w] + ix - 1]
108+
for (x, index_wx) in neighbors_with_edge_indices(g, w)
109+
!has_diagonal(g) || (w == x && continue)
110+
(x == v || iszero(color[x])) && continue
111111
if x == hub[star[index_wx]] # potential Case 2 (which is always false for trivial stars with two vertices, since the associated hub is negative)
112112
forbidden_colors[color[x]] = v
113113
end
@@ -120,14 +120,13 @@ function star_coloring(g::AdjacencyGraph, order::AbstractOrder, postprocessing::
120120
break
121121
end
122122
end
123-
_update_stars!(star, hub, g, v, color, first_neighbor, edge_to_index)
123+
_update_stars!(star, hub, g, v, color, first_neighbor)
124124
end
125125
star_set = StarSet(star, hub)
126126
if postprocessing
127-
# Reuse the vector forbidden_colors to compute offsets during post-processing
128-
postprocess!(color, star_set, g, forbidden_colors, edge_to_index)
127+
postprocess!(color, star_set, g)
129128
end
130-
return color, star_set, edge_to_index
129+
return color, star_set
131130
end
132131

133132
function _treat!(
@@ -141,6 +140,7 @@ function _treat!(
141140
color::AbstractVector{<:Integer},
142141
)
143142
for x in neighbors(g, w)
143+
!has_diagonal(g) || (w == x && continue)
144144
iszero(color[x]) && continue
145145
forbidden_colors[color[x]] = v
146146
end
@@ -157,17 +157,15 @@ function _update_stars!(
157157
v::Integer,
158158
color::AbstractVector{<:Integer},
159159
first_neighbor::AbstractVector{<:Tuple},
160-
edge_to_index::AbstractVector{<:Integer},
161160
)
162161
S = pattern(g)
163-
for (iw, w) in enumerate(neighbors2(g, v))
164-
(v == w || iszero(color[w])) && continue
165-
index_vw = edge_to_index[S.colptr[v] + iw - 1]
162+
for (w, index_vw) in neighbors_with_edge_indices(g, v)
163+
!has_diagonal(g) || (v == w && continue)
164+
iszero(color[w]) && continue
166165
x_exists = false
167-
for (ix, x) in enumerate(neighbors2(g, w))
168-
(w == x) && continue
166+
for (x, index_wx) in neighbors_with_edge_indices(g, w)
167+
!has_diagonal(g) || (w == x && continue)
169168
if x != v && color[x] == color[v] # vw, wx ∈ E
170-
index_wx = edge_to_index[S.colptr[w] + ix - 1]
171169
star_wx = star[index_wx]
172170
hub[star_wx] = w # this may already be true
173171
star[index_vw] = star_wx
@@ -235,24 +233,26 @@ function acyclic_coloring(g::AdjacencyGraph, order::AbstractOrder, postprocessin
235233
nv = nb_vertices(g)
236234
ne = nb_edges(g)
237235
color = zeros(Int, nv)
238-
forbidden_colors = zeros(Int, nv)
239-
edge_to_index = build_edge_to_index(S, forbidden_colors)
236+
forbidden_colors = g.vertex_buffer # Vector of length nv
237+
fill!(forbidden_colors, 0)
240238
first_neighbor = fill((0, 0, 0), nv) # at first no neighbors have been encountered
241239
first_visit_to_tree = fill((0, 0), ne)
242240
forest = Forest{Int}(ne)
243241
vertices_in_order = vertices(g, order)
244242

245243
for v in vertices_in_order
246244
for w in neighbors(g, v)
245+
!has_diagonal(g) || (v == w && continue)
247246
iszero(color[w]) && continue
248247
forbidden_colors[color[w]] = v
249248
end
250249
for w in neighbors(g, v)
250+
!has_diagonal(g) || (v == w && continue)
251251
iszero(color[w]) && continue
252-
for (ix, x) in enumerate(neighbors2(g, w))
253-
(w == x || iszero(color[x])) && continue
252+
for (x, index_wx) in neighbors_with_edge_indices(g, w)
253+
!has_diagonal(g) || (w == x && continue)
254+
iszero(color[x]) && continue
254255
if forbidden_colors[color[x]] != v
255-
index_wx = edge_to_index[S.colptr[w] + ix - 1]
256256
_prevent_cycle!(
257257
v,
258258
w,
@@ -272,28 +272,27 @@ function acyclic_coloring(g::AdjacencyGraph, order::AbstractOrder, postprocessin
272272
break
273273
end
274274
end
275-
for (iw, w) in enumerate(neighbors2(g, v)) # grow two-colored stars around the vertex v
276-
(v == w || iszero(color[w])) && continue
277-
index_vw = edge_to_index[S.colptr[v] + iw - 1]
275+
for (w, index_vw) in neighbors_with_edge_indices(g, v) # grow two-colored stars around the vertex v
276+
!has_diagonal(g) || (v == w && continue)
277+
iszero(color[w]) && continue
278278
_grow_star!(v, w, index_vw, color, first_neighbor, forest)
279279
end
280-
for (iw, w) in enumerate(neighbors2(g, v))
281-
(v == w || iszero(color[w])) && continue
282-
for (ix, x) in enumerate(neighbors2(g, w))
283-
(w == x || x == v || iszero(color[x])) && continue
280+
for (w, index_vw) in neighbors_with_edge_indices(g, v)
281+
!has_diagonal(g) || (v == w && continue)
282+
iszero(color[w]) && continue
283+
for (x, index_wx) in neighbors_with_edge_indices(g, w)
284+
!has_diagonal(g) || (w == x && continue)
285+
(x == v || iszero(color[x])) && continue
284286
if color[x] == color[v]
285-
index_vw = edge_to_index[S.colptr[v] + iw - 1]
286-
index_wx = edge_to_index[S.colptr[w] + ix - 1]
287287
_merge_trees!(v, w, x, index_vw, index_wx, forest) # merge trees T₁ ∋ vw and T₂ ∋ wx if T₁ != T₂
288288
end
289289
end
290290
end
291291
end
292292

293-
tree_set = TreeSet(g, forest, edge_to_index)
293+
tree_set = TreeSet(g, forest)
294294
if postprocessing
295-
# Reuse the vector forbidden_colors to compute offsets during post-processing
296-
postprocess!(color, tree_set, g, forbidden_colors, edge_to_index)
295+
postprocess!(color, tree_set, g)
297296
end
298297
return color, tree_set
299298
end
@@ -374,8 +373,9 @@ struct TreeSet
374373
is_star::Vector{Bool}
375374
end
376375

377-
function TreeSet(g::AdjacencyGraph, forest::Forest{Int}, edge_to_index::Vector{Int})
376+
function TreeSet(g::AdjacencyGraph, forest::Forest{Int})
378377
S = pattern(g)
378+
edge_to_index = edge_indices(g)
379379
nv = nb_vertices(g)
380380
nt = forest.num_trees
381381

@@ -520,10 +520,9 @@ function postprocess!(
520520
color::AbstractVector{<:Integer},
521521
star_or_tree_set::Union{StarSet,TreeSet},
522522
g::AdjacencyGraph,
523-
offsets::Vector{Int},
524-
edge_to_index::Vector{Int},
525523
)
526-
(; S) = g
524+
S = pattern(g)
525+
edge_to_index = edge_indices(g)
527526
# flag which colors are actually used during decompression
528527
nb_colors = maximum(color)
529528
color_used = zeros(Bool, nb_colors)
@@ -626,6 +625,7 @@ function postprocess!(
626625

627626
# if at least one of the colors is useless, modify the color assignments of vertices
628627
if any(!, color_used)
628+
offsets = g.vertex_buffer
629629
num_colors_useless = 0
630630

631631
# determine what are the useless colors and compute the offsets

src/graph.jl

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ SparseArrays.nzrange(S::SparsityPatternCSC, j::Integer) = S.colptr[j]:(S.colptr[
3636
3737
Return a [`SparsityPatternCSC`](@ref) corresponding to the transpose of `S`.
3838
"""
39-
function Base.transpose(S::SparsityPatternCSC{T}) where {T}
39+
function Base.transpose(S::SparsityPatternCSC{T}) where {T<:Integer}
4040
m, n = size(S)
4141
nnzA = nnz(S)
4242
A_colptr = S.colptr
@@ -98,10 +98,10 @@ end
9898
9999
Return a [`SparsityPatternCSC`](@ref) corresponding to the matrix `[0 Aᵀ; A 0]`, with a minimum of allocations.
100100
"""
101-
bidirectional_pattern(A::AbstractMatrix; symmetric_pattern) =
101+
bidirectional_pattern(A::AbstractMatrix; symmetric_pattern::Bool) =
102102
bidirectional_pattern(SparsityPatternCSC(SparseMatrixCSC(A)); symmetric_pattern)
103103

104-
function bidirectional_pattern(S::SparsityPatternCSC; symmetric_pattern)
104+
function bidirectional_pattern(S::SparsityPatternCSC; symmetric_pattern::Bool)
105105
m, n = size(S)
106106
p = m + n
107107
nnzS = nnz(S)
@@ -165,11 +165,10 @@ end
165165

166166
# Note that we don't need to do that for bicoloring,
167167
# we can build that in the same time than the transposed sparsity pattern of A
168-
function build_edge_to_index(S::SparsityPatternCSC, forbidden_colors::Vector{Int})
168+
function build_edge_to_index(S::SparsityPatternCSC{T}) where {T<:Integer}
169169
# edge_to_index gives an index for each edge
170-
edge_to_index = Vector{Int}(undef, nnz(S))
171-
# use forbidden_colors (or color) for the offsets of each column
172-
offsets = forbidden_colors
170+
edge_to_index = Vector{T}(undef, nnz(S))
171+
offsets = zeros(T, S.n)
173172
counter = 0
174173
rvS = rowvals(S)
175174
for j in axes(S, 2)
@@ -187,8 +186,7 @@ function build_edge_to_index(S::SparsityPatternCSC, forbidden_colors::Vector{Int
187186
end
188187
end
189188
end
190-
fill!(offsets, 0)
191-
return edge_to_index
189+
return edge_to_index, offsets
192190
end
193191

194192
## Adjacency graph
@@ -205,22 +203,27 @@ The adjacency graph of a symmetric matrix `A ∈ ℝ^{n × n}` is `G(A) = (V, E)
205203
206204
# Constructors
207205
208-
AdjacencyGraph(A::SparseMatrixCSC)
206+
AdjacencyGraph(A::SparseMatrixCSC; has_diagonal::Bool=true)
209207
210208
# Fields
211209
212210
- `S::SparsityPatternCSC{T}`: Underlying sparsity pattern, whose diagonal is empty whenever `has_diagonal` is `false`
211+
- `edge_to_index::Vector{T}`: A vector mapping each nonzero of `S` to a unique edge index (ignoring diagonal and accounting for symmetry, so that `(i, j)` and `(j, i)` get the same index)
212+
- `vertex_buffer::Vector{T}`: A buffer of length `S.n`, which is the number of vertices in the graph
213213
214214
# References
215215
216216
> [_What Color Is Your Jacobian? SparsityPatternCSC Coloring for Computing Derivatives_](https://epubs.siam.org/doi/10.1137/S0036144504444711), Gebremedhin et al. (2005)
217217
"""
218218
struct AdjacencyGraph{T,has_diagonal}
219219
S::SparsityPatternCSC{T}
220+
edge_to_index::Vector{T}
221+
vertex_buffer::Vector{T}
220222
end
221223

222224
function AdjacencyGraph(S::SparsityPatternCSC{T}; has_diagonal::Bool=true) where {T}
223-
return AdjacencyGraph{T,has_diagonal}(S)
225+
edge_to_index, vertex_buffer = build_edge_to_index(S)
226+
return AdjacencyGraph{T,has_diagonal}(S, edge_to_index, vertex_buffer)
224227
end
225228

226229
function AdjacencyGraph(A::SparseMatrixCSC; has_diagonal::Bool=true)
@@ -232,30 +235,27 @@ function AdjacencyGraph(A::AbstractMatrix; has_diagonal::Bool=true)
232235
end
233236

234237
pattern(g::AdjacencyGraph) = g.S
238+
edge_indices(g::AdjacencyGraph) = g.edge_to_index
235239
nb_vertices(g::AdjacencyGraph) = pattern(g).n
236240
vertices(g::AdjacencyGraph) = 1:nb_vertices(g)
241+
has_diagonal(::AdjacencyGraph{T,hd}) where {T,hd} = hd
237242

238-
has_diagonal(::AdjacencyGraph{T,hl}) where {T,hl} = hl
239-
240-
function neighbors(g::AdjacencyGraph{T,true}, v::Integer) where {T}
241-
S = pattern(g)
242-
neighbors_v = view(rowvals(S), nzrange(S, v))
243-
return Iterators.filter(!=(v), neighbors_v) # TODO: optimize
244-
end
245-
246-
function neighbors(g::AdjacencyGraph{T,false}, v::Integer) where {T}
243+
function neighbors(g::AdjacencyGraph, v::Integer)
247244
S = pattern(g)
248245
neighbors_v = view(rowvals(S), nzrange(S, v))
249246
return neighbors_v
250247
end
251248

252-
function neighbors2(g::AdjacencyGraph, v::Integer)
249+
function neighbors_with_edge_indices(g::AdjacencyGraph, v::Integer)
253250
S = pattern(g)
254251
neighbors_v = view(rowvals(S), nzrange(S, v))
255-
return neighbors_v
252+
edges_indices_v = view(edge_indices(g), nzrange(S, v))
253+
return zip(neighbors_v, edges_indices_v)
256254
end
257255

258-
function degree(g::AdjacencyGraph, v::Integer)
256+
degree(g::AdjacencyGraph{T,false}, v::Integer) where {T} = g.S.colptr[v + 1] - g.S.colptr[v]
257+
258+
function degree(g::AdjacencyGraph{T,true}, v::Integer) where {T}
259259
d = 0
260260
for u in neighbors(g, v)
261261
if u != v
@@ -265,12 +265,12 @@ function degree(g::AdjacencyGraph, v::Integer)
265265
return d
266266
end
267267

268-
function nb_edges(g::AdjacencyGraph)
268+
nb_edges(g::AdjacencyGraph{T,false}) where {T} = nnz(g.S) ÷ 2
269+
270+
function nb_edges(g::AdjacencyGraph{T,true}) where {T}
269271
ne = 0
270272
for v in vertices(g)
271-
for u in neighbors(g, v)
272-
ne += 1
273-
end
273+
ne += degree(g, v)
274274
end
275275
return ne ÷ 2
276276
end
@@ -280,6 +280,7 @@ minimum_degree(g::AdjacencyGraph) = minimum(Base.Fix1(degree, g), vertices(g))
280280

281281
function has_neighbor(g::AdjacencyGraph, v::Integer, u::Integer)
282282
for w in neighbors(g, v)
283+
!has_diagonal(g) || (v == w && continue)
283284
if w == u
284285
return true
285286
end
@@ -314,7 +315,7 @@ A `BipartiteGraph` has two sets of vertices, one for the rows of `A` (which we c
314315
315316
# Constructors
316317
317-
BipartiteGraph(A::SparseMatrixCSC; symmetric_pattern=false)
318+
BipartiteGraph(A::SparseMatrixCSC; symmetric_pattern::Bool=false)
318319
319320
When `symmetric_pattern` is `true`, this construction is more efficient.
320321

src/interface.jl

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -266,9 +266,9 @@ function _coloring(
266266
symmetric_pattern::Bool,
267267
)
268268
ag = AdjacencyGraph(A)
269-
color, star_set, edge_to_index = star_coloring(ag, algo.order, algo.postprocessing)
269+
color, star_set = star_coloring(ag, algo.order, algo.postprocessing)
270270
if speed_setting isa WithResult
271-
return StarSetColoringResult(A, ag, color, edge_to_index, star_set)
271+
return StarSetColoringResult(A, ag, color, star_set)
272272
else
273273
return color
274274
end
@@ -301,11 +301,9 @@ function _coloring(
301301
) where {R}
302302
A_and_Aᵀ = bidirectional_pattern(A; symmetric_pattern)
303303
ag = AdjacencyGraph(A_and_Aᵀ; has_diagonal=false)
304-
color, star_set, edge_to_index = star_coloring(ag, algo.order, algo.postprocessing)
304+
color, star_set = star_coloring(ag, algo.order, algo.postprocessing)
305305
if speed_setting isa WithResult
306-
symmetric_result = StarSetColoringResult(
307-
A_and_Aᵀ, ag, color, edge_to_index, star_set
308-
)
306+
symmetric_result = StarSetColoringResult(A_and_Aᵀ, ag, color, star_set)
309307
return BicoloringResult(A, ag, symmetric_result, R)
310308
else
311309
row_color, column_color, _ = remap_colors(color, maximum(color), size(A)...)

src/order.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ function vertices(
262262
u = pop_next_candidate!(db; direction)
263263
direction == :low2high ? push!(π, u) : pushfirst!(π, u)
264264
for v in neighbors(g, u)
265+
!has_diagonal(g) || (u == v && continue)
265266
already_ordered(db, v) && continue
266267
update_bucket!(db, v; degtype, direction)
267268
end

src/result.jl

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -239,14 +239,10 @@ struct StarSetColoringResult{M<:AbstractMatrix,G<:AdjacencyGraph,V} <:
239239
end
240240

241241
function StarSetColoringResult(
242-
A::AbstractMatrix,
243-
ag::AdjacencyGraph,
244-
color::Vector{Int},
245-
edge_to_index::Vector{Int},
246-
star_set::StarSet,
242+
A::AbstractMatrix, ag::AdjacencyGraph, color::Vector{Int}, star_set::StarSet
247243
)
248-
# Reuse edge_to_index to store the compressed indices for decompression
249-
compressed_indices = edge_to_index
244+
# Reuse ag.edge_to_index to store the compressed indices for decompression
245+
compressed_indices = edge_indices(ag)
250246

251247
(; star, hub) = star_set
252248
S = pattern(ag)

0 commit comments

Comments
 (0)