heuristic_graph_coloring/
lib.rs1#![warn(missing_docs)]
2#![doc = include_str!("../README.md")]
3
4pub trait ColorableGraph {
6 fn num_vertices(&self) -> usize;
8 fn neighbors(&self, vi: usize) -> &[usize];
10
11 fn degree(&self, vi: usize) -> usize {
13 self.neighbors(vi).len()
14 }
15
16 fn max_degree(&self) -> usize {
18 (0..self.num_vertices())
19 .map(|i| self.degree(i))
20 .max()
21 .unwrap_or(0)
22 }
23}
24
25#[derive(Debug, Clone)]
27pub struct VecVecGraph {
28 edges: Vec<Vec<usize>>,
29}
30
31impl ColorableGraph for &VecVecGraph {
32 fn num_vertices(&self) -> usize {
33 self.edges.len()
34 }
35
36 fn neighbors(&self, vi: usize) -> &[usize] {
37 &self.edges[vi]
38 }
39}
40
41impl VecVecGraph {
42 pub fn new(num_vertices: usize) -> Self {
44 VecVecGraph {
45 edges: (0..num_vertices).map(|_| vec![]).collect(),
46 }
47 }
48
49 pub fn add_edge(&mut self, w: usize, v: usize) {
53 self.edges[w].push(v);
54 self.edges[v].push(w);
55 }
56
57 pub fn from_dimacs_file(
61 path: &dyn AsRef<std::path::Path>,
62 ) -> Result<Self, Box<dyn std::error::Error>> {
63 use std::io::BufRead;
64
65 let f = std::fs::File::open(path)?;
66 let r = std::io::BufReader::new(f);
67 let mut graph = None;
68 let rgraph = &mut graph;
69 for (i, line) in r.lines().enumerate() {
70 let line = line?;
71 (|| {
72 let mut it = line.split(' ');
73 match it.next() {
74 Some("c" | "n" | "x" | "d" | "v") | None => {}
75 Some("p") => {
76 let format = it.next()?;
78 if format != "edge" {
79 return None;
80 }
81 let num_vertices = it.next()?.parse::<usize>().ok()?;
82 let _num_edges = it.next()?.parse::<usize>().ok()?;
83 if it.next().is_some() {
84 return None;
85 }
86 if rgraph.is_some() {
87 return None;
88 }
89 *rgraph = Some(Self::new(num_vertices));
90 }
91 Some("e") => {
92 let w = it.next()?.parse::<usize>().ok()? - 1;
94 let v = it.next()?.parse::<usize>().ok()? - 1;
95 let max = rgraph.as_ref()?.num_vertices();
96 if w >= max || v >= max {
97 return None;
98 }
99 if w != v {
100 rgraph.as_mut()?.add_edge(w, v);
101 }
102 }
103 _ => return None,
104 }
105 Some(())
106 })()
107 .ok_or(format!("invalid line {}: {}", i + 1, line))?;
108 }
109 Ok(graph.ok_or("no graph defined in file")?)
110 }
111}
112
113#[derive(Debug, Clone)]
117pub struct CsrGraph {
118 vertices: Vec<usize>,
119 edges: Vec<usize>,
120}
121
122impl ColorableGraph for &CsrGraph {
123 fn num_vertices(&self) -> usize {
124 self.vertices.len()
125 }
126
127 fn neighbors(&self, vi: usize) -> &[usize] {
128 &self.edges[if vi == 0 { 0 } else { self.vertices[vi - 1] }..self.vertices[vi]]
129 }
130}
131
132impl<T> From<T> for CsrGraph
133where
134 T: ColorableGraph,
135{
136 fn from(graph: T) -> Self {
137 let mut vertices = Vec::with_capacity(graph.num_vertices());
138 let mut edges = vec![];
139 let mut offset = 0;
140 for i in 0..graph.num_vertices() {
141 let neighbors = graph.neighbors(i);
142 edges.extend(neighbors);
143 offset += neighbors.len();
144 vertices.push(offset);
145 }
146 CsrGraph { vertices, edges }
147 }
148}
149
150fn color_greedy_by_order(
151 graph: impl ColorableGraph,
152 order: impl Iterator<Item = usize>,
153) -> Vec<usize> {
154 let mut coloring = vec![usize::MAX; graph.num_vertices()];
155
156 for i in order {
157 let mut ncs = vec![];
158 for &n in graph.neighbors(i) {
159 let nc = coloring[n];
160 if nc != usize::MAX {
161 if nc >= ncs.len() {
162 ncs.resize(nc + 1, false);
163 }
164 ncs[nc] = true;
165 }
166 }
167 for c in 0.. {
168 if c >= ncs.len() || !ncs[c] {
169 coloring[i] = c;
170 break;
171 }
172 }
173 }
174
175 coloring
176}
177
178pub fn color_greedy_naive(graph: impl ColorableGraph) -> Vec<usize> {
182 let range = 0..graph.num_vertices();
183 color_greedy_by_order(graph, range)
184}
185
186pub fn color_greedy_by_degree(graph: impl ColorableGraph) -> Vec<usize> {
191 let mut vertices: Vec<_> = (0..graph.num_vertices()).collect();
192 vertices.sort_by_key(|&i| std::cmp::Reverse(graph.neighbors(i).len()));
193 color_greedy_by_order(graph, vertices.iter().cloned())
194}
195
196#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
197struct DSatur {
198 saturation: usize,
199 degree_uncolored: usize,
200 index: std::cmp::Reverse<usize>,
201}
202
203pub fn color_greedy_dsatur(graph: impl ColorableGraph) -> Vec<usize> {
208 let mut coloring = vec![usize::MAX; graph.num_vertices()];
209
210 let mut neighbor_coloring: Vec<Vec<bool>> = vec![];
212 neighbor_coloring.resize_with(graph.num_vertices(), Vec::new);
213
214 let mut dsaturs = vec![];
216 for i in 0..graph.num_vertices() {
217 dsaturs.push(DSatur {
218 saturation: 0,
219 degree_uncolored: graph.neighbors(i).len(),
220 index: std::cmp::Reverse(i),
221 });
222 }
223 let mut heap = std::collections::BTreeSet::from_iter(dsaturs.iter().cloned());
224
225 loop {
226 let dsatur = match heap.iter().last() {
229 None => break,
230 Some(x) => x.clone(),
231 };
232 let i = dsatur.index.0;
233 let removed = heap.remove(&dsatur);
234 assert!(removed);
235
236 let ncs = &neighbor_coloring[i];
238 let mut c = usize::MAX;
239 for ci in 0.. {
240 if ci >= ncs.len() || !ncs[ci] {
241 c = ci;
242 break;
243 }
244 }
245 assert!(coloring[i] == usize::MAX);
246 coloring[i] = c;
247
248 for &n in graph.neighbors(i) {
250 if coloring[n] != usize::MAX {
252 continue;
253 }
254 let dsatur = &mut dsaturs[n];
255 let removed = heap.remove(dsatur);
256 assert!(removed);
257
258 let ncs = &mut neighbor_coloring[n];
260 if c >= ncs.len() {
261 ncs.resize(c + 1, false);
262 }
263 let has_color = ncs[c];
264 if !has_color {
265 ncs[c] = true;
266 dsatur.saturation += 1;
267 }
268 dsatur.degree_uncolored -= 1;
269
270 heap.insert(dsatur.clone());
271 }
272 }
273
274 coloring
275}
276
277#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
278struct Rlf {
279 next_neighbors: usize,
280 other_neighbors: std::cmp::Reverse<usize>,
281 index: std::cmp::Reverse<usize>,
282}
283
284pub fn color_rlf(graph: impl ColorableGraph) -> Vec<usize> {
288 let mut coloring = vec![usize::MAX; graph.num_vertices()];
289 let mut degree_next: Vec<usize> = vec![0; graph.num_vertices()];
290 let mut degree_other: Vec<usize> = vec![0; graph.num_vertices()];
291
292 let mut nocolor = usize::MAX;
293 let mut nextcolor = usize::MAX - 1;
294
295 for c in 0.. {
296 for i in 0..graph.num_vertices() {
298 if coloring[i] != nocolor {
299 continue;
300 }
301
302 degree_next[i] = 0;
303 degree_other[i] = graph
304 .neighbors(i)
305 .iter()
306 .filter(|&&n| coloring[n] == nocolor)
307 .count();
308 }
309
310 let mut current_vertex = (0..graph.num_vertices())
312 .filter(|&i| coloring[i] == nocolor)
313 .max_by_key(|&i| (degree_other[i], std::cmp::Reverse(i)));
314 if current_vertex.is_none() {
315 break;
316 }
317
318 while let Some(i) = current_vertex {
319 debug_assert!(coloring[i] == nocolor);
320 coloring[i] = c;
321
322 for &n in graph.neighbors(i) {
324 if coloring[n] != nocolor {
325 continue;
326 }
327 coloring[n] = nextcolor;
328
329 for &n2 in graph.neighbors(n) {
331 if coloring[n2] != nocolor {
332 continue;
333 }
334
335 degree_next[n2] += 1;
336 degree_other[n2] -= 1;
337 }
338 }
339
340 current_vertex = (0..graph.num_vertices())
343 .filter(|&i| coloring[i] == nocolor)
344 .max_by_key(|&i| {
345 (
346 degree_next[i],
347 std::cmp::Reverse(degree_other[i]),
348 std::cmp::Reverse(i),
349 )
350 });
351 }
352 std::mem::swap(&mut nextcolor, &mut nocolor);
353 }
354
355 coloring
356}
357
358pub fn count_colors(coloring: &[usize]) -> usize {
360 match coloring.iter().max() {
361 None => 0,
362 Some(x) => x + 1,
363 }
364}
365
366pub fn validate_coloring(graph: impl ColorableGraph, coloring: &[usize]) {
368 for i in 0..graph.num_vertices() {
369 let c = coloring[i];
370 assert!(c != usize::MAX, "no color for vertex {i}");
371 for &n in graph.neighbors(i) {
372 assert!(
373 c != coloring[n],
374 "vertex {} and neighbor {} both have color {}",
375 i + 1,
376 n + 1,
377 c
378 );
379 }
380 }
381}
382
383pub fn make_coloring_more_equitable(graph: impl ColorableGraph, coloring: &mut [usize], count_colors: usize) {
385 let mut amounts = vec![0; count_colors];
387 for &color in coloring.iter() {
388 amounts[color] += 1;
389 }
390
391 loop {
393 let mut changed = false;
394
395 for i in 0..graph.num_vertices() {
396 let ci = coloring[i];
397 let smallest_c = match (0..count_colors)
399 .filter(|&c| graph.neighbors(i).iter().all(|&j| coloring[j] != c))
400 .min_by_key(|&c| amounts[c])
401 {
402 None => continue,
403 Some(x) => x,
404 };
405 if amounts[smallest_c] < amounts[ci] - 1 {
407 coloring[i] = smallest_c;
408 amounts[ci] -= 1;
409 amounts[smallest_c] += 1;
410 changed = true;
411 }
412 }
413 if !changed {
414 break;
415 }
416 }
417}