problemreductions/models/set/
integer_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: "IntegerKnapsack",
14 display_name: "Integer Knapsack",
15 aliases: &[],
16 dimensions: &[],
17 module_path: module_path!(),
18 description: "Select items with integer multiplicities to maximize total value subject to capacity constraint",
19 fields: &[
20 FieldInfo { name: "sizes", type_name: "Vec<i64>", description: "Positive item sizes s(u)" },
21 FieldInfo { name: "values", type_name: "Vec<i64>", description: "Positive item values v(u)" },
22 FieldInfo { name: "capacity", type_name: "i64", description: "Nonnegative knapsack capacity B" },
23 ],
24 }
25}
26
27#[derive(Debug, Clone, Serialize)]
51#[serde(into = "RawIntegerKnapsack")]
52pub struct IntegerKnapsack {
53 sizes: Vec<i64>,
54 values: Vec<i64>,
55 capacity: i64,
56}
57
58impl IntegerKnapsack {
59 pub fn new(sizes: Vec<i64>, values: Vec<i64>, capacity: i64) -> Self {
65 assert_eq!(
66 sizes.len(),
67 values.len(),
68 "sizes and values must have the same length"
69 );
70 assert!(
71 sizes.iter().all(|&s| s > 0),
72 "IntegerKnapsack sizes must be positive"
73 );
74 assert!(
75 values.iter().all(|&v| v > 0),
76 "IntegerKnapsack values must be positive"
77 );
78 assert!(
79 capacity >= 0,
80 "IntegerKnapsack capacity must be nonnegative"
81 );
82 Self {
83 sizes,
84 values,
85 capacity,
86 }
87 }
88
89 pub fn sizes(&self) -> &[i64] {
91 &self.sizes
92 }
93
94 pub fn values(&self) -> &[i64] {
96 &self.values
97 }
98
99 pub fn capacity(&self) -> i64 {
101 self.capacity
102 }
103
104 pub fn num_items(&self) -> usize {
106 self.sizes.len()
107 }
108}
109
110impl Problem for IntegerKnapsack {
111 const NAME: &'static str = "IntegerKnapsack";
112 type Value = Max<i64>;
113
114 fn variant() -> Vec<(&'static str, &'static str)> {
115 crate::variant_params![]
116 }
117
118 fn dims(&self) -> Vec<usize> {
119 self.sizes
120 .iter()
121 .map(|&s| (self.capacity / s + 1) as usize)
122 .collect()
123 }
124
125 fn evaluate(&self, config: &[usize]) -> Max<i64> {
126 if config.len() != self.num_items() {
127 return Max(None);
128 }
129 let dims = self.dims();
130 if config.iter().zip(dims.iter()).any(|(&c, &d)| c >= d) {
131 return Max(None);
132 }
133 let total_size: i64 = config
134 .iter()
135 .enumerate()
136 .map(|(i, &c)| c as i64 * self.sizes[i])
137 .sum();
138 if total_size > self.capacity {
139 return Max(None);
140 }
141 let total_value: i64 = config
142 .iter()
143 .enumerate()
144 .map(|(i, &c)| c as i64 * self.values[i])
145 .sum();
146 Max(Some(total_value))
147 }
148}
149
150crate::declare_variants! {
151 default IntegerKnapsack => "(capacity + 1)^num_items",
152}
153
154#[derive(Deserialize, Serialize)]
156struct RawIntegerKnapsack {
157 sizes: Vec<i64>,
158 values: Vec<i64>,
159 capacity: i64,
160}
161
162impl From<IntegerKnapsack> for RawIntegerKnapsack {
163 fn from(ik: IntegerKnapsack) -> Self {
164 RawIntegerKnapsack {
165 sizes: ik.sizes,
166 values: ik.values,
167 capacity: ik.capacity,
168 }
169 }
170}
171
172impl TryFrom<RawIntegerKnapsack> for IntegerKnapsack {
173 type Error = String;
174
175 fn try_from(raw: RawIntegerKnapsack) -> Result<Self, String> {
176 if raw.sizes.len() != raw.values.len() {
177 return Err(format!(
178 "sizes and values must have the same length, got {} and {}",
179 raw.sizes.len(),
180 raw.values.len()
181 ));
182 }
183 if let Some(&s) = raw.sizes.iter().find(|&&s| s <= 0) {
184 return Err(format!("expected positive sizes, got {s}"));
185 }
186 if let Some(&v) = raw.values.iter().find(|&&v| v <= 0) {
187 return Err(format!("expected positive values, got {v}"));
188 }
189 if raw.capacity < 0 {
190 return Err(format!(
191 "expected nonnegative capacity, got {}",
192 raw.capacity
193 ));
194 }
195 Ok(IntegerKnapsack {
196 sizes: raw.sizes,
197 values: raw.values,
198 capacity: raw.capacity,
199 })
200 }
201}
202
203impl<'de> Deserialize<'de> for IntegerKnapsack {
204 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
205 where
206 D: serde::Deserializer<'de>,
207 {
208 let raw = RawIntegerKnapsack::deserialize(deserializer)?;
209 IntegerKnapsack::try_from(raw).map_err(serde::de::Error::custom)
210 }
211}
212
213#[cfg(feature = "example-db")]
214pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
215 vec![crate::example_db::specs::ModelExampleSpec {
218 id: "integer-knapsack",
219 instance: Box::new(IntegerKnapsack::new(
220 vec![3, 4, 5, 2, 7],
221 vec![4, 5, 7, 3, 9],
222 15,
223 )),
224 optimal_config: vec![0, 0, 1, 5, 0],
225 optimal_value: serde_json::json!(22),
226 }]
227}
228
229#[cfg(test)]
230#[path = "../../unit_tests/models/set/integer_knapsack.rs"]
231mod tests;