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;