1use crate::models::formula::{CNFClause, KSatisfiability, Satisfiability};
10use crate::reduction;
11use crate::rules::sat_helpers::SatVariableAllocator;
12use crate::rules::traits::{ReduceTo, ReductionResult};
13use crate::variant::{KValue, K2, K3, KN};
14
15#[derive(Debug, Clone)]
20pub struct ReductionSATToKSAT<K: KValue> {
21 source_num_vars: usize,
23 target: KSatisfiability<K>,
25}
26
27impl<K: KValue> ReductionResult for ReductionSATToKSAT<K> {
28 type Source = Satisfiability;
29 type Target = KSatisfiability<K>;
30
31 fn target_problem(&self) -> &Self::Target {
32 &self.target
33 }
34
35 fn extract_solution(
36 &self,
37 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
38 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
39 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
40
41 Ok({
42 target_solution[..self.source_num_vars].to_vec()
44 })
45 }
46}
47
48fn add_clause_to_ksat(
60 k: usize,
61 clause: &CNFClause,
62 result_clauses: &mut Vec<CNFClause>,
63 variables: &mut SatVariableAllocator,
64) -> Result<(), crate::registry::ConstructionError> {
65 let len = clause.len();
66
67 if len == k {
68 result_clauses.push(clause.clone());
70 } else if len < k {
71 let ancilla = variables.allocate()?;
75
76 let mut lits_pos = clause.literals.clone();
78 lits_pos.push(ancilla);
79 add_clause_to_ksat(k, &CNFClause::new(lits_pos), result_clauses, variables)?;
80
81 let mut lits_neg = clause.literals.clone();
83 lits_neg.push(-ancilla);
84 add_clause_to_ksat(k, &CNFClause::new(lits_neg), result_clauses, variables)?;
85 } else {
86 if k < 3 {
89 return Err(format!(
90 "cannot split a clause with {} literals into {k}-literal clauses",
91 clause.len()
92 )
93 .into());
94 }
95
96 let ancilla = variables.allocate()?;
97
98 let mut first_lits: Vec<i64> = clause.literals[..k - 1].to_vec();
100 first_lits.push(ancilla);
101 result_clauses.push(CNFClause::new(first_lits));
102
103 let mut remaining_lits = vec![-ancilla];
105 remaining_lits.extend_from_slice(&clause.literals[k - 1..]);
106 let remaining_clause = CNFClause::new(remaining_lits);
107
108 add_clause_to_ksat(k, &remaining_clause, result_clauses, variables)?;
110 }
111
112 Ok(())
113}
114
115macro_rules! impl_sat_to_ksat {
120 ($ktype:ty, $k:expr) => {
121 #[rustfmt::skip]
122 #[reduction(
123 transform = upper_bound {
124 num_clauses = "4 * num_clauses + num_literals",
125 num_vars = "num_vars + 3 * num_clauses + num_literals",
126 },
127 unavailable = {
128 num_literals = "the exact target parameter is not represented by this reduction's symbolic transform",
129 }
130)]
131 impl ReduceTo<KSatisfiability<$ktype>> for Satisfiability {
132 type Result = ReductionSATToKSAT<$ktype>;
133
134 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
135 let source_num_vars = self.num_vars();
136 let mut result_clauses = Vec::new();
137 let mut variables = SatVariableAllocator::new(
138 "Satisfiability -> KSatisfiability",
139 source_num_vars,
140 ).map_err(crate::rules::ReductionError::construction::<
141 Satisfiability,
142 KSatisfiability<$ktype>,
143 >)?;
144
145 for clause in self.clauses() {
146 add_clause_to_ksat($k, clause, &mut result_clauses, &mut variables)
147 .map_err(crate::rules::ReductionError::construction::<
148 Satisfiability,
149 KSatisfiability<$ktype>,
150 >)?;
151 }
152
153 let target = KSatisfiability::<$ktype>::new(variables.num_vars(), result_clauses);
154
155 Ok(ReductionSATToKSAT {
156 source_num_vars,
157 target,
158 })
159 }
160 }
161 };
162}
163
164impl_sat_to_ksat!(K3, 3);
166
167#[derive(Debug, Clone)]
171pub struct ReductionKSATToSAT<K: KValue> {
172 target: Satisfiability,
174 _phantom: std::marker::PhantomData<K>,
175}
176
177impl<K: KValue> ReductionResult for ReductionKSATToSAT<K> {
178 type Source = KSatisfiability<K>;
179 type Target = Satisfiability;
180
181 fn target_problem(&self) -> &Self::Target {
182 &self.target
183 }
184
185 fn extract_solution(
186 &self,
187 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
188 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
189 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
190
191 Ok({
192 target_solution.to_vec()
194 })
195 }
196}
197
198fn reduce_ksat_to_sat<K: KValue>(ksat: &KSatisfiability<K>) -> ReductionKSATToSAT<K> {
200 let clauses = ksat.clauses().to_vec();
201 let target = Satisfiability::new(ksat.num_vars(), clauses);
202
203 ReductionKSATToSAT {
204 target,
205 _phantom: std::marker::PhantomData,
206 }
207}
208
209macro_rules! impl_ksat_to_sat {
212 ($ktype:ty) => {
213#[rustfmt::skip]
214 #[reduction(
215 transform = exact {
216 num_clauses = "num_clauses",
217 num_vars = "num_vars",
218 num_literals = "num_literals",
219 })]
220 impl ReduceTo<Satisfiability> for KSatisfiability<$ktype> {
221 type Result = ReductionKSATToSAT<$ktype>;
222
223 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
224 Ok(reduce_ksat_to_sat(self))
225 }
226 }
227 };
228}
229
230impl_ksat_to_sat!(KN);
232
233impl ReduceTo<Satisfiability> for KSatisfiability<K3> {
236 type Result = ReductionKSATToSAT<K3>;
237 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
238 Ok(reduce_ksat_to_sat(self))
239 }
240}
241
242impl ReduceTo<Satisfiability> for KSatisfiability<K2> {
243 type Result = ReductionKSATToSAT<K2>;
244 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
245 Ok(reduce_ksat_to_sat(self))
246 }
247}
248
249#[cfg(feature = "example-db")]
250pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
251 use crate::export::SolutionPair;
252 use crate::models::formula::CNFClause;
253
254 vec![
255 crate::example_db::specs::RuleExampleSpec {
256 id: "satisfiability_to_ksatisfiability",
257 build: || {
258 let source = Satisfiability::new(
259 5,
260 vec![
261 CNFClause::new(vec![1]),
262 CNFClause::new(vec![2, -3]),
263 CNFClause::new(vec![-1, 3, 4]),
264 CNFClause::new(vec![2, -4, 5]),
265 CNFClause::new(vec![1, -2, 3, -5]),
266 CNFClause::new(vec![-1, 2, -3, 4, 5]),
267 ],
268 );
269 crate::example_db::specs::rule_example_with_witness::<_, KSatisfiability<K3>>(
270 source,
271 SolutionPair {
272 source_config: serde_json::json!(vec![true, true, true, false, true]),
273 target_config: serde_json::json!(vec![
274 true, true, true, false, true, false, false, false, false, true, true,
275 true
276 ]),
277 },
278 )
279 },
280 },
281 crate::example_db::specs::RuleExampleSpec {
282 id: "ksatisfiability_to_satisfiability",
283 build: || {
284 let source = KSatisfiability::<KN>::new(
285 4,
286 vec![
287 CNFClause::new(vec![1, -2, 3]),
288 CNFClause::new(vec![-1, 3, 4]),
289 CNFClause::new(vec![2, -3, -4]),
290 ],
291 );
292 crate::example_db::specs::rule_example_with_witness::<_, Satisfiability>(
293 source,
294 SolutionPair {
295 source_config: serde_json::json!(vec![true, true, true, false]),
296 target_config: serde_json::json!(vec![true, true, true, false]),
297 },
298 )
299 },
300 },
301 ]
302}
303
304#[cfg(test)]
305#[path = "../unit_tests/rules/sat_ksat.rs"]
306mod tests;