@@ -104,13 +104,18 @@ function vertices(bg::BipartiteGraph{T}, ::Val{side}, ::LargestFirst) where {T,s
104104 return sort (vertices (bg, Val (side)); by= criterion, rev= true )
105105end
106106
107+ const COLPACK_WARNING = """
108+ !!! danger
109+ The option `reproduce_colpack=true` induces a large slowdown to mirror the original implementation details of ColPack, it should not be used in performance-sensitive applications.
110+ This setting is mostly for the purpose of reproducing past research results which rely on implementation details.
111+ """
112+
107113"""
108- DynamicDegreeBasedOrder{degtype,direction}
114+ DynamicDegreeBasedOrder{degtype,direction}(; reproduce_colpack=false)
109115
110116Instance of [`AbstractOrder`](@ref) which sorts vertices using a dynamically computed degree.
111117
112- !!! danger
113- This order is still experimental and needs more tests, correctness is not yet guaranteed.
118+ This order works by assigning vertices to buckets based on their dynamic degree, and then updating buckets iteratively by transfering vertices between them.
114119
115120# Type parameters
116121
@@ -123,21 +128,43 @@ Instance of [`AbstractOrder`](@ref) which sorts vertices using a dynamically com
123128- [`SmallestLast`](@ref)
124129- [`DynamicLargestFirst`](@ref)
125130
131+ # Settings
132+
133+ - `reproduce_colpack::Bool`: whether to manage the buckets in the exact same way as the original ColPack implementation.
134+ - When `reproduce_colpack=true`, we always append and remove vertices at the end of a bucket (unilateral).
135+ - When `reproduce_colpack=false` (the default), we can append and remove vertices either at the start or at the end of a bucket (bilateral).
136+
137+ Allowing modifications on both sides of a bucket enables storage optimization, with a single fixed-size vector for all buckets instead of one dynamically-sized vector per bucket.
138+ Our implementation is optimized for this bilateral setting, which means we pay a large performance penalty to artificially imitate the unilateral setting.
139+
140+ $COLPACK_WARNING
141+
126142# References
127143
128144- [_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
129145"""
130- struct DynamicDegreeBasedOrder{degtype,direction} <: AbstractOrder end
146+ struct DynamicDegreeBasedOrder{degtype,direction} <: AbstractOrder
147+ reproduce_colpack:: Bool
148+ end
149+
150+ function DynamicDegreeBasedOrder {degtype,direction} (;
151+ reproduce_colpack:: Bool = false
152+ ) where {degtype,direction}
153+ return DynamicDegreeBasedOrder {degtype,direction} (reproduce_colpack)
154+ end
131155
132156struct DegreeBuckets{T}
133157 degrees:: Vector{T}
134158 bucket_storage:: Vector{T}
135159 bucket_low:: Vector{T}
136160 bucket_high:: Vector{T}
137161 positions:: Vector{T}
162+ reproduce_colpack:: Bool
138163end
139164
140- function DegreeBuckets (:: Type{T} , degrees:: Vector{T} , dmax:: Integer ) where {T}
165+ function DegreeBuckets (
166+ :: Type{T} , degrees:: Vector{T} , dmax:: Integer ; reproduce_colpack:: Bool
167+ ) where {T}
141168 # number of vertices per degree class
142169 deg_count = zeros (T, dmax + 1 )
143170 for d in degrees
@@ -157,7 +184,9 @@ function DegreeBuckets(::Type{T}, degrees::Vector{T}, dmax::Integer) where {T}
157184 bucket_storage[positions[v]] = v
158185 deg_count[d + 1 ] -= 1
159186 end
160- return DegreeBuckets (degrees, bucket_storage, bucket_low, bucket_high, positions)
187+ return DegreeBuckets (
188+ degrees, bucket_storage, bucket_low, bucket_high, positions, reproduce_colpack
189+ )
161190end
162191
163192maxdeg (db:: DegreeBuckets ) = length (db. bucket_low) - 1
@@ -205,8 +234,42 @@ function pop_next_candidate!(db::DegreeBuckets; direction::Symbol)
205234 return candidate
206235end
207236
237+ function rotate_bucket_left! (db:: DegreeBuckets , d:: Integer )
238+ (; bucket_storage, bucket_high, bucket_low, positions) = db
239+ low, high = bucket_low[d + 1 ], bucket_high[d + 1 ]
240+ # remember first element v
241+ v = bucket_storage[low]
242+ # shift everyone else one index down
243+ for i in (low + 1 ): high
244+ w = bucket_storage[i]
245+ bucket_storage[i - 1 ] = w
246+ positions[w] = i - 1
247+ end
248+ # put v back at the end
249+ bucket_storage[high] = v
250+ positions[v] = high
251+ return nothing
252+ end
253+
254+ function rotate_bucket_right! (db:: DegreeBuckets , d:: Integer )
255+ (; bucket_storage, bucket_high, bucket_low, positions) = db
256+ low, high = bucket_low[d + 1 ], bucket_high[d + 1 ]
257+ # remember last element v
258+ v = bucket_storage[high]
259+ # shift everyone else one index up
260+ for i in (high - 1 ): - 1 : low
261+ w = bucket_storage[i]
262+ bucket_storage[i + 1 ] = w
263+ positions[w] = i + 1
264+ end
265+ # put v back at the start
266+ bucket_storage[low] = v
267+ positions[v] = low
268+ return nothing
269+ end
270+
208271function update_bucket! (db:: DegreeBuckets , v:: Integer ; degtype, direction)
209- (; degrees, bucket_storage, bucket_low, bucket_high, positions) = db
272+ (; degrees, bucket_storage, bucket_low, bucket_high, positions, reproduce_colpack ) = db
210273 d, p = degrees[v], positions[v]
211274 low, high = bucket_low[d + 1 ], bucket_high[d + 1 ]
212275 # select previous or next bucket for the move
@@ -227,11 +290,27 @@ function update_bucket!(db::DegreeBuckets, v::Integer; degtype, direction)
227290 # update v's stats
228291 degrees[v] = d_new
229292 positions[v] = low_new - 1
293+ if reproduce_colpack
294+ # move v from start to end of the next bucket, preserving order
295+ rotate_bucket_left! (db, d_new) # expensive
296+ end
230297 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
298+ if reproduce_colpack
299+ # move the vertex w located at the end of the current bucket to v's position
300+ w = bucket_storage[high]
301+ bucket_storage[p] = w
302+ positions[w] = p
303+ # explicitly put v at the end
304+ bucket_storage[high] = v
305+ positions[v] = high
306+ # move v from end to start of the current bucket, preserving order
307+ rotate_bucket_right! (db, d) # expensive
308+ else
309+ # move the vertex w located at the start of the current bucket to v's position (!= ColPack)
310+ w = bucket_storage[low]
311+ bucket_storage[p] = w
312+ positions[w] = p
313+ end
235314 # shrink current bucket from the left
236315 # morally we put v at the start and then ignore it
237316 bucket_low[d + 1 ] += 1
@@ -249,14 +328,16 @@ function update_bucket!(db::DegreeBuckets, v::Integer; degtype, direction)
249328end
250329
251330function vertices (
252- g:: AdjacencyGraph{T} , :: DynamicDegreeBasedOrder{degtype,direction}
331+ g:: AdjacencyGraph{T} , order :: DynamicDegreeBasedOrder{degtype,direction}
253332) where {T<: Integer ,degtype,direction}
254333 if degree_increasing (; degtype, direction)
255334 degrees = zeros (T, nb_vertices (g))
256335 else
257336 degrees = T[degree (g, v) for v in vertices (g)]
258337 end
259- db = DegreeBuckets (T, degrees, maximum_degree (g))
338+ db = DegreeBuckets (
339+ T, degrees, maximum_degree (g); reproduce_colpack= order. reproduce_colpack
340+ )
260341 π = T[]
261342 sizehint! (π, nb_vertices (g))
262343 for _ in 1 : nb_vertices (g)
@@ -272,7 +353,7 @@ function vertices(
272353end
273354
274355function vertices (
275- g:: BipartiteGraph{T} , :: Val{side} , :: DynamicDegreeBasedOrder{degtype,direction}
356+ g:: BipartiteGraph{T} , :: Val{side} , order :: DynamicDegreeBasedOrder{degtype,direction}
276357) where {T<: Integer ,side,degtype,direction}
277358 other_side = 3 - side
278359 # compute dist-2 degrees in an optimized way
@@ -294,7 +375,7 @@ function vertices(
294375 degrees = degrees_dist2
295376 end
296377 maxd2 = maximum (degrees_dist2)
297- db = DegreeBuckets (T, degrees, maxd2)
378+ db = DegreeBuckets (T, degrees, maxd2; reproduce_colpack = order . reproduce_colpack )
298379 π = T[]
299380 sizehint! (π, n)
300381 visited = falses (n)
@@ -318,12 +399,11 @@ function vertices(
318399end
319400
320401"""
321- IncidenceDegree()
402+ IncidenceDegree(; reproduce_colpack=false )
322403
323404Instance of [`AbstractOrder`](@ref) which sorts vertices from lowest to highest using the dynamic back degree.
324405
325- !!! danger
326- This order is still experimental and needs more tests, correctness is not yet guaranteed.
406+ $COLPACK_WARNING
327407
328408# See also
329409
@@ -332,12 +412,11 @@ Instance of [`AbstractOrder`](@ref) which sorts vertices from lowest to highest
332412const IncidenceDegree = DynamicDegreeBasedOrder{:back ,:low2high }
333413
334414"""
335- SmallestLast()
415+ SmallestLast(; reproduce_colpack=false )
336416
337417Instance of [`AbstractOrder`](@ref) which sorts vertices from highest to lowest using the dynamic back degree.
338418
339- !!! danger
340- This order is still experimental and needs more tests, correctness is not yet guaranteed.
419+ $COLPACK_WARNING
341420
342421# See also
343422
@@ -346,12 +425,11 @@ Instance of [`AbstractOrder`](@ref) which sorts vertices from highest to lowest
346425const SmallestLast = DynamicDegreeBasedOrder{:back ,:high2low }
347426
348427"""
349- DynamicLargestFirst()
428+ DynamicLargestFirst(; reproduce_colpack=false )
350429
351430Instance of [`AbstractOrder`](@ref) which sorts vertices from lowest to highest using the dynamic forward degree.
352431
353- !!! danger
354- This order is still experimental and needs more tests, correctness is not yet guaranteed.
432+ $COLPACK_WARNING
355433
356434# See also
357435
0 commit comments