1use crate::models::formula::KSatisfiability;
16use crate::models::misc::FeasibleRegisterAssignment;
17use crate::reduction;
18use crate::rules::traits::{ReduceTo, ReductionResult};
19use crate::variant::K3;
20use std::collections::BTreeSet;
21
22fn s_pos_idx(var: usize) -> usize {
23 var
24}
25
26fn s_neg_idx(num_vars: usize, var: usize) -> usize {
27 num_vars + var
28}
29
30fn literal_base(num_vars: usize, clause_idx: usize, literal_pos: usize) -> usize {
31 2 * num_vars + 12 * clause_idx + 4 * literal_pos
32}
33
34fn p_idx(num_vars: usize, clause_idx: usize, literal_pos: usize) -> usize {
35 literal_base(num_vars, clause_idx, literal_pos)
36}
37
38fn q_idx(num_vars: usize, clause_idx: usize, literal_pos: usize) -> usize {
39 literal_base(num_vars, clause_idx, literal_pos) + 1
40}
41
42fn r_idx(num_vars: usize, clause_idx: usize, literal_pos: usize) -> usize {
43 literal_base(num_vars, clause_idx, literal_pos) + 2
44}
45
46fn rbar_idx(num_vars: usize, clause_idx: usize, literal_pos: usize) -> usize {
47 literal_base(num_vars, clause_idx, literal_pos) + 3
48}
49
50fn p_register(num_vars: usize, clause_idx: usize, literal_pos: usize) -> usize {
51 num_vars + 3 * (3 * clause_idx + literal_pos)
52}
53
54fn q_register(num_vars: usize, clause_idx: usize, literal_pos: usize) -> usize {
55 p_register(num_vars, clause_idx, literal_pos) + 1
56}
57
58fn r_register(num_vars: usize, clause_idx: usize, literal_pos: usize) -> usize {
59 p_register(num_vars, clause_idx, literal_pos) + 2
60}
61
62#[derive(Debug, Clone)]
63pub struct Reduction3SATToFeasibleRegisterAssignment {
64 target: FeasibleRegisterAssignment,
65 num_vars: usize,
66 source_variables: Vec<usize>,
67}
68
69impl ReductionResult for Reduction3SATToFeasibleRegisterAssignment {
70 type Source = KSatisfiability<K3>;
71 type Target = FeasibleRegisterAssignment;
72
73 fn target_problem(&self) -> &Self::Target {
74 &self.target
75 }
76
77 fn extract_solution(
78 &self,
79 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
80 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
81 crate::rules::traits::validate_target_witness(
82 self.target_problem(),
83 target_solution,
84 |value| value.0,
85 "target configuration is not a feasible register assignment realization",
86 )?;
87 let mut assignment = vec![false; self.num_vars];
88 let compact_vars = self.source_variables.len();
89 for (compact, &original) in self.source_variables.iter().enumerate() {
90 assignment[original] = target_solution[s_pos_idx(compact)]
91 < target_solution[s_neg_idx(compact_vars, compact)];
92 }
93 Ok(assignment)
94 }
95}
96
97#[crate::aggregate_reduction(identity)]
98impl crate::rules::AggregateReductionResult for Reduction3SATToFeasibleRegisterAssignment {}
99
100#[reduction(
101 transform = upper_bound {
102 num_vertices = "2 * num_vars + 12 * num_clauses",
103 num_arcs = "15 * num_clauses",
104 num_registers = "num_vars + 9 * num_clauses",
105 num_same_register_pairs = "num_vars + 3 * num_clauses",
106 }
107)]
108impl ReduceTo<FeasibleRegisterAssignment> for KSatisfiability<K3> {
109 type Result = Reduction3SATToFeasibleRegisterAssignment;
110
111 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
112 if self
113 .clauses()
114 .iter()
115 .any(|clause| clause.literals.is_empty())
116 {
117 return Ok(Reduction3SATToFeasibleRegisterAssignment {
120 target: FeasibleRegisterAssignment::new(3, vec![(2, 0), (2, 1)], 2, vec![0, 0, 1]),
121 num_vars: self.num_vars(),
122 source_variables: Vec::new(),
123 });
124 }
125 let source_variables: Vec<_> = self
126 .clauses()
127 .iter()
128 .flat_map(|clause| clause.literals.iter())
129 .map(|literal| {
130 usize::try_from(literal.unsigned_abs()).expect("native SAT indices fit usize") - 1
131 })
132 .collect::<BTreeSet<_>>()
133 .into_iter()
134 .collect();
135 let num_vars = source_variables.len();
136 let num_clauses = self.num_clauses();
137 let overflow = |operation| {
138 crate::rules::ReductionError::integer_overflow::<Self, FeasibleRegisterAssignment>(
139 operation,
140 )
141 };
142 let variable_vertices = num_vars
143 .checked_mul(2)
144 .ok_or_else(|| overflow("counting variable leaves"))?;
145 let clause_vertices = num_clauses
146 .checked_mul(12)
147 .ok_or_else(|| overflow("counting clause vertices"))?;
148 let num_vertices = variable_vertices
149 .checked_add(clause_vertices)
150 .ok_or_else(|| overflow("counting target vertices"))?;
151 let clause_registers = num_clauses
152 .checked_mul(9)
153 .ok_or_else(|| overflow("counting clause registers"))?;
154 let num_registers = num_vars
155 .checked_add(clause_registers)
156 .ok_or_else(|| overflow("counting target registers"))?;
157 let num_arcs = num_clauses
158 .checked_mul(15)
159 .ok_or_else(|| overflow("counting target arcs"))?;
160 let mut assignment = vec![0usize; num_vertices];
161 let mut arcs = Vec::with_capacity(num_arcs);
162
163 for var in 0..num_vars {
164 assignment[s_pos_idx(var)] = var;
165 assignment[s_neg_idx(num_vars, var)] = var;
166 }
167
168 for (clause_idx, clause) in self.clauses().iter().enumerate() {
169 let mut literals = clause.literals.clone();
172 literals.resize(3, literals[0]);
173 for literal_pos in 0..3 {
174 assignment[p_idx(num_vars, clause_idx, literal_pos)] =
175 p_register(num_vars, clause_idx, literal_pos);
176 assignment[q_idx(num_vars, clause_idx, literal_pos)] =
177 q_register(num_vars, clause_idx, literal_pos);
178 assignment[r_idx(num_vars, clause_idx, literal_pos)] =
179 r_register(num_vars, clause_idx, literal_pos);
180 assignment[rbar_idx(num_vars, clause_idx, literal_pos)] =
181 r_register(num_vars, clause_idx, literal_pos);
182
183 arcs.push((
184 q_idx(num_vars, clause_idx, literal_pos),
185 p_idx(num_vars, clause_idx, literal_pos),
186 ));
187 arcs.push((
188 p_idx(num_vars, clause_idx, literal_pos),
189 r_idx(num_vars, clause_idx, literal_pos),
190 ));
191 }
192
193 arcs.push((
194 q_idx(num_vars, clause_idx, 0),
195 rbar_idx(num_vars, clause_idx, 1),
196 ));
197 arcs.push((
198 q_idx(num_vars, clause_idx, 1),
199 rbar_idx(num_vars, clause_idx, 2),
200 ));
201 arcs.push((
202 q_idx(num_vars, clause_idx, 2),
203 rbar_idx(num_vars, clause_idx, 0),
204 ));
205
206 for (literal_pos, &literal) in literals.iter().enumerate() {
207 let original = usize::try_from(literal.unsigned_abs())
208 .expect("native SAT indices fit usize")
209 - 1;
210 let var = source_variables
211 .binary_search(&original)
212 .expect("all appearing variables were collected");
213 let (literal_leaf, opposite_leaf) = if literal > 0 {
214 (s_pos_idx(var), s_neg_idx(num_vars, var))
215 } else {
216 (s_neg_idx(num_vars, var), s_pos_idx(var))
217 };
218 arcs.push((r_idx(num_vars, clause_idx, literal_pos), literal_leaf));
219 arcs.push((rbar_idx(num_vars, clause_idx, literal_pos), opposite_leaf));
220 }
221 }
222
223 Ok(Reduction3SATToFeasibleRegisterAssignment {
224 target: FeasibleRegisterAssignment::new(num_vertices, arcs, num_registers, assignment),
225 num_vars: self.num_vars(),
226 source_variables,
227 })
228 }
229}
230
231#[cfg(feature = "example-db")]
232pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
233 use crate::export::SolutionPair;
234 use crate::models::algebraic::{Bounded, ILP};
235 use crate::models::formula::CNFClause;
236 use crate::solvers::ILPSolver;
237
238 vec![crate::example_db::specs::RuleExampleSpec {
239 id: "ksatisfiability_to_feasibleregisterassignment",
240 build: || {
241 let source = KSatisfiability::<K3>::new(
242 3,
243 vec![
244 CNFClause::new(vec![1, -2, 3]),
245 CNFClause::new(vec![-1, 2, -3]),
246 ],
247 );
248 let to_fra =
249 <KSatisfiability<K3> as ReduceTo<FeasibleRegisterAssignment>>::reduce_to(&source)
250 .expect("reduction should succeed");
251 let to_ilp =
252 <FeasibleRegisterAssignment as ReduceTo<ILP<i64, i64, Bounded>>>::reduce_to(
253 to_fra.target_problem(),
254 )
255 .expect("reduction should succeed");
256 let ilp_solution = ILPSolver::new()
257 .solve(to_ilp.target_problem())
258 .expect("canonical FRA example must reduce to a feasible ILP");
259 let target_config = to_ilp.extract_solution(&ilp_solution).unwrap();
260 let source_config = to_fra.extract_solution(&target_config).unwrap();
261 crate::example_db::specs::assemble_rule_example(
262 &source,
263 to_fra.target_problem(),
264 vec![SolutionPair {
265 source_config: serde_json::to_value(source_config)
266 .expect("solution serialization must succeed"),
267 target_config: serde_json::to_value(target_config)
268 .expect("solution serialization must succeed"),
269 }],
270 )
271 },
272 }]
273}
274
275#[cfg(test)]
276#[path = "../unit_tests/rules/ksatisfiability_feasibleregisterassignment.rs"]
277mod tests;