@@ -6,11 +6,14 @@ Abstract supertype for the vertex order used inside [`GreedyColoringAlgorithm`](
66In this algorithm, the rows and columns of a matrix form a graph, and the vertices are colored one after the other in a greedy fashion.
77Depending on how the vertices are ordered, the number of colors necessary may vary.
88
9- # Subtypes
9+ # Options
1010
1111- [`NaturalOrder`](@ref)
1212- [`RandomOrder`](@ref)
1313- [`LargestFirst`](@ref)
14+ - [`IncidenceDegree`](@ref) (experimental)
15+ - [`SmallestLast`](@ref) (experimental)
16+ - [`DynamicLargestFirst`](@ref) (experimental)
1417"""
1518abstract type AbstractOrder end
1619
@@ -79,3 +82,189 @@ function vertices(bg::BipartiteGraph, ::Val{side}, ::LargestFirst) where {side}
7982 criterion (v) = degrees_dist2[v]
8083 return sort (vertices (bg, Val (side)); by= criterion, rev= true )
8184end
85+
86+ """
87+ DynamicDegreeBasedOrder{degtype,direction}
88+
89+ Instance of [`AbstractOrder`](@ref) which sorts vertices using a dynamically computed degree.
90+
91+ !!! danger
92+ This order is still experimental and needs more tests, correctness is not yet guaranteed.
93+
94+ # Type parameters
95+
96+ - `degtype::Symbol`: can be `:forward` (for the forward degree) or `:back` (for the back degree)
97+ - `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`)
98+
99+ # Concrete variants
100+
101+ - [`IncidenceDegree`](@ref)
102+ - [`SmallestLast`](@ref)
103+ - [`DynamicLargestFirst`](@ref)
104+
105+ # References
106+
107+ - [_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
108+ """
109+ struct DynamicDegreeBasedOrder{degtype,direction} <: AbstractOrder end
110+
111+ struct DegreeBuckets{B}
112+ degrees:: Vector{Int}
113+ buckets:: B
114+ positions:: Vector{Int}
115+ end
116+
117+ function DegreeBuckets (degrees:: Vector{Int} , dmax)
118+ buckets = Dict (d => Int[] for d in 0 : dmax)
119+ positions = similar (degrees, Int)
120+ for v in eachindex (degrees, positions)
121+ d = degrees[v]
122+ push! (buckets[d], v) # TODO : optimize
123+ positions[v] = length (buckets[d])
124+ end
125+ return DegreeBuckets (degrees, buckets, positions)
126+ end
127+
128+ function degree_increasing (; degtype, direction)
129+ increasing =
130+ (degtype == :back && direction == :low2high ) ||
131+ (degtype == :forward && direction == :high2low )
132+ return increasing
133+ end
134+
135+ function mark_ordered! (db:: DegreeBuckets , v:: Integer )
136+ db. degrees[v] = - 1
137+ db. positions[v] = - 1
138+ return nothing
139+ end
140+
141+ already_ordered (db:: DegreeBuckets , v:: Integer ) = db. degrees[v] == - 1
142+
143+ function pop_next_candidate! (db:: DegreeBuckets ; direction:: Symbol )
144+ (; buckets) = db
145+ if direction == :low2high
146+ candidate_degree = maximum (d for (d, bucket) in pairs (buckets) if ! isempty (bucket))
147+ else
148+ candidate_degree = minimum (d for (d, bucket) in pairs (buckets) if ! isempty (bucket))
149+ end
150+ candidate_bucket = buckets[candidate_degree]
151+ candidate = pop! (candidate_bucket)
152+ mark_ordered! (db, candidate)
153+ return candidate
154+ end
155+
156+ function update_bucket! (db:: DegreeBuckets , v:: Integer ; degtype, direction)
157+ (; degrees, buckets, positions) = db
158+ d, p = degrees[v], positions[v]
159+ bucket = buckets[d]
160+ # select previous or next bucket for the move
161+ d_new = degree_increasing (; degtype, direction) ? d + 1 : d - 1
162+ bucket_new = buckets[d_new]
163+ # put v at the end of its bucket by swapping
164+ w = bucket[end ]
165+ bucket[p] = w
166+ positions[w] = p
167+ bucket[end ] = v
168+ positions[v] = length (bucket)
169+ # move v from the old bucket to the new one
170+ @assert pop! (bucket) == v
171+ push! (bucket_new, v)
172+ degrees[v] = d_new
173+ positions[v] = length (bucket_new)
174+ return nothing
175+ end
176+
177+ function vertices (
178+ g:: AdjacencyGraph , :: DynamicDegreeBasedOrder{degtype,direction}
179+ ) where {degtype,direction}
180+ if degree_increasing (; degtype, direction)
181+ degrees = zeros (Int, nb_vertices (g))
182+ else
183+ degrees = [degree (g, v) for v in vertices (g)]
184+ end
185+ db = DegreeBuckets (degrees, maximum_degree (g))
186+ π = Int[]
187+ for _ in 1 : nb_vertices (g)
188+ u = pop_next_candidate! (db; direction)
189+ direction == :low2high ? push! (π, u) : pushfirst! (π, u)
190+ for v in neighbors (g, u)
191+ already_ordered (db, v) && continue
192+ update_bucket! (db, v; degtype, direction)
193+ end
194+ end
195+ return π
196+ end
197+
198+ function vertices (
199+ g:: BipartiteGraph , :: Val{side} , :: DynamicDegreeBasedOrder{degtype,direction}
200+ ) where {side,degtype,direction}
201+ other_side = 3 - side
202+ if degree_increasing (; degtype, direction)
203+ degrees = zeros (Int, nb_vertices (g, Val (side)))
204+ else
205+ degrees = [degree_dist2 (g, Val (side), v) for v in vertices (g, Val (side))] # TODO : optimize
206+ end
207+ maxd2 = maximum (v -> degree_dist2 (g, Val (side), v), vertices (g, Val (side))) # TODO : optimize
208+ db = DegreeBuckets (degrees, maxd2)
209+ π = Int[]
210+ visited = falses (nb_vertices (g, Val (side)))
211+ for _ in 1 : nb_vertices (g, Val (side))
212+ u = pop_next_candidate! (db; direction)
213+ direction == :low2high ? push! (π, u) : pushfirst! (π, u)
214+ for w in neighbors (g, Val (side), u)
215+ for v in neighbors (g, Val (other_side), w)
216+ if v == u || visited[v]
217+ continue
218+ else
219+ visited[v] = true
220+ end
221+ already_ordered (db, v) && continue
222+ update_bucket! (db, v; degtype, direction)
223+ end
224+ end
225+ fill! (visited, false )
226+ end
227+ return π
228+ end
229+
230+ """
231+ IncidenceDegree()
232+
233+ Instance of [`AbstractOrder`](@ref) which sorts vertices from lowest to highest using the dynamic back degree.
234+
235+ !!! danger
236+ This order is still experimental and needs more tests, correctness is not yet guaranteed.
237+
238+ # See also
239+
240+ - [`DynamicDegreeBasedOrder`](@ref)
241+ """
242+ const IncidenceDegree = DynamicDegreeBasedOrder{:back ,:low2high }
243+
244+ """
245+ SmallestLast()
246+
247+ Instance of [`AbstractOrder`](@ref) which sorts vertices from highest to lowest using the dynamic back degree.
248+
249+ !!! danger
250+ This order is still experimental and needs more tests, correctness is not yet guaranteed.
251+
252+ # See also
253+
254+ - [`DynamicDegreeBasedOrder`](@ref)
255+ """
256+ const SmallestLast = DynamicDegreeBasedOrder{:back ,:high2low }
257+
258+ """
259+ DynamicLargestFirst()
260+
261+ Instance of [`AbstractOrder`](@ref) which sorts vertices from lowest to highest using the dynamic forward degree.
262+
263+ !!! danger
264+ This order is still experimental and needs more tests, correctness is not yet guaranteed.
265+
266+ # See also
267+
268+ - [`DynamicDegreeBasedOrder`](@ref)
269+ """
270+ const DynamicLargestFirst = DynamicDegreeBasedOrder{:forward ,:low2high }
0 commit comments