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 crate::rules::traits::validate_target_witness(
51 self.target_problem(),
52 target_solution,
53 |value| value.0,
54 "target assignment does not satisfy the multiplication circuit",
55 )?;
56
57 Ok({
58 let var_names = self.target.variable_names();
59
60 let var_map: std::collections::HashMap<&str, bool> = var_names
62 .iter()
63 .enumerate()
64 .map(|(i, name)| (name.as_str(), target_solution[i]))
65 .collect();
66
67 let decode = |names: &[String]| {
68 names
69 .iter()
70 .enumerate()
71 .try_fold(BigUint::zero(), |value, (index, name)| {
72 let bit = var_map.get(name.as_str()).copied().ok_or_else(|| {
73 crate::rules::ExtractionError::invalid(format!(
74 "target circuit does not contain factor variable {name}"
75 ))
76 })?;
77 Ok::<BigUint, crate::rules::ExtractionError>(if bit {
78 value + (BigUint::one() << index)
79 } else {
80 value
81 })
82 })
83 };
84 let left = decode(&self.p_vars)?;
85 let right = decode(&self.q_vars)?;
86 if left <= right {
87 (left, right)
88 } else {
89 (right, left)
90 }
91 })
92 }
93}
94
95impl ReductionFactoringToCircuit {
96 fn dimensions(m: usize, n: usize) -> Result<(usize, usize), crate::rules::ReductionError> {
98 let overflow = || {
99 crate::rules::ReductionError::integer_overflow::<Factoring, CircuitSAT>(
100 "computing multiplication circuit dimensions",
101 )
102 };
103 let width = m.checked_add(n).ok_or_else(overflow)?;
104 let capacity = m
105 .checked_mul(n)
106 .and_then(|v| v.checked_mul(6))
107 .and_then(|v| width.checked_mul(2).and_then(|w| v.checked_add(w)))
108 .and_then(|v| v.checked_add(2))
109 .ok_or_else(overflow)?;
110 Ok((width, capacity))
111 }
112
113 pub fn p_vars(&self) -> &[String] {
115 &self.p_vars
116 }
117
118 pub fn q_vars(&self) -> &[String] {
120 &self.q_vars
121 }
122
123 pub fn m_vars(&self) -> &[String] {
125 &self.m_vars
126 }
127}
128
129fn read_bit(n: &BigUint, i: usize) -> bool {
131 if i == 0 {
132 false
133 } else {
134 n.bit(u64::try_from(i - 1).expect("bit index fits u64"))
135 }
136}
137
138fn build_multiplier_cell(
144 s_name: &str,
145 c_name: &str,
146 p_name: &str,
147 q_name: &str,
148 s_pre: &BooleanExpr,
149 c_pre: &BooleanExpr,
150 cell_id: &str,
151) -> (Vec<Assignment>, Vec<String>) {
152 let a_name = format!("a_{}", cell_id);
154 let a_xor_s_name = format!("axs_{}", cell_id);
155 let a_xor_s_and_c_name = format!("axsc_{}", cell_id);
156 let a_and_s_name = format!("as_{}", cell_id);
157
158 let p = BooleanExpr::var(p_name);
159 let q = BooleanExpr::var(q_name);
160 let a = BooleanExpr::var(&a_name);
161 let a_xor_s = BooleanExpr::var(&a_xor_s_name);
162
163 let assign_a = Assignment::new(vec![a_name.clone()], BooleanExpr::and(vec![p, q]));
166
167 let assign_a_xor_s = Assignment::new(
169 vec![a_xor_s_name.clone()],
170 BooleanExpr::xor(vec![a.clone(), s_pre.clone()]),
171 );
172
173 let assign_s = Assignment::new(
175 vec![s_name.to_string()],
176 BooleanExpr::xor(vec![a_xor_s.clone(), c_pre.clone()]),
177 );
178
179 let assign_a_xor_s_and_c = Assignment::new(
181 vec![a_xor_s_and_c_name.clone()],
182 BooleanExpr::and(vec![a_xor_s, c_pre.clone()]),
183 );
184
185 let assign_a_and_s = Assignment::new(
187 vec![a_and_s_name.clone()],
188 BooleanExpr::and(vec![a, s_pre.clone()]),
189 );
190
191 let assign_c = Assignment::new(
193 vec![c_name.to_string()],
194 BooleanExpr::or(vec![
195 BooleanExpr::var(&a_xor_s_and_c_name),
196 BooleanExpr::var(&a_and_s_name),
197 ]),
198 );
199
200 let assignments = vec![
201 assign_a,
202 assign_a_xor_s,
203 assign_s,
204 assign_a_xor_s_and_c,
205 assign_a_and_s,
206 assign_c,
207 ];
208
209 let ancillas = vec![a_name, a_xor_s_name, a_xor_s_and_c_name, a_and_s_name];
210
211 (assignments, ancillas)
212}
213
214#[crate::aggregate_reduction(identity)]
215impl crate::rules::AggregateReductionResult for ReductionFactoringToCircuit {}
216
217#[reduction(transform = upper_bound {
218 num_variables = "6 * num_bits_first * num_bits_second + 2 * (num_bits_first + num_bits_second) + 1",
219 num_assignments = "6 * num_bits_first * num_bits_second + 2 * (num_bits_first + num_bits_second) + 2",
220 num_assignment_outputs = "6 * num_bits_first * num_bits_second + 2 * (num_bits_first + num_bits_second) + 2",
221 num_expression_nodes = "5 * (6 * num_bits_first * num_bits_second + 2 * (num_bits_first + num_bits_second) + 2)",
222})]
223impl ReduceTo<CircuitSAT> for Factoring {
224 type Result = ReductionFactoringToCircuit;
225
226 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
227 let n1 = self.m(); let n2 = self.n(); let target = self.target();
230 let (width, capacity) = ReductionFactoringToCircuit::dimensions(n1, n2)?;
231
232 let p_vars: Vec<String> = (1..=n1).map(|i| format!("p{}", i)).collect();
234 let q_vars: Vec<String> = (1..=n2).map(|i| format!("q{}", i)).collect();
235
236 let mut assignments = Vec::with_capacity(capacity);
238 let mut product_bits = Vec::with_capacity(width);
239
240 let mut s_pre = Vec::with_capacity(n2 + 1);
243 s_pre.push(BooleanExpr::constant(false));
244 s_pre.extend(q_vars.iter().map(|name| {
247 BooleanExpr::and(vec![BooleanExpr::var(name), BooleanExpr::constant(false)])
248 }));
249
250 for i in 1..=n1 {
252 let mut c_pre = BooleanExpr::constant(false);
254
255 for j in 1..=n2 {
256 let c_name = format!("c{}_{}", i, j);
258 let s_name = format!("s{}_{}", i, j);
259
260 let cell_id = format!("{}_{}", i, j);
262 let (cell_assignments, _ancillas) = build_multiplier_cell(
263 &s_name,
264 &c_name,
265 &p_vars[i - 1],
266 &q_vars[j - 1],
267 &s_pre[j], &c_pre,
269 &cell_id,
270 );
271
272 assignments.extend(cell_assignments);
273
274 c_pre = BooleanExpr::var(&c_name);
276
277 s_pre[j - 1] = BooleanExpr::var(&s_name);
280 }
281
282 s_pre[n2] = c_pre;
284
285 product_bits.push(s_pre[0].clone());
287 }
288
289 product_bits.extend(s_pre.into_iter().skip(1));
293 let m_vars: Vec<_> = (0..width).map(|i| format!("product_{i}")).collect();
294 for (name, expr) in m_vars.iter().zip(product_bits) {
295 assignments.push(Assignment::new(vec![name.clone()], expr));
296 }
297
298 for (i, m_var) in m_vars.iter().enumerate() {
300 let target_bit = read_bit(target, i + 1);
301 assignments.push(Assignment::new(
302 vec![m_var.clone()],
303 BooleanExpr::constant(target_bit),
304 ));
305 }
306
307 if target.bits() > u64::try_from(m_vars.len()).expect("product width fits u64") {
310 let overflow = "target_overflow".to_string();
311 assignments.push(Assignment::new(
312 vec![overflow.clone()],
313 BooleanExpr::constant(false),
314 ));
315 assignments.push(Assignment::new(vec![overflow], BooleanExpr::constant(true)));
316 }
317
318 let circuit = Circuit::new(assignments);
320 let circuit_sat = CircuitSAT::new(circuit);
321
322 Ok(ReductionFactoringToCircuit {
323 target: circuit_sat,
324 p_vars,
325 q_vars,
326 m_vars,
327 })
328 }
329}
330
331#[cfg(feature = "example-db")]
332pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
333 use crate::export::SolutionPair;
334
335 vec![crate::example_db::specs::RuleExampleSpec {
336 id: "factoring_to_circuitsat",
337 build: || {
338 crate::example_db::specs::rule_example_with_witness::<_, CircuitSAT>(
339 Factoring::with_factor_bits(35, 3, 3),
340 SolutionPair {
341 source_config: serde_json::to_value((BigUint::from(5u32), BigUint::from(7u32)))
342 .expect("solution serialization must succeed"),
343 target_config: serde_json::json!(vec![
344 true, true, true, false, false, false, true, true, true, false, false,
345 false, false, false, false, true, false, false, true, true, true, true,
346 true, false, false, true, true, false, false, false, false, false, false,
347 false, true, true, false, false, false, false, false, false, true, true,
348 true, true, false, true, true, true, false, false, false, true, true, true,
349 true, true, true, true, true, true, false, false, false, false
350 ]),
351 },
352 )
353 },
354 }]
355}
356
357#[cfg(test)]
358#[path = "../unit_tests/rules/factoring_circuit.rs"]
359mod tests;