1use crate::models::formula::KSatisfiability;
13use crate::models::misc::RegisterSufficiency;
14use crate::reduction;
15use crate::rules::traits::{ReduceTo, ReductionResult};
16use crate::variant::K3;
17use std::collections::BTreeSet;
18
19#[cfg_attr(not(any(test, feature = "example-db")), allow(dead_code))]
20#[derive(Debug, Clone)]
21struct SethiRegisterLayout {
22 num_vars: usize,
23 num_clauses: usize,
24 b_padding: usize,
25 a_start: usize,
26 b_start: usize,
27 c_start: usize,
28 f_start: usize,
29 initial_idx: usize,
30 d_idx: usize,
31 final_idx: usize,
32 r_starts: Vec<usize>,
33 s_starts: Vec<usize>,
34 t_starts: Vec<usize>,
35 u_start: usize,
36 w_start: usize,
37 x_pos_start: usize,
38 x_neg_start: usize,
39 z_start: usize,
40 total_vertices: usize,
41 arc_capacity: usize,
42 register_bound: usize,
43}
44
45#[cfg_attr(not(any(test, feature = "example-db")), allow(dead_code))]
46impl SethiRegisterLayout {
47 fn new(num_vars: usize, num_clauses: usize) -> Result<Self, crate::rules::ReductionError> {
48 let overflow = || {
49 crate::rules::ReductionError::integer_overflow::<KSatisfiability<K3>, RegisterSufficiency>(
50 "computing Sethi layout dimensions",
51 )
52 };
53 let twice_vars = num_vars.checked_mul(2).ok_or_else(overflow)?;
54 let square = num_vars.checked_mul(num_vars).ok_or_else(overflow)?;
55 let b_padding = twice_vars.saturating_sub(num_clauses);
56 let total_vertices = square
57 .checked_mul(3)
58 .and_then(|size| size.checked_add(num_vars.checked_mul(9)?))
59 .and_then(|size| size.checked_add(num_clauses.checked_mul(4)?))
60 .and_then(|size| size.checked_add(b_padding))
61 .and_then(|size| size.checked_add(4))
62 .ok_or_else(overflow)?;
63 let arc_capacity = square
64 .checked_mul(6)
65 .and_then(|size| size.checked_add(num_vars.checked_mul(19)?))
66 .and_then(|size| size.checked_add(num_clauses.checked_mul(16)?))
67 .and_then(|size| size.checked_add(b_padding.checked_mul(2)?))
68 .and_then(|size| size.checked_add(1))
69 .ok_or_else(overflow)?;
70 let register_bound = num_clauses
71 .checked_mul(3)
72 .and_then(|size| size.checked_add(num_vars.checked_mul(4)?))
73 .and_then(|size| size.checked_add(b_padding))
74 .and_then(|size| size.checked_add(1))
75 .ok_or_else(overflow)?;
76 let mut next = 0usize;
79
80 let a_start = next;
81 next += 2 * num_vars + 1;
82
83 let b_start = next;
84 next += b_padding;
85
86 let c_start = next;
87 next += num_clauses;
88
89 let f_start = next;
90 next += 3 * num_clauses;
91
92 let initial_idx = next;
93 let d_idx = next + 1;
94 let final_idx = next + 2;
95 next += 3;
96
97 let mut r_starts = Vec::with_capacity(num_vars);
98 for var in 0..num_vars {
99 r_starts.push(next);
100 next += 2 * num_vars - 2 * var;
101 }
102
103 let mut s_starts = Vec::with_capacity(num_vars);
104 for var in 0..num_vars {
105 s_starts.push(next);
106 next += 2 * num_vars - 2 * var - 1;
107 }
108
109 let mut t_starts = Vec::with_capacity(num_vars);
110 for var in 0..num_vars {
111 t_starts.push(next);
112 next += 2 * num_vars - 2 * var - 1;
113 }
114
115 let u_start = next;
116 next += 2 * num_vars;
117
118 let w_start = next;
119 next += num_vars;
120
121 let x_pos_start = next;
122 next += num_vars;
123
124 let x_neg_start = next;
125 next += num_vars;
126
127 let z_start = next;
128 next += num_vars;
129 debug_assert_eq!(next, total_vertices);
130
131 Ok(Self {
132 num_vars,
133 num_clauses,
134 b_padding,
135 a_start,
136 b_start,
137 c_start,
138 f_start,
139 initial_idx,
140 d_idx,
141 final_idx,
142 r_starts,
143 s_starts,
144 t_starts,
145 u_start,
146 w_start,
147 x_pos_start,
148 x_neg_start,
149 z_start,
150 total_vertices,
151 arc_capacity,
152 register_bound,
153 })
154 }
155
156 fn total_vertices(&self) -> usize {
157 self.total_vertices
158 }
159
160 fn bound(&self) -> usize {
161 self.register_bound
162 }
163
164 fn initial(&self) -> usize {
165 self.initial_idx
166 }
167
168 fn d(&self) -> usize {
169 self.d_idx
170 }
171
172 fn final_node(&self) -> usize {
173 self.final_idx
174 }
175
176 fn a(&self, index: usize) -> usize {
177 self.a_start + index
178 }
179
180 fn bnode(&self, index: usize) -> usize {
181 self.b_start + index
182 }
183
184 fn c(&self, clause: usize) -> usize {
185 self.c_start + clause
186 }
187
188 fn f(&self, clause: usize, literal_pos: usize) -> usize {
189 self.f_start + 3 * clause + literal_pos
190 }
191
192 fn r(&self, var: usize, index: usize) -> usize {
193 self.r_starts[var] + index
194 }
195
196 fn s(&self, var: usize, index: usize) -> usize {
197 self.s_starts[var] + index
198 }
199
200 fn t(&self, var: usize, index: usize) -> usize {
201 self.t_starts[var] + index
202 }
203
204 fn u(&self, var: usize, slot: usize) -> usize {
205 self.u_start + 2 * var + slot
206 }
207
208 fn w(&self, var: usize) -> usize {
209 self.w_start + var
210 }
211
212 fn x_pos(&self, var: usize) -> usize {
213 self.x_pos_start + var
214 }
215
216 fn x_neg(&self, var: usize) -> usize {
217 self.x_neg_start + var
218 }
219
220 fn z(&self, var: usize) -> usize {
221 self.z_start + var
222 }
223 #[cfg(any(test, feature = "example-db"))]
224 fn schedule_for_assignment(&self, assignment: &[bool]) -> Vec<usize> {
225 assert_eq!(assignment.len(), self.num_vars);
226 let mut order = Vec::with_capacity(self.total_vertices);
227 order.extend((0..2 * self.num_vars + 1).map(|i| self.a(i)));
228 order.extend((0..self.b_padding).map(|i| self.bnode(i)));
229 for clause in 0..self.num_clauses {
230 order.extend((0..3).map(|position| self.f(clause, position)));
231 }
232 for variable in 0..self.num_vars {
233 order.extend((0..2).map(|slot| self.u(variable, slot)));
234 }
235 order.push(self.initial());
236 for (variable, &positive) in assignment.iter().enumerate() {
237 order.extend((0..2 * self.num_vars - 2 * variable).map(|i| self.r(variable, i)));
238 order.push(self.z(variable));
239 order.extend((0..2 * self.num_vars - 2 * variable - 1).map(|i| {
240 if positive {
241 self.s(variable, i)
242 } else {
243 self.t(variable, i)
244 }
245 }));
246 order.push(if positive {
247 self.x_pos(variable)
248 } else {
249 self.x_neg(variable)
250 });
251 order.push(self.w(variable));
252 }
253 order.extend((0..self.num_clauses).map(|clause| self.c(clause)));
254 order.push(self.d());
255 for (variable, &positive) in assignment.iter().enumerate() {
256 order.extend((0..2 * self.num_vars - 2 * variable - 1).map(|i| {
257 if positive {
258 self.t(variable, i)
259 } else {
260 self.s(variable, i)
261 }
262 }));
263 order.push(if positive {
264 self.x_neg(variable)
265 } else {
266 self.x_pos(variable)
267 });
268 }
269 order.push(self.final_node());
270 assert_eq!(order.len(), self.total_vertices);
271 let mut positions = vec![0; self.total_vertices];
272 for (position, vertex) in order.into_iter().enumerate() {
273 positions[vertex] = position;
274 }
275 positions
276 }
277}
278
279#[derive(Debug, Clone)]
280pub struct Reduction3SATToRegisterSufficiency {
281 target: RegisterSufficiency,
282 layout: Option<SethiRegisterLayout>,
283 source_num_vars: usize,
284 source_variables: Vec<usize>,
285}
286
287impl ReductionResult for Reduction3SATToRegisterSufficiency {
288 type Source = KSatisfiability<K3>;
289 type Target = RegisterSufficiency;
290
291 fn target_problem(&self) -> &Self::Target {
292 &self.target
293 }
294
295 fn extract_solution(
296 &self,
297 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
298 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
299 crate::rules::traits::validate_target_witness(
300 self.target_problem(),
301 target_solution,
302 |value| value.0,
303 "target ordering does not satisfy the register bound and dependencies",
304 )?;
305 let mut assignment = vec![false; self.source_num_vars];
306 let Some(layout) = &self.layout else {
307 return Ok(assignment);
309 };
310 let cutoff = target_solution[layout.w(layout.num_vars - 1)];
311 for (variable, &original) in self.source_variables.iter().enumerate() {
312 let positive = target_solution[layout.x_pos(variable)] < cutoff;
313 let negative = target_solution[layout.x_neg(variable)] < cutoff;
314 if positive && negative {
315 return Err(crate::rules::ExtractionError::invalid(format!(
316 "both literals of variable {original} precede the extraction cutoff"
317 )));
318 }
319 assignment[original] = positive;
320 }
321 Ok(assignment)
322 }
323}
324
325#[crate::aggregate_reduction(identity)]
326impl crate::rules::AggregateReductionResult for Reduction3SATToRegisterSufficiency {}
327
328#[reduction(
329 transform = upper_bound {
330 num_vertices = "3 * num_vars^2 + 11 * num_vars + 4 * num_clauses + 4",
331 num_arcs = "6 * num_vars^2 + 23 * num_vars + 16 * num_clauses + 1",
332 bound = "6 * num_vars + 3 * num_clauses + 1",
333 num_sinks = "1",
334 }
335)]
336impl ReduceTo<RegisterSufficiency> for KSatisfiability<K3> {
337 type Result = Reduction3SATToRegisterSufficiency;
338
339 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
340 let empty_clause = self
341 .clauses()
342 .iter()
343 .any(|clause| clause.literals.is_empty());
344 if empty_clause || self.num_clauses() == 0 {
345 return Ok(Reduction3SATToRegisterSufficiency {
348 target: RegisterSufficiency::new(usize::from(empty_clause), Vec::new(), 0),
349 layout: None,
350 source_num_vars: self.num_vars(),
351 source_variables: Vec::new(),
352 });
353 }
354 let source_variables: Vec<_> = self
355 .clauses()
356 .iter()
357 .flat_map(|clause| &clause.literals)
358 .map(|literal| {
359 usize::try_from(literal.unsigned_abs())
360 .expect("native SAT variable indices fit usize")
361 - 1
362 })
363 .collect::<BTreeSet<_>>()
364 .into_iter()
365 .collect();
366 let clauses: Vec<_> = self
367 .clauses()
368 .iter()
369 .map(|clause| {
370 let mut literals = [0i64; 3];
371 for (position, &literal) in clause.literals.iter().enumerate() {
372 let original = usize::try_from(literal.unsigned_abs())
373 .expect("native SAT variable indices fit usize")
374 - 1;
375 let compact = source_variables
376 .binary_search(&original)
377 .expect("all appearing SAT variables were collected");
378 let variable = i64::try_from(compact + 1).expect("compact SAT indices fit i64");
379 literals[position] = if literal > 0 { variable } else { -variable };
380 }
381 for position in clause.literals.len()..3 {
384 literals[position] = literals[0];
385 }
386 literals
387 })
388 .collect();
389 let num_vars = source_variables.len();
390 let num_clauses = clauses.len();
391 let layout = SethiRegisterLayout::new(num_vars, num_clauses)?;
392 let mut arcs = Vec::with_capacity(layout.arc_capacity);
393
394 for index in 0..(2 * num_vars + 1) {
395 arcs.push((layout.initial(), layout.a(index)));
396 }
397 for index in 0..layout.b_padding {
398 arcs.push((layout.initial(), layout.bnode(index)));
399 }
400 for clause in 0..num_clauses {
401 for literal_pos in 0..3 {
402 arcs.push((layout.initial(), layout.f(clause, literal_pos)));
403 }
404 }
405 for var in 0..num_vars {
406 arcs.push((layout.initial(), layout.u(var, 0)));
407 arcs.push((layout.initial(), layout.u(var, 1)));
408 }
409
410 for clause in 0..num_clauses {
411 arcs.push((layout.c(clause), layout.initial()));
412 }
413 for var in 0..num_vars {
414 for index in 0..(2 * num_vars - 2 * var) {
415 arcs.push((layout.r(var, index), layout.initial()));
416 }
417 for index in 0..(2 * num_vars - 2 * var - 1) {
418 arcs.push((layout.s(var, index), layout.initial()));
419 arcs.push((layout.t(var, index), layout.initial()));
420 }
421 arcs.push((layout.w(var), layout.initial()));
422 }
423
424 for var in 0..num_vars {
425 arcs.push((layout.final_node(), layout.w(var)));
426 arcs.push((layout.final_node(), layout.x_pos(var)));
427 arcs.push((layout.final_node(), layout.x_neg(var)));
428 arcs.push((layout.final_node(), layout.z(var)));
429 }
430 arcs.push((layout.final_node(), layout.initial()));
431 arcs.push((layout.final_node(), layout.d()));
432
433 for var in 0..num_vars {
434 arcs.push((layout.x_pos(var), layout.z(var)));
435 arcs.push((layout.x_neg(var), layout.z(var)));
436 arcs.push((layout.x_pos(var), layout.u(var, 0)));
437 arcs.push((layout.x_neg(var), layout.u(var, 1)));
438 }
439
440 for var in 0..num_vars {
441 arcs.push((layout.w(var), layout.u(var, 0)));
442 arcs.push((layout.w(var), layout.u(var, 1)));
443 }
444
445 for var in 0..num_vars {
446 for index in 0..(2 * num_vars - 2 * var - 1) {
447 arcs.push((layout.x_pos(var), layout.s(var, index)));
448 arcs.push((layout.x_neg(var), layout.t(var, index)));
449 }
450 for index in 0..(2 * num_vars - 2 * var) {
451 arcs.push((layout.z(var), layout.r(var, index)));
452 }
453 }
454
455 for var in 1..num_vars {
456 arcs.push((layout.z(var), layout.w(var - 1)));
457 arcs.push((layout.z(var), layout.z(var - 1)));
458 }
459 if num_vars > 0 {
460 let last_var = num_vars - 1;
461 for clause in 0..num_clauses {
462 arcs.push((layout.c(clause), layout.w(last_var)));
463 arcs.push((layout.c(clause), layout.z(last_var)));
464 }
465 }
466
467 for clause in 0..num_clauses {
468 for literal_pos in 0..3 {
469 arcs.push((layout.c(clause), layout.f(clause, literal_pos)));
470 }
471 }
472
473 for index in 0..layout.b_padding {
474 arcs.push((layout.d(), layout.bnode(index)));
475 }
476 for clause in 0..num_clauses {
477 arcs.push((layout.d(), layout.c(clause)));
478 }
479
480 for (clause_idx, clause) in clauses.iter().enumerate() {
481 let mut lit_nodes = [0usize; 3];
482 let mut neg_nodes = [0usize; 3];
483
484 for (literal_pos, &literal) in clause.iter().enumerate() {
485 let var = usize::try_from(literal.unsigned_abs())
486 .expect("compact SAT variable indices fit usize")
487 - 1;
488 if literal > 0 {
489 lit_nodes[literal_pos] = layout.x_pos(var);
490 neg_nodes[literal_pos] = layout.x_neg(var);
491 } else {
492 lit_nodes[literal_pos] = layout.x_neg(var);
493 neg_nodes[literal_pos] = layout.x_pos(var);
494 }
495 arcs.push((lit_nodes[literal_pos], layout.f(clause_idx, literal_pos)));
496 }
497
498 for (earlier, &neg_node) in neg_nodes.iter().enumerate() {
499 for later in (earlier + 1)..3 {
500 arcs.push((neg_node, layout.f(clause_idx, later)));
501 }
502 }
503 }
504
505 Ok(Reduction3SATToRegisterSufficiency {
506 target: RegisterSufficiency::new(layout.total_vertices(), arcs, layout.bound()),
507 layout: Some(layout),
508 source_num_vars: self.num_vars(),
509 source_variables,
510 })
511 }
512}
513
514#[cfg(feature = "example-db")]
515pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
516 use crate::export::SolutionPair;
517 use crate::models::formula::CNFClause;
518
519 vec![crate::example_db::specs::RuleExampleSpec {
520 id: "ksatisfiability_to_registersufficiency",
521 build: || {
522 let source = KSatisfiability::<K3>::new(
523 3,
524 vec![
525 CNFClause::new(vec![1, -2, 3]),
526 CNFClause::new(vec![-1, 2, -3]),
527 ],
528 );
529 let to_registers =
530 <KSatisfiability<K3> as ReduceTo<RegisterSufficiency>>::reduce_to(&source)
531 .expect("reduction should succeed");
532
533 let target_config = to_registers
534 .layout
535 .as_ref()
536 .expect("canonical formula has nonempty clauses")
537 .schedule_for_assignment(&[true, true, true]);
538 let source_config = to_registers.extract_solution(&target_config).unwrap();
539
540 crate::example_db::specs::assemble_rule_example(
541 &source,
542 to_registers.target_problem(),
543 vec![SolutionPair {
544 source_config: serde_json::to_value(source_config)
545 .expect("solution serialization must succeed"),
546 target_config: serde_json::to_value(target_config)
547 .expect("solution serialization must succeed"),
548 }],
549 )
550 },
551 }]
552}
553
554#[cfg(test)]
555#[path = "../unit_tests/rules/ksatisfiability_registersufficiency.rs"]
556mod tests;