@@ -106,12 +106,6 @@ function vertices(bg::BipartiteGraph{T}, ::Val{side}, ::LargestFirst) where {T,s
106
106
return sort! (visited; by= criterion, rev= true )
107
107
end
108
108
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
-
115
109
"""
116
110
DynamicDegreeBasedOrder{degtype,direction}(; reproduce_colpack=false)
117
111
@@ -137,36 +131,56 @@ This order works by assigning vertices to buckets based on their dynamic degree,
137
131
- When `reproduce_colpack=false` (the default), we can append and remove vertices either at the start or at the end of a bucket (bilateral).
138
132
139
133
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.
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.
143
135
144
136
# References
145
137
146
138
- [_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
147
139
"""
148
- struct DynamicDegreeBasedOrder{degtype,direction} <: AbstractOrder
149
- reproduce_colpack:: Bool
150
- end
140
+ struct DynamicDegreeBasedOrder{degtype,direction,reproduce_colpack} <: AbstractOrder end
151
141
152
142
function DynamicDegreeBasedOrder {degtype,direction} (;
153
143
reproduce_colpack:: Bool = false
154
144
) 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}
156
154
end
157
155
158
- struct DegreeBuckets {T}
156
+ struct DegreeBucketsSMC{T} <: AbstractDegreeBuckets {T}
159
157
degrees:: Vector{T}
160
158
bucket_storage:: Vector{T}
161
159
bucket_low:: Vector{T}
162
160
bucket_high:: Vector{T}
163
161
positions:: Vector{T}
164
- reproduce_colpack:: Bool
165
162
end
166
163
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}
170
184
# number of vertices per degree class
171
185
deg_count = zeros (T, dmax + 1 )
172
186
for d in degrees
@@ -177,7 +191,7 @@ function DegreeBuckets(
177
191
bucket_low = similar (bucket_high)
178
192
bucket_low[1 ] = 1
179
193
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
181
195
bucket_storage = similar (degrees, T)
182
196
positions = similar (degrees, T)
183
197
for v in eachindex (positions, degrees)
@@ -186,12 +200,18 @@ function DegreeBuckets(
186
200
bucket_storage[positions[v]] = v
187
201
deg_count[d + 1 ] -= 1
188
202
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)
192
204
end
193
205
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
195
215
196
216
function degree_increasing (; degtype, direction)
197
217
increasing =
@@ -200,78 +220,52 @@ function degree_increasing(; degtype, direction)
200
220
return increasing
201
221
end
202
222
203
- function mark_ordered! (db:: DegreeBuckets {T} , v:: Integer ) where {T}
223
+ function mark_ordered! (db:: AbstractDegreeBuckets {T} , v:: Integer ) where {T}
204
224
db. degrees[v] = - 1
205
225
db. positions[v] = typemin (T)
206
226
return nothing
207
227
end
208
228
209
- already_ordered (db:: DegreeBuckets , v:: Integer ) = db. degrees[v] == - 1
229
+ already_ordered (db:: AbstractDegreeBuckets , v:: Integer ) = db. degrees[v] == - 1
210
230
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 )
213
232
dmax = maxdeg (db)
214
233
if direction == :low2high
215
234
candidate_degree = dmax + 1
216
235
for d in dmax: - 1 : 0
217
- if bucket_high[d + 1 ] >= bucket_low[d + 1 ] # not empty
236
+ if nonempty_bucket (db, d)
218
237
candidate_degree = d
219
238
break
220
239
end
221
240
end
222
241
else
223
242
candidate_degree = - 1
224
243
for d in 0 : dmax
225
- if bucket_high[d + 1 ] >= bucket_low[d + 1 ] # not empty
244
+ if nonempty_bucket (db, d)
226
245
candidate_degree = d
227
246
break
228
247
end
229
248
end
230
249
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
235
261
mark_ordered! (db, candidate)
236
262
return candidate
237
263
end
238
264
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
275
269
d, p = degrees[v], positions[v]
276
270
low, high = bucket_low[d + 1 ], bucket_high[d + 1 ]
277
271
# select previous or next bucket for the move
@@ -292,27 +286,11 @@ function update_bucket!(db::DegreeBuckets, v::Integer; degtype::Symbol, directio
292
286
# update v's stats
293
287
degrees[v] = d_new
294
288
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
299
289
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
316
294
# shrink current bucket from the left
317
295
# morally we put v at the start and then ignore it
318
296
bucket_low[d + 1 ] += 1
@@ -329,15 +307,42 @@ function update_bucket!(db::DegreeBuckets, v::Integer; degtype::Symbol, directio
329
307
return nothing
330
308
end
331
309
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
+
332
333
function 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 }
335
336
true_degrees = degrees = T[degree (g, v) for v in vertices (g)]
336
337
max_degrees = maximum (true_degrees)
337
338
if degree_increasing (; degtype, direction)
338
339
fill! (degrees, zero (T))
339
340
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
341
346
nv = nb_vertices (g)
342
347
π = Vector {T} (undef, nv)
343
348
index_π = (direction == :low2high ) ? (1 : nv) : (nv: - 1 : 1 )
@@ -354,8 +359,10 @@ function vertices(
354
359
end
355
360
356
361
function 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}
359
366
other_side = 3 - side
360
367
# compute dist-2 degrees in an optimized way
361
368
n = nb_vertices (g, Val (side))
@@ -375,7 +382,11 @@ function vertices(
375
382
if degree_increasing (; degtype, direction)
376
383
fill! (degrees, zero (T))
377
384
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
379
390
π = Vector {T} (undef, n)
380
391
index_π = (direction == :low2high ) ? (1 : n) : (n: - 1 : 1 )
381
392
for index in index_π
@@ -400,39 +411,39 @@ end
400
411
401
412
Instance of [`AbstractOrder`](@ref) which sorts vertices from lowest to highest using the dynamic back degree.
402
413
403
- $COLPACK_WARNING
404
-
405
414
# See also
406
415
407
416
- [`DynamicDegreeBasedOrder`](@ref)
408
417
"""
409
- const IncidenceDegree = DynamicDegreeBasedOrder{:back ,:low2high }
418
+ function IncidenceDegree (; reproduce_colpack:: Bool = false )
419
+ return DynamicDegreeBasedOrder {:back,:low2high,reproduce_colpack} ()
420
+ end
410
421
411
422
"""
412
423
SmallestLast(; reproduce_colpack=false)
413
424
414
425
Instance of [`AbstractOrder`](@ref) which sorts vertices from highest to lowest using the dynamic back degree.
415
426
416
- $COLPACK_WARNING
417
-
418
427
# See also
419
428
420
429
- [`DynamicDegreeBasedOrder`](@ref)
421
430
"""
422
- const SmallestLast = DynamicDegreeBasedOrder{:back ,:high2low }
431
+ function SmallestLast (; reproduce_colpack:: Bool = false )
432
+ return DynamicDegreeBasedOrder {:back,:high2low,reproduce_colpack} ()
433
+ end
423
434
424
435
"""
425
436
DynamicLargestFirst(; reproduce_colpack=false)
426
437
427
438
Instance of [`AbstractOrder`](@ref) which sorts vertices from lowest to highest using the dynamic forward degree.
428
439
429
- $COLPACK_WARNING
430
-
431
440
# See also
432
441
433
442
- [`DynamicDegreeBasedOrder`](@ref)
434
443
"""
435
- const DynamicLargestFirst = DynamicDegreeBasedOrder{:forward ,:low2high }
444
+ function DynamicLargestFirst (; reproduce_colpack:: Bool = false )
445
+ return DynamicDegreeBasedOrder {:forward,:low2high,reproduce_colpack} ()
446
+ end
436
447
437
448
"""
438
449
PerfectEliminationOrder(elimination_algorithm=CliqueTrees.MCS())
@@ -461,7 +472,12 @@ function all_orders()
461
472
RandomOrder (),
462
473
LargestFirst (),
463
474
SmallestLast (),
475
+ SmallestLast (; reproduce_colpack= true ),
464
476
IncidenceDegree (),
477
+ IncidenceDegree (; reproduce_colpack= true ),
465
478
DynamicLargestFirst (),
479
+ DynamicLargestFirst (; reproduce_colpack= true ),
480
+ DynamicDegreeBasedOrder {:forward,:high2low} (),
481
+ DynamicDegreeBasedOrder {:forward,:high2low} (; reproduce_colpack= true ),
466
482
]
467
483
end
0 commit comments