jagua_rs/probs/bpp/entities/
problem.rs1use crate::entities::Instance;
2use crate::entities::Layout;
3use crate::entities::{PItemKey, PlacedItem};
4use crate::geometry::DTransformation;
5use crate::probs::bpp::entities::BPInstance;
6use crate::probs::bpp::entities::BPSolution;
7use crate::probs::bpp::util::assertions::problem_matches_solution;
8use itertools::Itertools;
9use slotmap::{SlotMap, new_key_type};
10use std::time::Instant;
11
12new_key_type! {
13 pub struct LayKey;
15}
16
17#[derive(Clone)]
19pub struct BPProblem {
20 pub instance: BPInstance,
21 pub layouts: SlotMap<LayKey, Layout>,
22 pub item_demand_qtys: Vec<usize>,
23 pub bin_stock_qtys: Vec<usize>,
24}
25
26impl BPProblem {
27 pub fn new(instance: BPInstance) -> Self {
28 let item_demand_qtys = instance.items.iter().map(|(_, qty)| *qty).collect_vec();
29 let bin_stock_qtys = instance.bins.iter().map(|bin| bin.stock).collect_vec();
30
31 Self {
32 instance,
33 layouts: SlotMap::with_key(),
34 item_demand_qtys,
35 bin_stock_qtys,
36 }
37 }
38
39 pub fn remove_layout(&mut self, key: LayKey) {
41 self.deregister_layout(key);
42 }
43
44 pub fn place_item(&mut self, p_opt: BPPlacement) -> (LayKey, PItemKey) {
46 let lkey = match p_opt.layout_id {
47 BPLayoutType::Open(lkey) => lkey,
48 BPLayoutType::Closed { bin_id } => {
49 let bin = &self.instance.bins[bin_id];
51 let layout = Layout::new(bin.container.clone());
52 self.register_layout(layout)
53 }
54 };
55 let layout = &mut self.layouts[lkey];
56 let item = self.instance.item(p_opt.item_id);
57 let pik = layout.place_item(item, p_opt.d_transf);
58
59 self.register_included_item(p_opt.item_id);
60
61 (lkey, pik)
62 }
63
64 pub fn remove_item(&mut self, lkey: LayKey, pik: PItemKey) -> BPPlacement {
67 let pi = self.layouts[lkey].remove_item(pik);
68 self.deregister_included_item(pi.item_id);
69 if self.layouts[lkey].is_empty() {
70 let bin_id = self.layouts[lkey].container.id;
72 self.deregister_layout(lkey);
73 BPPlacement::from_placed_item(BPLayoutType::Closed { bin_id }, &pi)
74 } else {
75 BPPlacement::from_placed_item(BPLayoutType::Open(lkey), &pi)
76 }
77 }
78
79 pub fn save(&self) -> BPSolution {
81 let layout_snapshots = self
82 .layouts
83 .iter()
84 .map(|(lkey, l)| (lkey, l.save()))
85 .collect();
86
87 let solution = BPSolution {
88 layout_snapshots,
89 time_stamp: Instant::now(),
90 };
91
92 debug_assert!(problem_matches_solution(self, &solution));
93
94 solution
95 }
96
97 pub fn restore(&mut self, solution: &BPSolution) {
99 let mut layouts_to_remove = vec![];
100
101 for (lkey, layout) in self.layouts.iter_mut() {
104 match solution.layout_snapshots.get(lkey) {
105 Some(ls) => match layout.container.id == ls.container.id {
106 true => layout.restore(ls),
107 false => layouts_to_remove.push(lkey),
108 },
109 None => {
110 layouts_to_remove.push(lkey);
111 }
112 }
113 }
114
115 for lkey in layouts_to_remove {
117 self.layouts.remove(lkey);
118 }
119
120 for (lkey, ls) in solution.layout_snapshots.iter() {
122 if !self.layouts.contains_key(lkey) {
123 self.layouts.insert(Layout::from_snapshot(ls));
124 }
125 }
126
127 {
129 self.item_demand_qtys
130 .iter_mut()
131 .enumerate()
132 .for_each(|(id, demand)| {
133 *demand = self.instance.item_qty(id);
134 });
135
136 self.bin_stock_qtys
137 .iter_mut()
138 .enumerate()
139 .for_each(|(id, stock)| {
140 *stock = self.instance.bin_qty(id);
141 });
142
143 self.layouts.values().for_each(|layout| {
144 self.bin_stock_qtys[layout.container.id] -= 1;
145 layout
146 .placed_items
147 .values()
148 .for_each(|pi| self.item_demand_qtys[pi.item_id] -= 1);
149 });
150 }
151
152 debug_assert!(problem_matches_solution(self, solution));
153 }
154
155 pub fn density(&self) -> f32 {
156 let total_bin_area = self
157 .layouts
158 .values()
159 .map(|l| l.container.area())
160 .sum::<f32>();
161
162 let total_item_area = self
163 .layouts
164 .values()
165 .map(|l| l.placed_item_area(&self.instance))
166 .sum::<f32>();
167
168 total_item_area / total_bin_area
169 }
170
171 pub fn item_placed_qtys(&self) -> impl Iterator<Item = usize> {
172 self.item_demand_qtys
173 .iter()
174 .enumerate()
175 .map(|(i, demand)| self.instance.item_qty(i) - demand)
176 }
177
178 pub fn bin_used_qtys(&self) -> impl Iterator<Item = usize> {
179 self.bin_stock_qtys
180 .iter()
181 .enumerate()
182 .map(|(i, stock)| self.instance.bin_qty(i) - stock)
183 }
184
185 pub fn bin_cost(&self) -> u64 {
187 self.bin_used_qtys()
188 .enumerate()
189 .map(|(id, qty)| self.instance.bins[id].cost * qty as u64)
190 .sum()
191 }
192
193 fn register_layout(&mut self, layout: Layout) -> LayKey {
194 self.open_bin(layout.container.id);
195 layout
196 .placed_items
197 .values()
198 .for_each(|pi| self.register_included_item(pi.item_id));
199 self.layouts.insert(layout)
200 }
201
202 fn deregister_layout(&mut self, key: LayKey) {
203 let layout = self.layouts.remove(key).expect("layout key not present");
204 self.close_bin(layout.container.id);
205 layout
206 .placed_items
207 .values()
208 .for_each(|pi| self.deregister_included_item(pi.item_id));
209 }
210
211 fn register_included_item(&mut self, item_id: usize) {
212 self.item_demand_qtys[item_id] -= 1;
213 }
214
215 fn deregister_included_item(&mut self, item_id: usize) {
216 self.item_demand_qtys[item_id] += 1;
217 }
218
219 fn open_bin(&mut self, bin_id: usize) {
220 self.bin_stock_qtys[bin_id] -= 1
221 }
222
223 fn close_bin(&mut self, bin_id: usize) {
224 self.bin_stock_qtys[bin_id] += 1
225 }
226}
227
228#[derive(Clone, Debug, Copy)]
229pub struct BPPlacement {
231 pub layout_id: BPLayoutType,
233 pub item_id: usize,
235 pub d_transf: DTransformation,
237}
238
239impl BPPlacement {
240 pub fn from_placed_item(layout_id: BPLayoutType, placed_item: &PlacedItem) -> Self {
241 BPPlacement {
242 layout_id,
243 item_id: placed_item.item_id,
244 d_transf: placed_item.d_transf,
245 }
246 }
247}
248
249#[derive(Debug, Clone, Copy, PartialEq, Eq)]
251pub enum BPLayoutType {
252 Open(LayKey),
254 Closed { bin_id: usize },
256}