problemreductions/rules/
knapsack_qubo.rs1use crate::models::algebraic::QUBO;
14use crate::models::misc::Knapsack;
15use crate::reduction;
16use crate::rules::traits::{ReduceTo, ReductionResult};
17
18fn overflow(operation: &'static str) -> crate::rules::ReductionError {
19 crate::rules::ReductionError::integer_overflow::<Knapsack, QUBO<i64>>(operation)
20}
21
22#[derive(Debug, Clone)]
24pub struct ReductionKnapsackToQUBO {
25 target: QUBO<i64>,
26 num_items: usize,
27}
28
29impl ReductionResult for ReductionKnapsackToQUBO {
30 type Source = Knapsack;
31 type Target = QUBO<i64>;
32
33 fn target_problem(&self) -> &Self::Target {
34 &self.target
35 }
36
37 fn extract_solution(
38 &self,
39 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
40 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
41 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
42
43 Ok(target_solution[..self.num_items].to_vec())
44 }
45}
46
47#[reduction(transform = {
48 exact {
49 num_vars = "num_items + capacity_bits",
50 },
51 upper_bound {
52 num_quadratic_terms = "(num_items + capacity_bits)^2",
53 },
54})]
55impl ReduceTo<QUBO<i64>> for Knapsack {
56 type Result = ReductionKnapsackToQUBO;
57
58 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
59 let n = self.num_items();
60 let c = self.capacity();
61 let b = self.num_slack_bits();
62 let total = n + b;
63
64 let sum_values = self
66 .values()
67 .iter()
68 .try_fold(0_i64, |total, &value| total.checked_add(value))
69 .ok_or_else(|| {
70 crate::rules::ReductionError::integer_overflow::<Knapsack, QUBO<i64>>(
71 "summing item values for the QUBO penalty",
72 )
73 })?;
74 let penalty_i64 = sum_values.checked_add(1).ok_or_else(|| {
75 crate::rules::ReductionError::integer_overflow::<Knapsack, QUBO<i64>>(
76 "incrementing the QUBO penalty",
77 )
78 })?;
79 let penalty = penalty_i64;
80 let values = self.values();
81
82 let mut coeffs = vec![0_i64; total];
99 for (i, coeff) in coeffs.iter_mut().enumerate().take(n) {
100 *coeff = self.weights()[i];
101 }
102 for j in 0..b {
103 let bit = u32::try_from(j).map_err(|_| {
104 crate::rules::ReductionError::invalid_target::<Knapsack, QUBO<i64>>(
105 "slack-bit index does not fit u32",
106 )
107 })?;
108 let weight = 1_i64.checked_shl(bit).ok_or_else(|| {
109 crate::rules::ReductionError::integer_overflow::<Knapsack, QUBO<i64>>(
110 "constructing a slack-bit weight",
111 )
112 })?;
113 coeffs[n + j] = weight;
114 }
115
116 let mut matrix = vec![vec![0_i64; total]; total];
117
118 for k in 0..total {
120 let square = penalty
121 .checked_mul(coeffs[k])
122 .and_then(|value| value.checked_mul(coeffs[k]))
123 .ok_or_else(|| overflow("computing a knapsack QUBO square penalty"))?;
124 let linear = penalty
125 .checked_mul(c)
126 .and_then(|value| value.checked_mul(coeffs[k]))
127 .and_then(|value| value.checked_mul(2))
128 .ok_or_else(|| overflow("computing a knapsack QUBO linear penalty"))?;
129 matrix[k][k] = square
130 .checked_sub(linear)
131 .ok_or_else(|| overflow("combining knapsack QUBO diagonal penalties"))?;
132 if k < n {
133 matrix[k][k] = matrix[k][k]
134 .checked_sub(values[k])
135 .ok_or_else(|| overflow("adding a knapsack value to the QUBO objective"))?;
136 }
137 }
138
139 for i in 0..total {
141 for j in (i + 1)..total {
142 matrix[i][j] = penalty
143 .checked_mul(coeffs[i])
144 .and_then(|value| value.checked_mul(coeffs[j]))
145 .and_then(|value| value.checked_mul(2))
146 .ok_or_else(|| overflow("computing a knapsack QUBO interaction"))?;
147 }
148 }
149
150 Ok(ReductionKnapsackToQUBO {
151 target: QUBO::from_matrix(matrix).map_err(|message| {
152 crate::rules::ReductionError::construction::<Knapsack, QUBO<i64>>(message)
153 })?,
154 num_items: n,
155 })
156 }
157}
158
159#[cfg(feature = "example-db")]
160pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
161 use crate::export::SolutionPair;
162
163 vec![crate::example_db::specs::RuleExampleSpec {
164 id: "knapsack_to_qubo",
165 build: || {
166 crate::example_db::specs::rule_example_with_witness::<_, QUBO<i64>>(
167 Knapsack::new(vec![2, 3, 4, 5], vec![3, 4, 5, 7], 7),
168 SolutionPair {
169 source_config: serde_json::json!(vec![true, false, false, true]),
170 target_config: serde_json::json!(vec![
171 true, false, false, true, false, false, false
172 ]),
173 },
174 )
175 },
176 }]
177}
178
179#[cfg(test)]
180#[path = "../unit_tests/rules/knapsack_qubo.rs"]
181mod tests;