problemreductions/models/misc/
knapsack.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
7use crate::traits::Problem;
8use crate::types::Max;
9use serde::{Deserialize, Serialize};
10
11inventory::submit! {
12 ProblemSchemaEntry {
13 name: "Knapsack",
14 display_name: "Knapsack",
15 aliases: &[],
16 dimensions: &[],
17 module_path: module_path!(),
18 description: "Select items to maximize total value subject to weight capacity constraint",
19 fields: &[
20 FieldInfo { name: "weights", type_name: "Vec<i64>", description: "Nonnegative item weights w_i" },
21 FieldInfo { name: "values", type_name: "Vec<i64>", description: "Nonnegative item values v_i" },
22 FieldInfo { name: "capacity", type_name: "i64", description: "Nonnegative knapsack capacity C" },
23 ],
24 }
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct Knapsack {
51 #[serde(deserialize_with = "nonnegative_i64_vec::deserialize")]
52 weights: Vec<i64>,
53 #[serde(deserialize_with = "nonnegative_i64_vec::deserialize")]
54 values: Vec<i64>,
55 #[serde(deserialize_with = "nonnegative_i64::deserialize")]
56 capacity: i64,
57}
58
59impl Knapsack {
60 pub fn new(weights: Vec<i64>, values: Vec<i64>, capacity: i64) -> Self {
66 assert_eq!(
67 weights.len(),
68 values.len(),
69 "weights and values must have the same length"
70 );
71 assert!(
72 weights.iter().all(|&weight| weight >= 0),
73 "Knapsack weights must be nonnegative"
74 );
75 assert!(
76 values.iter().all(|&value| value >= 0),
77 "Knapsack values must be nonnegative"
78 );
79 assert!(capacity >= 0, "Knapsack capacity must be nonnegative");
80 Self {
81 weights,
82 values,
83 capacity,
84 }
85 }
86
87 pub fn weights(&self) -> &[i64] {
89 &self.weights
90 }
91
92 pub fn values(&self) -> &[i64] {
94 &self.values
95 }
96
97 pub fn capacity(&self) -> i64 {
99 self.capacity
100 }
101
102 pub fn num_items(&self) -> usize {
104 self.weights.len()
105 }
106
107 pub fn num_slack_bits(&self) -> usize {
112 if self.capacity == 0 {
113 1
114 } else {
115 (u64::BITS - (self.capacity as u64).leading_zeros()) as usize
116 }
117 }
118}
119
120impl Problem for Knapsack {
121 const NAME: &'static str = "Knapsack";
122 type Value = Max<i64>;
123
124 fn variant() -> Vec<(&'static str, &'static str)> {
125 crate::variant_params![]
126 }
127
128 fn dims(&self) -> Vec<usize> {
129 vec![2; self.num_items()]
130 }
131
132 fn evaluate(&self, config: &[usize]) -> Max<i64> {
133 if config.len() != self.num_items() {
134 return Max(None);
135 }
136 if config.iter().any(|&v| v >= 2) {
137 return Max(None);
138 }
139 let total_weight: i64 = config
140 .iter()
141 .enumerate()
142 .filter(|(_, &x)| x == 1)
143 .map(|(i, _)| self.weights[i])
144 .sum();
145 if total_weight > self.capacity {
146 return Max(None);
147 }
148 let total_value: i64 = config
149 .iter()
150 .enumerate()
151 .filter(|(_, &x)| x == 1)
152 .map(|(i, _)| self.values[i])
153 .sum();
154 Max(Some(total_value))
155 }
156}
157
158crate::declare_variants! {
159 default Knapsack => "2^(num_items / 2)",
160}
161
162mod nonnegative_i64 {
163 use serde::de::Error;
164 use serde::{Deserialize, Deserializer};
165
166 pub fn deserialize<'de, D>(deserializer: D) -> Result<i64, D::Error>
167 where
168 D: Deserializer<'de>,
169 {
170 let value = i64::deserialize(deserializer)?;
171 if value < 0 {
172 return Err(D::Error::custom(format!(
173 "expected nonnegative integer, got {value}"
174 )));
175 }
176 Ok(value)
177 }
178}
179
180mod nonnegative_i64_vec {
181 use serde::de::Error;
182 use serde::{Deserialize, Deserializer};
183
184 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<i64>, D::Error>
185 where
186 D: Deserializer<'de>,
187 {
188 let values = Vec::<i64>::deserialize(deserializer)?;
189 if let Some(value) = values.iter().copied().find(|value| *value < 0) {
190 return Err(D::Error::custom(format!(
191 "expected nonnegative integers, got {value}"
192 )));
193 }
194 Ok(values)
195 }
196}
197
198#[cfg(feature = "example-db")]
199pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
200 vec![crate::example_db::specs::ModelExampleSpec {
203 id: "knapsack",
204 instance: Box::new(Knapsack::new(vec![2, 3, 4, 5], vec![3, 4, 5, 7], 7)),
205 optimal_config: vec![1, 0, 0, 1],
206 optimal_value: serde_json::json!(10),
207 }]
208}
209
210#[cfg(test)]
211#[path = "../../unit_tests/models/misc/knapsack.rs"]
212mod tests;