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;