1use crate::models::formula::{Assignment, BooleanExpr, Circuit, CircuitSAT};
11use crate::models::misc::Factoring;
12use crate::reduction;
13use crate::rules::traits::{ReduceTo, ReductionResult};
14#[derive(Debug, Clone)]
22pub struct ReductionFactoringToCircuit {
23 target: CircuitSAT,
25 p_vars: Vec<String>,
27 q_vars: Vec<String>,
29 m_vars: Vec<String>,
31}
32
33impl ReductionResult for ReductionFactoringToCircuit {
34 type Source = Factoring;
35 type Target = CircuitSAT;
36
37 fn target_problem(&self) -> &Self::Target {
38 &self.target
39 }
40
41 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
46 let var_names = self.target.variable_names();
47
48 let var_map: std::collections::HashMap<&str, usize> = var_names
50 .iter()
51 .enumerate()
52 .map(|(i, name)| (name.as_str(), target_solution.get(i).copied().unwrap_or(0)))
53 .collect();
54
55 let p_bits: Vec<usize> = self
57 .p_vars
58 .iter()
59 .map(|name| *var_map.get(name.as_str()).unwrap_or(&0))
60 .collect();
61
62 let q_bits: Vec<usize> = self
64 .q_vars
65 .iter()
66 .map(|name| *var_map.get(name.as_str()).unwrap_or(&0))
67 .collect();
68
69 let mut result = p_bits;
71 result.extend(q_bits);
72 result
73 }
74}
75
76impl ReductionFactoringToCircuit {
77 pub fn p_vars(&self) -> &[String] {
79 &self.p_vars
80 }
81
82 pub fn q_vars(&self) -> &[String] {
84 &self.q_vars
85 }
86
87 pub fn m_vars(&self) -> &[String] {
89 &self.m_vars
90 }
91}
92
93fn read_bit(n: u64, i: usize) -> bool {
95 if i == 0 || i > 64 {
96 false
97 } else {
98 ((n >> (i - 1)) & 1) == 1
99 }
100}
101
102fn build_multiplier_cell(
108 s_name: &str,
109 c_name: &str,
110 p_name: &str,
111 q_name: &str,
112 s_pre: &BooleanExpr,
113 c_pre: &BooleanExpr,
114 cell_id: &str,
115) -> (Vec<Assignment>, Vec<String>) {
116 let a_name = format!("a_{}", cell_id);
118 let a_xor_s_name = format!("axs_{}", cell_id);
119 let a_xor_s_and_c_name = format!("axsc_{}", cell_id);
120 let a_and_s_name = format!("as_{}", cell_id);
121
122 let p = BooleanExpr::var(p_name);
123 let q = BooleanExpr::var(q_name);
124 let a = BooleanExpr::var(&a_name);
125 let a_xor_s = BooleanExpr::var(&a_xor_s_name);
126
127 let assign_a = Assignment::new(vec![a_name.clone()], BooleanExpr::and(vec![p, q]));
130
131 let assign_a_xor_s = Assignment::new(
133 vec![a_xor_s_name.clone()],
134 BooleanExpr::xor(vec![a.clone(), s_pre.clone()]),
135 );
136
137 let assign_s = Assignment::new(
139 vec![s_name.to_string()],
140 BooleanExpr::xor(vec![a_xor_s.clone(), c_pre.clone()]),
141 );
142
143 let assign_a_xor_s_and_c = Assignment::new(
145 vec![a_xor_s_and_c_name.clone()],
146 BooleanExpr::and(vec![a_xor_s, c_pre.clone()]),
147 );
148
149 let assign_a_and_s = Assignment::new(
151 vec![a_and_s_name.clone()],
152 BooleanExpr::and(vec![a, s_pre.clone()]),
153 );
154
155 let assign_c = Assignment::new(
157 vec![c_name.to_string()],
158 BooleanExpr::or(vec![
159 BooleanExpr::var(&a_xor_s_and_c_name),
160 BooleanExpr::var(&a_and_s_name),
161 ]),
162 );
163
164 let assignments = vec![
165 assign_a,
166 assign_a_xor_s,
167 assign_s,
168 assign_a_xor_s_and_c,
169 assign_a_and_s,
170 assign_c,
171 ];
172
173 let ancillas = vec![a_name, a_xor_s_name, a_xor_s_and_c_name, a_and_s_name];
174
175 (assignments, ancillas)
176}
177
178#[reduction(overhead = {
179 num_variables = "6 * num_bits_first * num_bits_second + num_bits_first + num_bits_second",
180 num_assignments = "6 * num_bits_first * num_bits_second + num_bits_first + num_bits_second",
181})]
182impl ReduceTo<CircuitSAT> for Factoring {
183 type Result = ReductionFactoringToCircuit;
184
185 fn reduce_to(&self) -> Self::Result {
186 let n1 = self.m(); let n2 = self.n(); let target = self.target();
189
190 let p_vars: Vec<String> = (1..=n1).map(|i| format!("p{}", i)).collect();
192 let q_vars: Vec<String> = (1..=n2).map(|i| format!("q{}", i)).collect();
193
194 let mut assignments = Vec::new();
196 let mut m_vars = Vec::new();
197
198 let mut s_pre: Vec<BooleanExpr> = (0..=n2).map(|_| BooleanExpr::constant(false)).collect();
201
202 for i in 1..=n1 {
204 let mut c_pre = BooleanExpr::constant(false);
206
207 for j in 1..=n2 {
208 let c_name = format!("c{}_{}", i, j);
210 let s_name = format!("s{}_{}", i, j);
211
212 let cell_id = format!("{}_{}", i, j);
214 let (cell_assignments, _ancillas) = build_multiplier_cell(
215 &s_name,
216 &c_name,
217 &p_vars[i - 1],
218 &q_vars[j - 1],
219 &s_pre[j], &c_pre,
221 &cell_id,
222 );
223
224 assignments.extend(cell_assignments);
225
226 c_pre = BooleanExpr::var(&c_name);
228
229 s_pre[j - 1] = BooleanExpr::var(&s_name);
232 }
233
234 s_pre[n2] = c_pre;
236
237 m_vars.push(format!("s{}_{}", i, 1));
239 }
240
241 for j in 2..=n2 {
243 m_vars.push(format!("s{}_{}", n1, j));
244 }
245 m_vars.push(format!("c{}_{}", n1, n2));
247
248 for (i, m_var) in m_vars.iter().enumerate() {
250 let target_bit = read_bit(target, i + 1);
251 assignments.push(Assignment::new(
252 vec![m_var.clone()],
253 BooleanExpr::constant(target_bit),
254 ));
255 }
256
257 let circuit = Circuit::new(assignments);
259 let circuit_sat = CircuitSAT::new(circuit);
260
261 ReductionFactoringToCircuit {
262 target: circuit_sat,
263 p_vars,
264 q_vars,
265 m_vars,
266 }
267 }
268}
269
270#[cfg(feature = "example-db")]
271pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
272 use crate::export::SolutionPair;
273
274 vec![crate::example_db::specs::RuleExampleSpec {
275 id: "factoring_to_circuitsat",
276 build: || {
277 crate::example_db::specs::rule_example_with_witness::<_, CircuitSAT>(
278 Factoring::new(3, 3, 35),
279 SolutionPair {
280 source_config: vec![1, 0, 1, 1, 1, 1],
281 target_config: vec![
282 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0,
283 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1,
284 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
285 ],
286 },
287 )
288 },
289 }]
290}
291
292#[cfg(test)]
293#[path = "../unit_tests/rules/factoring_circuit.rs"]
294mod tests;