problemreductions/models/misc/
bin_packing.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
7use crate::traits::Problem;
8use crate::types::{Min, WeightElement};
9use serde::{Deserialize, Serialize};
10
11inventory::submit! {
12 ProblemSchemaEntry {
13 name: "BinPacking",
14 display_name: "Bin Packing",
15 aliases: &[],
16 dimensions: &[VariantDimension::new("weight", "i32", &["i32", "f64"])],
17 module_path: module_path!(),
18 description: "Assign items to bins minimizing number of bins used, subject to capacity",
19 fields: &[
20 FieldInfo { name: "sizes", type_name: "Vec<W>", description: "Item sizes s_i for each item" },
21 FieldInfo { name: "capacity", type_name: "W", description: "Bin capacity C" },
22 ],
23 }
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct BinPacking<W> {
56 sizes: Vec<W>,
58 capacity: W,
60}
61
62impl<W: Clone> BinPacking<W> {
63 pub fn new(sizes: Vec<W>, capacity: W) -> Self {
65 Self { sizes, capacity }
66 }
67
68 pub fn sizes(&self) -> &[W] {
70 &self.sizes
71 }
72
73 pub fn capacity(&self) -> &W {
75 &self.capacity
76 }
77
78 pub fn num_items(&self) -> usize {
80 self.sizes.len()
81 }
82}
83
84impl<W> Problem for BinPacking<W>
85where
86 W: WeightElement + crate::variant::VariantParam,
87 W::Sum: PartialOrd,
88{
89 const NAME: &'static str = "BinPacking";
90 type Value = Min<i32>;
91
92 fn variant() -> Vec<(&'static str, &'static str)> {
93 crate::variant_params![W]
94 }
95
96 fn dims(&self) -> Vec<usize> {
97 let n = self.sizes.len();
98 vec![n; n]
99 }
100
101 fn evaluate(&self, config: &[usize]) -> Min<i32> {
102 if !is_valid_packing(&self.sizes, &self.capacity, config) {
103 return Min(None);
104 }
105 let num_bins = count_bins(config);
106 Min(Some(num_bins as i32))
107 }
108}
109
110fn is_valid_packing<W: WeightElement>(sizes: &[W], capacity: &W, config: &[usize]) -> bool
112where
113 W::Sum: PartialOrd,
114{
115 if config.len() != sizes.len() {
116 return false;
117 }
118 let n = sizes.len();
119 if config.iter().any(|&b| b >= n) {
121 return false;
122 }
123 let cap_sum = capacity.to_sum();
125 let mut bin_load: Vec<W::Sum> = vec![W::Sum::default(); n];
126 for (i, &bin) in config.iter().enumerate() {
127 bin_load[bin] += sizes[i].to_sum();
128 }
129 bin_load.iter().all(|load| *load <= cap_sum)
131}
132
133fn count_bins(config: &[usize]) -> usize {
135 let mut used = vec![false; config.len()];
136 for &bin in config {
137 if bin < used.len() {
138 used[bin] = true;
139 }
140 }
141 used.iter().filter(|&&u| u).count()
142}
143
144crate::declare_variants! {
145 default BinPacking<i32> => "2^num_items",
146 BinPacking<f64> => "2^num_items",
147}
148
149#[cfg(feature = "example-db")]
150pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
151 vec![crate::example_db::specs::ModelExampleSpec {
152 id: "bin_packing",
153 instance: Box::new(BinPacking::<i32>::new(vec![3, 3, 4], 7)),
155 optimal_config: vec![0, 1, 0],
156 optimal_value: serde_json::json!(2),
157 }]
158}
159
160#[cfg(test)]
161#[path = "../../unit_tests/models/misc/bin_packing.rs"]
162mod tests;