Skip to main content

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;