1use crate::models::formula::{Assignment, BooleanExpr, Circuit, CircuitSAT};
11use crate::models::misc::Factoring;
12use crate::reduction;
13use crate::rules::traits::{ReduceTo, ReductionResult};
14use num_bigint::BigUint;
15use num_traits::{One, Zero};
16#[derive(Debug, Clone)]
24pub struct ReductionFactoringToCircuit {
25 target: CircuitSAT,
27 p_vars: Vec<String>,
29 q_vars: Vec<String>,
31 m_vars: Vec<String>,
33}
34
35impl ReductionResult for ReductionFactoringToCircuit {
36 type Source = Factoring;
37 type Target = CircuitSAT;
38
39 fn target_problem(&self) -> &Self::Target {
40 &self.target
41 }
42
43 fn extract_solution(
47 &self,
48 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
49 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
50 let value =
51 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
52 if !value.0 {
53 return Err(crate::rules::ExtractionError::invalid(
54 "target assignment does not satisfy the multiplication circuit",
55 ));
56 }
57
58 Ok({
59 let var_names = self.target.variable_names();
60
61 let var_map: std::collections::HashMap<&str, bool> = var_names
63 .iter()
64 .enumerate()
65 .map(|(i, name)| (name.as_str(), target_solution[i]))
66 .collect();
67
68 let decode = |names: &[String]| {
69 names
70 .iter()
71 .enumerate()
72 .try_fold(BigUint::zero(), |value, (index, name)| {
73 let bit = var_map.get(name.as_str()).copied().ok_or_else(|| {
74 crate::rules::ExtractionError::invalid(format!(
75 "target circuit does not contain factor variable {name}"
76 ))
77 })?;
78 Ok::<BigUint, crate::rules::ExtractionError>(if bit {
79 value + (BigUint::one() << index)
80 } else {
81 value
82 })
83 })
84 };
85 let left = decode(&self.p_vars)?;
86 let right = decode(&self.q_vars)?;
87 if left <= right {
88 (left, right)
89 } else {
90 (right, left)
91 }
92 })
93 }
94}
95
96impl ReductionFactoringToCircuit {
97 fn dimensions(m: usize, n: usize) -> Result<(usize, usize), crate::rules::ReductionError> {
99 let overflow = || {
100 crate::rules::ReductionError::integer_overflow::<Factoring, CircuitSAT>(
101 "computing multiplication circuit dimensions",
102 )
103 };
104 let width = m.checked_add(n).ok_or_else(overflow)?;
105 let capacity = m
106 .checked_mul(n)
107 .and_then(|v| v.checked_mul(6))
108 .and_then(|v| width.checked_mul(2).and_then(|w| v.checked_add(w)))
109 .and_then(|v| v.checked_add(2))
110 .ok_or_else(overflow)?;
111 Ok((width, capacity))
112 }
113
114 pub fn p_vars(&self) -> &[String] {
116 &self.p_vars
117 }
118
119 pub fn q_vars(&self) -> &[String] {
121 &self.q_vars
122 }
123
124 pub fn m_vars(&self) -> &[String] {
126 &self.m_vars
127 }
128}
129
130fn read_bit(n: &BigUint, i: usize) -> bool {
132 if i == 0 {
133 false
134 } else {
135 n.bit(u64::try_from(i - 1).expect("bit index fits u64"))
136 }
137}
138
139fn build_multiplier_cell(
145 s_name: &str,
146 c_name: &str,
147 p_name: &str,
148 q_name: &str,
149 s_pre: &BooleanExpr,
150 c_pre: &BooleanExpr,
151 cell_id: &str,
152) -> (Vec<Assignment>, Vec<String>) {
153 let a_name = format!("a_{}", cell_id);
155 let a_xor_s_name = format!("axs_{}", cell_id);
156 let a_xor_s_and_c_name = format!("axsc_{}", cell_id);
157 let a_and_s_name = format!("as_{}", cell_id);
158
159 let p = BooleanExpr::var(p_name);
160 let q = BooleanExpr::var(q_name);
161 let a = BooleanExpr::var(&a_name);
162 let a_xor_s = BooleanExpr::var(&a_xor_s_name);
163
164 let assign_a = Assignment::new(vec![a_name.clone()], BooleanExpr::and(vec![p, q]));
167
168 let assign_a_xor_s = Assignment::new(
170 vec![a_xor_s_name.clone()],
171 BooleanExpr::xor(vec![a.clone(), s_pre.clone()]),
172 );
173
174 let assign_s = Assignment::new(
176 vec![s_name.to_string()],
177 BooleanExpr::xor(vec![a_xor_s.clone(), c_pre.clone()]),
178 );
179
180 let assign_a_xor_s_and_c = Assignment::new(
182 vec![a_xor_s_and_c_name.clone()],
183 BooleanExpr::and(vec![a_xor_s, c_pre.clone()]),
184 );
185
186 let assign_a_and_s = Assignment::new(
188 vec![a_and_s_name.clone()],
189 BooleanExpr::and(vec![a, s_pre.clone()]),
190 );
191
192 let assign_c = Assignment::new(
194 vec![c_name.to_string()],
195 BooleanExpr::or(vec![
196 BooleanExpr::var(&a_xor_s_and_c_name),
197 BooleanExpr::var(&a_and_s_name),
198 ]),
199 );
200
201 let assignments = vec![
202 assign_a,
203 assign_a_xor_s,
204 assign_s,
205 assign_a_xor_s_and_c,
206 assign_a_and_s,
207 assign_c,
208 ];
209
210 let ancillas = vec![a_name, a_xor_s_name, a_xor_s_and_c_name, a_and_s_name];
211
212 (assignments, ancillas)
213}
214
215#[reduction(
216 transform = upper_bound {
217 num_variables = "6 * num_bits_first * num_bits_second + 2 * (num_bits_first + num_bits_second) + 1",
218 num_assignments = "6 * num_bits_first * num_bits_second + 2 * (num_bits_first + num_bits_second) + 2",
219 },
220 unavailable = {
221 num_assignment_outputs = "the exact target parameter is not represented by this reduction's symbolic transform",
222 num_expression_nodes = "the exact target parameter is not represented by this reduction's symbolic transform",
223 }
224)]
225impl ReduceTo<CircuitSAT> for Factoring {
226 type Result = ReductionFactoringToCircuit;
227
228 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
229 let n1 = self.m(); let n2 = self.n(); let target = self.target();
232 let (width, capacity) = ReductionFactoringToCircuit::dimensions(n1, n2)?;
233
234 let p_vars: Vec<String> = (1..=n1).map(|i| format!("p{}", i)).collect();
236 let q_vars: Vec<String> = (1..=n2).map(|i| format!("q{}", i)).collect();
237
238 let mut assignments = Vec::with_capacity(capacity);
240 let mut product_bits = Vec::with_capacity(width);
241
242 let mut s_pre = Vec::with_capacity(n2 + 1);
245 s_pre.push(BooleanExpr::constant(false));
246 s_pre.extend(q_vars.iter().map(|name| {
249 BooleanExpr::and(vec![BooleanExpr::var(name), BooleanExpr::constant(false)])
250 }));
251
252 for i in 1..=n1 {
254 let mut c_pre = BooleanExpr::constant(false);
256
257 for j in 1..=n2 {
258 let c_name = format!("c{}_{}", i, j);
260 let s_name = format!("s{}_{}", i, j);
261
262 let cell_id = format!("{}_{}", i, j);
264 let (cell_assignments, _ancillas) = build_multiplier_cell(
265 &s_name,
266 &c_name,
267 &p_vars[i - 1],
268 &q_vars[j - 1],
269 &s_pre[j], &c_pre,
271 &cell_id,
272 );
273
274 assignments.extend(cell_assignments);
275
276 c_pre = BooleanExpr::var(&c_name);
278
279 s_pre[j - 1] = BooleanExpr::var(&s_name);
282 }
283
284 s_pre[n2] = c_pre;
286
287 product_bits.push(s_pre[0].clone());
289 }
290
291 product_bits.extend(s_pre.into_iter().skip(1));
295 let m_vars: Vec<_> = (0..width).map(|i| format!("product_{i}")).collect();
296 for (name, expr) in m_vars.iter().zip(product_bits) {
297 assignments.push(Assignment::new(vec![name.clone()], expr));
298 }
299
300 for (i, m_var) in m_vars.iter().enumerate() {
302 let target_bit = read_bit(target, i + 1);
303 assignments.push(Assignment::new(
304 vec![m_var.clone()],
305 BooleanExpr::constant(target_bit),
306 ));
307 }
308
309 if target.bits() > u64::try_from(m_vars.len()).expect("product width fits u64") {
312 let overflow = "target_overflow".to_string();
313 assignments.push(Assignment::new(
314 vec![overflow.clone()],
315 BooleanExpr::constant(false),
316 ));
317 assignments.push(Assignment::new(vec![overflow], BooleanExpr::constant(true)));
318 }
319
320 let circuit = Circuit::new(assignments);
322 let circuit_sat = CircuitSAT::new(circuit);
323
324 Ok(ReductionFactoringToCircuit {
325 target: circuit_sat,
326 p_vars,
327 q_vars,
328 m_vars,
329 })
330 }
331}
332
333#[cfg(feature = "example-db")]
334pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
335 use crate::export::SolutionPair;
336
337 vec![crate::example_db::specs::RuleExampleSpec {
338 id: "factoring_to_circuitsat",
339 build: || {
340 crate::example_db::specs::rule_example_with_witness::<_, CircuitSAT>(
341 Factoring::with_factor_bits(35, 3, 3),
342 SolutionPair {
343 source_config: serde_json::to_value((BigUint::from(5u32), BigUint::from(7u32)))
344 .expect("solution serialization must succeed"),
345 target_config: serde_json::json!(vec![
346 true, true, true, false, false, false, true, true, true, false, false,
347 false, false, false, false, true, false, false, true, true, true, true,
348 true, false, false, true, true, false, false, false, false, false, false,
349 false, true, true, false, false, false, false, false, false, true, true,
350 true, true, false, true, true, true, false, false, false, true, true, true,
351 true, true, true, true, true, true, false, false, false, false
352 ]),
353 },
354 )
355 },
356 }]
357}
358
359#[cfg(test)]
360#[path = "../unit_tests/rules/factoring_circuit.rs"]
361mod tests;