problemreductions/rules/
ksatisfiability_kernel.rs1use crate::models::formula::KSatisfiability;
9use crate::models::graph::Kernel;
10use crate::reduction;
11use crate::rules::traits::{ReduceTo, ReductionResult};
12use crate::topology::DirectedGraph;
13use crate::variant::K3;
14use std::collections::BTreeSet;
15
16#[derive(Debug, Clone)]
18pub struct Reduction3SatToKernel {
19 target: Kernel,
20 source_num_vars: usize,
21 source_variables: Vec<usize>,
22}
23
24impl ReductionResult for Reduction3SatToKernel {
25 type Source = KSatisfiability<K3>;
26 type Target = Kernel;
27
28 fn target_problem(&self) -> &Self::Target {
29 &self.target
30 }
31
32 fn extract_solution(
33 &self,
34 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
35 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
36 let value =
37 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
38 if !value.0 {
39 return Err(crate::rules::ExtractionError::invalid(
40 "target vertex selection is not a kernel",
41 ));
42 }
43 let mut assignment = vec![false; self.source_num_vars];
44 for (compact, &original) in self.source_variables.iter().enumerate() {
45 assignment[original] = target_solution[2 * compact];
46 }
47 Ok(assignment)
48 }
49}
50
51#[reduction(
52 transform = upper_bound {
53 num_vertices = "2 * num_vars + 3 * num_clauses",
54 num_arcs = "2 * num_vars + 6 * num_clauses",
55 }
56)]
57impl ReduceTo<Kernel> for KSatisfiability<K3> {
58 type Result = Reduction3SatToKernel;
59
60 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
61 let source_variables: Vec<_> = self
62 .clauses()
63 .iter()
64 .flat_map(|clause| clause.literals.iter())
65 .map(|literal| {
66 usize::try_from(literal.unsigned_abs()).expect("native SAT indices fit usize") - 1
67 })
68 .collect::<BTreeSet<_>>()
69 .into_iter()
70 .collect();
71 let num_vars = source_variables.len();
72 let num_clauses = self.num_clauses();
73 let overflow =
74 |operation| crate::rules::ReductionError::integer_overflow::<Self, Kernel>(operation);
75 let variable_vertices = num_vars
76 .checked_mul(2)
77 .ok_or_else(|| overflow("counting variable vertices"))?;
78 let clause_vertices = num_clauses
79 .checked_mul(3)
80 .ok_or_else(|| overflow("counting clause vertices"))?;
81 let num_vertices = variable_vertices
82 .checked_add(clause_vertices)
83 .ok_or_else(|| overflow("counting target vertices"))?;
84 let arc_capacity = num_clauses
85 .checked_mul(6)
86 .and_then(|clause_arcs| variable_vertices.checked_add(clause_arcs))
87 .ok_or_else(|| overflow("counting target arcs"))?;
88 let mut arcs = Vec::with_capacity(arc_capacity);
89
90 for variable in 0..num_vars {
91 let positive = 2 * variable;
92 let negative = positive + 1;
93 arcs.push((positive, negative));
94 arcs.push((negative, positive));
95 }
96
97 for (clause_index, clause) in self.clauses().iter().enumerate() {
98 let clause_base = 2 * num_vars + 3 * clause_index;
99 arcs.push((clause_base, clause_base + 1));
100 arcs.push((clause_base + 1, clause_base + 2));
101 arcs.push((clause_base + 2, clause_base));
102
103 for (literal_index, &literal) in clause.literals.iter().enumerate() {
104 let original = usize::try_from(literal.unsigned_abs())
105 .expect("native SAT indices fit usize")
106 - 1;
107 let compact = source_variables
108 .binary_search(&original)
109 .expect("all appearing variables were collected");
110 let literal_vertex = 2 * compact + usize::from(literal < 0);
111 arcs.push((clause_base + literal_index, literal_vertex));
112 }
113 }
114
115 Ok(Reduction3SatToKernel {
116 target: Kernel::new(DirectedGraph::new(num_vertices, arcs)),
117 source_num_vars: self.num_vars(),
118 source_variables,
119 })
120 }
121}
122
123#[cfg(feature = "example-db")]
124pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
125 use crate::export::SolutionPair;
126 use crate::models::formula::CNFClause;
127
128 vec![crate::example_db::specs::RuleExampleSpec {
129 id: "ksatisfiability_to_kernel",
130 build: || {
131 crate::example_db::specs::rule_example_with_witness::<_, Kernel>(
132 KSatisfiability::<K3>::new(
133 3,
134 vec![
135 CNFClause::new(vec![1, 2, 3]),
136 CNFClause::new(vec![-1, -2, 3]),
137 ],
138 ),
139 SolutionPair {
140 source_config: serde_json::json!(vec![true, true, true]),
141 target_config: serde_json::json!(vec![
142 true, false, true, false, true, false, false, false, false, false, true,
143 false
144 ]),
145 },
146 )
147 },
148 }]
149}
150#[cfg(test)]
151#[path = "../unit_tests/rules/ksatisfiability_kernel.rs"]
152mod tests;