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 let value =
300 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
301 if !value.0 {
302 return Err(crate::rules::ExtractionError::invalid(
303 "target ordering does not satisfy the register bound and dependencies",
304 ));
305 }
306 let mut assignment = vec![false; self.source_num_vars];
307 let Some(layout) = &self.layout else {
308 return Ok(assignment);
310 };
311 let cutoff = target_solution[layout.w(layout.num_vars - 1)];
312 for (variable, &original) in self.source_variables.iter().enumerate() {
313 let positive = target_solution[layout.x_pos(variable)] < cutoff;
314 let negative = target_solution[layout.x_neg(variable)] < cutoff;
315 if positive && negative {
316 return Err(crate::rules::ExtractionError::invalid(format!(
317 "both literals of variable {original} precede the extraction cutoff"
318 )));
319 }
320 assignment[original] = positive;
321 }
322 Ok(assignment)
323 }
324}
325
326#[reduction(
327 transform = upper_bound {
328 num_vertices = "3 * num_vars^2 + 11 * num_vars + 4 * num_clauses + 4",
329 num_arcs = "6 * num_vars^2 + 23 * num_vars + 16 * num_clauses + 1",
330 bound = "6 * num_vars + 3 * num_clauses + 1",
331 num_sinks = "1",
332 }
333)]
334impl ReduceTo<RegisterSufficiency> for KSatisfiability<K3> {
335 type Result = Reduction3SATToRegisterSufficiency;
336
337 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
338 let empty_clause = self
339 .clauses()
340 .iter()
341 .any(|clause| clause.literals.is_empty());
342 if empty_clause || self.num_clauses() == 0 {
343 return Ok(Reduction3SATToRegisterSufficiency {
346 target: RegisterSufficiency::new(usize::from(empty_clause), Vec::new(), 0),
347 layout: None,
348 source_num_vars: self.num_vars(),
349 source_variables: Vec::new(),
350 });
351 }
352 let source_variables: Vec<_> = self
353 .clauses()
354 .iter()
355 .flat_map(|clause| &clause.literals)
356 .map(|literal| {
357 usize::try_from(literal.unsigned_abs())
358 .expect("native SAT variable indices fit usize")
359 - 1
360 })
361 .collect::<BTreeSet<_>>()
362 .into_iter()
363 .collect();
364 let clauses: Vec<_> = self
365 .clauses()
366 .iter()
367 .map(|clause| {
368 let mut literals = [0i64; 3];
369 for (position, &literal) in clause.literals.iter().enumerate() {
370 let original = usize::try_from(literal.unsigned_abs())
371 .expect("native SAT variable indices fit usize")
372 - 1;
373 let compact = source_variables
374 .binary_search(&original)
375 .expect("all appearing SAT variables were collected");
376 let variable = i64::try_from(compact + 1).expect("compact SAT indices fit i64");
377 literals[position] = if literal > 0 { variable } else { -variable };
378 }
379 for position in clause.literals.len()..3 {
382 literals[position] = literals[0];
383 }
384 literals
385 })
386 .collect();
387 let num_vars = source_variables.len();
388 let num_clauses = clauses.len();
389 let layout = SethiRegisterLayout::new(num_vars, num_clauses)?;
390 let mut arcs = Vec::with_capacity(layout.arc_capacity);
391
392 for index in 0..(2 * num_vars + 1) {
393 arcs.push((layout.initial(), layout.a(index)));
394 }
395 for index in 0..layout.b_padding {
396 arcs.push((layout.initial(), layout.bnode(index)));
397 }
398 for clause in 0..num_clauses {
399 for literal_pos in 0..3 {
400 arcs.push((layout.initial(), layout.f(clause, literal_pos)));
401 }
402 }
403 for var in 0..num_vars {
404 arcs.push((layout.initial(), layout.u(var, 0)));
405 arcs.push((layout.initial(), layout.u(var, 1)));
406 }
407
408 for clause in 0..num_clauses {
409 arcs.push((layout.c(clause), layout.initial()));
410 }
411 for var in 0..num_vars {
412 for index in 0..(2 * num_vars - 2 * var) {
413 arcs.push((layout.r(var, index), layout.initial()));
414 }
415 for index in 0..(2 * num_vars - 2 * var - 1) {
416 arcs.push((layout.s(var, index), layout.initial()));
417 arcs.push((layout.t(var, index), layout.initial()));
418 }
419 arcs.push((layout.w(var), layout.initial()));
420 }
421
422 for var in 0..num_vars {
423 arcs.push((layout.final_node(), layout.w(var)));
424 arcs.push((layout.final_node(), layout.x_pos(var)));
425 arcs.push((layout.final_node(), layout.x_neg(var)));
426 arcs.push((layout.final_node(), layout.z(var)));
427 }
428 arcs.push((layout.final_node(), layout.initial()));
429 arcs.push((layout.final_node(), layout.d()));
430
431 for var in 0..num_vars {
432 arcs.push((layout.x_pos(var), layout.z(var)));
433 arcs.push((layout.x_neg(var), layout.z(var)));
434 arcs.push((layout.x_pos(var), layout.u(var, 0)));
435 arcs.push((layout.x_neg(var), layout.u(var, 1)));
436 }
437
438 for var in 0..num_vars {
439 arcs.push((layout.w(var), layout.u(var, 0)));
440 arcs.push((layout.w(var), layout.u(var, 1)));
441 }
442
443 for var in 0..num_vars {
444 for index in 0..(2 * num_vars - 2 * var - 1) {
445 arcs.push((layout.x_pos(var), layout.s(var, index)));
446 arcs.push((layout.x_neg(var), layout.t(var, index)));
447 }
448 for index in 0..(2 * num_vars - 2 * var) {
449 arcs.push((layout.z(var), layout.r(var, index)));
450 }
451 }
452
453 for var in 1..num_vars {
454 arcs.push((layout.z(var), layout.w(var - 1)));
455 arcs.push((layout.z(var), layout.z(var - 1)));
456 }
457 if num_vars > 0 {
458 let last_var = num_vars - 1;
459 for clause in 0..num_clauses {
460 arcs.push((layout.c(clause), layout.w(last_var)));
461 arcs.push((layout.c(clause), layout.z(last_var)));
462 }
463 }
464
465 for clause in 0..num_clauses {
466 for literal_pos in 0..3 {
467 arcs.push((layout.c(clause), layout.f(clause, literal_pos)));
468 }
469 }
470
471 for index in 0..layout.b_padding {
472 arcs.push((layout.d(), layout.bnode(index)));
473 }
474 for clause in 0..num_clauses {
475 arcs.push((layout.d(), layout.c(clause)));
476 }
477
478 for (clause_idx, clause) in clauses.iter().enumerate() {
479 let mut lit_nodes = [0usize; 3];
480 let mut neg_nodes = [0usize; 3];
481
482 for (literal_pos, &literal) in clause.iter().enumerate() {
483 let var = usize::try_from(literal.unsigned_abs())
484 .expect("compact SAT variable indices fit usize")
485 - 1;
486 if literal > 0 {
487 lit_nodes[literal_pos] = layout.x_pos(var);
488 neg_nodes[literal_pos] = layout.x_neg(var);
489 } else {
490 lit_nodes[literal_pos] = layout.x_neg(var);
491 neg_nodes[literal_pos] = layout.x_pos(var);
492 }
493 arcs.push((lit_nodes[literal_pos], layout.f(clause_idx, literal_pos)));
494 }
495
496 for (earlier, &neg_node) in neg_nodes.iter().enumerate() {
497 for later in (earlier + 1)..3 {
498 arcs.push((neg_node, layout.f(clause_idx, later)));
499 }
500 }
501 }
502
503 Ok(Reduction3SATToRegisterSufficiency {
504 target: RegisterSufficiency::new(layout.total_vertices(), arcs, layout.bound()),
505 layout: Some(layout),
506 source_num_vars: self.num_vars(),
507 source_variables,
508 })
509 }
510}
511
512#[cfg(feature = "example-db")]
513pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
514 use crate::export::SolutionPair;
515 use crate::models::formula::CNFClause;
516
517 vec![crate::example_db::specs::RuleExampleSpec {
518 id: "ksatisfiability_to_registersufficiency",
519 build: || {
520 let source = KSatisfiability::<K3>::new(
521 3,
522 vec![
523 CNFClause::new(vec![1, -2, 3]),
524 CNFClause::new(vec![-1, 2, -3]),
525 ],
526 );
527 let to_registers =
528 <KSatisfiability<K3> as ReduceTo<RegisterSufficiency>>::reduce_to(&source)
529 .expect("reduction should succeed");
530
531 let target_config = to_registers
532 .layout
533 .as_ref()
534 .expect("canonical formula has nonempty clauses")
535 .schedule_for_assignment(&[true, true, true]);
536 let source_config = to_registers.extract_solution(&target_config).unwrap();
537
538 crate::example_db::specs::assemble_rule_example(
539 &source,
540 to_registers.target_problem(),
541 vec![SolutionPair {
542 source_config: serde_json::to_value(source_config)
543 .expect("solution serialization must succeed"),
544 target_config: serde_json::to_value(target_config)
545 .expect("solution serialization must succeed"),
546 }],
547 )
548 },
549 }]
550}
551
552#[cfg(test)]
553#[path = "../unit_tests/rules/ksatisfiability_registersufficiency.rs"]
554mod tests;