@@ -106,12 +106,6 @@ function vertices(bg::BipartiteGraph{T}, ::Val{side}, ::LargestFirst) where {T,s
106106 return sort! (visited; by= criterion, rev= true )
107107end
108108
109- const COLPACK_WARNING = """
110- !!! danger
111- 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.
112- This setting is mostly for the purpose of reproducing past research results which rely on implementation details.
113- """
114-
115109"""
116110 DynamicDegreeBasedOrder{degtype,direction}(; reproduce_colpack=false)
117111
@@ -137,36 +131,56 @@ This order works by assigning vertices to buckets based on their dynamic degree,
137131 - When `reproduce_colpack=false` (the default), we can append and remove vertices either at the start or at the end of a bucket (bilateral).
138132
139133Allowing 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.
140- Our implementation is optimized for this bilateral setting, which means we pay a large performance penalty to artificially imitate the unilateral setting.
141-
142- $COLPACK_WARNING
134+ As a result, the default setting `reproduce_colpack=false` is slightly more memory-efficient.
143135
144136# References
145137
146138- [_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
147139"""
148- struct DynamicDegreeBasedOrder{degtype,direction} <: AbstractOrder
149- reproduce_colpack:: Bool
150- end
140+ struct DynamicDegreeBasedOrder{degtype,direction,reproduce_colpack} <: AbstractOrder end
151141
152142function DynamicDegreeBasedOrder {degtype,direction} (;
153143 reproduce_colpack:: Bool = false
154144) where {degtype,direction}
155- return DynamicDegreeBasedOrder {degtype,direction} (reproduce_colpack)
145+ return DynamicDegreeBasedOrder {degtype,direction,reproduce_colpack} ()
146+ end
147+
148+ abstract type AbstractDegreeBuckets{T} end
149+
150+ struct DegreeBucketsColPack{T} <: AbstractDegreeBuckets{T}
151+ degrees:: Vector{T}
152+ buckets:: Vector{Vector{T}}
153+ positions:: Vector{T}
156154end
157155
158- struct DegreeBuckets {T}
156+ struct DegreeBucketsSMC{T} <: AbstractDegreeBuckets {T}
159157 degrees:: Vector{T}
160158 bucket_storage:: Vector{T}
161159 bucket_low:: Vector{T}
162160 bucket_high:: Vector{T}
163161 positions:: Vector{T}
164- reproduce_colpack:: Bool
165162end
166163
167- function DegreeBuckets (
168- :: Type{T} , degrees:: Vector{T} , dmax:: Integer ; reproduce_colpack:: Bool
169- ) where {T}
164+ function DegreeBucketsColPack (:: Type{T} , degrees:: Vector{T} , dmax:: Integer ) where {T}
165+ # number of vertices per degree class
166+ deg_count = zeros (T, dmax + 1 )
167+ for d in degrees
168+ deg_count[d + 1 ] += 1
169+ end
170+ # one vector per bucket
171+ buckets = [Vector {T} (undef, deg_count[d + 1 ]) for d in 0 : dmax]
172+ positions = similar (degrees, T)
173+ # assign each vertex to the correct local position inside its bucket
174+ for v in eachindex (positions, degrees)
175+ d = degrees[v]
176+ positions[v] = length (buckets[d + 1 ]) - deg_count[d + 1 ] + 1
177+ buckets[d + 1 ][positions[v]] = v
178+ deg_count[d + 1 ] -= 1
179+ end
180+ return DegreeBucketsColPack (degrees, buckets, positions)
181+ end
182+
183+ function DegreeBucketsSMC (:: Type{T} , degrees:: Vector{T} , dmax:: Integer ) where {T}
170184 # number of vertices per degree class
171185 deg_count = zeros (T, dmax + 1 )
172186 for d in degrees
@@ -177,7 +191,7 @@ function DegreeBuckets(
177191 bucket_low = similar (bucket_high)
178192 bucket_low[1 ] = 1
179193 bucket_low[2 : end ] .= @view (bucket_high[1 : (end - 1 )]) .+ 1
180- # assign each vertex to the correct position inside its degree class
194+ # assign each vertex to the correct global position inside its bucket
181195 bucket_storage = similar (degrees, T)
182196 positions = similar (degrees, T)
183197 for v in eachindex (positions, degrees)
@@ -186,12 +200,18 @@ function DegreeBuckets(
186200 bucket_storage[positions[v]] = v
187201 deg_count[d + 1 ] -= 1
188202 end
189- return DegreeBuckets (
190- degrees, bucket_storage, bucket_low, bucket_high, positions, reproduce_colpack
191- )
203+ return DegreeBucketsSMC (degrees, bucket_storage, bucket_low, bucket_high, positions)
192204end
193205
194- maxdeg (db:: DegreeBuckets ) = length (db. bucket_low) - 1
206+ maxdeg (db:: DegreeBucketsColPack ) = length (db. buckets) - 1
207+ maxdeg (db:: DegreeBucketsSMC ) = length (db. bucket_low) - 1
208+
209+ function nonempty_bucket (db:: DegreeBucketsSMC , d:: Integer )
210+ return db. bucket_high[d + 1 ] >= db. bucket_low[d + 1 ]
211+ end
212+ function nonempty_bucket (db:: DegreeBucketsColPack , d:: Integer )
213+ return ! isempty (db. buckets[d + 1 ])
214+ end
195215
196216function degree_increasing (; degtype, direction)
197217 increasing =
@@ -200,78 +220,52 @@ function degree_increasing(; degtype, direction)
200220 return increasing
201221end
202222
203- function mark_ordered! (db:: DegreeBuckets {T} , v:: Integer ) where {T}
223+ function mark_ordered! (db:: AbstractDegreeBuckets {T} , v:: Integer ) where {T}
204224 db. degrees[v] = - 1
205225 db. positions[v] = typemin (T)
206226 return nothing
207227end
208228
209- already_ordered (db:: DegreeBuckets , v:: Integer ) = db. degrees[v] == - 1
229+ already_ordered (db:: AbstractDegreeBuckets , v:: Integer ) = db. degrees[v] == - 1
210230
211- function pop_next_candidate! (db:: DegreeBuckets ; direction:: Symbol )
212- (; bucket_storage, bucket_low, bucket_high) = db
231+ function pop_next_candidate! (db:: AbstractDegreeBuckets ; direction:: Symbol )
213232 dmax = maxdeg (db)
214233 if direction == :low2high
215234 candidate_degree = dmax + 1
216235 for d in dmax: - 1 : 0
217- if bucket_high[d + 1 ] >= bucket_low[d + 1 ] # not empty
236+ if nonempty_bucket (db, d)
218237 candidate_degree = d
219238 break
220239 end
221240 end
222241 else
223242 candidate_degree = - 1
224243 for d in 0 : dmax
225- if bucket_high[d + 1 ] >= bucket_low[d + 1 ] # not empty
244+ if nonempty_bucket (db, d)
226245 candidate_degree = d
227246 break
228247 end
229248 end
230249 end
231- high = bucket_high[candidate_degree + 1 ]
232- candidate = bucket_storage[high]
233- bucket_storage[high] = - 1
234- bucket_high[candidate_degree + 1 ] -= 1
250+ if db isa DegreeBucketsColPack
251+ (; buckets) = db
252+ bucket = buckets[candidate_degree + 1 ]
253+ candidate = pop! (bucket)
254+ else
255+ (; bucket_storage, bucket_high) = db
256+ high = bucket_high[candidate_degree + 1 ]
257+ candidate = bucket_storage[high]
258+ bucket_storage[high] = - 1
259+ bucket_high[candidate_degree + 1 ] -= 1
260+ end
235261 mark_ordered! (db, candidate)
236262 return candidate
237263end
238264
239- function rotate_bucket_left! (db:: DegreeBuckets , d:: Integer )
240- (; bucket_storage, bucket_high, bucket_low, positions) = db
241- low, high = bucket_low[d + 1 ], bucket_high[d + 1 ]
242- # remember first element v
243- v = bucket_storage[low]
244- # shift everyone else one index down
245- for i in (low + 1 ): high
246- w = bucket_storage[i]
247- bucket_storage[i - 1 ] = w
248- positions[w] = i - 1
249- end
250- # put v back at the end
251- bucket_storage[high] = v
252- positions[v] = high
253- return nothing
254- end
255-
256- function rotate_bucket_right! (db:: DegreeBuckets , d:: Integer )
257- (; bucket_storage, bucket_high, bucket_low, positions) = db
258- low, high = bucket_low[d + 1 ], bucket_high[d + 1 ]
259- # remember last element v
260- v = bucket_storage[high]
261- # shift everyone else one index up
262- for i in (high - 1 ): - 1 : low
263- w = bucket_storage[i]
264- bucket_storage[i + 1 ] = w
265- positions[w] = i + 1
266- end
267- # put v back at the start
268- bucket_storage[low] = v
269- positions[v] = low
270- return nothing
271- end
272-
273- function update_bucket! (db:: DegreeBuckets , v:: Integer ; degtype:: Symbol , direction:: Symbol )
274- (; degrees, bucket_storage, bucket_low, bucket_high, positions, reproduce_colpack) = db
265+ function update_bucket! (
266+ db:: DegreeBucketsSMC , v:: Integer ; degtype:: Symbol , direction:: Symbol
267+ )
268+ (; degrees, bucket_storage, bucket_low, bucket_high, positions) = db
275269 d, p = degrees[v], positions[v]
276270 low, high = bucket_low[d + 1 ], bucket_high[d + 1 ]
277271 # select previous or next bucket for the move
@@ -292,27 +286,11 @@ function update_bucket!(db::DegreeBuckets, v::Integer; degtype::Symbol, directio
292286 # update v's stats
293287 degrees[v] = d_new
294288 positions[v] = low_new - 1
295- if reproduce_colpack
296- # move v from start to end of the next bucket, preserving order
297- rotate_bucket_left! (db, d_new) # expensive
298- end
299289 else
300- if reproduce_colpack
301- # move the vertex w located at the end of the current bucket to v's position
302- w = bucket_storage[high]
303- bucket_storage[p] = w
304- positions[w] = p
305- # explicitly put v at the end
306- bucket_storage[high] = v
307- positions[v] = high
308- # move v from end to start of the current bucket, preserving order
309- rotate_bucket_right! (db, d) # expensive
310- else
311- # move the vertex w located at the start of the current bucket to v's position (!= ColPack)
312- w = bucket_storage[low]
313- bucket_storage[p] = w
314- positions[w] = p
315- end
290+ # move the vertex w located at the start of the current bucket to v's position (!= ColPack)
291+ w = bucket_storage[low]
292+ bucket_storage[p] = w
293+ positions[w] = p
316294 # shrink current bucket from the left
317295 # morally we put v at the start and then ignore it
318296 bucket_low[d + 1 ] += 1
@@ -329,15 +307,42 @@ function update_bucket!(db::DegreeBuckets, v::Integer; degtype::Symbol, directio
329307 return nothing
330308end
331309
310+ function update_bucket! (
311+ db:: DegreeBucketsColPack , v:: Integer ; degtype:: Symbol , direction:: Symbol
312+ )
313+ (; degrees, buckets, positions) = db
314+ d, p = degrees[v], positions[v]
315+ bucket = buckets[d + 1 ]
316+ # select previous or next bucket for the move
317+ d_new = degree_increasing (; degtype, direction) ? d + 1 : d - 1
318+ bucket_new = buckets[d_new + 1 ]
319+ # put v at the end of its bucket by swapping
320+ w = bucket[end ]
321+ bucket[p] = w
322+ positions[w] = p
323+ bucket[end ] = v
324+ positions[v] = length (bucket)
325+ # move v from the old bucket to the new one
326+ @assert pop! (bucket) == v
327+ push! (bucket_new, v)
328+ degrees[v] = d_new
329+ positions[v] = length (bucket_new)
330+ return nothing
331+ end
332+
332333function vertices (
333- g:: AdjacencyGraph{T} , order :: DynamicDegreeBasedOrder{degtype,direction}
334- ) where {T<: Integer ,degtype,direction}
334+ g:: AdjacencyGraph{T} , :: DynamicDegreeBasedOrder{degtype,direction,reproduce_colpack }
335+ ) where {T<: Integer ,degtype,direction,reproduce_colpack }
335336 true_degrees = degrees = T[degree (g, v) for v in vertices (g)]
336337 max_degrees = maximum (true_degrees)
337338 if degree_increasing (; degtype, direction)
338339 fill! (degrees, zero (T))
339340 end
340- db = DegreeBuckets (T, degrees, max_degrees; reproduce_colpack= order. reproduce_colpack)
341+ db = if reproduce_colpack
342+ DegreeBucketsColPack (T, degrees, max_degrees)
343+ else
344+ DegreeBucketsSMC (T, degrees, max_degrees)
345+ end
341346 nv = nb_vertices (g)
342347 π = Vector {T} (undef, nv)
343348 index_π = (direction == :low2high ) ? (1 : nv) : (nv: - 1 : 1 )
@@ -354,8 +359,10 @@ function vertices(
354359end
355360
356361function vertices (
357- g:: BipartiteGraph{T} , :: Val{side} , order:: DynamicDegreeBasedOrder{degtype,direction}
358- ) where {T<: Integer ,side,degtype,direction}
362+ g:: BipartiteGraph{T} ,
363+ :: Val{side} ,
364+ :: DynamicDegreeBasedOrder{degtype,direction,reproduce_colpack} ,
365+ ) where {T<: Integer ,side,degtype,direction,reproduce_colpack}
359366 other_side = 3 - side
360367 # compute dist-2 degrees in an optimized way
361368 n = nb_vertices (g, Val (side))
@@ -375,7 +382,11 @@ function vertices(
375382 if degree_increasing (; degtype, direction)
376383 fill! (degrees, zero (T))
377384 end
378- db = DegreeBuckets (T, degrees, maxd2; reproduce_colpack= order. reproduce_colpack)
385+ db = if reproduce_colpack
386+ DegreeBucketsColPack (T, degrees, maxd2)
387+ else
388+ DegreeBucketsSMC (T, degrees, maxd2)
389+ end
379390 π = Vector {T} (undef, n)
380391 index_π = (direction == :low2high ) ? (1 : n) : (n: - 1 : 1 )
381392 for index in index_π
@@ -400,39 +411,39 @@ end
400411
401412Instance of [`AbstractOrder`](@ref) which sorts vertices from lowest to highest using the dynamic back degree.
402413
403- $COLPACK_WARNING
404-
405414# See also
406415
407416- [`DynamicDegreeBasedOrder`](@ref)
408417"""
409- const IncidenceDegree = DynamicDegreeBasedOrder{:back ,:low2high }
418+ function IncidenceDegree (; reproduce_colpack:: Bool = false )
419+ return DynamicDegreeBasedOrder {:back,:low2high,reproduce_colpack} ()
420+ end
410421
411422"""
412423 SmallestLast(; reproduce_colpack=false)
413424
414425Instance of [`AbstractOrder`](@ref) which sorts vertices from highest to lowest using the dynamic back degree.
415426
416- $COLPACK_WARNING
417-
418427# See also
419428
420429- [`DynamicDegreeBasedOrder`](@ref)
421430"""
422- const SmallestLast = DynamicDegreeBasedOrder{:back ,:high2low }
431+ function SmallestLast (; reproduce_colpack:: Bool = false )
432+ return DynamicDegreeBasedOrder {:back,:high2low,reproduce_colpack} ()
433+ end
423434
424435"""
425436 DynamicLargestFirst(; reproduce_colpack=false)
426437
427438Instance of [`AbstractOrder`](@ref) which sorts vertices from lowest to highest using the dynamic forward degree.
428439
429- $COLPACK_WARNING
430-
431440# See also
432441
433442- [`DynamicDegreeBasedOrder`](@ref)
434443"""
435- const DynamicLargestFirst = DynamicDegreeBasedOrder{:forward ,:low2high }
444+ function DynamicLargestFirst (; reproduce_colpack:: Bool = false )
445+ return DynamicDegreeBasedOrder {:forward,:low2high,reproduce_colpack} ()
446+ end
436447
437448"""
438449 PerfectEliminationOrder(elimination_algorithm=CliqueTrees.MCS())
@@ -461,7 +472,12 @@ function all_orders()
461472 RandomOrder (),
462473 LargestFirst (),
463474 SmallestLast (),
475+ SmallestLast (; reproduce_colpack= true ),
464476 IncidenceDegree (),
477+ IncidenceDegree (; reproduce_colpack= true ),
465478 DynamicLargestFirst (),
479+ DynamicLargestFirst (; reproduce_colpack= true ),
480+ DynamicDegreeBasedOrder {:forward,:high2low} (),
481+ DynamicDegreeBasedOrder {:forward,:high2low} (; reproduce_colpack= true ),
466482 ]
467483end
0 commit comments