Skip to main content

problemreductions/rules/
minimummaximalmatching_maximumachromaticnumber.rs

1//! Reduction from MinimumMaximalMatching (on a bipartite graph) to
2//! MaximumAchromaticNumber.
3//!
4//! Classical reduction of Yannakakis and Gavril (1980) establishing
5//! NP-completeness of Achromatic Number (G&J GT5). For a bipartite graph `G`,
6//! the identity `ach(complement(G)) = |V| - mm(G)` holds, where `mm(G)` is the
7//! minimum maximal matching size of `G`. The decision-version correspondence
8//! used in the reduction is `(G, K) -> (complement(G), |V| - K)`.
9
10use crate::models::graph::{MaximumAchromaticNumber, MinimumMaximalMatching};
11use crate::reduction;
12use crate::rules::traits::{ReduceTo, ReductionResult};
13use crate::topology::{BipartiteGraph, Graph, SimpleGraph};
14
15/// Result of reducing `MinimumMaximalMatching<BipartiteGraph>` to
16/// `MaximumAchromaticNumber<SimpleGraph>`.
17///
18/// Stores the target problem along with the source edge list (in unified vertex
19/// coordinates) so that `extract_solution` can map a target coloring back to a
20/// maximal matching of the source graph.
21#[derive(Debug, Clone)]
22pub struct ReductionMMMToAchromatic {
23    target: MaximumAchromaticNumber<SimpleGraph>,
24    /// Source edges in unified vertex coordinates, in the same order as
25    /// `source.graph().edges()` (which determines the source `dims()`).
26    source_edges: Vec<(usize, usize)>,
27}
28
29impl ReductionResult for ReductionMMMToAchromatic {
30    type Source = MinimumMaximalMatching<BipartiteGraph>;
31    type Target = MaximumAchromaticNumber<SimpleGraph>;
32
33    fn target_problem(&self) -> &Self::Target {
34        &self.target
35    }
36
37    /// Extract a maximal matching of the source graph from an achromatic
38    /// coloring of `complement(G)`.
39    ///
40    /// Each color class of size exactly 2 in `complement(G)` is an independent
41    /// set there and thus a clique in `G`. For bipartite `G` such a clique has
42    /// size 2, i.e., a source edge. A source edge `(u, v)` belongs to the
43    /// extracted matching iff `u` and `v` share a color, which we detect in a
44    /// single pass over `source_edges`.
45    fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
46        self.source_edges
47            .iter()
48            .map(|&(u, v)| usize::from(target_solution[u] == target_solution[v]))
49            .collect()
50    }
51}
52
53#[reduction(
54    overhead = {
55        num_vertices = "num_vertices",
56        num_edges = "num_vertices * (num_vertices - 1) / 2 - num_edges",
57    }
58)]
59impl ReduceTo<MaximumAchromaticNumber<SimpleGraph>> for MinimumMaximalMatching<BipartiteGraph> {
60    type Result = ReductionMMMToAchromatic;
61
62    fn reduce_to(&self) -> Self::Result {
63        let n = self.graph().num_vertices();
64        let source_edges = self.graph().edges();
65
66        // Build adjacency lookup over unified coordinates.
67        let source_edge_set: std::collections::HashSet<(usize, usize)> = source_edges
68            .iter()
69            .map(|&(u, v)| if u < v { (u, v) } else { (v, u) })
70            .collect();
71
72        // Complement graph: all non-edges of G become edges of H.
73        let mut complement_edges = Vec::new();
74        for u in 0..n {
75            for v in (u + 1)..n {
76                if !source_edge_set.contains(&(u, v)) {
77                    complement_edges.push((u, v));
78                }
79            }
80        }
81
82        let target = MaximumAchromaticNumber::new(SimpleGraph::new(n, complement_edges));
83
84        ReductionMMMToAchromatic {
85            target,
86            source_edges,
87        }
88    }
89}
90
91#[cfg(feature = "example-db")]
92pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
93    use crate::export::SolutionPair;
94
95    vec![crate::example_db::specs::RuleExampleSpec {
96        id: "minimummaximalmatching_to_maximumachromaticnumber",
97        build: || {
98            // "T" tree (spider with three legs at the centre v1):
99            //   v0 -- v1 -- v2 -- v3
100            //         |
101            //         v4
102            //
103            // Five vertices and four edges. Bipartite with
104            // A = {v0, v2, v4} and B = {v1, v3}.
105            //
106            // BipartiteGraph encoding (left_size = 3, right_size = 2):
107            //   left  local 0 -> v0, local 1 -> v2, local 2 -> v4
108            //   right local 0 -> v1, local 1 -> v3
109            //   edges (left_idx, right_idx):
110            //     (v0, v1) -> (0, 0)
111            //     (v1, v2) -> (1, 0)
112            //     (v2, v3) -> (1, 1)
113            //     (v1, v4) -> (2, 0)
114            //
115            // Unified vertex labels:
116            //   0 = v0 (left 0), 1 = v2 (left 1), 2 = v4 (left 2),
117            //   3 = v1 (right 0), 4 = v3 (right 1).
118            //
119            // Unified edges from Graph::edges() (in source order):
120            //   (0, 3), (1, 3), (1, 4), (2, 3).
121            //
122            // Maximal matchings of G:
123            //   - {(v1, v2)}                 size 1   <-- mm(G) = 1
124            //   - {(v0, v1), (v2, v3)}       size 2   (suboptimal 1)
125            //   - {(v1, v4), (v2, v3)}       size 2   (suboptimal 2)
126            // So the canonical example exhibits >=2 suboptimal maximal
127            // matchings besides the optimum.
128            //
129            // Canonical optimum: pick the central edge (v1, v2), which is
130            // source edge index 1. source_config = [0, 1, 0, 0].
131            //
132            // complement(G) on K_5: 10 - 4 = 6 edges, namely
133            //   (0, 1), (0, 2), (0, 4), (1, 2), (2, 4), (3, 4).
134            //
135            // Canonical achromatic 4-coloring of complement(G):
136            //   v0 (idx 0) -> color 1
137            //   v2 (idx 1) -> color 0  (paired with v1 = G-edge (v1, v2))
138            //   v4 (idx 2) -> color 3
139            //   v1 (idx 3) -> color 0
140            //   v3 (idx 4) -> color 2
141            // target_config = [1, 0, 3, 0, 2]; psi(H) = |V| - mm(G) = 4.
142            let source = MinimumMaximalMatching::new(BipartiteGraph::new(
143                3,
144                2,
145                vec![(0, 0), (1, 0), (1, 1), (2, 0)],
146            ));
147            crate::example_db::specs::rule_example_with_witness::<
148                _,
149                MaximumAchromaticNumber<SimpleGraph>,
150            >(
151                source,
152                SolutionPair {
153                    source_config: vec![0, 1, 0, 0],
154                    target_config: vec![1, 0, 3, 0, 2],
155                },
156            )
157        },
158    }]
159}
160
161#[cfg(test)]
162#[path = "../unit_tests/rules/minimummaximalmatching_maximumachromaticnumber.rs"]
163mod tests;