rustworkx_core/
coloring.rs

1// Licensed under the Apache License, Version 2.0 (the "License"); you may
2// not use this file except in compliance with the License. You may obtain
3// a copy of the License at
4//
5//     http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10// License for the specific language governing permissions and limitations
11// under the License.
12
13use foldhash::fast::RandomState;
14use priority_queue::PriorityQueue;
15use std::cmp::Ordering;
16use std::cmp::Reverse;
17use std::convert::Infallible;
18use std::hash::Hash;
19
20use crate::dictmap::*;
21use crate::line_graph::line_graph;
22
23use hashbrown::{HashMap, HashSet};
24use indexmap::IndexSet;
25use petgraph::graph::NodeIndex;
26use petgraph::visit::{
27    EdgeCount, EdgeIndexable, EdgeRef, GraphBase, GraphProp, IntoEdges, IntoNeighborsDirected,
28    IntoNodeIdentifiers, NodeCount, NodeIndexable,
29};
30use petgraph::{Incoming, Outgoing};
31use rayon::prelude::*;
32
33/// Compute a two-coloring of a graph
34///
35/// If a two coloring is not possible for the input graph (meaning it is not
36/// bipartite), `None` is returned.
37///
38/// Arguments:
39///
40/// * `graph` - The graph to find the coloring for
41///
42/// # Example
43///
44/// ```rust
45/// use rustworkx_core::petgraph::prelude::*;
46/// use rustworkx_core::coloring::two_color;
47/// use rustworkx_core::dictmap::*;
48///
49/// let edge_list = vec![
50///  (0, 1),
51///  (1, 2),
52///  (2, 3),
53///  (3, 4),
54/// ];
55///
56/// let graph = UnGraph::<i32, i32>::from_edges(&edge_list);
57/// let coloring = two_color(&graph).unwrap();
58/// let mut expected_colors = DictMap::new();
59/// expected_colors.insert(NodeIndex::new(0), 1);
60/// expected_colors.insert(NodeIndex::new(1), 0);
61/// expected_colors.insert(NodeIndex::new(2), 1);
62/// expected_colors.insert(NodeIndex::new(3), 0);
63/// expected_colors.insert(NodeIndex::new(4), 1);
64/// assert_eq!(coloring, expected_colors)
65/// ```
66pub fn two_color<G>(graph: G) -> Option<DictMap<G::NodeId, u8>>
67where
68    G: NodeIndexable
69        + IntoNodeIdentifiers
70        + IntoNeighborsDirected
71        + GraphBase
72        + GraphProp
73        + NodeCount,
74    <G as GraphBase>::NodeId: std::cmp::Eq + Hash,
75{
76    let mut colors = DictMap::with_capacity(graph.node_count());
77    for node in graph.node_identifiers() {
78        if colors.contains_key(&node) {
79            continue;
80        }
81        let mut queue = vec![node];
82        colors.insert(node, 1);
83        while let Some(v) = queue.pop() {
84            let v_color: u8 = *colors.get(&v).unwrap();
85            let color: u8 = 1 - v_color;
86            for w in graph
87                .neighbors_directed(v, Outgoing)
88                .chain(graph.neighbors_directed(v, Incoming))
89            {
90                if let Some(color_w) = colors.get(&w) {
91                    if *color_w == v_color {
92                        return None;
93                    }
94                } else {
95                    colors.insert(w, color);
96                    queue.push(w);
97                }
98            }
99        }
100    }
101    Some(colors)
102}
103
104/// coloring strategies for greedy node- and edge- coloring algorithms
105#[derive(Clone, PartialEq)]
106pub enum ColoringStrategy {
107    Degree,
108    Saturation,
109    IndependentSet,
110}
111
112fn inner_greedy_node_color<G, F, E>(
113    graph: G,
114    preset_color_fn: F,
115    strategy: ColoringStrategy,
116) -> Result<DictMap<G::NodeId, usize>, E>
117where
118    G: NodeCount + IntoNodeIdentifiers + IntoEdges,
119    G::NodeId: Hash + Eq + Send + Sync,
120    F: FnMut(G::NodeId) -> Result<Option<usize>, E>,
121{
122    match strategy {
123        ColoringStrategy::Degree => inner_greedy_node_color_strategy_degree(graph, preset_color_fn),
124        ColoringStrategy::Saturation => {
125            inner_greedy_node_color_strategy_saturation(graph, preset_color_fn)
126        }
127        ColoringStrategy::IndependentSet => {
128            inner_greedy_node_color_strategy_independent_set(graph, preset_color_fn)
129        }
130    }
131}
132
133fn inner_greedy_node_color_strategy_degree<G, F, E>(
134    graph: G,
135    mut preset_color_fn: F,
136) -> Result<DictMap<G::NodeId, usize>, E>
137where
138    G: NodeCount + IntoNodeIdentifiers + IntoEdges,
139    G::NodeId: Hash + Eq + Send + Sync,
140    F: FnMut(G::NodeId) -> Result<Option<usize>, E>,
141{
142    let mut colors: DictMap<G::NodeId, usize> = DictMap::with_capacity(graph.node_count());
143    let mut node_vec: Vec<G::NodeId> = Vec::with_capacity(graph.node_count());
144    let mut sort_map: HashMap<G::NodeId, usize> = HashMap::with_capacity(graph.node_count());
145    for k in graph.node_identifiers() {
146        if let Some(color) = preset_color_fn(k)? {
147            colors.insert(k, color);
148            continue;
149        }
150        node_vec.push(k);
151        sort_map.insert(k, graph.edges(k).count());
152    }
153    node_vec.par_sort_by_key(|k| Reverse(sort_map.get(k)));
154
155    for node in node_vec {
156        let mut neighbor_colors: HashSet<usize> = HashSet::new();
157        for edge in graph.edges(node) {
158            let target = edge.target();
159            let existing_color = match colors.get(&target) {
160                Some(color) => color,
161                None => continue,
162            };
163            neighbor_colors.insert(*existing_color);
164        }
165        let mut current_color: usize = 0;
166        loop {
167            if !neighbor_colors.contains(&current_color) {
168                break;
169            }
170            current_color += 1;
171        }
172        colors.insert(node, current_color);
173    }
174    Ok(colors)
175}
176
177/// Data associated to nodes for the greedy coloring algorithm
178/// using the "saturation first" strategy: always picking the node that
179/// has the largest number of different colors already assigned to its
180/// neighbors, and, in case of a tie, the node that has the largest number
181/// of uncolored neighbors.
182#[derive(Clone, Eq, PartialEq)]
183struct SaturationStrategyData {
184    // degree of a node: number of neighbors without color
185    degree: usize,
186    // saturation degree of a node: number of colors used by neighbors
187    saturation: usize,
188}
189
190impl Ord for SaturationStrategyData {
191    fn cmp(&self, other: &SaturationStrategyData) -> Ordering {
192        self.saturation
193            .cmp(&other.saturation)
194            .then_with(|| self.degree.cmp(&other.degree))
195    }
196}
197
198impl PartialOrd for SaturationStrategyData {
199    fn partial_cmp(&self, other: &SaturationStrategyData) -> Option<Ordering> {
200        Some(self.cmp(other))
201    }
202}
203
204fn inner_greedy_node_color_strategy_saturation<G, F, E>(
205    graph: G,
206    mut preset_color_fn: F,
207) -> Result<DictMap<G::NodeId, usize>, E>
208where
209    G: NodeCount + IntoNodeIdentifiers + IntoEdges,
210    G::NodeId: Hash + Eq + Send + Sync,
211    F: FnMut(G::NodeId) -> Result<Option<usize>, E>,
212{
213    let mut colors: DictMap<G::NodeId, usize> = DictMap::with_capacity(graph.node_count());
214    let mut nbd_colors: HashMap<G::NodeId, HashSet<usize>> = graph
215        .node_identifiers()
216        .map(|k| (k, HashSet::new()))
217        .collect();
218
219    let mut pq: PriorityQueue<G::NodeId, SaturationStrategyData> =
220        PriorityQueue::with_capacity(graph.node_count());
221
222    // Handle preset nodes
223    for k in graph.node_identifiers() {
224        if let Some(color) = preset_color_fn(k)? {
225            colors.insert(k, color);
226            for v in graph.neighbors(k) {
227                nbd_colors.get_mut(&v).unwrap().insert(color);
228            }
229        }
230    }
231
232    // Add non-preset nodes to priority queue
233    for k in graph.node_identifiers() {
234        if colors.get(&k).is_none() {
235            let degree = graph
236                .neighbors(k)
237                .filter(|v| colors.get(v).is_none())
238                .count();
239            let saturation = nbd_colors.get(&k).unwrap().len();
240            pq.push(k, SaturationStrategyData { degree, saturation });
241        }
242    }
243
244    // Greedily process nodes
245    while let Some((k, _)) = pq.pop() {
246        let neighbor_colors = nbd_colors.get(&k).unwrap();
247        let mut current_color: usize = 0;
248        while neighbor_colors.contains(&current_color) {
249            current_color += 1;
250        }
251
252        colors.insert(k, current_color);
253        for v in graph.neighbors(k) {
254            if colors.get(&v).is_none() {
255                nbd_colors.get_mut(&v).unwrap().insert(current_color);
256                let (_, vdata) = pq.get(&v).unwrap();
257
258                pq.push(
259                    v,
260                    SaturationStrategyData {
261                        degree: vdata.degree - 1,
262                        saturation: nbd_colors.get(&v).unwrap().len(),
263                    },
264                );
265            }
266        }
267    }
268
269    Ok(colors)
270}
271
272fn inner_greedy_node_color_strategy_independent_set<G, F, E>(
273    graph: G,
274    mut preset_color_fn: F,
275) -> Result<DictMap<G::NodeId, usize>, E>
276where
277    G: NodeCount + IntoNodeIdentifiers + IntoEdges,
278    G::NodeId: Hash + Eq + Send + Sync,
279    F: FnMut(G::NodeId) -> Result<Option<usize>, E>,
280{
281    let mut colors: DictMap<G::NodeId, usize> = DictMap::with_capacity(graph.node_count());
282
283    let mut preset: HashSet<G::NodeId> = HashSet::new();
284    let mut unprocessed: IndexSet<G::NodeId, RandomState> =
285        IndexSet::with_hasher(RandomState::default());
286
287    // Handle preset nodes
288    for k in graph.node_identifiers() {
289        if let Some(color) = preset_color_fn(k)? {
290            colors.insert(k, color);
291            preset.insert(k);
292        } else {
293            unprocessed.insert(k);
294        }
295    }
296
297    let mut current_color = 0;
298    while !unprocessed.is_empty() {
299        let mut remaining: IndexSet<G::NodeId, RandomState> =
300            IndexSet::with_hasher(RandomState::default());
301
302        // Remove neighbors of preset nodes with the given color
303        for k in &preset {
304            if colors.get(k) == Some(&current_color) {
305                for u in graph.neighbors(*k) {
306                    if unprocessed.swap_take(&u).is_some() {
307                        remaining.insert(u);
308                    }
309                }
310            }
311        }
312
313        // Greedily extract maximal independent set
314        while !unprocessed.is_empty() {
315            // Greedily take any node
316            // Possible optimization is to choose node with smallest degree among unprocessed
317            let k = *unprocessed.iter().next().unwrap();
318            colors.insert(k, current_color);
319            unprocessed.swap_remove(&k);
320            for u in graph.neighbors(k) {
321                if unprocessed.swap_take(&u).is_some() {
322                    remaining.insert(u);
323                }
324            }
325        }
326
327        unprocessed = remaining;
328        current_color += 1;
329    }
330
331    Ok(colors)
332}
333
334/// Color a graph using a greedy graph coloring algorithm.
335///
336/// This function uses a `largest-first` strategy as described in:
337///
338/// Adrian Kosowski, and Krzysztof Manuszewski, Classical Coloring of Graphs,
339/// Graph Colorings, 2-19, 2004. ISBN 0-8218-3458-4.
340///
341/// to color the nodes with higher degree first.
342///
343/// The coloring problem is NP-hard and this is a heuristic algorithm
344/// which may not return an optimal solution.
345///
346/// # Note:
347/// Please consider using ``greedy_node_color_with_coloring_strategy``, which is
348/// a more general version of this function.
349///
350/// Arguments:
351///
352/// * `graph` - The graph object to run the algorithm on
353///
354/// # Example
355/// ```rust
356///
357/// use petgraph::graph::Graph;
358/// use petgraph::graph::NodeIndex;
359/// use petgraph::Undirected;
360/// use rustworkx_core::dictmap::*;
361/// use rustworkx_core::coloring::greedy_node_color;
362///
363/// let g = Graph::<(), (), Undirected>::from_edges(&[(0, 1), (0, 2)]);
364/// let colors = greedy_node_color(&g);
365/// let mut expected_colors = DictMap::new();
366/// expected_colors.insert(NodeIndex::new(0), 0);
367/// expected_colors.insert(NodeIndex::new(1), 1);
368/// expected_colors.insert(NodeIndex::new(2), 1);
369/// assert_eq!(colors, expected_colors);
370/// ```
371///
372pub fn greedy_node_color<G>(graph: G) -> DictMap<G::NodeId, usize>
373where
374    G: NodeCount + IntoNodeIdentifiers + IntoEdges,
375    G::NodeId: Hash + Eq + Send + Sync,
376{
377    inner_greedy_node_color(
378        graph,
379        |_| Ok::<Option<usize>, Infallible>(None),
380        ColoringStrategy::Degree,
381    )
382    .unwrap()
383}
384
385/// Color a graph using a greedy graph coloring algorithm with preset colors
386///
387/// This function uses a `largest-first` strategy as described in:
388///
389/// Adrian Kosowski, and Krzysztof Manuszewski, Classical Coloring of Graphs,
390/// Graph Colorings, 2-19, 2004. ISBN 0-8218-3458-4.
391///
392/// to color the nodes with higher degree first.
393///
394/// The coloring problem is NP-hard and this is a heuristic algorithm
395/// which may not return an optimal solution.
396///
397/// # Note:
398/// Please consider using ``greedy_node_color_with_coloring_strategy``, which is
399/// a more general version of this function.
400///
401/// Arguments:
402///
403/// * `graph` - The graph object to run the algorithm on
404/// * `preset_color_fn` - A callback function that will receive the node identifier
405///   for each node in the graph and is expected to return an `Option<usize>`
406///   (wrapped in a `Result`) that is `None` if the node has no preset and
407///   the usize represents the preset color.
408///
409/// # Example
410/// ```rust
411///
412/// use petgraph::graph::Graph;
413/// use petgraph::graph::NodeIndex;
414/// use petgraph::Undirected;
415/// use rustworkx_core::dictmap::*;
416/// use std::convert::Infallible;
417/// use rustworkx_core::coloring::greedy_node_color_with_preset_colors;
418///
419/// let preset_color_fn = |node_idx: NodeIndex| -> Result<Option<usize>, Infallible> {
420///     if node_idx.index() == 0 {
421///         Ok(Some(1))
422///     } else {
423///         Ok(None)
424///     }
425/// };
426///
427/// let g = Graph::<(), (), Undirected>::from_edges(&[(0, 1), (0, 2)]);
428/// let colors = greedy_node_color_with_preset_colors(&g, preset_color_fn).unwrap();
429/// let mut expected_colors = DictMap::new();
430/// expected_colors.insert(NodeIndex::new(0), 1);
431/// expected_colors.insert(NodeIndex::new(1), 0);
432/// expected_colors.insert(NodeIndex::new(2), 0);
433/// assert_eq!(colors, expected_colors);
434/// ```
435pub fn greedy_node_color_with_preset_colors<G, F, E>(
436    graph: G,
437    preset_color_fn: F,
438) -> Result<DictMap<G::NodeId, usize>, E>
439where
440    G: NodeCount + IntoNodeIdentifiers + IntoEdges,
441    G::NodeId: Hash + Eq + Send + Sync,
442    F: FnMut(G::NodeId) -> Result<Option<usize>, E>,
443{
444    inner_greedy_node_color(graph, preset_color_fn, ColoringStrategy::Degree)
445}
446
447/// Color a graph using a greedy graph coloring algorithm with preset colors.
448///
449/// This function uses one of several greedy strategies described in:
450///
451/// Adrian Kosowski, and Krzysztof Manuszewski, Classical Coloring of Graphs,
452/// Graph Colorings, 2-19, 2004. ISBN 0-8218-3458-4.
453///
454/// The `Degree` (aka `largest-first`) strategy colors the nodes with higher degree
455/// first. The `Saturation` (aka `DSATUR` and `SLF`) strategy dynamically
456/// chooses the vertex that has the largest number of different colors already
457/// assigned to its neighbors, and, in case of a tie, the vertex that has the
458/// largest number of uncolored neighbors. The `IndependentSet` strategy finds
459/// independent subsets of the graph and assigns a different color to each of these
460/// subsets.
461///
462/// to color the nodes with higher degree first.
463///
464/// The coloring problem is NP-hard and this is a heuristic algorithm
465/// which may not return an optimal solution.
466///
467/// Arguments:
468///
469/// * `graph` - The graph object to run the algorithm on.
470/// * `preset_color_fn` - A callback function that will receive the node identifier
471///   for each node in the graph and is expected to return an `Option<usize>`
472///   (wrapped in a `Result`) that is `None` if the node has no preset and
473///   the usize represents the preset color.
474/// * `strategy` - The greedy strategy used by the algorithm.
475///
476/// # Example
477/// ```rust
478///
479/// use petgraph::graph::Graph;
480/// use petgraph::graph::NodeIndex;
481/// use petgraph::Undirected;
482/// use rustworkx_core::dictmap::*;
483/// use std::convert::Infallible;
484/// use rustworkx_core::coloring::{greedy_node_color_with_coloring_strategy, ColoringStrategy};
485///
486/// let preset_color_fn = |node_idx: NodeIndex| -> Result<Option<usize>, Infallible> {
487///     if node_idx.index() == 0 {
488///         Ok(Some(1))
489///     } else {
490///         Ok(None)
491///     }
492/// };
493///
494/// let g = Graph::<(), (), Undirected>::from_edges(&[(0, 1), (0, 2)]);
495/// let colors = greedy_node_color_with_coloring_strategy(&g, preset_color_fn, ColoringStrategy::Degree).unwrap();
496/// let mut expected_colors = DictMap::new();
497/// expected_colors.insert(NodeIndex::new(0), 1);
498/// expected_colors.insert(NodeIndex::new(1), 0);
499/// expected_colors.insert(NodeIndex::new(2), 0);
500/// assert_eq!(colors, expected_colors);
501/// ```
502pub fn greedy_node_color_with_coloring_strategy<G, F, E>(
503    graph: G,
504    preset_color_fn: F,
505    strategy: ColoringStrategy,
506) -> Result<DictMap<G::NodeId, usize>, E>
507where
508    G: NodeCount + IntoNodeIdentifiers + IntoEdges,
509    G::NodeId: Hash + Eq + Send + Sync,
510    F: FnMut(G::NodeId) -> Result<Option<usize>, E>,
511{
512    inner_greedy_node_color(graph, preset_color_fn, strategy)
513}
514
515/// Color edges of a graph using a greedy approach.
516///
517/// This function works by greedily coloring the line graph of the given graph.
518///
519/// The coloring problem is NP-hard and this is a heuristic algorithm
520/// which may not return an optimal solution.
521///
522/// # Note:
523/// Please consider using ``greedy_edge_color_with_coloring_strategy``, which is
524/// a more general version of this function.
525/// Arguments:
526///
527/// * `graph` - The graph object to run the algorithm on
528///
529/// # Example
530/// ```rust
531///
532/// use petgraph::graph::Graph;
533/// use petgraph::graph::EdgeIndex;
534/// use petgraph::Undirected;
535/// use rustworkx_core::dictmap::*;
536/// use rustworkx_core::coloring::greedy_edge_color;
537///
538/// let g = Graph::<(), (), Undirected>::from_edges(&[(0, 1), (1, 2), (0, 2), (2, 3)]);
539/// let colors = greedy_edge_color(&g);
540/// let mut expected_colors = DictMap::new();
541/// expected_colors.insert(EdgeIndex::new(0), 2);
542/// expected_colors.insert(EdgeIndex::new(1), 0);
543/// expected_colors.insert(EdgeIndex::new(2), 1);
544/// expected_colors.insert(EdgeIndex::new(3), 2);
545/// assert_eq!(colors, expected_colors);
546/// ```
547///
548pub fn greedy_edge_color<G>(graph: G) -> DictMap<G::EdgeId, usize>
549where
550    G: EdgeCount + IntoNodeIdentifiers + IntoEdges,
551    G::EdgeId: Hash + Eq,
552{
553    let (new_graph, edge_to_node_map): (
554        petgraph::graph::UnGraph<(), ()>,
555        HashMap<G::EdgeId, NodeIndex>,
556    ) = line_graph(&graph, || (), || ());
557
558    let colors = greedy_node_color(&new_graph);
559
560    let mut edge_colors: DictMap<G::EdgeId, usize> = DictMap::with_capacity(graph.edge_count());
561
562    for edge in graph.edge_references() {
563        let edge_index = edge.id();
564        let node_index = edge_to_node_map.get(&edge_index).unwrap();
565        let edge_color = colors.get(node_index).unwrap();
566        edge_colors.insert(edge_index, *edge_color);
567    }
568
569    edge_colors
570}
571
572/// Color edges of a graph using a greedy graph coloring algorithm with preset
573/// colors.
574///
575/// This function works by greedily coloring the line graph of the given graph.
576///
577/// The coloring problem is NP-hard and this is a heuristic algorithm
578/// which may not return an optimal solution.
579///
580/// Arguments:
581///
582/// * `graph` - The graph object to run the algorithm on.
583/// * `preset_color_fn` - A callback function that will receive the edge identifier
584///   for each edge in the graph and is expected to return an `Option<usize>`
585///   (wrapped in a `Result`) that is `None` if the edge has no preset and
586///   the usize represents the preset color.
587/// * `strategy` - The greedy strategy used by the algorithm.
588///
589/// # Example
590/// ```rust
591///
592/// use petgraph::graph::Graph;
593/// use petgraph::graph::EdgeIndex;
594/// use petgraph::Undirected;
595/// use rustworkx_core::dictmap::*;
596/// use rustworkx_core::coloring::{greedy_edge_color_with_coloring_strategy, ColoringStrategy};
597/// use std::convert::Infallible;
598///
599/// let g = Graph::<(), (), Undirected>::from_edges(&[(0, 1), (1, 2), (0, 2), (2, 3)]);
600/// let preset_color_fn = |_| Ok::<Option<usize>, Infallible>(None);
601/// let colors = greedy_edge_color_with_coloring_strategy(&g, preset_color_fn, ColoringStrategy::Degree).unwrap();
602/// let mut expected_colors = DictMap::new();
603/// expected_colors.insert(EdgeIndex::new(0), 2);
604/// expected_colors.insert(EdgeIndex::new(1), 0);
605/// expected_colors.insert(EdgeIndex::new(2), 1);
606/// expected_colors.insert(EdgeIndex::new(3), 2);
607/// assert_eq!(colors, expected_colors);
608/// ```
609///
610pub fn greedy_edge_color_with_coloring_strategy<G, F, E>(
611    graph: G,
612    preset_color_fn: F,
613    strategy: ColoringStrategy,
614) -> Result<DictMap<G::EdgeId, usize>, E>
615where
616    G: EdgeCount + IntoNodeIdentifiers + IntoEdges,
617    G::EdgeId: Hash + Eq,
618    F: Fn(G::EdgeId) -> Result<Option<usize>, E>,
619{
620    let (new_graph, edge_to_node_map): (
621        petgraph::graph::UnGraph<(), ()>,
622        HashMap<G::EdgeId, NodeIndex>,
623    ) = line_graph(&graph, || (), || ());
624
625    let node_to_edge_map: HashMap<&NodeIndex, &G::EdgeId> =
626        edge_to_node_map.iter().map(|(k, v)| (v, k)).collect();
627    let new_graph_preset_color_fn =
628        |x: NodeIndex| preset_color_fn(**node_to_edge_map.get(&x).unwrap());
629
630    let colors = inner_greedy_node_color(&new_graph, new_graph_preset_color_fn, strategy)?;
631
632    let mut edge_colors: DictMap<G::EdgeId, usize> = DictMap::with_capacity(graph.edge_count());
633
634    for edge in graph.edge_references() {
635        let edge_index = edge.id();
636        let node_index = edge_to_node_map.get(&edge_index).unwrap();
637        let edge_color = colors.get(node_index).unwrap();
638        edge_colors.insert(edge_index, *edge_color);
639    }
640
641    Ok(edge_colors)
642}
643
644struct MisraGries<G: GraphBase> {
645    // The input graph
646    graph: G,
647    // Maximum node degree in the graph
648    max_node_degree: usize,
649    // Partially assigned colors (indexed by internal edge index)
650    colors: Vec<Option<usize>>,
651    // Performance optimization: explicitly storing edge colors used at each node
652    node_used_colors: Vec<HashSet<usize>>,
653}
654
655impl<G> MisraGries<G>
656where
657    G: EdgeIndexable + IntoEdges + NodeIndexable + IntoNodeIdentifiers,
658{
659    pub fn new(graph: G) -> Self {
660        let colors = vec![None; graph.edge_bound()];
661        let max_node_degree = graph
662            .node_identifiers()
663            .map(|node| graph.edges(node).count())
664            .max()
665            .unwrap_or(0);
666        let empty_set = HashSet::new();
667        let node_used_colors = vec![empty_set; graph.node_bound()];
668
669        MisraGries {
670            graph,
671            max_node_degree,
672            colors,
673            node_used_colors,
674        }
675    }
676
677    // Updates edge colors for a set of edges while keeping track of
678    // explicitly stored used node colors
679    fn update_edge_colors(&mut self, new_colors: &Vec<(G::EdgeRef, usize)>) {
680        // First, remove node colors that are going to be unassigned
681        for (e, _) in new_colors {
682            if let Some(old_color) = self.get_edge_color(*e) {
683                self.remove_node_used_color(e.source(), old_color);
684                self.remove_node_used_color(e.target(), old_color);
685            }
686        }
687        // Next, add node colors that are going to be assigned
688        for (e, c) in new_colors {
689            self.add_node_used_color(e.source(), *c);
690            self.add_node_used_color(e.target(), *c);
691        }
692        for (e, c) in new_colors {
693            self.colors[EdgeIndexable::to_index(&self.graph, e.id())] = Some(*c);
694        }
695    }
696
697    // Updates used node colors at u adding c
698    fn add_node_used_color(&mut self, u: G::NodeId, c: usize) {
699        let uindex = NodeIndexable::to_index(&self.graph, u);
700        self.node_used_colors[uindex].insert(c);
701    }
702
703    // Updates used node colors at u removing c
704    fn remove_node_used_color(&mut self, u: G::NodeId, c: usize) {
705        let uindex = NodeIndexable::to_index(&self.graph, u);
706        self.node_used_colors[uindex].remove(&c);
707    }
708
709    // Gets edge color
710    fn get_edge_color(&self, e: G::EdgeRef) -> Option<usize> {
711        self.colors[EdgeIndexable::to_index(&self.graph, e.id())]
712    }
713
714    // Returns colors used at node u
715    fn get_used_colors(&self, u: G::NodeId) -> &HashSet<usize> {
716        let uindex = NodeIndexable::to_index(&self.graph, u);
717        &self.node_used_colors[uindex]
718    }
719
720    // Returns the smallest free (aka unused) color at node u
721    fn get_free_color(&self, u: G::NodeId) -> usize {
722        let used_colors = self.get_used_colors(u);
723        let free_color: usize = (0..self.max_node_degree + 1)
724            .position(|color| !used_colors.contains(&color))
725            .unwrap();
726        free_color
727    }
728
729    // Returns true iff color c is free at node u
730    fn is_free_color(&self, u: G::NodeId, c: usize) -> bool {
731        !self.get_used_colors(u).contains(&c)
732    }
733
734    // Returns the maximal fan on edge (u, v) at u
735    fn get_maximal_fan(&self, e: G::EdgeRef, u: G::NodeId, v: G::NodeId) -> Vec<G::EdgeRef> {
736        let mut fan: Vec<G::EdgeRef> = vec![e];
737
738        let mut neighbors: Vec<G::EdgeRef> = self.graph.edges(u).collect();
739
740        let mut last_node_in_fan = v;
741        neighbors.remove(
742            neighbors
743                .iter()
744                .position(|x| x.target() == last_node_in_fan)
745                .unwrap(),
746        );
747
748        let mut fan_extended: bool = true;
749        while fan_extended {
750            fan_extended = false;
751
752            for edge in &neighbors {
753                if let Some(color) = self.get_edge_color(*edge) {
754                    if self.is_free_color(last_node_in_fan, color) {
755                        fan.push(*edge);
756                        last_node_in_fan = edge.target();
757                        fan_extended = true;
758                        neighbors.remove(
759                            neighbors
760                                .iter()
761                                .position(|x| x.target() == last_node_in_fan)
762                                .unwrap(),
763                        );
764                        break;
765                    }
766                }
767            }
768        }
769
770        fan
771    }
772
773    // Assuming that color is either c or d, returns the other color
774    fn flip_color(&self, c: usize, d: usize, e: usize) -> usize {
775        if e == c {
776            d
777        } else {
778            c
779        }
780    }
781
782    // Returns the longest path starting at node u with alternating colors c, d, c, d, c, etc.
783    fn get_cdu_path(&self, u: G::NodeId, c: usize, d: usize) -> Vec<(G::EdgeRef, usize)> {
784        let mut path: Vec<(G::EdgeRef, usize)> = Vec::new();
785        let mut cur_node = u;
786        let mut cur_color = c;
787        let mut path_extended = true;
788
789        while path_extended {
790            path_extended = false;
791            for edge in self.graph.edges(cur_node) {
792                if let Some(color) = self.get_edge_color(edge) {
793                    if color == cur_color {
794                        path_extended = true;
795                        path.push((edge, cur_color));
796                        cur_node = edge.target();
797                        cur_color = self.flip_color(c, d, cur_color);
798                        break;
799                    }
800                }
801            }
802        }
803        path
804    }
805
806    // Main function
807    pub fn run_algorithm(&mut self) -> &Vec<Option<usize>> {
808        for edge in self.graph.edge_references() {
809            let u: G::NodeId = edge.source();
810            let v: G::NodeId = edge.target();
811            let fan = self.get_maximal_fan(edge, u, v);
812            let c = self.get_free_color(u);
813            let d = self.get_free_color(fan.last().unwrap().target());
814
815            // find cdu-path
816            let cdu_path = self.get_cdu_path(u, d, c);
817
818            // invert colors on cdu-path
819            let mut new_cdu_path_colors: Vec<(G::EdgeRef, usize)> =
820                Vec::with_capacity(cdu_path.len());
821            for (cdu_edge, color) in cdu_path {
822                let flipped_color = self.flip_color(c, d, color);
823                new_cdu_path_colors.push((cdu_edge, flipped_color));
824            }
825            self.update_edge_colors(&new_cdu_path_colors);
826
827            // find sub-fan fan[0..w] such that d is free on fan[w]
828            let mut w = 0;
829            for (i, x) in fan.iter().enumerate() {
830                if self.is_free_color(x.target(), d) {
831                    w = i;
832                    break;
833                }
834            }
835
836            // rotate fan + fill additional color
837            let mut new_fan_colors: Vec<(G::EdgeRef, usize)> = Vec::with_capacity(w + 1);
838            for i in 1..w + 1 {
839                let next_color = self.get_edge_color(fan[i]).unwrap();
840                new_fan_colors.push((fan[i - 1], next_color));
841            }
842            new_fan_colors.push((fan[w], d));
843            self.update_edge_colors(&new_fan_colors);
844        }
845
846        &self.colors
847    }
848}
849
850/// Color edges of a graph using the Misra-Gries edge coloring algorithm.
851///
852/// Based on the paper: "A constructive proof of Vizing's theorem" by
853/// Misra and Gries, 1992.
854/// <https://www.cs.utexas.edu/users/misra/psp.dir/vizing.pdf>
855///
856/// The coloring produces at most d + 1 colors where d is the maximum degree
857/// of the graph.
858///
859/// The coloring problem is NP-hard and this is a heuristic algorithm
860/// which may not return an optimal solution.
861///
862/// Arguments:
863///
864/// * `graph` - The graph object to run the algorithm on
865///
866/// # Example
867/// ```rust
868///
869/// use petgraph::graph::Graph;
870/// use petgraph::graph::EdgeIndex;
871/// use petgraph::Undirected;
872/// use rustworkx_core::dictmap::*;
873/// use rustworkx_core::coloring::misra_gries_edge_color;///
874/// let g = Graph::<(), (), Undirected>::from_edges(&[(0, 1), (1, 2), (0, 2), (2, 3)]);
875/// let colors = misra_gries_edge_color(&g);
876///
877/// let expected_colors: DictMap<EdgeIndex, usize> = [
878///     (EdgeIndex::new(0), 2),
879///     (EdgeIndex::new(1), 1),
880///     (EdgeIndex::new(2), 0),
881///     (EdgeIndex::new(3), 2),
882/// ]
883/// .into_iter()
884/// .collect();
885///
886///  assert_eq!(colors, expected_colors);
887/// ```
888///
889pub fn misra_gries_edge_color<G>(graph: G) -> DictMap<G::EdgeId, usize>
890where
891    G: EdgeIndexable + IntoEdges + EdgeCount + NodeIndexable + IntoNodeIdentifiers,
892    G::EdgeId: Eq + Hash,
893{
894    let mut mg: MisraGries<G> = MisraGries::new(graph);
895    let colors = mg.run_algorithm();
896
897    let mut edge_colors: DictMap<G::EdgeId, usize> = DictMap::with_capacity(graph.edge_count());
898    for edge in graph.edge_references() {
899        let edge_index = edge.id();
900        let color = colors[EdgeIndexable::to_index(&graph, edge_index)].unwrap();
901        edge_colors.insert(edge_index, color);
902    }
903    edge_colors
904}
905
906#[cfg(test)]
907mod test_node_coloring {
908    use crate::coloring::{
909        greedy_node_color, greedy_node_color_with_coloring_strategy, two_color, ColoringStrategy,
910    };
911    use crate::dictmap::*;
912    use crate::generators::{complete_graph, cycle_graph, heavy_hex_graph, path_graph};
913    use std::convert::Infallible;
914    use std::hash::Hash;
915
916    use crate::petgraph::graph::NodeIndex;
917    use crate::petgraph::prelude::*;
918    use crate::petgraph::Undirected;
919    use petgraph::visit::{IntoEdgeReferences, IntoNodeIdentifiers};
920
921    /// Helper function to check validity of node coloring
922    fn check_node_colors<G>(graph: G, colors: &DictMap<G::NodeId, usize>)
923    where
924        G: IntoNodeIdentifiers + IntoEdgeReferences,
925        G::NodeId: Hash + Eq + Send + Sync,
926    {
927        // Check that every node has valid color
928        for k in graph.node_identifiers() {
929            if !colors.contains_key(&k) {
930                panic!("Problem: some nodes have no color assigned.");
931            } else {
932                println!("Valid color: ok");
933            }
934        }
935
936        // Check that nodes connected by an edge have different colors
937        for e in graph.edge_references() {
938            if colors.get(&e.source()) == colors.get(&e.target()) {
939                panic!("Problem: same color for connected nodes.");
940            } else {
941                println!("Connected nodes: ok");
942            }
943        }
944    }
945
946    /// Helper function to check validity of node coloring with preset colors
947    fn check_preset_colors<G, F, E>(
948        graph: G,
949        colors: &DictMap<G::NodeId, usize>,
950        mut preset_color_fn: F,
951    ) where
952        G: IntoNodeIdentifiers + IntoEdgeReferences,
953        G::NodeId: Hash + Eq + Send + Sync,
954        F: FnMut(G::NodeId) -> Result<Option<usize>, E>,
955    {
956        // Check preset values
957        for k in graph.node_identifiers() {
958            if let Ok(Some(color)) = preset_color_fn(k) {
959                if *colors.get(&k).unwrap() != color {
960                    panic!("Problem: colors are different from preset vales.");
961                } else {
962                    println!("Preset values: ok");
963                }
964            }
965        }
966    }
967
968    #[test]
969    fn test_legacy_greedy_node_color_empty_graph() {
970        // Empty graph
971        let graph = Graph::<(), (), Undirected>::new_undirected();
972        let colors = greedy_node_color(&graph);
973        let expected_colors: DictMap<NodeIndex, usize> = [].into_iter().collect();
974        assert_eq!(colors, expected_colors);
975    }
976
977    #[test]
978    fn test_legacy_greedy_node_color_simple_graph() {
979        // Simple graph
980        let graph = Graph::<(), (), Undirected>::from_edges([(0, 1), (0, 2)]);
981        let colors = greedy_node_color(&graph);
982        let expected_colors: DictMap<NodeIndex, usize> = [
983            (NodeIndex::new(0), 0),
984            (NodeIndex::new(1), 1),
985            (NodeIndex::new(2), 1),
986        ]
987        .into_iter()
988        .collect();
989        assert_eq!(colors, expected_colors);
990    }
991
992    #[test]
993    fn test_legacy_greedy_node_color_simple_graph_large_degree() {
994        // Graph with multiple edges
995        let graph = Graph::<(), (), Undirected>::from_edges([
996            (0, 1),
997            (0, 2),
998            (0, 2),
999            (0, 2),
1000            (0, 2),
1001            (0, 2),
1002        ]);
1003        let colors = greedy_node_color(&graph);
1004        let expected_colors: DictMap<NodeIndex, usize> = [
1005            (NodeIndex::new(0), 0),
1006            (NodeIndex::new(1), 1),
1007            (NodeIndex::new(2), 1),
1008        ]
1009        .into_iter()
1010        .collect();
1011        assert_eq!(colors, expected_colors);
1012    }
1013
1014    #[test]
1015    fn test_greedy_node_color_empty_graph() {
1016        // Empty graph
1017        let graph = Graph::<(), (), Undirected>::new_undirected();
1018        let preset_color_fn = |_| Ok::<Option<usize>, Infallible>(None);
1019
1020        for strategy in [
1021            ColoringStrategy::Degree,
1022            ColoringStrategy::Saturation,
1023            ColoringStrategy::IndependentSet,
1024        ] {
1025            let colors =
1026                greedy_node_color_with_coloring_strategy(&graph, preset_color_fn, strategy);
1027            let expected_colors: DictMap<NodeIndex, usize> = [].into_iter().collect();
1028            assert_eq!(colors, Ok(expected_colors));
1029        }
1030    }
1031
1032    #[test]
1033    fn test_greedy_node_color_simple_graph() {
1034        // Simple graph
1035        let graph = Graph::<(), (), Undirected>::from_edges([(0, 1), (0, 2)]);
1036        let preset_color_fn = |_| Ok::<Option<usize>, Infallible>(None);
1037        let colors = greedy_node_color_with_coloring_strategy(
1038            &graph,
1039            preset_color_fn,
1040            ColoringStrategy::Degree,
1041        );
1042        let expected_colors: DictMap<NodeIndex, usize> = [
1043            (NodeIndex::new(0), 0),
1044            (NodeIndex::new(1), 1),
1045            (NodeIndex::new(2), 1),
1046        ]
1047        .into_iter()
1048        .collect();
1049        assert_eq!(colors, Ok(expected_colors));
1050    }
1051
1052    #[test]
1053    fn test_greedy_node_color_simple_graph_large_degree() {
1054        // Graph with multiple edges
1055        let graph = Graph::<(), (), Undirected>::from_edges([
1056            (0, 1),
1057            (0, 2),
1058            (0, 2),
1059            (0, 2),
1060            (0, 2),
1061            (0, 2),
1062        ]);
1063        let preset_color_fn = |_| Ok::<Option<usize>, Infallible>(None);
1064        let colors = greedy_node_color_with_coloring_strategy(
1065            &graph,
1066            preset_color_fn,
1067            ColoringStrategy::Degree,
1068        );
1069        let expected_colors: DictMap<NodeIndex, usize> = [
1070            (NodeIndex::new(0), 0),
1071            (NodeIndex::new(1), 1),
1072            (NodeIndex::new(2), 1),
1073        ]
1074        .into_iter()
1075        .collect();
1076        assert_eq!(colors, Ok(expected_colors));
1077    }
1078
1079    #[test]
1080    fn test_greedy_node_color_saturation() {
1081        // Simple graph
1082        let graph = Graph::<(), (), Undirected>::from_edges([
1083            (0, 1),
1084            (0, 2),
1085            (0, 3),
1086            (3, 4),
1087            (4, 5),
1088            (5, 6),
1089            (5, 7),
1090        ]);
1091
1092        let preset_color_fn = |_| Ok::<Option<usize>, Infallible>(None);
1093        let colors = greedy_node_color_with_coloring_strategy(
1094            &graph,
1095            preset_color_fn,
1096            ColoringStrategy::Saturation,
1097        )
1098        .unwrap();
1099        check_node_colors(&graph, &colors);
1100
1101        let expected_colors: DictMap<NodeIndex, usize> = [
1102            (NodeIndex::new(0), 0),
1103            (NodeIndex::new(1), 1),
1104            (NodeIndex::new(2), 1),
1105            (NodeIndex::new(3), 1),
1106            (NodeIndex::new(4), 0),
1107            (NodeIndex::new(5), 1),
1108            (NodeIndex::new(6), 0),
1109            (NodeIndex::new(7), 0),
1110        ]
1111        .into_iter()
1112        .collect();
1113        assert_eq!(colors, expected_colors);
1114    }
1115
1116    #[test]
1117    fn test_greedy_node_color_saturation_and_preset() {
1118        // Simple graph
1119        let graph = Graph::<(), (), Undirected>::from_edges([(0, 1), (0, 2), (2, 3), (2, 4)]);
1120
1121        let preset_color_fn = |node_idx: NodeIndex| -> Result<Option<usize>, Infallible> {
1122            if node_idx.index() == 0 {
1123                Ok(Some(1))
1124            } else {
1125                Ok(None)
1126            }
1127        };
1128
1129        let colors = greedy_node_color_with_coloring_strategy(
1130            &graph,
1131            preset_color_fn,
1132            ColoringStrategy::Saturation,
1133        )
1134        .unwrap();
1135        check_node_colors(&graph, &colors);
1136        check_preset_colors(&graph, &colors, preset_color_fn);
1137
1138        let expected_colors: DictMap<NodeIndex, usize> = [
1139            (NodeIndex::new(0), 1),
1140            (NodeIndex::new(1), 0),
1141            (NodeIndex::new(2), 0),
1142            (NodeIndex::new(3), 1),
1143            (NodeIndex::new(4), 1),
1144        ]
1145        .into_iter()
1146        .collect();
1147        assert_eq!(colors, expected_colors);
1148    }
1149
1150    #[test]
1151    fn test_greedy_node_color_independent_set() {
1152        // Simple graph
1153        let graph = Graph::<(), (), Undirected>::from_edges([
1154            (0, 1),
1155            (0, 2),
1156            (0, 3),
1157            (3, 4),
1158            (4, 5),
1159            (5, 6),
1160            (5, 7),
1161        ]);
1162
1163        let preset_color_fn = |_| Ok::<Option<usize>, Infallible>(None);
1164        let colors = greedy_node_color_with_coloring_strategy(
1165            &graph,
1166            preset_color_fn,
1167            ColoringStrategy::IndependentSet,
1168        )
1169        .unwrap();
1170        check_node_colors(&graph, &colors);
1171
1172        let expected_colors: DictMap<NodeIndex, usize> = [
1173            (NodeIndex::new(0), 0),
1174            (NodeIndex::new(1), 1),
1175            (NodeIndex::new(2), 1),
1176            (NodeIndex::new(3), 1),
1177            (NodeIndex::new(4), 0),
1178            (NodeIndex::new(5), 1),
1179            (NodeIndex::new(6), 0),
1180            (NodeIndex::new(7), 0),
1181        ]
1182        .into_iter()
1183        .collect();
1184        assert_eq!(colors, expected_colors);
1185    }
1186
1187    #[test]
1188    fn test_greedy_node_color_independent_set_and_preset() {
1189        // Simple graph
1190        let graph = Graph::<(), (), Undirected>::from_edges([
1191            (0, 1),
1192            (0, 2),
1193            (0, 3),
1194            (3, 4),
1195            (4, 5),
1196            (5, 6),
1197            (5, 7),
1198        ]);
1199
1200        let preset_color_fn = |node_idx: NodeIndex| -> Result<Option<usize>, Infallible> {
1201            if node_idx.index() == 0 {
1202                Ok(Some(1))
1203            } else if node_idx.index() == 3 {
1204                Ok(Some(0))
1205            } else {
1206                Ok(None)
1207            }
1208        };
1209
1210        let colors = greedy_node_color_with_coloring_strategy(
1211            &graph,
1212            preset_color_fn,
1213            ColoringStrategy::IndependentSet,
1214        )
1215        .unwrap();
1216        check_node_colors(&graph, &colors);
1217        check_preset_colors(&graph, &colors, preset_color_fn);
1218
1219        let expected_colors: DictMap<NodeIndex, usize> = [
1220            (NodeIndex::new(0), 1),
1221            (NodeIndex::new(1), 0),
1222            (NodeIndex::new(2), 0),
1223            (NodeIndex::new(3), 0),
1224            (NodeIndex::new(4), 1),
1225            (NodeIndex::new(5), 2),
1226            (NodeIndex::new(6), 0),
1227            (NodeIndex::new(7), 0),
1228        ]
1229        .into_iter()
1230        .collect();
1231        assert_eq!(colors, expected_colors);
1232    }
1233
1234    #[test]
1235    fn test_two_color_directed() {
1236        let edge_list = vec![(0, 1), (1, 2), (2, 3), (3, 4)];
1237
1238        let graph = DiGraph::<i32, i32>::from_edges(edge_list);
1239        let coloring = two_color(&graph).unwrap();
1240        let mut expected_colors = DictMap::new();
1241        expected_colors.insert(NodeIndex::new(0), 1);
1242        expected_colors.insert(NodeIndex::new(1), 0);
1243        expected_colors.insert(NodeIndex::new(2), 1);
1244        expected_colors.insert(NodeIndex::new(3), 0);
1245        expected_colors.insert(NodeIndex::new(4), 1);
1246        assert_eq!(coloring, expected_colors)
1247    }
1248
1249    #[test]
1250    fn test_two_color_directed_not_bipartite() {
1251        let edge_list = vec![(0, 1), (1, 2), (2, 3), (3, 0), (3, 1)];
1252
1253        let graph = DiGraph::<i32, i32>::from_edges(edge_list);
1254        let coloring = two_color(&graph);
1255        assert_eq!(None, coloring)
1256    }
1257
1258    #[test]
1259    fn test_two_color_undirected_not_bipartite() {
1260        let edge_list = vec![(0, 1), (1, 2), (2, 3), (3, 0), (3, 1)];
1261
1262        let graph = UnGraph::<i32, i32>::from_edges(edge_list);
1263        let coloring = two_color(&graph);
1264        assert_eq!(None, coloring)
1265    }
1266
1267    #[test]
1268    fn test_two_color_directed_with_isolates() {
1269        let edge_list = vec![(0, 1), (1, 2), (2, 3), (3, 4)];
1270
1271        let mut graph = DiGraph::<i32, i32>::from_edges(edge_list);
1272        graph.add_node(10);
1273        graph.add_node(11);
1274        let coloring = two_color(&graph).unwrap();
1275        let mut expected_colors = DictMap::new();
1276        expected_colors.insert(NodeIndex::new(0), 1);
1277        expected_colors.insert(NodeIndex::new(1), 0);
1278        expected_colors.insert(NodeIndex::new(2), 1);
1279        expected_colors.insert(NodeIndex::new(3), 0);
1280        expected_colors.insert(NodeIndex::new(4), 1);
1281        expected_colors.insert(NodeIndex::new(5), 1);
1282        expected_colors.insert(NodeIndex::new(6), 1);
1283        assert_eq!(coloring, expected_colors)
1284    }
1285
1286    #[test]
1287    fn test_two_color_undirected_with_isolates() {
1288        let edge_list = vec![(0, 1), (1, 2), (2, 3), (3, 4)];
1289
1290        let mut graph = UnGraph::<i32, i32>::from_edges(edge_list);
1291        graph.add_node(10);
1292        graph.add_node(11);
1293        let coloring = two_color(&graph).unwrap();
1294        let mut expected_colors = DictMap::new();
1295        expected_colors.insert(NodeIndex::new(0), 1);
1296        expected_colors.insert(NodeIndex::new(1), 0);
1297        expected_colors.insert(NodeIndex::new(2), 1);
1298        expected_colors.insert(NodeIndex::new(3), 0);
1299        expected_colors.insert(NodeIndex::new(4), 1);
1300        expected_colors.insert(NodeIndex::new(5), 1);
1301        expected_colors.insert(NodeIndex::new(6), 1);
1302        assert_eq!(coloring, expected_colors)
1303    }
1304
1305    #[test]
1306    fn test_path_graph() {
1307        let graph: petgraph::graph::UnGraph<(), ()> =
1308            path_graph(Some(7), None, || (), || (), false).unwrap();
1309        let preset_color_fn = |_| Ok::<Option<usize>, Infallible>(None);
1310
1311        for strategy in [
1312            ColoringStrategy::Degree,
1313            ColoringStrategy::Saturation,
1314            ColoringStrategy::IndependentSet,
1315        ] {
1316            let colors =
1317                greedy_node_color_with_coloring_strategy(&graph, preset_color_fn, strategy)
1318                    .unwrap();
1319            check_node_colors(&graph, &colors);
1320        }
1321    }
1322
1323    #[test]
1324    fn test_cycle_graph() {
1325        let graph: petgraph::graph::UnGraph<(), ()> =
1326            cycle_graph(Some(15), None, || (), || (), false).unwrap();
1327        let preset_color_fn = |_| Ok::<Option<usize>, Infallible>(None);
1328
1329        for strategy in [
1330            ColoringStrategy::Degree,
1331            ColoringStrategy::Saturation,
1332            ColoringStrategy::IndependentSet,
1333        ] {
1334            let colors =
1335                greedy_node_color_with_coloring_strategy(&graph, preset_color_fn, strategy)
1336                    .unwrap();
1337            check_node_colors(&graph, &colors);
1338        }
1339    }
1340
1341    #[test]
1342    fn test_heavy_hex_graph() {
1343        let graph: petgraph::graph::UnGraph<(), ()> =
1344            heavy_hex_graph(7, || (), || (), false).unwrap();
1345        let preset_color_fn = |_| Ok::<Option<usize>, Infallible>(None);
1346
1347        for strategy in [
1348            ColoringStrategy::Degree,
1349            ColoringStrategy::Saturation,
1350            ColoringStrategy::IndependentSet,
1351        ] {
1352            let colors =
1353                greedy_node_color_with_coloring_strategy(&graph, preset_color_fn, strategy)
1354                    .unwrap();
1355            check_node_colors(&graph, &colors);
1356        }
1357    }
1358
1359    #[test]
1360    fn test_complete_graph() {
1361        let graph: petgraph::graph::UnGraph<(), ()> =
1362            complete_graph(Some(10), None, || (), || ()).unwrap();
1363        let preset_color_fn = |_| Ok::<Option<usize>, Infallible>(None);
1364
1365        for strategy in [
1366            ColoringStrategy::Degree,
1367            ColoringStrategy::Saturation,
1368            ColoringStrategy::IndependentSet,
1369        ] {
1370            let colors =
1371                greedy_node_color_with_coloring_strategy(&graph, preset_color_fn, strategy)
1372                    .unwrap();
1373            check_node_colors(&graph, &colors);
1374        }
1375    }
1376}
1377
1378#[cfg(test)]
1379mod test_edge_coloring {
1380    use crate::coloring::{
1381        greedy_edge_color, greedy_edge_color_with_coloring_strategy, ColoringStrategy,
1382    };
1383    use crate::dictmap::DictMap;
1384    use crate::petgraph::Graph;
1385    use std::convert::Infallible;
1386
1387    use petgraph::graph::{edge_index, EdgeIndex};
1388    use petgraph::Undirected;
1389
1390    #[test]
1391    fn test_legacy_greedy_edge_color_empty_graph() {
1392        // Empty graph
1393        let graph = Graph::<(), (), Undirected>::new_undirected();
1394        let colors = greedy_edge_color(&graph);
1395        let expected_colors: DictMap<EdgeIndex, usize> = [].into_iter().collect();
1396        assert_eq!(colors, expected_colors);
1397    }
1398
1399    #[test]
1400    fn test_legacy_greedy_edge_color_simple_graph() {
1401        // Graph with an edge removed
1402        let graph = Graph::<(), (), Undirected>::from_edges([(0, 1), (1, 2), (2, 3)]);
1403        let colors = greedy_edge_color(&graph);
1404        let expected_colors: DictMap<EdgeIndex, usize> = [
1405            (EdgeIndex::new(0), 1),
1406            (EdgeIndex::new(1), 0),
1407            (EdgeIndex::new(2), 1),
1408        ]
1409        .into_iter()
1410        .collect();
1411        assert_eq!(colors, expected_colors);
1412    }
1413
1414    #[test]
1415    fn test_legacy_greedy_edge_color_graph_with_removed_edges() {
1416        // Simple graph
1417        let mut graph = Graph::<(), (), Undirected>::from_edges([(0, 1), (1, 2), (2, 3), (3, 0)]);
1418        graph.remove_edge(edge_index(1));
1419        let colors = greedy_edge_color(&graph);
1420        let expected_colors: DictMap<EdgeIndex, usize> = [
1421            (EdgeIndex::new(0), 1),
1422            (EdgeIndex::new(1), 0),
1423            (EdgeIndex::new(2), 1),
1424        ]
1425        .into_iter()
1426        .collect();
1427        assert_eq!(colors, expected_colors);
1428    }
1429
1430    #[test]
1431    fn test_greedy_edge_color_empty_graph() {
1432        // Empty graph
1433        let graph = Graph::<(), (), Undirected>::new_undirected();
1434        let preset_color_fn = |_| Ok::<Option<usize>, Infallible>(None);
1435
1436        let colors = greedy_edge_color_with_coloring_strategy(
1437            &graph,
1438            preset_color_fn,
1439            ColoringStrategy::Degree,
1440        )
1441        .unwrap();
1442        let expected_colors: DictMap<EdgeIndex, usize> = [].into_iter().collect();
1443        assert_eq!(colors, expected_colors);
1444    }
1445
1446    #[test]
1447    fn test_greedy_edge_color_simple_graph() {
1448        // Graph with an edge removed
1449        let graph = Graph::<(), (), Undirected>::from_edges([(0, 1), (1, 2), (2, 3)]);
1450        let preset_color_fn = |_| Ok::<Option<usize>, Infallible>(None);
1451
1452        let colors = greedy_edge_color_with_coloring_strategy(
1453            &graph,
1454            preset_color_fn,
1455            ColoringStrategy::Degree,
1456        )
1457        .unwrap();
1458        let expected_colors: DictMap<EdgeIndex, usize> = [
1459            (EdgeIndex::new(0), 1),
1460            (EdgeIndex::new(1), 0),
1461            (EdgeIndex::new(2), 1),
1462        ]
1463        .into_iter()
1464        .collect();
1465        assert_eq!(colors, expected_colors);
1466    }
1467
1468    #[test]
1469    fn test_greedy_edge_color_graph_with_removed_edges() {
1470        // Simple graph
1471        let mut graph = Graph::<(), (), Undirected>::from_edges([(0, 1), (1, 2), (2, 3), (3, 0)]);
1472        graph.remove_edge(edge_index(1));
1473
1474        let preset_color_fn = |_| Ok::<Option<usize>, Infallible>(None);
1475
1476        let colors = greedy_edge_color_with_coloring_strategy(
1477            &graph,
1478            preset_color_fn,
1479            ColoringStrategy::Degree,
1480        )
1481        .unwrap();
1482
1483        let expected_colors: DictMap<EdgeIndex, usize> = [
1484            (EdgeIndex::new(0), 1),
1485            (EdgeIndex::new(1), 0),
1486            (EdgeIndex::new(2), 1),
1487        ]
1488        .into_iter()
1489        .collect();
1490        assert_eq!(colors, expected_colors);
1491    }
1492
1493    #[test]
1494    fn test_greedy_edge_color_degree() {
1495        // Simple graph
1496        let graph =
1497            Graph::<(), (), Undirected>::from_edges([(0, 1), (1, 2), (2, 3), (3, 0), (2, 4)]);
1498        let preset_color_fn = |_| Ok::<Option<usize>, Infallible>(None);
1499
1500        let colors = greedy_edge_color_with_coloring_strategy(
1501            &graph,
1502            preset_color_fn,
1503            ColoringStrategy::Degree,
1504        )
1505        .unwrap();
1506        let expected_colors: DictMap<EdgeIndex, usize> = [
1507            (EdgeIndex::new(0), 1),
1508            (EdgeIndex::new(1), 0),
1509            (EdgeIndex::new(2), 1),
1510            (EdgeIndex::new(3), 0),
1511            (EdgeIndex::new(4), 2),
1512        ]
1513        .into_iter()
1514        .collect();
1515        assert_eq!(colors, expected_colors);
1516    }
1517
1518    #[test]
1519    fn test_greedy_edge_color_degree_with_preset() {
1520        // Simple graph
1521        let graph =
1522            Graph::<(), (), Undirected>::from_edges([(0, 1), (1, 2), (2, 3), (3, 0), (2, 4)]);
1523
1524        let preset_color_fn = |node_idx: EdgeIndex| -> Result<Option<usize>, Infallible> {
1525            if node_idx.index() == 1 {
1526                Ok(Some(1))
1527            } else {
1528                Ok(None)
1529            }
1530        };
1531
1532        let colors = greedy_edge_color_with_coloring_strategy(
1533            &graph,
1534            preset_color_fn,
1535            ColoringStrategy::Degree,
1536        )
1537        .unwrap();
1538        let expected_colors: DictMap<EdgeIndex, usize> = [
1539            (EdgeIndex::new(0), 0),
1540            (EdgeIndex::new(1), 1),
1541            (EdgeIndex::new(2), 0),
1542            (EdgeIndex::new(3), 1),
1543            (EdgeIndex::new(4), 2),
1544        ]
1545        .into_iter()
1546        .collect();
1547        assert_eq!(colors, expected_colors);
1548    }
1549
1550    #[test]
1551    fn test_greedy_edge_color_saturation() {
1552        // Simple graph
1553        let graph =
1554            Graph::<(), (), Undirected>::from_edges([(0, 1), (1, 2), (2, 3), (3, 0), (2, 4)]);
1555        let preset_color_fn = |_| Ok::<Option<usize>, Infallible>(None);
1556
1557        let colors = greedy_edge_color_with_coloring_strategy(
1558            &graph,
1559            preset_color_fn,
1560            ColoringStrategy::Saturation,
1561        )
1562        .unwrap();
1563        let expected_colors: DictMap<EdgeIndex, usize> = [
1564            (EdgeIndex::new(0), 1),
1565            (EdgeIndex::new(1), 0),
1566            (EdgeIndex::new(2), 1),
1567            (EdgeIndex::new(3), 0),
1568            (EdgeIndex::new(4), 2),
1569        ]
1570        .into_iter()
1571        .collect();
1572        assert_eq!(colors, expected_colors);
1573    }
1574
1575    #[test]
1576    fn test_greedy_edge_color_saturation_with_preset() {
1577        // Simple graph
1578        let graph =
1579            Graph::<(), (), Undirected>::from_edges([(0, 1), (1, 2), (2, 3), (3, 0), (2, 4)]);
1580
1581        let preset_color_fn = |node_idx: EdgeIndex| -> Result<Option<usize>, Infallible> {
1582            if node_idx.index() == 1 {
1583                Ok(Some(1))
1584            } else if node_idx.index() == 4 {
1585                Ok(Some(0))
1586            } else {
1587                Ok(None)
1588            }
1589        };
1590
1591        let colors = greedy_edge_color_with_coloring_strategy(
1592            &graph,
1593            preset_color_fn,
1594            ColoringStrategy::Saturation,
1595        )
1596        .unwrap();
1597        let expected_colors: DictMap<EdgeIndex, usize> = [
1598            (EdgeIndex::new(0), 0),
1599            (EdgeIndex::new(1), 1),
1600            (EdgeIndex::new(2), 2),
1601            (EdgeIndex::new(3), 1),
1602            (EdgeIndex::new(4), 0),
1603        ]
1604        .into_iter()
1605        .collect();
1606        assert_eq!(colors, expected_colors);
1607    }
1608
1609    #[test]
1610    fn test_greedy_edge_color_independent_set() {
1611        // Simple graph
1612        let graph =
1613            Graph::<(), (), Undirected>::from_edges([(0, 1), (1, 2), (2, 3), (3, 0), (2, 4)]);
1614        let preset_color_fn = |_| Ok::<Option<usize>, Infallible>(None);
1615
1616        let colors = greedy_edge_color_with_coloring_strategy(
1617            &graph,
1618            preset_color_fn,
1619            ColoringStrategy::IndependentSet,
1620        )
1621        .unwrap();
1622        let expected_colors: DictMap<EdgeIndex, usize> = [
1623            (EdgeIndex::new(0), 0),
1624            (EdgeIndex::new(1), 1),
1625            (EdgeIndex::new(2), 2),
1626            (EdgeIndex::new(3), 1),
1627            (EdgeIndex::new(4), 0),
1628        ]
1629        .into_iter()
1630        .collect();
1631        assert_eq!(colors, expected_colors);
1632    }
1633
1634    #[test]
1635    fn test_greedy_edge_color_independent_set_with_preset() {
1636        // Simple graph
1637        let graph =
1638            Graph::<(), (), Undirected>::from_edges([(0, 1), (1, 2), (2, 3), (3, 0), (2, 4)]);
1639
1640        let preset_color_fn = |node_idx: EdgeIndex| -> Result<Option<usize>, Infallible> {
1641            if node_idx.index() == 1 {
1642                Ok(Some(0))
1643            } else if node_idx.index() == 4 {
1644                Ok(Some(2))
1645            } else {
1646                Ok(None)
1647            }
1648        };
1649
1650        let colors = greedy_edge_color_with_coloring_strategy(
1651            &graph,
1652            preset_color_fn,
1653            ColoringStrategy::IndependentSet,
1654        )
1655        .unwrap();
1656        let expected_colors: DictMap<EdgeIndex, usize> = [
1657            (EdgeIndex::new(0), 1),
1658            (EdgeIndex::new(1), 0),
1659            (EdgeIndex::new(2), 1),
1660            (EdgeIndex::new(3), 0),
1661            (EdgeIndex::new(4), 2),
1662        ]
1663        .into_iter()
1664        .collect();
1665        assert_eq!(colors, expected_colors);
1666    }
1667}
1668
1669#[cfg(test)]
1670mod test_misra_gries_edge_coloring {
1671    use crate::coloring::misra_gries_edge_color;
1672    use crate::dictmap::DictMap;
1673    use crate::generators::{complete_graph, cycle_graph, heavy_hex_graph, path_graph};
1674    use crate::petgraph::Graph;
1675
1676    use hashbrown::HashSet;
1677    use petgraph::graph::EdgeIndex;
1678    use petgraph::visit::{EdgeRef, IntoEdges, IntoNodeIdentifiers, NodeIndexable};
1679    use petgraph::Undirected;
1680    use std::fmt::Debug;
1681    use std::hash::Hash;
1682
1683    fn check_edge_coloring<G>(graph: G, colors: &DictMap<G::EdgeId, usize>)
1684    where
1685        G: NodeIndexable + IntoEdges + IntoNodeIdentifiers,
1686        G::EdgeId: Eq + Hash + Debug,
1687    {
1688        // Check that every edge has valid color
1689        for edge in graph.edge_references() {
1690            if !colors.contains_key(&edge.id()) {
1691                panic!("Problem: edge {:?} has no color assigned.", &edge.id());
1692            }
1693        }
1694
1695        // Check that for every node all of its edges have different colors
1696        // (i.e. the number of used colors is equal to the degree).
1697        // Also compute maximum color used and maximum node degree.
1698        let mut max_color = 0;
1699        let mut max_node_degree = 0;
1700        let node_indices: Vec<G::NodeId> = graph.node_identifiers().collect();
1701        for node in node_indices {
1702            let mut cur_node_degree = 0;
1703            let mut used_colors: HashSet<usize> = HashSet::new();
1704            for edge in graph.edges(node) {
1705                let color = colors.get(&edge.id()).unwrap();
1706                used_colors.insert(*color);
1707                cur_node_degree += 1;
1708                if max_color < *color {
1709                    max_color = *color;
1710                }
1711            }
1712            if used_colors.len() < cur_node_degree {
1713                panic!(
1714                    "Problem: node {:?} does not have enough colors.",
1715                    NodeIndexable::to_index(&graph, node)
1716                );
1717            }
1718
1719            if cur_node_degree > max_node_degree {
1720                max_node_degree = cur_node_degree
1721            }
1722        }
1723
1724        // Check that number of colors used is at most max_node_degree + 1
1725        // (note that number of colors is max_color + 1).
1726        if max_color > max_node_degree {
1727            panic!(
1728                "Problem: too many colors are used ({} colors used, {} max node degree)",
1729                max_color + 1,
1730                max_node_degree
1731            );
1732        }
1733    }
1734
1735    #[test]
1736    fn test_simple_graph() {
1737        let graph = Graph::<(), (), Undirected>::from_edges([(0, 1), (0, 2), (0, 3), (3, 4)]);
1738        let colors = misra_gries_edge_color(&graph);
1739        check_edge_coloring(&graph, &colors);
1740
1741        let expected_colors: DictMap<EdgeIndex, usize> = [
1742            (EdgeIndex::new(0), 0),
1743            (EdgeIndex::new(1), 2),
1744            (EdgeIndex::new(2), 1),
1745            (EdgeIndex::new(3), 3),
1746        ]
1747        .into_iter()
1748        .collect();
1749        assert_eq!(colors, expected_colors);
1750    }
1751
1752    #[test]
1753    fn test_path_graph() {
1754        let graph: petgraph::graph::UnGraph<(), ()> =
1755            path_graph(Some(7), None, || (), || (), false).unwrap();
1756        let colors = misra_gries_edge_color(&graph);
1757        check_edge_coloring(&graph, &colors);
1758    }
1759
1760    #[test]
1761    fn test_cycle_graph() {
1762        let graph: petgraph::graph::UnGraph<(), ()> =
1763            cycle_graph(Some(15), None, || (), || (), false).unwrap();
1764        let colors = misra_gries_edge_color(&graph);
1765        check_edge_coloring(&graph, &colors);
1766    }
1767
1768    #[test]
1769    fn test_heavy_hex_graph() {
1770        let graph: petgraph::graph::UnGraph<(), ()> =
1771            heavy_hex_graph(7, || (), || (), false).unwrap();
1772        let colors = misra_gries_edge_color(&graph);
1773        check_edge_coloring(&graph, &colors);
1774    }
1775
1776    #[test]
1777    fn test_complete_graph() {
1778        let graph: petgraph::graph::UnGraph<(), ()> =
1779            complete_graph(Some(10), None, || (), || ()).unwrap();
1780        let colors = misra_gries_edge_color(&graph);
1781        check_edge_coloring(&graph, &colors);
1782    }
1783}