1use crate::models::formula::KSatisfiability;
11use crate::models::misc::RegisterSufficiency;
12use crate::reduction;
13use crate::rules::traits::{ReduceTo, ReductionResult};
14use crate::variant::K3;
15
16#[cfg_attr(not(any(test, feature = "example-db")), allow(dead_code))]
17#[derive(Debug, Clone)]
18struct SethiRegisterLayout {
19 num_vars: usize,
20 num_clauses: usize,
21 b_padding: usize,
22 a_start: usize,
23 b_start: usize,
24 c_start: usize,
25 f_start: usize,
26 initial_idx: usize,
27 d_idx: usize,
28 final_idx: usize,
29 r_starts: Vec<usize>,
30 s_starts: Vec<usize>,
31 t_starts: Vec<usize>,
32 u_start: usize,
33 w_start: usize,
34 x_pos_start: usize,
35 x_neg_start: usize,
36 z_start: usize,
37 total_vertices: usize,
38}
39
40#[cfg_attr(not(any(test, feature = "example-db")), allow(dead_code))]
41impl SethiRegisterLayout {
42 fn new(num_vars: usize, num_clauses: usize) -> Self {
43 let b_padding = (2 * num_vars).saturating_sub(num_clauses);
44 let mut next = 0usize;
45
46 let a_start = next;
47 next += 2 * num_vars + 1;
48
49 let b_start = next;
50 next += b_padding;
51
52 let c_start = next;
53 next += num_clauses;
54
55 let f_start = next;
56 next += 3 * num_clauses;
57
58 let initial_idx = next;
59 let d_idx = next + 1;
60 let final_idx = next + 2;
61 next += 3;
62
63 let mut r_starts = Vec::with_capacity(num_vars);
64 for var in 0..num_vars {
65 r_starts.push(next);
66 next += 2 * num_vars - 2 * var;
67 }
68
69 let mut s_starts = Vec::with_capacity(num_vars);
70 for var in 0..num_vars {
71 s_starts.push(next);
72 next += 2 * num_vars - 2 * var - 1;
73 }
74
75 let mut t_starts = Vec::with_capacity(num_vars);
76 for var in 0..num_vars {
77 t_starts.push(next);
78 next += 2 * num_vars - 2 * var - 1;
79 }
80
81 let u_start = next;
82 next += 2 * num_vars;
83
84 let w_start = next;
85 next += num_vars;
86
87 let x_pos_start = next;
88 next += num_vars;
89
90 let x_neg_start = next;
91 next += num_vars;
92
93 let z_start = next;
94 next += num_vars;
95
96 Self {
97 num_vars,
98 num_clauses,
99 b_padding,
100 a_start,
101 b_start,
102 c_start,
103 f_start,
104 initial_idx,
105 d_idx,
106 final_idx,
107 r_starts,
108 s_starts,
109 t_starts,
110 u_start,
111 w_start,
112 x_pos_start,
113 x_neg_start,
114 z_start,
115 total_vertices: next,
116 }
117 }
118
119 fn total_vertices(&self) -> usize {
120 self.total_vertices
121 }
122
123 fn bound(&self) -> usize {
124 3 * self.num_clauses + 4 * self.num_vars + 1 + self.b_padding
125 }
126
127 fn initial(&self) -> usize {
128 self.initial_idx
129 }
130
131 fn d(&self) -> usize {
132 self.d_idx
133 }
134
135 fn final_node(&self) -> usize {
136 self.final_idx
137 }
138
139 fn a(&self, index: usize) -> usize {
140 self.a_start + index
141 }
142
143 fn bnode(&self, index: usize) -> usize {
144 self.b_start + index
145 }
146
147 fn c(&self, clause: usize) -> usize {
148 self.c_start + clause
149 }
150
151 fn f(&self, clause: usize, literal_pos: usize) -> usize {
152 self.f_start + 3 * clause + literal_pos
153 }
154
155 fn r(&self, var: usize, index: usize) -> usize {
156 self.r_starts[var] + index
157 }
158
159 fn s(&self, var: usize, index: usize) -> usize {
160 self.s_starts[var] + index
161 }
162
163 fn t(&self, var: usize, index: usize) -> usize {
164 self.t_starts[var] + index
165 }
166
167 fn u(&self, var: usize, slot: usize) -> usize {
168 self.u_start + 2 * var + slot
169 }
170
171 fn w(&self, var: usize) -> usize {
172 self.w_start + var
173 }
174
175 fn x_pos(&self, var: usize) -> usize {
176 self.x_pos_start + var
177 }
178
179 fn x_neg(&self, var: usize) -> usize {
180 self.x_neg_start + var
181 }
182
183 fn z(&self, var: usize) -> usize {
184 self.z_start + var
185 }
186}
187
188#[derive(Debug, Clone)]
189pub struct Reduction3SATToRegisterSufficiency {
190 target: RegisterSufficiency,
191 layout: SethiRegisterLayout,
192}
193
194impl ReductionResult for Reduction3SATToRegisterSufficiency {
195 type Source = KSatisfiability<K3>;
196 type Target = RegisterSufficiency;
197
198 fn target_problem(&self) -> &Self::Target {
199 &self.target
200 }
201
202 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
203 if self.layout.num_vars == 0 {
204 return Vec::new();
205 }
206
207 let cutoff = target_solution[self.layout.w(self.layout.num_vars - 1)];
208 (0..self.layout.num_vars)
209 .map(|var| {
210 let x_pos_before = target_solution[self.layout.x_pos(var)] < cutoff;
211 let x_neg_before = target_solution[self.layout.x_neg(var)] < cutoff;
212 debug_assert!(
213 !(x_pos_before && x_neg_before),
214 "Sethi extraction expects at most one of x_pos/x_neg before w[n]",
215 );
216 usize::from(x_pos_before)
217 })
218 .collect()
219 }
220}
221
222#[reduction(overhead = {
223 num_vertices = "3 * num_vars^2 + 9 * num_vars + 4 * num_clauses + register_sufficiency_padding + 4",
224 num_arcs = "6 * num_vars^2 + 19 * num_vars + 16 * num_clauses + 2 * register_sufficiency_padding + 1",
225 bound = "3 * num_clauses + 4 * num_vars + 1 + register_sufficiency_padding",
226})]
227impl ReduceTo<RegisterSufficiency> for KSatisfiability<K3> {
228 type Result = Reduction3SATToRegisterSufficiency;
229
230 fn reduce_to(&self) -> Self::Result {
231 let layout = SethiRegisterLayout::new(self.num_vars(), self.num_clauses());
232 let mut arcs = Vec::with_capacity(
233 6 * self.num_vars() * self.num_vars()
234 + 19 * self.num_vars()
235 + 16 * self.num_clauses()
236 + 2 * layout.b_padding
237 + 1,
238 );
239
240 for index in 0..(2 * self.num_vars() + 1) {
241 arcs.push((layout.initial(), layout.a(index)));
242 }
243 for index in 0..layout.b_padding {
244 arcs.push((layout.initial(), layout.bnode(index)));
245 }
246 for clause in 0..self.num_clauses() {
247 for literal_pos in 0..3 {
248 arcs.push((layout.initial(), layout.f(clause, literal_pos)));
249 }
250 }
251 for var in 0..self.num_vars() {
252 arcs.push((layout.initial(), layout.u(var, 0)));
253 arcs.push((layout.initial(), layout.u(var, 1)));
254 }
255
256 for clause in 0..self.num_clauses() {
257 arcs.push((layout.c(clause), layout.initial()));
258 }
259 for var in 0..self.num_vars() {
260 for index in 0..(2 * self.num_vars() - 2 * var) {
261 arcs.push((layout.r(var, index), layout.initial()));
262 }
263 for index in 0..(2 * self.num_vars() - 2 * var - 1) {
264 arcs.push((layout.s(var, index), layout.initial()));
265 arcs.push((layout.t(var, index), layout.initial()));
266 }
267 arcs.push((layout.w(var), layout.initial()));
268 }
269
270 for var in 0..self.num_vars() {
271 arcs.push((layout.final_node(), layout.w(var)));
272 arcs.push((layout.final_node(), layout.x_pos(var)));
273 arcs.push((layout.final_node(), layout.x_neg(var)));
274 arcs.push((layout.final_node(), layout.z(var)));
275 }
276 arcs.push((layout.final_node(), layout.initial()));
277 arcs.push((layout.final_node(), layout.d()));
278
279 for var in 0..self.num_vars() {
280 arcs.push((layout.x_pos(var), layout.z(var)));
281 arcs.push((layout.x_neg(var), layout.z(var)));
282 arcs.push((layout.x_pos(var), layout.u(var, 0)));
283 arcs.push((layout.x_neg(var), layout.u(var, 1)));
284 }
285
286 for var in 0..self.num_vars() {
287 arcs.push((layout.w(var), layout.u(var, 0)));
288 arcs.push((layout.w(var), layout.u(var, 1)));
289 }
290
291 for var in 0..self.num_vars() {
292 for index in 0..(2 * self.num_vars() - 2 * var - 1) {
293 arcs.push((layout.x_pos(var), layout.s(var, index)));
294 arcs.push((layout.x_neg(var), layout.t(var, index)));
295 }
296 for index in 0..(2 * self.num_vars() - 2 * var) {
297 arcs.push((layout.z(var), layout.r(var, index)));
298 }
299 }
300
301 for var in 1..self.num_vars() {
302 arcs.push((layout.z(var), layout.w(var - 1)));
303 arcs.push((layout.z(var), layout.z(var - 1)));
304 }
305 if self.num_vars() > 0 {
306 let last_var = self.num_vars() - 1;
307 for clause in 0..self.num_clauses() {
308 arcs.push((layout.c(clause), layout.w(last_var)));
309 arcs.push((layout.c(clause), layout.z(last_var)));
310 }
311 }
312
313 for clause in 0..self.num_clauses() {
314 for literal_pos in 0..3 {
315 arcs.push((layout.c(clause), layout.f(clause, literal_pos)));
316 }
317 }
318
319 for index in 0..layout.b_padding {
320 arcs.push((layout.d(), layout.bnode(index)));
321 }
322 for clause in 0..self.num_clauses() {
323 arcs.push((layout.d(), layout.c(clause)));
324 }
325
326 for (clause_idx, clause) in self.clauses().iter().enumerate() {
327 let mut lit_nodes = [0usize; 3];
328 let mut neg_nodes = [0usize; 3];
329
330 for (literal_pos, &literal) in clause.literals.iter().enumerate() {
331 let var = literal.unsigned_abs() as usize - 1;
332 if literal > 0 {
333 lit_nodes[literal_pos] = layout.x_pos(var);
334 neg_nodes[literal_pos] = layout.x_neg(var);
335 } else {
336 lit_nodes[literal_pos] = layout.x_neg(var);
337 neg_nodes[literal_pos] = layout.x_pos(var);
338 }
339 arcs.push((lit_nodes[literal_pos], layout.f(clause_idx, literal_pos)));
340 }
341
342 for (earlier, &neg_node) in neg_nodes.iter().enumerate() {
343 for later in (earlier + 1)..3 {
344 arcs.push((neg_node, layout.f(clause_idx, later)));
345 }
346 }
347 }
348
349 Reduction3SATToRegisterSufficiency {
350 target: RegisterSufficiency::new(layout.total_vertices(), arcs, layout.bound()),
351 layout,
352 }
353 }
354}
355
356#[cfg(feature = "example-db")]
357pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
358 use crate::export::SolutionPair;
359 use crate::models::formula::CNFClause;
360
361 vec![crate::example_db::specs::RuleExampleSpec {
362 id: "ksatisfiability_to_registersufficiency",
363 build: || {
364 let source = KSatisfiability::<K3>::new(
365 3,
366 vec![
367 CNFClause::new(vec![1, -2, 3]),
368 CNFClause::new(vec![-1, 2, -3]),
369 ],
370 );
371 let to_registers =
372 <KSatisfiability<K3> as ReduceTo<RegisterSufficiency>>::reduce_to(&source);
373
374 let target_config = to_registers
377 .target_problem()
378 .solve_exact()
379 .expect("satisfying 3-SAT instance must yield a feasible RS witness");
380 let source_config = to_registers.extract_solution(&target_config);
381
382 crate::example_db::specs::assemble_rule_example(
383 &source,
384 to_registers.target_problem(),
385 vec![SolutionPair {
386 source_config,
387 target_config,
388 }],
389 )
390 },
391 }]
392}
393
394#[cfg(test)]
395#[path = "../unit_tests/rules/ksatisfiability_registersufficiency.rs"]
396mod tests;