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