problemreductions/rules/
subsetsum_closestvectorproblem.rs1use crate::models::algebraic::ClosestVectorProblem;
4use crate::models::decision::Decision;
5use crate::models::misc::SubsetSum;
6use crate::reduction;
7use crate::rules::traits::{ReduceTo, ReductionResult};
8
9#[derive(Debug, Clone)]
11pub struct ReductionSubsetSumToClosestVectorProblem {
12 target: Decision<ClosestVectorProblem>,
13 num_elements: usize,
14}
15
16impl ReductionResult for ReductionSubsetSumToClosestVectorProblem {
17 type Source = SubsetSum;
18 type Target = Decision<ClosestVectorProblem>;
19
20 fn target_problem(&self) -> &Self::Target {
21 &self.target
22 }
23
24 fn extract_solution(
25 &self,
26 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
27 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
28 crate::rules::traits::validate_target_witness(
29 self.target_problem(),
30 target_solution,
31 |value| value.0,
32 "target lattice vector does not certify a subset sum",
33 )?;
34 Ok(target_solution[..self.num_elements]
35 .iter()
36 .map(|&value| value == 1)
37 .collect())
38 }
39}
40
41#[crate::aggregate_reduction(identity)]
42impl crate::rules::AggregateReductionResult for ReductionSubsetSumToClosestVectorProblem {}
43
44impl ReductionSubsetSumToClosestVectorProblem {
45 fn dimensions(
47 num_elements: usize,
48 bit_width: u64,
49 ) -> Result<(usize, usize, usize), crate::rules::ReductionError> {
50 let overflow = || {
51 crate::rules::ReductionError::integer_overflow::<
52 SubsetSum,
53 Decision<ClosestVectorProblem>,
54 >("sizing the binary-carry lattice")
55 };
56 let bits = usize::try_from(bit_width).map_err(|_| overflow())?;
57 let carries = bits.checked_sub(1).ok_or_else(overflow)?;
58 let columns = num_elements.checked_add(carries).ok_or_else(overflow)?;
59 let rows = columns
60 .checked_add(num_elements)
61 .and_then(|value| value.checked_add(1))
62 .ok_or_else(overflow)?;
63 rows.checked_mul(columns)
64 .and_then(|entries| entries.checked_mul(std::mem::size_of::<i64>()))
65 .ok_or_else(overflow)?;
66 Ok((bits, rows, columns))
67 }
68}
69
70#[reduction(transform = {
76 exact {
77 ambient_dimension = "2 * num_elements + max_numeric_magnitude_bits",
78 num_basis_vectors = "num_elements + max_numeric_magnitude_bits - 1",
79 },
80 upper_bound {
81 max_numeric_magnitude_bits = "2",
82 coefficient_box_bits = "(num_elements + max_numeric_magnitude_bits - 1) * ((num_elements + max_numeric_magnitude_bits - 1) * (max_numeric_magnitude_bits + 3) / 2 + 2)",
83 }
84})]
85impl ReduceTo<Decision<ClosestVectorProblem>> for SubsetSum {
86 type Result = ReductionSubsetSumToClosestVectorProblem;
87
88 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
89 let n = self.num_elements();
90 let bit_width = self.max_numeric_magnitude_bits();
91 let (bits, rows, columns) =
92 ReductionSubsetSumToClosestVectorProblem::dimensions(n, bit_width)?;
93 let mut basis = Vec::with_capacity(columns);
94 for (i, size) in self.sizes().iter().enumerate() {
97 let mut column = vec![0_i64; rows];
98 column[i] = 1;
99 column[n + i] = 1;
100 for bit in 0..bits {
101 column[rows - 1 - bit] = i64::from(size.bit(bit as u64));
102 }
103 basis.push(column);
104 }
105 for bit in (1..bits).rev() {
108 let mut column = vec![0_i64; rows];
109 column[rows - 1 - bit] = 1;
110 column[rows - bit] = -2;
111 basis.push(column);
112 }
113 let mut target = vec![0_i64; rows];
114 target[n..2 * n].fill(1);
115 for bit in 0..bits {
116 target[rows - 1 - bit] = i64::from(self.target().bit(bit as u64));
117 }
118 let target_squared_distance =
119 <Self as ReduceTo<Decision<ClosestVectorProblem>>>::exact_i64(
120 n,
121 "representing the subset-sum squared-distance threshold",
122 )?;
123 let target = ClosestVectorProblem::new(basis, target)
124 .map_err(<Self as ReduceTo<Decision<ClosestVectorProblem>>>::target_construction)?;
125 Ok(ReductionSubsetSumToClosestVectorProblem {
126 target: Decision::new(target, target_squared_distance),
127 num_elements: n,
128 })
129 }
130}
131
132#[cfg(feature = "example-db")]
133pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
134 use crate::export::SolutionPair;
135
136 vec![crate::example_db::specs::RuleExampleSpec {
137 id: "subsetsum_to_closestvectorproblem",
138 build: || {
139 crate::example_db::specs::rule_example_with_witness::<_, Decision<ClosestVectorProblem>>(
140 SubsetSum::new(vec![3u32, 7, 1, 8], 11u32),
141 SolutionPair {
142 source_config: serde_json::json!(vec![true, false, false, true]),
143 target_config: serde_json::json!(vec![1, 0, 0, 1, 0, 0, 0]),
144 },
145 )
146 },
147 }]
148}
149
150#[cfg(test)]
151#[path = "../unit_tests/rules/subsetsum_closestvectorproblem.rs"]
152mod tests;