Skip to content

Commit b1c86db

Browse files
committed
Star coloring without dictionary
1 parent 848a6f3 commit b1c86db

3 files changed

Lines changed: 54 additions & 28 deletions

File tree

src/SparseMatrixColorings.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ using SparseArrays:
4040
nzrange,
4141
rowvals,
4242
sparse,
43-
spzeros
43+
spzeros,
44+
triu
4445

4546
include("graph.jl")
4647
include("order.jl")

src/coloring.jl

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ function star_coloring(g::AdjacencyGraph, order::AbstractOrder; postprocessing::
8484
forbidden_colors = zeros(Int, nv)
8585
first_neighbor = fill((0, 0), nv) # at first no neighbors have been encountered
8686
treated = zeros(Int, nv)
87-
star = Dict{Tuple{Int,Int},Int}()
87+
star = Vector{Int}(undef, ne)
8888
sizehint!(star, ne)
8989
hub = Int[] # one hub for each star, including the trivial ones
9090
nb_spokes = Int[] # number of spokes for each star
@@ -107,7 +107,8 @@ function star_coloring(g::AdjacencyGraph, order::AbstractOrder; postprocessing::
107107
for x in neighbors(g, w)
108108
(x == v || iszero(color[x])) && continue
109109
wx = _sort(w, x)
110-
if x == hub[star[wx]] # potential Case 2 (which is always false for trivial stars with two vertices, since the associated hub is negative)
110+
index_wx = g.M[wx...]
111+
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)
111112
forbidden_colors[color[x]] = v
112113
end
113114
end
@@ -121,7 +122,7 @@ function star_coloring(g::AdjacencyGraph, order::AbstractOrder; postprocessing::
121122
end
122123
_update_stars!(star, hub, nb_spokes, g, v, color, first_neighbor)
123124
end
124-
star_set = StarSet(star, hub, nb_spokes)
125+
star_set = StarSet(g, star, hub, nb_spokes)
125126
if postprocessing
126127
# Reuse the vector forbidden_colors to compute offsets during post-processing
127128
postprocess!(color, star_set, g, forbidden_colors)
@@ -140,33 +141,44 @@ $TYPEDFIELDS
140141
"""
141142
struct StarSet
142143
"a mapping from edges (pair of vertices) to their star index"
143-
star::Dict{Tuple{Int,Int},Int}
144+
star::Vector{Int}
144145
"a mapping from star indices to their hub (undefined hubs for single-edge stars are the negative value of one of the vertices, picked arbitrarily)"
145146
hub::Vector{Int}
146147
"a mapping from star indices to the vector of their spokes"
147148
spokes::Vector{Vector{Int}}
149+
M::SparseMatrixCSC{Int,Int}
148150
end
149151

150-
function StarSet(star::Dict{Tuple{Int,Int},Int}, hub::Vector{Int}, nb_spokes::Vector{Int})
152+
function StarSet(
153+
g::AdjacencyGraph{Int}, star::Vector{Int}, hub::Vector{Int}, nb_spokes::Vector{Int}
154+
)
151155
# Create a list of spokes for each star, preallocating their sizes based on nb_spokes
152156
spokes = [Vector{Int}(undef, ns) for ns in nb_spokes]
153157

154158
# Reuse nb_spokes as counters to track the current index while filling the spokes
155159
fill!(nb_spokes, 0)
156160

157-
for ((i, j), s) in pairs(star)
158-
h = abs(hub[s])
159-
nb_spokes[s] += 1
160-
index = nb_spokes[s]
161-
162-
# Assign the non-hub vertex (spoke) to the correct position in spokes
163-
if i == h
164-
spokes[s][index] = j
165-
elseif j == h
166-
spokes[s][index] = i
161+
nv = nb_vertices(g)
162+
k = 0
163+
rows = rowvals(g.M)
164+
for j in 1:nv
165+
for p in nzrange(g.M, j)
166+
i = rows[p]
167+
k += 1
168+
s = star[k]
169+
h = abs(hub[s])
170+
nb_spokes[s] += 1
171+
index = nb_spokes[s]
172+
173+
# Assign the non-hub vertex (spoke) to the correct position in spokes
174+
if i == h
175+
spokes[s][index] = j
176+
elseif j == h
177+
spokes[s][index] = i
178+
end
167179
end
168180
end
169-
return StarSet(star, hub, spokes)
181+
return StarSet(star, hub, spokes, g.M)
170182
end
171183

172184
_sort(u, v) = (min(u, v), max(u, v))
@@ -191,7 +203,7 @@ end
191203

192204
function _update_stars!(
193205
# modified
194-
star::Dict{<:Tuple,<:Integer},
206+
star::AbstractVector{<:Integer},
195207
hub::AbstractVector{<:Integer},
196208
nb_spokes::AbstractVector{<:Integer},
197209
# not modified
@@ -203,14 +215,16 @@ function _update_stars!(
203215
for w in neighbors(g, v)
204216
iszero(color[w]) && continue
205217
vw = _sort(v, w)
218+
index_vw = g.M[vw...]
206219
x_exists = false
207220
for x in neighbors(g, w)
208221
if x != v && color[x] == color[v] # vw, wx ∈ E
209222
wx = _sort(w, x)
210-
star_wx = star[wx]
223+
index_wx = g.M[wx...]
224+
star_wx = star[index_wx]
211225
hub[star_wx] = w # this may already be true
212226
nb_spokes[star_wx] += 1
213-
star[vw] = star_wx
227+
star[index_vw] = star_wx
214228
x_exists = true
215229
break
216230
end
@@ -219,14 +233,15 @@ function _update_stars!(
219233
(p, q) = first_neighbor[color[w]]
220234
if p == v && q != w # vw, vq ∈ E and color[w] = color[q]
221235
vq = _sort(v, q)
222-
star_vq = star[vq]
236+
index_vq = g.M[vq...]
237+
star_vq = star[index_vq]
223238
hub[star_vq] = v # this may already be true
224239
nb_spokes[star_vq] += 1
225-
star[vw] = star_vq
240+
star[index_vw] = star_vq
226241
else # vw forms a new star
227242
push!(hub, -max(v, w)) # star is trivial (composed only of two vertices) so we set the hub to a negative value, but it allows us to choose one of the two vertices
228243
push!(nb_spokes, 1)
229-
star[vw] = length(hub)
244+
star[index_vw] = length(hub)
230245
end
231246
end
232247
end
@@ -251,7 +266,7 @@ This function corresponds to algorithm `DirectRecover2` in the paper.
251266
function symmetric_coefficient(
252267
i::Integer, j::Integer, color::AbstractVector{<:Integer}, star_set::StarSet
253268
)
254-
(; star, hub) = star_set
269+
(; M, star, hub) = star_set
255270
if i == j
256271
# diagonal
257272
return i, color[j]
@@ -260,7 +275,8 @@ function symmetric_coefficient(
260275
# star only contains one triangle
261276
i, j = j, i
262277
end
263-
star_id = star[i, j]
278+
index_ij = M[i, j]
279+
star_id = star[index_ij]
264280
h = abs(hub[star_id])
265281
if h == j
266282
# i is the spoke

src/graph.jl

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,14 +189,23 @@ The adjacency graph of a symmetric matrix `A ∈ ℝ^{n × n}` is `G(A) = (V, E)
189189
"""
190190
struct AdjacencyGraph{T,has_diagonal}
191191
S::SparsityPatternCSC{T}
192+
M::SparseMatrixCSC{T,T}
192193
end
193194

194-
function AdjacencyGraph(S::SparsityPatternCSC{T}; has_diagonal::Bool=true) where {T}
195-
return AdjacencyGraph{T,has_diagonal}(S)
195+
function AdjacencyGraph(
196+
S::SparsityPatternCSC{T}, M::SparseMatrixCSC{T,T}; has_diagonal::Bool=true
197+
) where {T}
198+
return AdjacencyGraph{T,has_diagonal}(S, M)
196199
end
197200

198201
function AdjacencyGraph(A::SparseMatrixCSC; has_diagonal::Bool=true)
199-
return AdjacencyGraph(SparsityPatternCSC(A); has_diagonal)
202+
S = SparsityPatternCSC(A)
203+
M = triu(A, 1)
204+
nnzM = Int(nnz(M))
205+
nzval = collect(Base.OneTo(nnzM))
206+
m, n = size(A)
207+
M = SparseMatrixCSC(m, n, M.colptr, M.rowval, nzval)
208+
return AdjacencyGraph(S, M; has_diagonal)
200209
end
201210

202211
function AdjacencyGraph(A::AbstractMatrix; has_diagonal::Bool=true)

0 commit comments

Comments
 (0)