Skip to content

Commit 98184f5

Browse files
gdalleamontoison
authored andcommitted
feat: allow exact reproduction of ColPack's dynamic orders
1 parent ac3da1a commit 98184f5

2 files changed

Lines changed: 93 additions & 18 deletions

File tree

src/order.jl

Lines changed: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ function vertices(bg::BipartiteGraph{T}, ::Val{side}, ::LargestFirst) where {T,s
105105
end
106106

107107
"""
108-
DynamicDegreeBasedOrder{degtype,direction}
108+
DynamicDegreeBasedOrder{degtype,direction}(; reproduce_colpack=false)
109109
110110
Instance of [`AbstractOrder`](@ref) which sorts vertices using a dynamically computed degree.
111111
@@ -117,6 +117,10 @@ Instance of [`AbstractOrder`](@ref) which sorts vertices using a dynamically com
117117
- `degtype::Symbol`: can be `:forward` (for the forward degree) or `:back` (for the back degree)
118118
- `direction::Symbol`: can be `:low2high` (if the order is defined from lowest to highest, i.e. `1` to `n`) or `:high2low` (if the order is defined from highest to lowest, i.e. `n` to `1`)
119119
120+
# Settings
121+
122+
- `reproduce_colpack::Bool`: whether to manage the buckets in the same way as the original ColPack implementation. When `reproduce_colpack=true`, we always append and remove vertices from the end of a bucket, which incurs a large performance penalty because every modification requires a circular permutation of the corresponding bucket. This setting is mostly for the purpose of reproducing past research results which rely on implementation details.
123+
120124
# Concrete variants
121125
122126
- [`IncidenceDegree`](@ref)
@@ -127,17 +131,28 @@ Instance of [`AbstractOrder`](@ref) which sorts vertices using a dynamically com
127131
128132
- [_ColPack: Software for graph coloring and related problems in scientific computing_](https://dl.acm.org/doi/10.1145/2513109.2513110), Gebremedhin et al. (2013), Section 5
129133
"""
130-
struct DynamicDegreeBasedOrder{degtype,direction} <: AbstractOrder end
134+
struct DynamicDegreeBasedOrder{degtype,direction} <: AbstractOrder
135+
reproduce_colpack::Bool
136+
end
137+
138+
function DynamicDegreeBasedOrder{degtype,direction}(;
139+
reproduce_colpack::Bool=false
140+
) where {degtype,direction}
141+
return DynamicDegreeBasedOrder{degtype,direction}(reproduce_colpack)
142+
end
131143

132144
struct DegreeBuckets{T}
133145
degrees::Vector{T}
134146
bucket_storage::Vector{T}
135147
bucket_low::Vector{T}
136148
bucket_high::Vector{T}
137149
positions::Vector{T}
150+
reproduce_colpack::Bool
138151
end
139152

140-
function DegreeBuckets(::Type{T}, degrees::Vector{T}, dmax::Integer) where {T}
153+
function DegreeBuckets(
154+
::Type{T}, degrees::Vector{T}, dmax::Integer; reproduce_colpack::Bool
155+
) where {T}
141156
# number of vertices per degree class
142157
deg_count = zeros(T, dmax + 1)
143158
for d in degrees
@@ -157,7 +172,9 @@ function DegreeBuckets(::Type{T}, degrees::Vector{T}, dmax::Integer) where {T}
157172
bucket_storage[positions[v]] = v
158173
deg_count[d + 1] -= 1
159174
end
160-
return DegreeBuckets(degrees, bucket_storage, bucket_low, bucket_high, positions)
175+
return DegreeBuckets(
176+
degrees, bucket_storage, bucket_low, bucket_high, positions, reproduce_colpack
177+
)
161178
end
162179

163180
maxdeg(db::DegreeBuckets) = length(db.bucket_low) - 1
@@ -205,8 +222,42 @@ function pop_next_candidate!(db::DegreeBuckets; direction::Symbol)
205222
return candidate
206223
end
207224

225+
function rotate_bucket_left!(db::DegreeBuckets, d::Integer)
226+
(; bucket_storage, bucket_high, bucket_low, positions) = db
227+
low, high = bucket_low[d + 1], bucket_high[d + 1]
228+
# remember first element v
229+
v = bucket_storage[low]
230+
# shift everyone else one index down
231+
for i in (low + 1):high
232+
w = bucket_storage[i]
233+
bucket_storage[i - 1] = w
234+
positions[w] = i - 1
235+
end
236+
# put v back at the end
237+
bucket_storage[high] = v
238+
positions[v] = high
239+
return nothing
240+
end
241+
242+
function rotate_bucket_right!(db::DegreeBuckets, d::Integer)
243+
(; bucket_storage, bucket_high, bucket_low, positions) = db
244+
low, high = bucket_low[d + 1], bucket_high[d + 1]
245+
# remember last element v
246+
v = bucket_storage[high]
247+
# shift everyone else one index up
248+
for i in (high - 1):-1:low
249+
w = bucket_storage[i]
250+
bucket_storage[i + 1] = w
251+
positions[w] = i + 1
252+
end
253+
# put v back at the start
254+
bucket_storage[low] = v
255+
positions[v] = low
256+
return nothing
257+
end
258+
208259
function update_bucket!(db::DegreeBuckets, v::Integer; degtype, direction)
209-
(; degrees, bucket_storage, bucket_low, bucket_high, positions) = db
260+
(; degrees, bucket_storage, bucket_low, bucket_high, positions, reproduce_colpack) = db
210261
d, p = degrees[v], positions[v]
211262
low, high = bucket_low[d + 1], bucket_high[d + 1]
212263
# select previous or next bucket for the move
@@ -227,11 +278,27 @@ function update_bucket!(db::DegreeBuckets, v::Integer; degtype, direction)
227278
# update v's stats
228279
degrees[v] = d_new
229280
positions[v] = low_new - 1
281+
if reproduce_colpack
282+
# move v from start to end of the next bucket, preserving order
283+
rotate_bucket_left!(db, d_new) # expensive
284+
end
230285
else
231-
# move the vertex w located at the start of the current bucket to v's position (!= ColPack)
232-
w = bucket_storage[low]
233-
bucket_storage[p] = w
234-
positions[w] = p
286+
if reproduce_colpack
287+
# move the vertex w located at the end of the current bucket to v's position
288+
w = bucket_storage[high]
289+
bucket_storage[p] = w
290+
positions[w] = p
291+
# explicitly put v at the end
292+
bucket_storage[high] = v
293+
positions[v] = high
294+
# move v from end to start of the current bucket, preserving order
295+
rotate_bucket_right!(db, d) # expensive
296+
else
297+
# move the vertex w located at the start of the current bucket to v's position (!= ColPack)
298+
w = bucket_storage[low]
299+
bucket_storage[p] = w
300+
positions[w] = p
301+
end
235302
# shrink current bucket from the left
236303
# morally we put v at the start and then ignore it
237304
bucket_low[d + 1] += 1
@@ -249,14 +316,16 @@ function update_bucket!(db::DegreeBuckets, v::Integer; degtype, direction)
249316
end
250317

251318
function vertices(
252-
g::AdjacencyGraph{T}, ::DynamicDegreeBasedOrder{degtype,direction}
319+
g::AdjacencyGraph{T}, order::DynamicDegreeBasedOrder{degtype,direction}
253320
) where {T<:Integer,degtype,direction}
254321
if degree_increasing(; degtype, direction)
255322
degrees = zeros(T, nb_vertices(g))
256323
else
257324
degrees = T[degree(g, v) for v in vertices(g)]
258325
end
259-
db = DegreeBuckets(T, degrees, maximum_degree(g))
326+
db = DegreeBuckets(
327+
T, degrees, maximum_degree(g); reproduce_colpack=order.reproduce_colpack
328+
)
260329
π = T[]
261330
sizehint!(π, nb_vertices(g))
262331
for _ in 1:nb_vertices(g)
@@ -272,7 +341,7 @@ function vertices(
272341
end
273342

274343
function vertices(
275-
g::BipartiteGraph{T}, ::Val{side}, ::DynamicDegreeBasedOrder{degtype,direction}
344+
g::BipartiteGraph{T}, ::Val{side}, order::DynamicDegreeBasedOrder{degtype,direction}
276345
) where {T<:Integer,side,degtype,direction}
277346
other_side = 3 - side
278347
# compute dist-2 degrees in an optimized way
@@ -294,7 +363,7 @@ function vertices(
294363
degrees = degrees_dist2
295364
end
296365
maxd2 = maximum(degrees_dist2)
297-
db = DegreeBuckets(T, degrees, maxd2)
366+
db = DegreeBuckets(T, degrees, maxd2; reproduce_colpack=order.reproduce_colpack)
298367
π = T[]
299368
sizehint!(π, n)
300369
visited = falses(n)
@@ -318,7 +387,7 @@ function vertices(
318387
end
319388

320389
"""
321-
IncidenceDegree()
390+
IncidenceDegree(; reproduce_colpack=false)
322391
323392
Instance of [`AbstractOrder`](@ref) which sorts vertices from lowest to highest using the dynamic back degree.
324393
@@ -332,7 +401,7 @@ Instance of [`AbstractOrder`](@ref) which sorts vertices from lowest to highest
332401
const IncidenceDegree = DynamicDegreeBasedOrder{:back,:low2high}
333402

334403
"""
335-
SmallestLast()
404+
SmallestLast(; reproduce_colpack=false)
336405
337406
Instance of [`AbstractOrder`](@ref) which sorts vertices from highest to lowest using the dynamic back degree.
338407
@@ -346,7 +415,7 @@ Instance of [`AbstractOrder`](@ref) which sorts vertices from highest to lowest
346415
const SmallestLast = DynamicDegreeBasedOrder{:back,:high2low}
347416

348417
"""
349-
DynamicLargestFirst()
418+
DynamicLargestFirst(; reproduce_colpack=false)
350419
351420
Instance of [`AbstractOrder`](@ref) which sorts vertices from lowest to highest using the dynamic forward degree.
352421

test/order.jl

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,14 @@ end;
8989
end;
9090

9191
@testset "Dynamic degree-based orders" begin
92-
@testset "$order" for order in
93-
[SmallestLast(), IncidenceDegree(), DynamicLargestFirst()]
92+
@testset "$order" for order in [
93+
SmallestLast(),
94+
SmallestLast(; reproduce_colpack=true),
95+
IncidenceDegree(),
96+
IncidenceDegree(; reproduce_colpack=true),
97+
DynamicLargestFirst(),
98+
DynamicLargestFirst(; reproduce_colpack=true),
99+
]
94100
@testset "AdjacencyGraph" begin
95101
for (n, p) in Iterators.product(20:20:100, 0.0:0.1:0.2)
96102
yield()

0 commit comments

Comments
 (0)