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 let value =
82 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
83 if !value.0 {
84 return Err(crate::rules::ExtractionError::invalid(
85 "target configuration is not a feasible register assignment realization",
86 ));
87 }
88 let mut assignment = vec![false; self.num_vars];
89 let compact_vars = self.source_variables.len();
90 for (compact, &original) in self.source_variables.iter().enumerate() {
91 assignment[original] = target_solution[s_pos_idx(compact)]
92 < target_solution[s_neg_idx(compact_vars, compact)];
93 }
94 Ok(assignment)
95 }
96}
97
98#[reduction(
99 transform = upper_bound {
100 num_vertices = "2 * num_vars + 12 * num_clauses",
101 num_arcs = "15 * num_clauses",
102 num_registers = "num_vars + 9 * num_clauses",
103 num_same_register_pairs = "num_vars + 3 * num_clauses",
104 }
105)]
106impl ReduceTo<FeasibleRegisterAssignment> for KSatisfiability<K3> {
107 type Result = Reduction3SATToFeasibleRegisterAssignment;
108
109 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
110 if self
111 .clauses()
112 .iter()
113 .any(|clause| clause.literals.is_empty())
114 {
115 return Ok(Reduction3SATToFeasibleRegisterAssignment {
118 target: FeasibleRegisterAssignment::new(3, vec![(2, 0), (2, 1)], 2, vec![0, 0, 1]),
119 num_vars: self.num_vars(),
120 source_variables: Vec::new(),
121 });
122 }
123 let source_variables: Vec<_> = self
124 .clauses()
125 .iter()
126 .flat_map(|clause| clause.literals.iter())
127 .map(|literal| {
128 usize::try_from(literal.unsigned_abs()).expect("native SAT indices fit usize") - 1
129 })
130 .collect::<BTreeSet<_>>()
131 .into_iter()
132 .collect();
133 let num_vars = source_variables.len();
134 let num_clauses = self.num_clauses();
135 let overflow = |operation| {
136 crate::rules::ReductionError::integer_overflow::<Self, FeasibleRegisterAssignment>(
137 operation,
138 )
139 };
140 let variable_vertices = num_vars
141 .checked_mul(2)
142 .ok_or_else(|| overflow("counting variable leaves"))?;
143 let clause_vertices = num_clauses
144 .checked_mul(12)
145 .ok_or_else(|| overflow("counting clause vertices"))?;
146 let num_vertices = variable_vertices
147 .checked_add(clause_vertices)
148 .ok_or_else(|| overflow("counting target vertices"))?;
149 let clause_registers = num_clauses
150 .checked_mul(9)
151 .ok_or_else(|| overflow("counting clause registers"))?;
152 let num_registers = num_vars
153 .checked_add(clause_registers)
154 .ok_or_else(|| overflow("counting target registers"))?;
155 let num_arcs = num_clauses
156 .checked_mul(15)
157 .ok_or_else(|| overflow("counting target arcs"))?;
158 let mut assignment = vec![0usize; num_vertices];
159 let mut arcs = Vec::with_capacity(num_arcs);
160
161 for var in 0..num_vars {
162 assignment[s_pos_idx(var)] = var;
163 assignment[s_neg_idx(num_vars, var)] = var;
164 }
165
166 for (clause_idx, clause) in self.clauses().iter().enumerate() {
167 let mut literals = clause.literals.clone();
170 literals.resize(3, literals[0]);
171 for literal_pos in 0..3 {
172 assignment[p_idx(num_vars, clause_idx, literal_pos)] =
173 p_register(num_vars, clause_idx, literal_pos);
174 assignment[q_idx(num_vars, clause_idx, literal_pos)] =
175 q_register(num_vars, clause_idx, literal_pos);
176 assignment[r_idx(num_vars, clause_idx, literal_pos)] =
177 r_register(num_vars, clause_idx, literal_pos);
178 assignment[rbar_idx(num_vars, clause_idx, literal_pos)] =
179 r_register(num_vars, clause_idx, literal_pos);
180
181 arcs.push((
182 q_idx(num_vars, clause_idx, literal_pos),
183 p_idx(num_vars, clause_idx, literal_pos),
184 ));
185 arcs.push((
186 p_idx(num_vars, clause_idx, literal_pos),
187 r_idx(num_vars, clause_idx, literal_pos),
188 ));
189 }
190
191 arcs.push((
192 q_idx(num_vars, clause_idx, 0),
193 rbar_idx(num_vars, clause_idx, 1),
194 ));
195 arcs.push((
196 q_idx(num_vars, clause_idx, 1),
197 rbar_idx(num_vars, clause_idx, 2),
198 ));
199 arcs.push((
200 q_idx(num_vars, clause_idx, 2),
201 rbar_idx(num_vars, clause_idx, 0),
202 ));
203
204 for (literal_pos, &literal) in literals.iter().enumerate() {
205 let original = usize::try_from(literal.unsigned_abs())
206 .expect("native SAT indices fit usize")
207 - 1;
208 let var = source_variables
209 .binary_search(&original)
210 .expect("all appearing variables were collected");
211 let (literal_leaf, opposite_leaf) = if literal > 0 {
212 (s_pos_idx(var), s_neg_idx(num_vars, var))
213 } else {
214 (s_neg_idx(num_vars, var), s_pos_idx(var))
215 };
216 arcs.push((r_idx(num_vars, clause_idx, literal_pos), literal_leaf));
217 arcs.push((rbar_idx(num_vars, clause_idx, literal_pos), opposite_leaf));
218 }
219 }
220
221 Ok(Reduction3SATToFeasibleRegisterAssignment {
222 target: FeasibleRegisterAssignment::new(num_vertices, arcs, num_registers, assignment),
223 num_vars: self.num_vars(),
224 source_variables,
225 })
226 }
227}
228
229#[cfg(feature = "example-db")]
230pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
231 use crate::export::SolutionPair;
232 use crate::models::algebraic::ILP;
233 use crate::models::formula::CNFClause;
234 use crate::solvers::ILPSolver;
235
236 vec![crate::example_db::specs::RuleExampleSpec {
237 id: "ksatisfiability_to_feasibleregisterassignment",
238 build: || {
239 let source = KSatisfiability::<K3>::new(
240 3,
241 vec![
242 CNFClause::new(vec![1, -2, 3]),
243 CNFClause::new(vec![-1, 2, -3]),
244 ],
245 );
246 let to_fra =
247 <KSatisfiability<K3> as ReduceTo<FeasibleRegisterAssignment>>::reduce_to(&source)
248 .expect("reduction should succeed");
249 let to_ilp = <FeasibleRegisterAssignment as ReduceTo<ILP<i64>>>::reduce_to(
250 to_fra.target_problem(),
251 )
252 .expect("reduction should succeed");
253 let ilp_solution = ILPSolver::new()
254 .solve(to_ilp.target_problem())
255 .expect("canonical FRA example must reduce to a feasible ILP");
256 let target_config = to_ilp.extract_solution(&ilp_solution).unwrap();
257 let source_config = to_fra.extract_solution(&target_config).unwrap();
258 crate::example_db::specs::assemble_rule_example(
259 &source,
260 to_fra.target_problem(),
261 vec![SolutionPair {
262 source_config: serde_json::to_value(source_config)
263 .expect("solution serialization must succeed"),
264 target_config: serde_json::to_value(target_config)
265 .expect("solution serialization must succeed"),
266 }],
267 )
268 },
269 }]
270}
271
272#[cfg(test)]
273#[path = "../unit_tests/rules/ksatisfiability_feasibleregisterassignment.rs"]
274mod tests;