1use crate::models::graph::IndependentSet;
12use crate::models::satisfiability::Satisfiability;
13use crate::rules::traits::{ReduceTo, ReductionResult};
14use crate::traits::Problem;
15use crate::types::ProblemSize;
16use num_traits::{Num, Zero};
17use std::ops::AddAssign;
18
19#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct BoolVar {
22 pub name: usize,
24 pub neg: bool,
26}
27
28impl BoolVar {
29 pub fn new(name: usize, neg: bool) -> Self {
31 Self { name, neg }
32 }
33
34 pub fn from_literal(lit: i32) -> Self {
37 let name = lit.unsigned_abs() as usize - 1; let neg = lit < 0;
39 Self { name, neg }
40 }
41
42 pub fn is_complement(&self, other: &BoolVar) -> bool {
44 self.name == other.name && self.neg != other.neg
45 }
46}
47
48#[derive(Debug, Clone)]
56pub struct ReductionSATToIS<W> {
57 target: IndependentSet<W>,
59 literals: Vec<BoolVar>,
61 num_source_variables: usize,
63 num_clauses: usize,
65 source_size: ProblemSize,
67}
68
69impl<W> ReductionResult for ReductionSATToIS<W>
70where
71 W: Clone + Default + PartialOrd + Num + Zero + AddAssign + 'static,
72{
73 type Source = Satisfiability<W>;
74 type Target = IndependentSet<W>;
75
76 fn target_problem(&self) -> &Self::Target {
77 &self.target
78 }
79
80 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
86 let mut assignment = vec![0usize; self.num_source_variables];
87 let mut covered = vec![false; self.num_source_variables];
88
89 for (vertex_idx, &selected) in target_solution.iter().enumerate() {
90 if selected == 1 {
91 let literal = &self.literals[vertex_idx];
92 assignment[literal.name] = if literal.neg { 0 } else { 1 };
95 covered[literal.name] = true;
96 }
97 }
98
99 assignment
102 }
103
104 fn source_size(&self) -> ProblemSize {
105 self.source_size.clone()
106 }
107
108 fn target_size(&self) -> ProblemSize {
109 self.target.problem_size()
110 }
111}
112
113impl<W> ReductionSATToIS<W> {
114 pub fn num_clauses(&self) -> usize {
116 self.num_clauses
117 }
118
119 pub fn literals(&self) -> &[BoolVar] {
121 &self.literals
122 }
123}
124
125impl<W> ReduceTo<IndependentSet<W>> for Satisfiability<W>
126where
127 W: Clone + Default + PartialOrd + Num + Zero + AddAssign + From<i32> + 'static,
128{
129 type Result = ReductionSATToIS<W>;
130
131 fn reduce_to(&self) -> Self::Result {
132 let mut literals: Vec<BoolVar> = Vec::new();
133 let mut edges: Vec<(usize, usize)> = Vec::new();
134 let mut vertex_count = 0;
135
136 for clause in self.clauses() {
139 let clause_start = vertex_count;
140
141 for &lit in &clause.literals {
143 literals.push(BoolVar::from_literal(lit));
144 vertex_count += 1;
145 }
146
147 for i in clause_start..vertex_count {
149 for j in (i + 1)..vertex_count {
150 edges.push((i, j));
151 }
152 }
153 }
154
155 for i in 0..vertex_count {
159 for j in (i + 1)..vertex_count {
160 if literals[i].is_complement(&literals[j]) {
161 edges.push((i, j));
162 }
163 }
164 }
165
166 let target = IndependentSet::new(vertex_count, edges);
167
168 ReductionSATToIS {
169 target,
170 literals,
171 num_source_variables: self.num_vars(),
172 num_clauses: self.num_clauses(),
173 source_size: self.problem_size(),
174 }
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181 use crate::models::satisfiability::CNFClause;
182 use crate::solvers::{BruteForce, Solver};
183
184 #[test]
185 fn test_boolvar_creation() {
186 let var = BoolVar::new(0, false);
187 assert_eq!(var.name, 0);
188 assert!(!var.neg);
189
190 let neg_var = BoolVar::new(1, true);
191 assert_eq!(neg_var.name, 1);
192 assert!(neg_var.neg);
193 }
194
195 #[test]
196 fn test_boolvar_from_literal() {
197 let var = BoolVar::from_literal(1);
199 assert_eq!(var.name, 0);
200 assert!(!var.neg);
201
202 let neg_var = BoolVar::from_literal(-2);
204 assert_eq!(neg_var.name, 1);
205 assert!(neg_var.neg);
206 }
207
208 #[test]
209 fn test_boolvar_complement() {
210 let x = BoolVar::new(0, false);
211 let not_x = BoolVar::new(0, true);
212 let y = BoolVar::new(1, false);
213
214 assert!(x.is_complement(¬_x));
215 assert!(not_x.is_complement(&x));
216 assert!(!x.is_complement(&y));
217 assert!(!x.is_complement(&x));
218 }
219
220 #[test]
221 fn test_simple_sat_to_is() {
222 let sat = Satisfiability::<i32>::new(1, vec![CNFClause::new(vec![1])]);
224 let reduction = ReduceTo::<IndependentSet<i32>>::reduce_to(&sat);
225 let is_problem = reduction.target_problem();
226
227 assert_eq!(is_problem.num_vertices(), 1);
229 assert_eq!(is_problem.num_edges(), 0);
231 }
232
233 #[test]
234 fn test_two_clause_sat_to_is() {
235 let sat = Satisfiability::<i32>::new(
238 1,
239 vec![CNFClause::new(vec![1]), CNFClause::new(vec![-1])],
240 );
241 let reduction = ReduceTo::<IndependentSet<i32>>::reduce_to(&sat);
242 let is_problem = reduction.target_problem();
243
244 assert_eq!(is_problem.num_vertices(), 2);
246 assert_eq!(is_problem.num_edges(), 1);
248
249 let solver = BruteForce::new();
251 let solutions = solver.find_best(is_problem);
252 for sol in &solutions {
253 assert_eq!(sol.iter().sum::<usize>(), 1);
254 }
255 }
256
257 #[test]
258 fn test_satisfiable_formula() {
259 let sat = Satisfiability::<i32>::new(
262 2,
263 vec![
264 CNFClause::new(vec![1, 2]), CNFClause::new(vec![-1, 2]), CNFClause::new(vec![1, -2]), ],
268 );
269 let reduction = ReduceTo::<IndependentSet<i32>>::reduce_to(&sat);
270 let is_problem = reduction.target_problem();
271
272 assert_eq!(is_problem.num_vertices(), 6);
274
275 let solver = BruteForce::new();
286 let is_solutions = solver.find_best(is_problem);
287
288 for sol in &is_solutions {
290 assert_eq!(sol.iter().sum::<usize>(), 3);
291 }
292
293 for sol in &is_solutions {
295 let sat_sol = reduction.extract_solution(sol);
296 let assignment: Vec<bool> = sat_sol.iter().map(|&v| v == 1).collect();
297 assert!(
298 sat.is_satisfying(&assignment),
299 "Extracted solution {:?} should satisfy the SAT formula",
300 assignment
301 );
302 }
303 }
304
305 #[test]
306 fn test_unsatisfiable_formula() {
307 let sat = Satisfiability::<i32>::new(
309 1,
310 vec![CNFClause::new(vec![1]), CNFClause::new(vec![-1])],
311 );
312 let reduction = ReduceTo::<IndependentSet<i32>>::reduce_to(&sat);
313 let is_problem = reduction.target_problem();
314
315 let solver = BruteForce::new();
316 let is_solutions = solver.find_best(is_problem);
317
318 for sol in &is_solutions {
321 assert!(
322 sol.iter().sum::<usize>() < reduction.num_clauses(),
323 "For unsatisfiable formula, IS size should be less than num_clauses"
324 );
325 }
326 }
327
328 #[test]
329 fn test_three_sat_example() {
330 let sat = Satisfiability::<i32>::new(
332 3,
333 vec![
334 CNFClause::new(vec![1, 2, 3]), CNFClause::new(vec![-1, -2, 3]), CNFClause::new(vec![1, -2, -3]), ],
338 );
339
340 let reduction = ReduceTo::<IndependentSet<i32>>::reduce_to(&sat);
341 let is_problem = reduction.target_problem();
342
343 assert_eq!(is_problem.num_vertices(), 9);
345
346 let solver = BruteForce::new();
347 let is_solutions = solver.find_best(is_problem);
348
349 let max_size = is_solutions[0].iter().sum::<usize>();
351 assert_eq!(max_size, 3, "3-SAT should be satisfiable with IS size = 3");
352
353 for sol in &is_solutions {
355 let sat_sol = reduction.extract_solution(sol);
356 let assignment: Vec<bool> = sat_sol.iter().map(|&v| v == 1).collect();
357 assert!(sat.is_satisfying(&assignment));
358 }
359 }
360
361 #[test]
362 fn test_extract_solution_basic() {
363 let sat = Satisfiability::<i32>::new(2, vec![CNFClause::new(vec![1, 2])]);
365 let reduction = ReduceTo::<IndependentSet<i32>>::reduce_to(&sat);
366
367 let is_sol = vec![1, 0];
369 let sat_sol = reduction.extract_solution(&is_sol);
370 assert_eq!(sat_sol, vec![1, 0]); let is_sol = vec![0, 1];
374 let sat_sol = reduction.extract_solution(&is_sol);
375 assert_eq!(sat_sol, vec![0, 1]); }
377
378 #[test]
379 fn test_extract_solution_with_negation() {
380 let sat = Satisfiability::<i32>::new(1, vec![CNFClause::new(vec![-1])]);
382 let reduction = ReduceTo::<IndependentSet<i32>>::reduce_to(&sat);
383
384 let is_sol = vec![1];
385 let sat_sol = reduction.extract_solution(&is_sol);
386 assert_eq!(sat_sol, vec![0]); }
388
389 #[test]
390 fn test_clique_edges_in_clause() {
391 let sat = Satisfiability::<i32>::new(3, vec![CNFClause::new(vec![1, 2, 3])]);
393 let reduction = ReduceTo::<IndependentSet<i32>>::reduce_to(&sat);
394 let is_problem = reduction.target_problem();
395
396 assert_eq!(is_problem.num_vertices(), 3);
398 assert_eq!(is_problem.num_edges(), 3);
399 }
400
401 #[test]
402 fn test_complement_edges_across_clauses() {
403 let sat = Satisfiability::<i32>::new(
407 2,
408 vec![
409 CNFClause::new(vec![1]),
410 CNFClause::new(vec![-1]),
411 CNFClause::new(vec![2]),
412 ],
413 );
414 let reduction = ReduceTo::<IndependentSet<i32>>::reduce_to(&sat);
415 let is_problem = reduction.target_problem();
416
417 assert_eq!(is_problem.num_vertices(), 3);
418 assert_eq!(is_problem.num_edges(), 1); }
420
421 #[test]
422 fn test_source_and_target_size() {
423 let sat = Satisfiability::<i32>::new(
424 3,
425 vec![CNFClause::new(vec![1, 2]), CNFClause::new(vec![-1, 3])],
426 );
427 let reduction = ReduceTo::<IndependentSet<i32>>::reduce_to(&sat);
428
429 let source_size = reduction.source_size();
430 let target_size = reduction.target_size();
431
432 assert_eq!(source_size.get("num_vars"), Some(3));
433 assert_eq!(source_size.get("num_clauses"), Some(2));
434 assert_eq!(target_size.get("num_vertices"), Some(4)); }
436
437 #[test]
438 fn test_empty_sat() {
439 let sat = Satisfiability::<i32>::new(0, vec![]);
441 let reduction = ReduceTo::<IndependentSet<i32>>::reduce_to(&sat);
442 let is_problem = reduction.target_problem();
443
444 assert_eq!(is_problem.num_vertices(), 0);
445 assert_eq!(is_problem.num_edges(), 0);
446 assert_eq!(reduction.num_clauses(), 0);
447 }
448
449 #[test]
450 fn test_sat_is_solution_correspondence() {
451 let sat = Satisfiability::<i32>::new(
453 2,
454 vec![CNFClause::new(vec![1, 2]), CNFClause::new(vec![-1, -2])],
455 );
456
457 let sat_solver = BruteForce::new();
459 let direct_sat_solutions = sat_solver.find_best(&sat);
460
461 let reduction = ReduceTo::<IndependentSet<i32>>::reduce_to(&sat);
463 let is_problem = reduction.target_problem();
464 let is_solutions = sat_solver.find_best(is_problem);
465
466 let extracted_sat_solutions: Vec<_> = is_solutions
468 .iter()
469 .map(|s| reduction.extract_solution(s))
470 .collect();
471
472 for sol in &extracted_sat_solutions {
474 let assignment: Vec<bool> = sol.iter().map(|&v| v == 1).collect();
475 assert!(sat.is_satisfying(&assignment));
476 }
477
478 for sol in &direct_sat_solutions {
481 let assignment: Vec<bool> = sol.iter().map(|&v| v == 1).collect();
482 assert!(sat.is_satisfying(&assignment));
483 }
484 }
485
486 #[test]
487 fn test_literals_accessor() {
488 let sat = Satisfiability::<i32>::new(
489 2,
490 vec![CNFClause::new(vec![1, -2])],
491 );
492 let reduction = ReduceTo::<IndependentSet<i32>>::reduce_to(&sat);
493
494 let literals = reduction.literals();
495 assert_eq!(literals.len(), 2);
496 assert_eq!(literals[0], BoolVar::new(0, false)); assert_eq!(literals[1], BoolVar::new(1, true)); }
499}
500
501use crate::poly;
503use crate::rules::registry::{ReductionEntry, ReductionOverhead};
504
505inventory::submit! {
506 ReductionEntry {
507 source_name: "Satisfiability",
508 target_name: "IndependentSet",
509 source_graph: "CNF",
510 target_graph: "SimpleGraph",
511 overhead_fn: || ReductionOverhead::new(vec![
512 ("num_vertices", poly!(7 * num_clauses)),
513 ("num_edges", poly!(21 * num_clauses)),
514 ]),
515 }
516}