scirs2_optimize/combinatorial/
graph_coloring.rs1use crate::error::OptimizeError;
7
8pub type ColoringResult<T> = Result<T, OptimizeError>;
10
11#[derive(Debug, Clone)]
15pub struct GraphColoring {
16 pub adj: Vec<Vec<usize>>,
18 pub n: usize,
20}
21
22impl GraphColoring {
23 pub fn new(n: usize) -> Self {
25 Self {
26 adj: vec![vec![]; n],
27 n,
28 }
29 }
30
31 pub fn add_edge(&mut self, u: usize, v: usize) -> ColoringResult<()> {
38 if u >= self.n || v >= self.n {
39 return Err(OptimizeError::InvalidInput(format!(
40 "Edge ({u},{v}) out of range for graph with {} vertices",
41 self.n
42 )));
43 }
44 if u == v {
45 return Ok(()); }
47 if !self.adj[u].contains(&v) {
48 self.adj[u].push(v);
49 }
50 if !self.adj[v].contains(&u) {
51 self.adj[v].push(u);
52 }
53 Ok(())
54 }
55
56 pub fn degree(&self, v: usize) -> usize {
58 self.adj[v].len()
59 }
60
61 pub fn greedy_coloring(&self) -> Vec<usize> {
69 if self.n == 0 {
70 return vec![];
71 }
72
73 let mut order: Vec<usize> = (0..self.n).collect();
75 order.sort_by(|&a, &b| self.degree(b).cmp(&self.degree(a)));
76
77 let mut color = vec![usize::MAX; self.n];
78
79 for &v in &order {
80 let mut neighbor_colors = std::collections::HashSet::new();
81 for &u in &self.adj[v] {
82 if color[u] != usize::MAX {
83 neighbor_colors.insert(color[u]);
84 }
85 }
86 let mut c = 0;
88 while neighbor_colors.contains(&c) {
89 c += 1;
90 }
91 color[v] = c;
92 }
93
94 color
95 }
96
97 pub fn dsatur_coloring(&self) -> Vec<usize> {
105 if self.n == 0 {
106 return vec![];
107 }
108
109 let mut color = vec![usize::MAX; self.n];
110 let mut saturation = vec![0usize; self.n];
112 let mut neighbor_colors: Vec<std::collections::HashSet<usize>> =
114 vec![std::collections::HashSet::new(); self.n];
115 let mut colored = 0usize;
116
117 while colored < self.n {
118 let v = match (0..self.n)
121 .filter(|&u| color[u] == usize::MAX)
122 .max_by(|&a, &b| {
123 saturation[a]
124 .cmp(&saturation[b])
125 .then(self.degree(a).cmp(&self.degree(b)))
126 }) {
127 Some(vertex) => vertex,
128 None => break, };
130
131 let mut c = 0;
133 while neighbor_colors[v].contains(&c) {
134 c += 1;
135 }
136 color[v] = c;
137 colored += 1;
138
139 for &u in &self.adj[v] {
141 if color[u] == usize::MAX && !neighbor_colors[u].contains(&c) {
142 neighbor_colors[u].insert(c);
143 saturation[u] += 1;
144 }
145 }
146 }
147
148 color
149 }
150
151 pub fn backtrack_coloring(&self, k: usize) -> Option<Vec<usize>> {
157 if self.n == 0 {
158 return Some(vec![]);
159 }
160 if k == 0 {
161 return None;
162 }
163
164 let mut color = vec![k; self.n]; let mut order: Vec<usize> = (0..self.n).collect();
167 order.sort_by(|&a, &b| self.degree(b).cmp(&self.degree(a)));
168 let pos_of: Vec<usize> = {
169 let mut p = vec![0usize; self.n];
170 for (pos, &v) in order.iter().enumerate() {
171 p[v] = pos;
172 }
173 p
174 };
175
176 if self.backtrack_inner(&mut color, &order, &pos_of, k, 0) {
177 Some(color)
178 } else {
179 None
180 }
181 }
182
183 fn backtrack_inner(
184 &self,
185 color: &mut Vec<usize>,
186 order: &[usize],
187 pos_of: &[usize],
188 k: usize,
189 idx: usize,
190 ) -> bool {
191 if idx == self.n {
192 return true;
193 }
194 let v = order[idx];
195
196 let mut forbidden = vec![false; k];
198 for &u in &self.adj[v] {
199 if color[u] < k {
200 forbidden[color[u]] = true;
202 }
203 }
204
205 for c in 0..k {
206 if forbidden[c] {
207 continue;
208 }
209 let ok = self.adj[v].iter().all(|&u| {
212 if color[u] < k {
213 return true; }
215 let used: std::collections::HashSet<usize> = self.adj[u]
217 .iter()
218 .filter(|&&w| color[w] < k && w != v)
219 .map(|&w| color[w])
220 .collect();
221 let available = (0..k).filter(|&x| x != c && !used.contains(&x)).count();
223 if pos_of[u] > idx {
225 available > 0
226 } else {
227 true
228 }
229 });
230
231 if !ok {
232 continue;
233 }
234
235 color[v] = c;
236 if self.backtrack_inner(color, order, pos_of, k, idx + 1) {
237 return true;
238 }
239 color[v] = k; }
241
242 false
243 }
244
245 pub fn chromatic_number(&self) -> usize {
252 if self.n == 0 {
253 return 0;
254 }
255 let has_edges = self.adj.iter().any(|nbrs| !nbrs.is_empty());
257 let lower = if has_edges { 2 } else { 1 };
258
259 let upper_coloring = self.dsatur_coloring();
261 let upper = upper_coloring
262 .iter()
263 .cloned()
264 .max()
265 .map(|m| m + 1)
266 .unwrap_or(1);
267
268 if lower >= upper {
269 return lower;
270 }
271
272 let mut lo = lower;
274 let mut hi = upper;
275 while lo < hi {
276 let mid = (lo + hi) / 2;
277 if self.backtrack_coloring(mid).is_some() {
278 hi = mid;
279 } else {
280 lo = mid + 1;
281 }
282 }
283 lo
284 }
285
286 pub fn is_valid_coloring(&self, coloring: &[usize]) -> bool {
291 if coloring.len() != self.n {
292 return false;
293 }
294 for u in 0..self.n {
295 for &v in &self.adj[u] {
296 if coloring[u] == coloring[v] {
297 return false;
298 }
299 }
300 }
301 true
302 }
303}
304
305#[cfg(test)]
309mod tests {
310 use super::*;
311
312 fn cycle_graph(n: usize) -> GraphColoring {
313 let mut g = GraphColoring::new(n);
314 for i in 0..n {
315 g.add_edge(i, (i + 1) % n).expect("unexpected None or Err");
316 }
317 g
318 }
319
320 fn complete_graph(n: usize) -> GraphColoring {
321 let mut g = GraphColoring::new(n);
322 for i in 0..n {
323 for j in i + 1..n {
324 g.add_edge(i, j).expect("unexpected None or Err");
325 }
326 }
327 g
328 }
329
330 #[test]
331 fn test_greedy_valid() {
332 let g = cycle_graph(6);
333 let c = g.greedy_coloring();
334 assert!(g.is_valid_coloring(&c));
335 }
336
337 #[test]
338 fn test_dsatur_valid() {
339 let g = complete_graph(5);
340 let c = g.dsatur_coloring();
341 assert!(g.is_valid_coloring(&c));
342 let num_colors = c
344 .iter()
345 .cloned()
346 .max()
347 .expect("failed to create num_colors")
348 + 1;
349 assert_eq!(num_colors, 5);
350 }
351
352 #[test]
353 fn test_bipartite_2_colorable() {
354 let mut g = GraphColoring::new(6);
356 for u in 0..3 {
357 for v in 3..6 {
358 g.add_edge(u, v).expect("unexpected None or Err");
359 }
360 }
361 let c = g.backtrack_coloring(2);
362 assert!(c.is_some());
363 assert!(g.is_valid_coloring(c.as_ref().expect("unexpected None or Err")));
364 }
365
366 #[test]
367 fn test_odd_cycle_not_2_colorable() {
368 let g = cycle_graph(5); assert!(g.backtrack_coloring(2).is_none());
370 assert!(g.backtrack_coloring(3).is_some());
371 }
372
373 #[test]
374 fn test_chromatic_number_cycle() {
375 let even = cycle_graph(6);
376 assert_eq!(even.chromatic_number(), 2);
377
378 let odd = cycle_graph(5);
379 assert_eq!(odd.chromatic_number(), 3);
380 }
381
382 #[test]
383 fn test_chromatic_number_complete() {
384 for n in 1..=5 {
385 let g = complete_graph(n);
386 assert_eq!(g.chromatic_number(), n);
387 }
388 }
389
390 #[test]
391 fn test_empty_graph() {
392 let g = GraphColoring::new(4);
393 let c = g.greedy_coloring();
394 assert!(c.iter().all(|&x| x == 0));
396 assert!(g.is_valid_coloring(&c));
397 assert_eq!(g.chromatic_number(), 1);
398 }
399
400 #[test]
401 fn test_self_loop_ignored() {
402 let mut g = GraphColoring::new(3);
403 g.add_edge(0, 0).expect("unexpected None or Err"); g.add_edge(0, 1).expect("unexpected None or Err");
405 assert_eq!(g.degree(0), 1);
406 }
407
408 #[test]
409 fn test_invalid_edge() {
410 let mut g = GraphColoring::new(3);
411 assert!(g.add_edge(0, 5).is_err());
412 }
413}