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(
46        &self,
47        target_solution: &<Self::Target as crate::traits::Problem>::Solution,
48    ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
49        crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
50
51        Ok({
52            self.source_edges
53                .iter()
54                .map(|&(u, v)| target_solution[u] == target_solution[v])
55                .collect()
56        })
57    }
58}
59
60#[reduction(
61    transform = exact {
62        num_vertices = "num_vertices",
63        num_edges = "num_vertices * (num_vertices - 1) / 2 - num_edges",
64    }
65)]
66impl ReduceTo<MaximumAchromaticNumber<SimpleGraph>> for MinimumMaximalMatching<BipartiteGraph> {
67    type Result = ReductionMMMToAchromatic;
68
69    fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
70        let n = self.graph().num_vertices();
71        let source_edges = self.graph().edges();
72
73        // Build adjacency lookup over unified coordinates.
74        let source_edge_set: std::collections::HashSet<(usize, usize)> = source_edges
75            .iter()
76            .map(|&(u, v)| if u < v { (u, v) } else { (v, u) })
77            .collect();
78
79        // Complement graph: all non-edges of G become edges of H.
80        let mut complement_edges = Vec::new();
81        for u in 0..n {
82            for v in (u + 1)..n {
83                if !source_edge_set.contains(&(u, v)) {
84                    complement_edges.push((u, v));
85                }
86            }
87        }
88
89        let target = MaximumAchromaticNumber::new(SimpleGraph::new(n, complement_edges));
90
91        Ok(ReductionMMMToAchromatic {
92            target,
93            source_edges,
94        })
95    }
96}
97
98#[cfg(feature = "example-db")]
99pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
100    use crate::export::SolutionPair;
101
102    vec![crate::example_db::specs::RuleExampleSpec {
103        id: "minimummaximalmatching_to_maximumachromaticnumber",
104        build: || {
105            // "T" tree (spider with three legs at the centre v1):
106            //   v0 -- v1 -- v2 -- v3
107            //         |
108            //         v4
109            //
110            // Five vertices and four edges. Bipartite with
111            // A = {v0, v2, v4} and B = {v1, v3}.
112            //
113            // BipartiteGraph encoding (left_size = 3, right_size = 2):
114            //   left  local 0 -> v0, local 1 -> v2, local 2 -> v4
115            //   right local 0 -> v1, local 1 -> v3
116            //   edges (left_idx, right_idx):
117            //     (v0, v1) -> (0, 0)
118            //     (v1, v2) -> (1, 0)
119            //     (v2, v3) -> (1, 1)
120            //     (v1, v4) -> (2, 0)
121            //
122            // Unified vertex labels:
123            //   0 = v0 (left 0), 1 = v2 (left 1), 2 = v4 (left 2),
124            //   3 = v1 (right 0), 4 = v3 (right 1).
125            //
126            // Unified edges from Graph::edges() (in source order):
127            //   (0, 3), (1, 3), (1, 4), (2, 3).
128            //
129            // Maximal matchings of G:
130            //   - {(v1, v2)}                 size 1   <-- mm(G) = 1
131            //   - {(v0, v1), (v2, v3)}       size 2   (suboptimal 1)
132            //   - {(v1, v4), (v2, v3)}       size 2   (suboptimal 2)
133            // So the canonical example exhibits >=2 suboptimal maximal
134            // matchings besides the optimum.
135            //
136            // Canonical optimum: pick the central edge (v1, v2), which is
137            // source edge index 1. source_config = [0, 1, 0, 0].
138            //
139            // complement(G) on K_5: 10 - 4 = 6 edges, namely
140            //   (0, 1), (0, 2), (0, 4), (1, 2), (2, 4), (3, 4).
141            //
142            // Canonical achromatic 4-coloring of complement(G):
143            //   v0 (idx 0) -> color 1
144            //   v2 (idx 1) -> color 0  (paired with v1 = G-edge (v1, v2))
145            //   v4 (idx 2) -> color 3
146            //   v1 (idx 3) -> color 0
147            //   v3 (idx 4) -> color 2
148            // target_config = [1, 0, 3, 0, 2]; psi(H) = |V| - mm(G) = 4.
149            let source = MinimumMaximalMatching::new(BipartiteGraph::new(
150                3,
151                2,
152                vec![(0, 0), (1, 0), (1, 1), (2, 0)],
153            ));
154            crate::example_db::specs::rule_example_with_witness::<
155                _,
156                MaximumAchromaticNumber<SimpleGraph>,
157            >(
158                source,
159                SolutionPair {
160                    source_config: serde_json::json!(vec![false, true, false, false]),
161                    target_config: serde_json::json!(vec![1, 0, 3, 0, 2]),
162                },
163            )
164        },
165    }]
166}
167
168#[cfg(test)]
169#[path = "../unit_tests/rules/minimummaximalmatching_maximumachromaticnumber.rs"]
170mod tests;