problemreductions/models/misc/
bin_packing.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
7use crate::traits::{OptimizationProblem, Problem};
8use crate::types::{Direction, SolutionSize, WeightElement};
9use serde::{Deserialize, Serialize};
10
11inventory::submit! {
12 ProblemSchemaEntry {
13 name: "BinPacking",
14 module_path: module_path!(),
15 description: "Assign items to bins minimizing number of bins used, subject to capacity",
16 fields: &[
17 FieldInfo { name: "sizes", type_name: "Vec<W>", description: "Item sizes s_i for each item" },
18 FieldInfo { name: "capacity", type_name: "W", description: "Bin capacity C" },
19 ],
20 }
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct BinPacking<W> {
53 sizes: Vec<W>,
55 capacity: W,
57}
58
59impl<W: Clone> BinPacking<W> {
60 pub fn new(sizes: Vec<W>, capacity: W) -> Self {
62 Self { sizes, capacity }
63 }
64
65 pub fn sizes(&self) -> &[W] {
67 &self.sizes
68 }
69
70 pub fn capacity(&self) -> &W {
72 &self.capacity
73 }
74
75 pub fn num_items(&self) -> usize {
77 self.sizes.len()
78 }
79}
80
81impl<W> Problem for BinPacking<W>
82where
83 W: WeightElement + crate::variant::VariantParam,
84 W::Sum: PartialOrd,
85{
86 const NAME: &'static str = "BinPacking";
87 type Metric = SolutionSize<i32>;
88
89 fn variant() -> Vec<(&'static str, &'static str)> {
90 crate::variant_params![W]
91 }
92
93 fn dims(&self) -> Vec<usize> {
94 let n = self.sizes.len();
95 vec![n; n]
96 }
97
98 fn evaluate(&self, config: &[usize]) -> SolutionSize<i32> {
99 if !is_valid_packing(&self.sizes, &self.capacity, config) {
100 return SolutionSize::Invalid;
101 }
102 let num_bins = count_bins(config);
103 SolutionSize::Valid(num_bins as i32)
104 }
105}
106
107impl<W> OptimizationProblem for BinPacking<W>
108where
109 W: WeightElement + crate::variant::VariantParam,
110 W::Sum: PartialOrd,
111{
112 type Value = i32;
113
114 fn direction(&self) -> Direction {
115 Direction::Minimize
116 }
117}
118
119fn is_valid_packing<W: WeightElement>(sizes: &[W], capacity: &W, config: &[usize]) -> bool
121where
122 W::Sum: PartialOrd,
123{
124 if config.len() != sizes.len() {
125 return false;
126 }
127 let n = sizes.len();
128 if config.iter().any(|&b| b >= n) {
130 return false;
131 }
132 let cap_sum = capacity.to_sum();
134 let mut bin_load: Vec<W::Sum> = vec![W::Sum::default(); n];
135 for (i, &bin) in config.iter().enumerate() {
136 bin_load[bin] += sizes[i].to_sum();
137 }
138 bin_load.iter().all(|load| *load <= cap_sum)
140}
141
142fn count_bins(config: &[usize]) -> usize {
144 let mut used = vec![false; config.len()];
145 for &bin in config {
146 if bin < used.len() {
147 used[bin] = true;
148 }
149 }
150 used.iter().filter(|&&u| u).count()
151}
152
153crate::declare_variants! {
154 BinPacking<i32> => "2^num_items",
155 BinPacking<f64> => "2^num_items",
156}
157
158#[cfg(test)]
159#[path = "../../unit_tests/models/misc/bin_packing.rs"]
160mod tests;