problemreductions/rules/
factoring_ilp.rs1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
23use crate::models::misc::Factoring;
24use crate::reduction;
25use crate::rules::ilp_helpers::mccormick_product;
26use crate::rules::traits::{ReduceTo, ReductionResult};
27use std::cmp::min;
28
29#[derive(Debug, Clone)]
36pub struct ReductionFactoringToILP {
37 target: ILP<i64>,
38 m: usize, n: usize, }
41
42impl ReductionFactoringToILP {
43 fn p_var(&self, i: usize) -> usize {
45 i
46 }
47
48 fn q_var(&self, j: usize) -> usize {
50 self.m + j
51 }
52
53 #[cfg(test)]
55 fn z_var(&self, i: usize, j: usize) -> usize {
56 self.m + self.n + i * self.n + j
57 }
58
59 #[cfg(test)]
61 fn carry_var(&self, k: usize) -> usize {
62 self.m + self.n + self.m * self.n + k
63 }
64}
65
66impl ReductionResult for ReductionFactoringToILP {
67 type Source = Factoring;
68 type Target = ILP<i64>;
69
70 fn target_problem(&self) -> &ILP<i64> {
71 &self.target
72 }
73
74 fn extract_solution(
80 &self,
81 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
82 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
83 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
84
85 Ok({
86 let p = (0..self.m)
88 .filter(|&i| target_solution[self.p_var(i)] == 1)
89 .fold(num_bigint::BigUint::from(0u8), |value, index| {
90 value + (num_bigint::BigUint::from(1u8) << index)
91 });
92
93 let q = (0..self.n)
95 .filter(|&j| target_solution[self.q_var(j)] == 1)
96 .fold(num_bigint::BigUint::from(0u8), |value, index| {
97 value + (num_bigint::BigUint::from(1u8) << index)
98 });
99 if p <= q {
100 (p, q)
101 } else {
102 (q, p)
103 }
104 })
105 }
106}
107
108#[reduction(transform = upper_bound {
109 num_vars = "num_bits_first * num_bits_second + 2 * num_bits_first + 2 * num_bits_second + target_bits",
110 num_constraints = "3 * num_bits_first * num_bits_second + 4 * num_bits_first + 4 * num_bits_second + 3 * target_bits + 1",
111},
112 unavailable = {
113 num_nonzeros = "the exact target parameter is not represented by this reduction's symbolic transform",
114 }
115)]
116impl ReduceTo<ILP<i64>> for Factoring {
117 type Result = ReductionFactoringToILP;
118
119 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
120 let m = self.m();
121 let n = self.n();
122 let target = self.target();
123
124 let target_bits = self.target_bits();
126
127 let num_bit_positions = std::cmp::max(m + n, target_bits);
132
133 let num_p = m;
135 let num_q = n;
136 let num_z = m * n;
137 let num_carries = num_bit_positions;
138 let num_vars = num_p + num_q + num_z + num_carries;
139
140 let p_var = |i: usize| -> usize { i };
142 let q_var = |j: usize| -> usize { m + j };
143 let z_var = |i: usize, j: usize| -> usize { m + n + i * n + j };
144 let carry_var = |k: usize| -> usize { m + n + m * n + k };
145
146 let mut constraints = Vec::new();
147
148 for i in 0..m {
154 for j in 0..n {
155 let z = z_var(i, j);
156 let p = p_var(i);
157 let q = q_var(j);
158
159 constraints.extend(mccormick_product(z, p, q));
160 }
161 }
162
163 for k in 0..num_bit_positions {
168 let mut terms: Vec<(usize, i64)> = Vec::new();
169
170 for i in 0..m {
172 if k >= i && k - i < n {
173 let j = k - i;
174 terms.push((z_var(i, j), 1));
175 }
176 }
177
178 if k > 0 {
180 terms.push((carry_var(k - 1), 1));
181 }
182
183 terms.push((carry_var(k), -2));
185
186 let n_k = i64::from(target.bit(u64::try_from(k).expect("bit index fits u64")));
188 constraints.push(LinearConstraint::eq(terms, n_k));
189 }
190
191 constraints.push(LinearConstraint::eq(
193 vec![(carry_var(num_bit_positions - 1), 1)],
194 0,
195 ));
196
197 for i in 0..m {
199 constraints.push(LinearConstraint::le(vec![(p_var(i), 1)], 1));
200 }
201 for j in 0..n {
202 constraints.push(LinearConstraint::le(vec![(q_var(j), 1)], 1));
203 }
204
205 let carry_upper =
207 <Self as ReduceTo<ILP<i64>>>::exact_i64(min(m, n), "encoding a carry bound")?;
208 for k in 0..num_carries {
209 let cv = carry_var(k);
210 constraints.push(LinearConstraint::ge(vec![(cv, 1)], 0));
211 constraints.push(LinearConstraint::le(vec![(cv, 1)], carry_upper));
212 }
213
214 let objective: Vec<(usize, i64)> = vec![];
216
217 let ilp = ILP::<i64>::new(num_vars, constraints, objective, ObjectiveSense::Minimize)
218 .map_err(<Self as ReduceTo<ILP<i64>>>::target_construction)?;
219
220 Ok(ReductionFactoringToILP { target: ilp, m, n })
221 }
222}
223
224#[cfg(feature = "example-db")]
225pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
226 vec![crate::example_db::specs::RuleExampleSpec {
227 id: "factoring_to_ilp",
228 build: || {
229 let source = Factoring::with_factor_bits(35, 3, 3);
230 crate::example_db::specs::rule_example_via_ilp::<_, i64>(source)
231 },
232 }]
233}
234
235#[cfg(test)]
236#[path = "../unit_tests/rules/factoring_ilp.rs"]
237mod tests;