scirs2_graph/algorithms/
coloring.rs

1//! Graph coloring algorithms
2//!
3//! This module contains algorithms for graph coloring problems.
4
5use crate::base::{EdgeWeight, Graph, Node};
6use std::collections::{HashMap, HashSet};
7
8/// Result of graph coloring
9#[derive(Debug, Clone)]
10pub struct GraphColoring<N: Node> {
11    /// The coloring as a map from node to color (0-based)
12    pub coloring: HashMap<N, usize>,
13    /// The number of colors used
14    pub num_colors: usize,
15}
16
17/// Colors a graph using a greedy algorithm
18///
19/// This algorithm assigns colors to vertices one by one, using the smallest
20/// available color that hasn't been used by any adjacent vertex.
21///
22/// # Arguments
23/// * `graph` - The graph to color
24///
25/// # Returns
26/// * A graph coloring
27#[allow(dead_code)]
28pub fn greedy_coloring<N, E, Ix>(graph: &Graph<N, E, Ix>) -> GraphColoring<N>
29where
30    N: Node + std::fmt::Debug,
31    E: EdgeWeight,
32    Ix: petgraph::graph::IndexType,
33{
34    let mut coloring: HashMap<N, usize> = HashMap::new();
35    let mut max_color = 0;
36
37    // Color nodes in the order they appear
38    for node_idx in graph.inner().node_indices() {
39        // Find colors used by neighbors
40        let mut used_colors = HashSet::new();
41        for neighbor_idx in graph.inner().neighbors(node_idx) {
42            if let Some(neighbor_node) = graph.inner().node_weight(neighbor_idx) {
43                if let Some(&color) = coloring.get(neighbor_node) {
44                    used_colors.insert(color);
45                }
46            }
47        }
48
49        // Find the smallest unused color
50        let mut color = 0;
51        while used_colors.contains(&color) {
52            color += 1;
53        }
54
55        let node = graph.inner()[node_idx].clone();
56        coloring.insert(node, color);
57        max_color = max_color.max(color);
58    }
59
60    GraphColoring {
61        coloring,
62        num_colors: max_color + 1,
63    }
64}
65
66/// Finds the chromatic number of a graph (minimum number of colors needed)
67///
68/// Uses an exhaustive search up to max_colors to find the minimum coloring.
69/// This is an NP-complete problem, so it may be slow for large graphs.
70///
71/// # Arguments
72/// * `graph` - The graph to analyze
73/// * `max_colors` - Maximum number of colors to try
74///
75/// # Returns
76/// * The chromatic number if found within max_colors, None otherwise
77#[allow(dead_code)]
78pub fn chromatic_number<N, E, Ix>(graph: &Graph<N, E, Ix>, max_colors: usize) -> Option<usize>
79where
80    N: Node + std::fmt::Debug,
81    E: EdgeWeight,
82    Ix: petgraph::graph::IndexType,
83{
84    if graph.inner().node_count() == 0 {
85        return Some(0);
86    }
87
88    // Try coloring with 1, 2, 3, ... _colors
89    (1..=max_colors).find(|&num_colors| can_color_with_k_colors(graph, num_colors))
90}
91
92/// Helper function to check if a graph can be colored with k colors
93#[allow(dead_code)]
94fn can_color_with_k_colors<N, E, Ix>(graph: &Graph<N, E, Ix>, k: usize) -> bool
95where
96    N: Node + std::fmt::Debug,
97    E: EdgeWeight,
98    Ix: petgraph::graph::IndexType,
99{
100    let nodes: Vec<_> = graph.inner().node_indices().collect();
101    let mut coloring = vec![0; nodes.len()];
102
103    fn backtrack<N, E, Ix>(
104        _graph: &Graph<N, E, Ix>,
105        nodes: &[petgraph::graph::NodeIndex<Ix>],
106        coloring: &mut [usize],
107        node_idx: usize,
108        k: usize,
109    ) -> bool
110    where
111        N: Node + std::fmt::Debug,
112        E: EdgeWeight,
113        Ix: petgraph::graph::IndexType,
114    {
115        if node_idx == nodes.len() {
116            return true;
117        }
118
119        let node = nodes[node_idx];
120
121        for color in 0..k {
122            // Check if this color is valid (no adjacent node has the same color)
123            let mut valid = true;
124            for (i, &other_node) in nodes.iter().enumerate().take(node_idx) {
125                if (_graph.inner().contains_edge(node, other_node)
126                    || _graph.inner().contains_edge(other_node, node))
127                    && coloring[i] == color
128                {
129                    valid = false;
130                    break;
131                }
132            }
133
134            if valid {
135                coloring[node_idx] = color;
136                if backtrack(_graph, nodes, coloring, node_idx + 1, k) {
137                    return true;
138                }
139            }
140        }
141
142        false
143    }
144
145    backtrack(graph, &nodes, &mut coloring, 0, k)
146}
147
148#[cfg(test)]
149mod tests {
150    use super::*;
151    use crate::generators::create_graph;
152
153    #[test]
154    fn test_greedy_coloring() {
155        // Create a triangle (needs 3 colors with greedy)
156        let mut graph = create_graph::<char, ()>();
157        graph.add_edge('A', 'B', ()).unwrap();
158        graph.add_edge('B', 'C', ()).unwrap();
159        graph.add_edge('C', 'A', ()).unwrap();
160
161        let coloring = greedy_coloring(&graph);
162        assert_eq!(coloring.num_colors, 3);
163
164        // Check that adjacent nodes have different colors
165        assert_ne!(coloring.coloring[&'A'], coloring.coloring[&'B']);
166        assert_ne!(coloring.coloring[&'B'], coloring.coloring[&'C']);
167        assert_ne!(coloring.coloring[&'C'], coloring.coloring[&'A']);
168    }
169
170    #[test]
171    fn test_bipartite_graph_coloring() {
172        // Create a bipartite graph (needs only 2 colors)
173        let mut graph = create_graph::<i32, ()>();
174
175        // Bipartite structure: 0-1, 0-3, 2-1, 2-3
176        graph.add_edge(0, 1, ()).unwrap();
177        graph.add_edge(0, 3, ()).unwrap();
178        graph.add_edge(2, 1, ()).unwrap();
179        graph.add_edge(2, 3, ()).unwrap();
180
181        let coloring = greedy_coloring(&graph);
182        assert!(coloring.num_colors <= 2);
183    }
184
185    #[test]
186    fn test_chromatic_number() {
187        // Triangle graph needs exactly 3 colors
188        let mut triangle = create_graph::<i32, ()>();
189
190        triangle.add_edge(0, 1, ()).unwrap();
191        triangle.add_edge(1, 2, ()).unwrap();
192        triangle.add_edge(2, 0, ()).unwrap();
193
194        assert_eq!(chromatic_number(&triangle, 5), Some(3));
195
196        // Bipartite graph needs exactly 2 colors
197        let mut bipartite = create_graph::<i32, ()>();
198
199        bipartite.add_edge(0, 1, ()).unwrap();
200        bipartite.add_edge(1, 2, ()).unwrap();
201        bipartite.add_edge(2, 3, ()).unwrap();
202        bipartite.add_edge(3, 0, ()).unwrap();
203
204        assert_eq!(chromatic_number(&bipartite, 5), Some(2));
205
206        // Empty graph needs 0 colors
207        let empty = create_graph::<i32, ()>();
208        assert_eq!(chromatic_number(&empty, 5), Some(0));
209    }
210}