1use crate::models::graph::Coloring;
12use crate::models::satisfiability::Satisfiability;
13use crate::rules::sat_independentset::BoolVar;
14use crate::rules::traits::{ReduceTo, ReductionResult};
15use crate::traits::Problem;
16use crate::types::ProblemSize;
17use num_traits::{Num, Zero};
18use std::collections::HashMap;
19use std::marker::PhantomData;
20use std::ops::AddAssign;
21
22struct SATColoringConstructor {
24 edges: Vec<(usize, usize)>,
26 num_vertices: usize,
28 pos_vertices: Vec<usize>,
30 neg_vertices: Vec<usize>,
32 vmap: HashMap<(usize, bool), usize>,
34}
35
36impl SATColoringConstructor {
37 fn new(num_vars: usize) -> Self {
45 let num_vertices = 2 * num_vars + 3;
46 let mut edges = Vec::new();
47
48 edges.push((0, 1));
50 edges.push((0, 2));
51 edges.push((1, 2));
52
53 let mut pos_vertices = Vec::with_capacity(num_vars);
55 let mut neg_vertices = Vec::with_capacity(num_vars);
56 let mut vmap = HashMap::new();
57
58 for i in 0..num_vars {
59 let pos_v = 3 + i;
60 let neg_v = 3 + num_vars + i;
61 pos_vertices.push(pos_v);
62 neg_vertices.push(neg_v);
63
64 edges.push((pos_v, 2));
66 edges.push((neg_v, 2));
67
68 edges.push((pos_v, neg_v));
70
71 vmap.insert((i, false), pos_v); vmap.insert((i, true), neg_v); }
75
76 Self {
77 edges,
78 num_vertices,
79 pos_vertices,
80 neg_vertices,
81 vmap,
82 }
83 }
84
85 fn true_vertex(&self) -> usize {
87 0
88 }
89
90 fn false_vertex(&self) -> usize {
92 1
93 }
94
95 fn aux_vertex(&self) -> usize {
97 2
98 }
99
100 fn attach_to_aux(&mut self, idx: usize) {
102 self.add_edge(idx, self.aux_vertex());
103 }
104
105 fn attach_to_false(&mut self, idx: usize) {
107 self.add_edge(idx, self.false_vertex());
108 }
109
110 fn attach_to_true(&mut self, idx: usize) {
112 self.add_edge(idx, self.true_vertex());
113 }
114
115 fn add_edge(&mut self, u: usize, v: usize) {
117 self.edges.push((u, v));
118 }
119
120 fn add_vertices(&mut self, n: usize) -> Vec<usize> {
122 let start = self.num_vertices;
123 self.num_vertices += n;
124 (start..self.num_vertices).collect()
125 }
126
127 fn set_true(&mut self, idx: usize) {
130 self.attach_to_aux(idx);
131 self.attach_to_false(idx);
132 }
133
134 fn get_vertex(&self, var: &BoolVar) -> usize {
136 self.vmap[&(var.name, var.neg)]
137 }
138
139 fn add_clause(&mut self, literals: &[i32]) {
143 assert!(!literals.is_empty(), "Clause must have at least one literal");
144
145 let first_var = BoolVar::from_literal(literals[0]);
146 let mut output_node = self.get_vertex(&first_var);
147
148 for &lit in &literals[1..] {
150 let var = BoolVar::from_literal(lit);
151 let input2 = self.get_vertex(&var);
152 output_node = self.add_or_gadget(output_node, input2);
153 }
154
155 self.set_true(output_node);
157 }
158
159 fn add_or_gadget(&mut self, input1: usize, input2: usize) -> usize {
168 let new_vertices = self.add_vertices(5);
170 let ancilla1 = new_vertices[0];
171 let ancilla2 = new_vertices[1];
172 let entrance1 = new_vertices[2];
173 let entrance2 = new_vertices[3];
174 let output = new_vertices[4];
175
176 self.attach_to_aux(output);
178
179 self.attach_to_true(ancilla1);
181
182 self.add_edge(ancilla1, ancilla2);
187 self.add_edge(ancilla2, input1);
188 self.add_edge(ancilla2, input2);
189 self.add_edge(entrance1, entrance2);
190 self.add_edge(output, ancilla1);
191 self.add_edge(input1, entrance2);
192 self.add_edge(input2, entrance1);
193 self.add_edge(entrance1, output);
194 self.add_edge(entrance2, output);
195
196 output
197 }
198
199 fn build_coloring(&self) -> Coloring {
201 Coloring::new(self.num_vertices, 3, self.edges.clone())
202 }
203}
204
205#[derive(Debug, Clone)]
212pub struct ReductionSATToColoring<W> {
213 target: Coloring,
215 pos_vertices: Vec<usize>,
217 neg_vertices: Vec<usize>,
219 num_source_variables: usize,
221 num_clauses: usize,
223 source_size: ProblemSize,
225 _phantom: PhantomData<W>,
227}
228
229impl<W> ReductionResult for ReductionSATToColoring<W>
230where
231 W: Clone + Default + PartialOrd + Num + Zero + AddAssign + 'static,
232{
233 type Source = Satisfiability<W>;
234 type Target = Coloring;
235
236 fn target_problem(&self) -> &Self::Target {
237 &self.target
238 }
239
240 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
250 assert!(
253 target_solution.len() >= 3,
254 "Invalid solution: coloring must have at least 3 vertices"
255 );
256 let true_color = target_solution[0];
257 let false_color = target_solution[1];
258 let aux_color = target_solution[2];
259
260 assert!(
262 true_color != false_color && true_color != aux_color,
263 "Invalid coloring solution: special vertices must have distinct colors"
264 );
265
266 let mut assignment = vec![0usize; self.num_source_variables];
267
268 for (i, &pos_vertex) in self.pos_vertices.iter().enumerate() {
269 let vertex_color = target_solution[pos_vertex];
270
271 assert!(
273 vertex_color != aux_color,
274 "Invalid coloring solution: variable vertex has auxiliary color"
275 );
276
277 assignment[i] = if vertex_color == true_color { 1 } else { 0 };
280 }
281
282 assignment
283 }
284
285 fn source_size(&self) -> ProblemSize {
286 self.source_size.clone()
287 }
288
289 fn target_size(&self) -> ProblemSize {
290 self.target.problem_size()
291 }
292}
293
294impl<W> ReductionSATToColoring<W> {
295 pub fn num_clauses(&self) -> usize {
297 self.num_clauses
298 }
299
300 pub fn pos_vertices(&self) -> &[usize] {
302 &self.pos_vertices
303 }
304
305 pub fn neg_vertices(&self) -> &[usize] {
307 &self.neg_vertices
308 }
309}
310
311impl<W> ReduceTo<Coloring> for Satisfiability<W>
312where
313 W: Clone + Default + PartialOrd + Num + Zero + AddAssign + From<i32> + 'static,
314{
315 type Result = ReductionSATToColoring<W>;
316
317 fn reduce_to(&self) -> Self::Result {
318 let mut constructor = SATColoringConstructor::new(self.num_vars());
319
320 for clause in self.clauses() {
322 constructor.add_clause(&clause.literals);
323 }
324
325 let target = constructor.build_coloring();
326
327 ReductionSATToColoring {
328 target,
329 pos_vertices: constructor.pos_vertices,
330 neg_vertices: constructor.neg_vertices,
331 num_source_variables: self.num_vars(),
332 num_clauses: self.num_clauses(),
333 source_size: self.problem_size(),
334 _phantom: PhantomData,
335 }
336 }
337}
338
339#[cfg(test)]
340mod tests {
341 use super::*;
342 use crate::models::satisfiability::CNFClause;
343 use crate::solvers::{BruteForce, Solver};
344
345 #[test]
346 fn test_constructor_basic_structure() {
347 let constructor = SATColoringConstructor::new(2);
348
349 assert_eq!(constructor.num_vertices, 7);
351
352 assert_eq!(constructor.pos_vertices, vec![3, 4]);
354 assert_eq!(constructor.neg_vertices, vec![5, 6]);
355
356 assert_eq!(constructor.vmap[&(0, false)], 3);
358 assert_eq!(constructor.vmap[&(0, true)], 5);
359 assert_eq!(constructor.vmap[&(1, false)], 4);
360 assert_eq!(constructor.vmap[&(1, true)], 6);
361 }
362
363 #[test]
364 fn test_special_vertex_accessors() {
365 let constructor = SATColoringConstructor::new(1);
366 assert_eq!(constructor.true_vertex(), 0);
367 assert_eq!(constructor.false_vertex(), 1);
368 assert_eq!(constructor.aux_vertex(), 2);
369 }
370
371 #[test]
372 fn test_simple_sat_to_coloring() {
373 let sat = Satisfiability::<i32>::new(1, vec![CNFClause::new(vec![1])]);
375 let reduction = ReduceTo::<Coloring>::reduce_to(&sat);
376 let coloring = reduction.target_problem();
377
378 assert!(coloring.num_vertices() >= 5);
381 }
382
383 #[test]
384 fn test_reduction_structure() {
385 let sat = Satisfiability::<i32>::new(
388 2,
389 vec![CNFClause::new(vec![1, 2]), CNFClause::new(vec![-1, 2])],
390 );
391
392 let reduction = ReduceTo::<Coloring>::reduce_to(&sat);
393 let coloring = reduction.target_problem();
394
395 assert_eq!(coloring.num_vertices(), 17);
399 assert_eq!(coloring.num_colors(), 3);
400 assert_eq!(reduction.pos_vertices().len(), 2);
401 assert_eq!(reduction.neg_vertices().len(), 2);
402 }
403
404 #[test]
405 fn test_unsatisfiable_formula() {
406 let sat = Satisfiability::<i32>::new(
408 1,
409 vec![CNFClause::new(vec![1]), CNFClause::new(vec![-1])],
410 );
411
412 let reduction = ReduceTo::<Coloring>::reduce_to(&sat);
413 let coloring = reduction.target_problem();
414
415 let solver = BruteForce::new();
417 let solutions = solver.find_best(coloring);
418
419 let mut found_satisfying = false;
422 for sol in &solutions {
423 if coloring.solution_size(sol).is_valid {
424 let sat_sol = reduction.extract_solution(sol);
425 let assignment: Vec<bool> = sat_sol.iter().map(|&v| v == 1).collect();
426 if sat.is_satisfying(&assignment) {
427 found_satisfying = true;
428 break;
429 }
430 }
431 }
432
433 assert!(
441 !found_satisfying,
442 "Unsatisfiable formula should not produce satisfying assignment"
443 );
444 }
445
446 #[test]
447 fn test_three_literal_clause_structure() {
448 let sat = Satisfiability::<i32>::new(3, vec![CNFClause::new(vec![1, 2, 3])]);
450
451 let reduction = ReduceTo::<Coloring>::reduce_to(&sat);
452 let coloring = reduction.target_problem();
453
454 assert_eq!(coloring.num_vertices(), 19);
459 assert_eq!(coloring.num_colors(), 3);
460 assert_eq!(reduction.pos_vertices().len(), 3);
461 assert_eq!(reduction.neg_vertices().len(), 3);
462 }
463
464 #[test]
465 fn test_source_and_target_size() {
466 let sat = Satisfiability::<i32>::new(
467 3,
468 vec![CNFClause::new(vec![1, 2]), CNFClause::new(vec![-1, 3])],
469 );
470 let reduction = ReduceTo::<Coloring>::reduce_to(&sat);
471
472 let source_size = reduction.source_size();
473 let target_size = reduction.target_size();
474
475 assert_eq!(source_size.get("num_vars"), Some(3));
476 assert_eq!(source_size.get("num_clauses"), Some(2));
477 assert!(target_size.get("num_vertices").is_some());
478 assert!(target_size.get("num_colors").unwrap() == 3);
479 }
480
481 #[test]
482 fn test_extract_solution_basic() {
483 let sat = Satisfiability::<i32>::new(1, vec![CNFClause::new(vec![1])]);
485 let reduction = ReduceTo::<Coloring>::reduce_to(&sat);
486
487 assert_eq!(reduction.pos_vertices().len(), 1);
502 assert_eq!(reduction.neg_vertices().len(), 1);
503 }
504
505 #[test]
506 fn test_complex_formula_structure() {
507 let sat = Satisfiability::<i32>::new(
509 3,
510 vec![
511 CNFClause::new(vec![1, 2]), CNFClause::new(vec![-1, 3]), CNFClause::new(vec![-2, -3]), ],
515 );
516
517 let reduction = ReduceTo::<Coloring>::reduce_to(&sat);
518 let coloring = reduction.target_problem();
519
520 assert_eq!(coloring.num_vertices(), 24);
524 assert_eq!(coloring.num_colors(), 3);
525 assert_eq!(reduction.num_clauses(), 3);
526 }
527
528 #[test]
529 fn test_single_literal_clauses() {
530 let sat = Satisfiability::<i32>::new(
532 2,
533 vec![CNFClause::new(vec![1]), CNFClause::new(vec![2])],
534 );
535
536 let reduction = ReduceTo::<Coloring>::reduce_to(&sat);
537 let coloring = reduction.target_problem();
538
539 let solver = BruteForce::new();
540 let solutions = solver.find_best(coloring);
541
542 let mut found_correct = false;
543 for sol in &solutions {
544 if coloring.solution_size(sol).is_valid {
545 let sat_sol = reduction.extract_solution(sol);
546 if sat_sol == vec![1, 1] {
547 found_correct = true;
548 break;
549 }
550 }
551 }
552
553 assert!(
554 found_correct,
555 "Should find solution where both x1 and x2 are true"
556 );
557 }
558
559 #[test]
560 fn test_empty_sat() {
561 let sat = Satisfiability::<i32>::new(0, vec![]);
563 let reduction = ReduceTo::<Coloring>::reduce_to(&sat);
564
565 assert_eq!(reduction.num_clauses(), 0);
566 assert!(reduction.pos_vertices().is_empty());
567 assert!(reduction.neg_vertices().is_empty());
568
569 let coloring = reduction.target_problem();
570 assert_eq!(coloring.num_vertices(), 3);
572 }
573
574 #[test]
575 fn test_num_clauses_accessor() {
576 let sat = Satisfiability::<i32>::new(
577 2,
578 vec![CNFClause::new(vec![1, 2]), CNFClause::new(vec![-1])],
579 );
580 let reduction = ReduceTo::<Coloring>::reduce_to(&sat);
581 assert_eq!(reduction.num_clauses(), 2);
582 }
583
584 #[test]
585 fn test_or_gadget_construction() {
586 let mut constructor = SATColoringConstructor::new(2);
588 let initial_vertices = constructor.num_vertices;
589
590 let input1 = constructor.pos_vertices[0]; let input2 = constructor.pos_vertices[1]; let output = constructor.add_or_gadget(input1, input2);
594
595 assert_eq!(constructor.num_vertices, initial_vertices + 5);
597
598 assert_eq!(output, constructor.num_vertices - 1);
600 }
601
602 #[test]
603 fn test_manual_coloring_extraction() {
604 let sat = Satisfiability::<i32>::new(1, vec![CNFClause::new(vec![1])]);
607 let reduction = ReduceTo::<Coloring>::reduce_to(&sat);
608 let coloring = reduction.target_problem();
609
610 let valid_coloring = vec![0, 1, 2, 0, 1];
622
623 assert_eq!(coloring.num_vertices(), 5);
624 let extracted = reduction.extract_solution(&valid_coloring);
625 assert_eq!(extracted, vec![1]);
627 }
628
629 #[test]
630 fn test_extraction_with_different_color_assignment() {
631 let sat = Satisfiability::<i32>::new(1, vec![CNFClause::new(vec![1])]);
634 let reduction = ReduceTo::<Coloring>::reduce_to(&sat);
635
636 let coloring_permuted = vec![2, 0, 1, 2, 0];
639 let extracted = reduction.extract_solution(&coloring_permuted);
640 assert_eq!(extracted, vec![1]);
642
643 let coloring_permuted2 = vec![1, 2, 0, 1, 2];
646 let extracted2 = reduction.extract_solution(&coloring_permuted2);
647 assert_eq!(extracted2, vec![1]);
648 }
649}
650
651use crate::poly;
653use crate::rules::registry::{ReductionEntry, ReductionOverhead};
654
655inventory::submit! {
656 ReductionEntry {
657 source_name: "Satisfiability",
658 target_name: "Coloring",
659 source_graph: "CNF",
660 target_graph: "SimpleGraph",
661 overhead_fn: || ReductionOverhead::new(vec![
662 ("num_vertices", poly!(3 * num_vars)),
663 ("num_colors", poly!(3)),
664 ]),
665 }
666}