1use crate::models::formula::{KSatisfiability, NAESatisfiability, Satisfiability};
12use crate::models::graph::MonochromaticTriangle;
13use crate::reduction;
14use crate::rules::sat_helpers::SatVariableAllocator;
15use crate::rules::satisfiability_naesatisfiability::ReductionSATToNAESAT;
16use crate::rules::traits::{ReduceTo, ReductionResult};
17use crate::topology::SimpleGraph;
18use crate::variant::K3;
19
20fn add_equality_sender(
27 edges: &mut Vec<(usize, usize)>,
28 first: (usize, usize),
29 second: (usize, usize),
30 private: usize,
31) {
32 let [u, v, w] = [private, private + 1, private + 2];
33 edges.extend([(u, v), (u, w), (v, w)]);
34 for endpoint in [first.0, first.1, second.0, second.1] {
35 edges.extend([(endpoint, u), (endpoint, v), (endpoint, w)]);
36 }
37}
38
39#[derive(Debug, Clone)]
41pub struct Reduction3SATToMonochromaticTriangle {
42 target: MonochromaticTriangle<SimpleGraph>,
43 nae_reduction: ReductionSATToNAESAT,
44}
45
46impl ReductionResult for Reduction3SATToMonochromaticTriangle {
47 type Source = KSatisfiability<K3>;
48 type Target = MonochromaticTriangle<SimpleGraph>;
49
50 fn target_problem(&self) -> &Self::Target {
51 &self.target
52 }
53
54 fn extract_solution(
55 &self,
56 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
57 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
58 crate::rules::traits::validate_target_witness(
59 self.target_problem(),
60 target_solution,
61 |value| value.0,
62 "target witness does not satisfy the target problem",
63 )?;
64 let nae_solution = (0..self.nae_reduction.target_problem().num_vars())
65 .map(|index| target_solution[2 * index])
66 .collect();
67 self.nae_reduction.extract_solution(&nae_solution)
70 }
71}
72
73#[crate::aggregate_reduction(identity)]
74impl crate::rules::AggregateReductionResult for Reduction3SATToMonochromaticTriangle {}
75
76#[reduction(
77 transform = upper_bound {
78 num_vertices = "16 * num_vars + 40 * num_clauses + 16",
79 num_edges = "50 * num_vars + 146 * num_clauses + 50",
80 num_triangles = "58 * num_vars + 174 * num_clauses + 58",
81 }
82)]
83impl ReduceTo<MonochromaticTriangle<SimpleGraph>> for KSatisfiability<K3> {
84 type Result = Reduction3SATToMonochromaticTriangle;
85
86 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
87 let sat = ReduceTo::<Satisfiability>::reduce_to(self)?;
88 let nae_reduction = ReduceTo::<NAESatisfiability>::reduce_to(sat.target_problem())?;
89 let nae = nae_reduction.target_problem();
90 let auxiliary_count = nae.clauses().iter().filter(|c| c.len() == 4).count();
91 let overflow = || {
92 crate::rules::ReductionError::integer_overflow::<Self, MonochromaticTriangle<SimpleGraph>>(
93 "computing signal-sender graph size",
94 )
95 };
96 let variable_count = nae
97 .num_vars()
98 .checked_add(auxiliary_count)
99 .ok_or_else(overflow)?;
100 let triple_count = variable_count
101 .checked_add(nae.num_clauses())
102 .and_then(|n| n.checked_add(auxiliary_count))
103 .ok_or_else(overflow)?;
104 let num_vertices = variable_count
105 .checked_mul(4)
106 .and_then(|n| triple_count.checked_mul(12).and_then(|t| n.checked_add(t)))
107 .ok_or_else(overflow)?;
108 let num_edges = variable_count
109 .checked_mul(2)
110 .and_then(|n| triple_count.checked_mul(48).and_then(|t| n.checked_add(t)))
111 .ok_or_else(overflow)?;
112
113 let mut variables =
114 SatVariableAllocator::new("KSatisfiability -> MonochromaticTriangle", nae.num_vars())
115 .map_err(
116 crate::rules::ReductionError::construction::<
117 Self,
118 MonochromaticTriangle<SimpleGraph>,
119 >,
120 )?;
121 let mut triples = Vec::with_capacity(triple_count);
122 for index in 0..variable_count {
123 let literal = i64::try_from(index + 1).map_err(|_| overflow())?;
124 triples.push([literal, literal, -literal]);
125 }
126 for clause in nae.clauses() {
127 let literals = &clause.literals;
128 if literals.len() == 4 {
129 let z = variables.allocate().map_err(
130 crate::rules::ReductionError::construction::<
131 Self,
132 MonochromaticTriangle<SimpleGraph>,
133 >,
134 )?;
135 triples.push([literals[0], literals[1], z]);
136 triples.push([-z, literals[2], literals[3]]);
137 } else {
138 triples.push([literals[0], literals[1], literals[literals.len() - 1]]);
141 }
142 }
143
144 let mut edges = Vec::with_capacity(num_edges);
145 for index in 0..variable_count {
146 edges.extend([(4 * index, 4 * index + 1), (4 * index + 2, 4 * index + 3)]);
147 }
148 let mut next_vertex = 4 * variable_count;
150 for triple in triples {
151 let [a, b, c] = [next_vertex, next_vertex + 1, next_vertex + 2];
152 next_vertex += 3;
153 let sides = [(a, b), (a, c), (b, c)];
154 edges.extend(sides);
155 for (literal, side) in triple.into_iter().zip(sides) {
156 let index = usize::try_from(literal.unsigned_abs() - 1)
157 .expect("validated SAT variable index fits usize");
158 let endpoint = 4 * index + if literal < 0 { 2 } else { 0 };
159 add_equality_sender(&mut edges, (endpoint, endpoint + 1), side, next_vertex);
160 next_vertex += 3;
161 }
162 }
163 debug_assert_eq!(next_vertex, num_vertices);
164 debug_assert_eq!(edges.len(), num_edges);
165 let target = MonochromaticTriangle::new(SimpleGraph::new(num_vertices, edges));
166 Ok(Reduction3SATToMonochromaticTriangle {
167 target,
168 nae_reduction,
169 })
170 }
171}
172
173#[cfg(feature = "example-db")]
174pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
175 use crate::export::SolutionPair;
176 use crate::models::formula::CNFClause;
177 use crate::solvers::ILPSolver;
178
179 vec![crate::example_db::specs::RuleExampleSpec {
180 id: "ksatisfiability_to_monochromatictriangle",
181 build: || {
182 let source = KSatisfiability::<K3>::new(3, vec![CNFClause::new(vec![1, 2, 3])]);
183 let reduction = ReduceTo::<MonochromaticTriangle<SimpleGraph>>::reduce_to(&source)
184 .expect("reduction should succeed");
185 let target_config = ILPSolver::new()
186 .solve(reduction.target_problem())
187 .expect("canonical target must be colourable");
188 let source_config = reduction.extract_solution(&target_config).unwrap();
189 crate::example_db::specs::assemble_rule_example(
190 &source,
191 reduction.target_problem(),
192 vec![SolutionPair {
193 source_config: serde_json::to_value(source_config)
194 .expect("solution serialization must succeed"),
195 target_config: serde_json::to_value(target_config)
196 .expect("solution serialization must succeed"),
197 }],
198 )
199 },
200 }]
201}
202
203#[cfg(test)]
204#[path = "../unit_tests/rules/ksatisfiability_monochromatictriangle.rs"]
205mod tests;