problemreductions/rules/
subsetsum_closestvectorproblem.rs1use crate::models::algebraic::ClosestVectorProblem;
4use crate::models::misc::SubsetSum;
5use crate::reduction;
6use crate::registry::ConstructionError;
7use crate::rules::traits::{ReduceTo, ReductionResult};
8use crate::types::{Min, Or};
9
10#[derive(Debug, Clone)]
12pub struct ReductionSubsetSumToClosestVectorProblem {
13 target: ClosestVectorProblem<i64>,
14 num_elements: usize,
15 target_distance: f64,
16}
17
18impl ReductionResult for ReductionSubsetSumToClosestVectorProblem {
19 type Source = SubsetSum;
20 type Target = ClosestVectorProblem<i64>;
21
22 fn target_problem(&self) -> &Self::Target {
23 &self.target
24 }
25
26 fn extract_solution(
27 &self,
28 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
29 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
30 let value =
31 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
32 let certificate = crate::rules::AggregateReductionResult::extract_value(self, value);
33 if !certificate.0 {
34 return Err(crate::rules::ExtractionError::invalid(
35 "target lattice vector does not certify a subset sum",
36 ));
37 }
38 Ok(target_solution[..self.num_elements]
39 .iter()
40 .map(|&value| value == 1)
41 .collect())
42 }
43}
44
45impl crate::rules::AggregateReductionResult for ReductionSubsetSumToClosestVectorProblem {
46 type Source = SubsetSum;
47 type Target = ClosestVectorProblem<i64>;
48
49 fn target_problem(&self) -> &Self::Target {
50 &self.target
51 }
52
53 fn extract_value(&self, target_value: Min<f64>) -> Or {
54 Or(target_value == Min(Some(self.target_distance)))
55 }
56}
57
58impl ReductionSubsetSumToClosestVectorProblem {
59 fn dimensions(
61 num_elements: usize,
62 bit_width: u64,
63 ) -> Result<(usize, usize, usize), crate::rules::ReductionError> {
64 let overflow = || {
65 crate::rules::ReductionError::integer_overflow::<SubsetSum, ClosestVectorProblem<i64>>(
66 "sizing the binary-carry lattice",
67 )
68 };
69 let bits = usize::try_from(bit_width).map_err(|_| overflow())?;
70 let carries = bits.checked_sub(1).ok_or_else(overflow)?;
71 let columns = num_elements.checked_add(carries).ok_or_else(overflow)?;
72 let rows = columns
73 .checked_add(num_elements)
74 .and_then(|value| value.checked_add(1))
75 .ok_or_else(overflow)?;
76 rows.checked_mul(columns)
77 .and_then(|entries| entries.checked_mul(std::mem::size_of::<i64>()))
78 .ok_or_else(overflow)?;
79 Ok((bits, rows, columns))
80 }
81}
82
83#[reduction(
84 aggregate = custom,
85 transform = unavailable {
86 ambient_dimension = "2n+b depends on input bit length b, which is not a registered SubsetSum parameter",
87 num_basis_vectors = "n+b-1 depends on input bit length b, which is not a registered SubsetSum parameter",
88 },
89)]
90impl ReduceTo<ClosestVectorProblem<i64>> for SubsetSum {
91 type Result = ReductionSubsetSumToClosestVectorProblem;
92
93 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
94 let n = self.num_elements();
95 let bit_width = self
96 .sizes()
97 .iter()
98 .fold(self.target().bits().max(1), |bits, size| {
99 bits.max(size.bits())
100 });
101 let (bits, rows, columns) =
102 ReductionSubsetSumToClosestVectorProblem::dimensions(n, bit_width)?;
103 let mut basis = Vec::with_capacity(columns);
104 for (i, size) in self.sizes().iter().enumerate() {
107 let mut column = vec![0_i64; rows];
108 column[i] = 1;
109 column[n + i] = 1;
110 for bit in 0..bits {
111 column[rows - 1 - bit] = i64::from(size.bit(bit as u64));
112 }
113 basis.push(column);
114 }
115 for bit in (1..bits).rev() {
119 let mut column = vec![0_i64; rows];
120 column[rows - 1 - bit] = 1;
121 column[rows - bit] = -2;
122 basis.push(column);
123 }
124 let mut target = vec![0_i64; rows];
125 target[n..2 * n].fill(1);
126 for bit in 0..bits {
127 target[rows - 1 - bit] = i64::from(self.target().bit(bit as u64));
128 }
129 let count = <Self as ReduceTo<ClosestVectorProblem<i64>>>::exact_i64(
132 n,
133 "representing the subset-sum distance threshold",
134 )?;
135 let target_distance = crate::types::i64_to_exact_f64(count)
136 .map_err(ConstructionError::from)
137 .map_err(<Self as ReduceTo<ClosestVectorProblem<i64>>>::target_construction)?
138 .sqrt();
139 let target = ClosestVectorProblem::new(basis, target)
140 .map_err(<Self as ReduceTo<ClosestVectorProblem<i64>>>::target_construction)?;
141 Ok(ReductionSubsetSumToClosestVectorProblem {
142 target,
143 num_elements: n,
144 target_distance,
145 })
146 }
147}
148
149#[cfg(feature = "example-db")]
150pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
151 use crate::export::SolutionPair;
152
153 vec![crate::example_db::specs::RuleExampleSpec {
154 id: "subsetsum_to_closestvectorproblem",
155 build: || {
156 crate::example_db::specs::rule_example_with_witness::<_, ClosestVectorProblem<i64>>(
157 SubsetSum::new(vec![3u32, 7, 1, 8], 11u32),
158 SolutionPair {
159 source_config: serde_json::json!(vec![true, false, false, true]),
160 target_config: serde_json::json!(vec![1, 0, 0, 1, 0, 0, 0]),
161 },
162 )
163 },
164 }]
165}
166
167#[cfg(test)]
168#[path = "../unit_tests/rules/subsetsum_closestvectorproblem.rs"]
169mod tests;