problemreductions/rules/ksatisfiability_minimumvertexcover.rs
1//! Reduction from KSatisfiability (3-SAT) to MinimumVertexCover.
2//!
3//! Classical Garey & Johnson reduction (Theorem 3.3). For each variable u_i,
4//! add two vertices {u_i, not-u_i} connected by a truth-setting edge. For each
5//! clause c_j, add 3 vertices forming a satisfaction-testing triangle. For each
6//! literal l_k in clause c_j, add a communication edge from the triangle vertex
7//! j_k to the literal vertex l_k.
8//!
9//! The resulting graph has a vertex cover of size n + 2m if and only if the
10//! 3-SAT formula is satisfiable (n = num_vars, m = num_clauses).
11//!
12//! Reference: Garey & Johnson, "Computers and Intractability", 1979, Theorem 3.3
13
14use crate::models::formula::KSatisfiability;
15use crate::models::graph::MinimumVertexCover;
16use crate::reduction;
17use crate::rules::traits::{ReduceTo, ReductionResult};
18use crate::topology::SimpleGraph;
19use crate::variant::K3;
20
21/// Result of reducing KSatisfiability<K3> to MinimumVertexCover.
22#[derive(Debug, Clone)]
23pub struct Reduction3SATToMVC {
24 target: MinimumVertexCover<SimpleGraph, i32>,
25 source_num_vars: usize,
26}
27
28impl ReductionResult for Reduction3SATToMVC {
29 type Source = KSatisfiability<K3>;
30 type Target = MinimumVertexCover<SimpleGraph, i32>;
31
32 fn target_problem(&self) -> &Self::Target {
33 &self.target
34 }
35
36 /// Extract a SAT assignment from a vertex cover solution.
37 ///
38 /// Vertex layout: indices 0..2n are literal vertices (even = positive,
39 /// odd = negated). For variable i, vertex 2*i is u_i and vertex 2*i+1
40 /// is not-u_i. Each truth-setting edge forces exactly one of these two
41 /// into any minimum vertex cover. If u_i is in the cover, set x_i = 1;
42 /// if not-u_i is in the cover, set x_i = 0.
43 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
44 (0..self.source_num_vars)
45 .map(|i| {
46 // u_i is at index 2*i, not-u_i is at index 2*i+1
47 if target_solution[2 * i] == 1 {
48 1
49 } else {
50 0
51 }
52 })
53 .collect()
54 }
55}
56
57#[reduction(
58 overhead = {
59 num_vertices = "2 * num_vars + 3 * num_clauses",
60 num_edges = "num_vars + 6 * num_clauses",
61 }
62)]
63impl ReduceTo<MinimumVertexCover<SimpleGraph, i32>> for KSatisfiability<K3> {
64 type Result = Reduction3SATToMVC;
65
66 fn reduce_to(&self) -> Self::Result {
67 let n = self.num_vars();
68 let m = self.num_clauses();
69 let total_vertices = 2 * n + 3 * m;
70 let mut edges: Vec<(usize, usize)> = Vec::with_capacity(n + 6 * m);
71
72 // Step 1: Truth-setting components.
73 // For each variable i, add edge (2*i, 2*i+1) connecting u_i and not-u_i.
74 for i in 0..n {
75 edges.push((2 * i, 2 * i + 1));
76 }
77
78 // Step 2: Satisfaction-testing components (triangles) and communication edges.
79 // For each clause j, triangle vertices are at indices 2*n + 3*j, 2*n + 3*j + 1, 2*n + 3*j + 2.
80 for (j, clause) in self.clauses().iter().enumerate() {
81 let base = 2 * n + 3 * j;
82
83 // Triangle edges within clause j
84 edges.push((base, base + 1));
85 edges.push((base + 1, base + 2));
86 edges.push((base, base + 2));
87
88 // Communication edges: connect triangle vertex k to the literal vertex
89 for (k, &lit) in clause.literals.iter().enumerate() {
90 let var_idx = lit.unsigned_abs() as usize - 1; // 0-indexed variable
91 let literal_vertex = if lit > 0 {
92 2 * var_idx // positive literal vertex
93 } else {
94 2 * var_idx + 1 // negated literal vertex
95 };
96 edges.push((base + k, literal_vertex));
97 }
98 }
99
100 let graph = SimpleGraph::new(total_vertices, edges);
101 let weights = vec![1i32; total_vertices];
102 let target = MinimumVertexCover::new(graph, weights);
103
104 Reduction3SATToMVC {
105 target,
106 source_num_vars: n,
107 }
108 }
109}
110
111#[cfg(feature = "example-db")]
112pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
113 use crate::export::SolutionPair;
114 use crate::models::formula::CNFClause;
115
116 vec![crate::example_db::specs::RuleExampleSpec {
117 id: "ksatisfiability_to_minimumvertexcover",
118 build: || {
119 let source = KSatisfiability::<K3>::new(
120 3,
121 vec![
122 CNFClause::new(vec![1, 2, 3]),
123 CNFClause::new(vec![-1, -2, 3]),
124 ],
125 );
126 crate::example_db::specs::rule_example_with_witness::<
127 _,
128 MinimumVertexCover<SimpleGraph, i32>,
129 >(
130 source,
131 SolutionPair {
132 // x1=0, x2=0, x3=1 satisfies both clauses
133 source_config: vec![0, 0, 1],
134 // Literal vertices: u1(0), ~u1(1), u2(2), ~u2(3), u3(4), ~u3(5)
135 // Clause 0 triangle: v6, v7, v8 (literals x1, x2, x3)
136 // Clause 1 triangle: v9, v10, v11 (literals ~x1, ~x2, x3)
137 // VC: from truth-setting, pick ~u1(1), ~u2(3), u3(4)
138 // Clause 0: u1,u2 not in cover -> pick v6,v7; u3 in cover -> v8 free
139 // Clause 1: ~u1,~u2,u3 all in cover -> pick any 2: v9,v10
140 // Total cover size = 3 + 2 + 2 = 7 = n + 2m
141 target_config: vec![0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0],
142 },
143 )
144 },
145 }]
146}
147
148#[cfg(test)]
149#[path = "../unit_tests/rules/ksatisfiability_minimumvertexcover.rs"]
150mod tests;