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)]
20pub struct BPProblem {
21 pub instance: BPInstance,
22 pub layouts: SlotMap<LayKey, Layout>,
23 pub item_demand_qtys: Vec<usize>,
24 pub bin_stock_qtys: Vec<usize>,
25}
26
27impl BPProblem {
28 pub fn new(instance: BPInstance) -> Self {
29 let item_demand_qtys = instance.items.iter().map(|(_, qty)| *qty).collect_vec();
30 let bin_stock_qtys = instance.bins.iter().map(|bin| bin.stock).collect_vec();
31
32 Self {
33 instance,
34 layouts: SlotMap::with_key(),
35 item_demand_qtys,
36 bin_stock_qtys,
37 }
38 }
39
40 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(
67 &mut self,
68 lkey: LayKey,
69 pik: PItemKey,
70 commit_instant: bool,
71 ) -> BPPlacement {
72 let pi = self.layouts[lkey].remove_item(pik, commit_instant);
73 self.deregister_included_item(pi.item_id);
74 if self.layouts[lkey].is_empty() {
75 let bin_id = self.layouts[lkey].container.id;
77 self.deregister_layout(lkey);
78 BPPlacement::from_placed_item(BPLayoutType::Closed { bin_id }, &pi)
79 } else {
80 BPPlacement::from_placed_item(BPLayoutType::Open(lkey), &pi)
81 }
82 }
83
84 pub fn save(&mut self) -> BPSolution {
86 let layout_snapshots = self
87 .layouts
88 .iter_mut()
89 .map(|(lkey, l)| (lkey, l.save()))
90 .collect();
91
92 let solution = BPSolution {
93 layout_snapshots,
94 time_stamp: Instant::now(),
95 };
96
97 debug_assert!(problem_matches_solution(self, &solution));
98
99 solution
100 }
101
102 pub fn restore(&mut self, solution: &BPSolution) {
104 let mut layouts_to_remove = vec![];
105
106 for (lkey, layout) in self.layouts.iter_mut() {
109 match solution.layout_snapshots.get(lkey) {
110 Some(ls) => match layout.container.id == ls.container.id {
111 true => layout.restore(ls),
112 false => layouts_to_remove.push(lkey),
113 },
114 None => {
115 layouts_to_remove.push(lkey);
116 }
117 }
118 }
119
120 for lkey in layouts_to_remove {
122 self.layouts.remove(lkey);
123 }
124
125 for (lkey, ls) in solution.layout_snapshots.iter() {
127 if !self.layouts.contains_key(lkey) {
128 self.layouts.insert(Layout::from_snapshot(ls));
129 }
130 }
131
132 {
134 self.item_demand_qtys
135 .iter_mut()
136 .enumerate()
137 .for_each(|(id, demand)| {
138 *demand = self.instance.item_qty(id);
139 });
140
141 self.bin_stock_qtys
142 .iter_mut()
143 .enumerate()
144 .for_each(|(id, stock)| {
145 *stock = self.instance.bin_qty(id);
146 });
147
148 self.layouts.values().for_each(|layout| {
149 self.bin_stock_qtys[layout.container.id] -= 1;
150 layout
151 .placed_items()
152 .values()
153 .for_each(|pi| self.item_demand_qtys[pi.item_id] -= 1);
154 });
155 }
156
157 debug_assert!(problem_matches_solution(self, solution));
158 }
159
160 pub fn density(&self) -> f32 {
161 let total_bin_area = self
162 .layouts
163 .values()
164 .map(|l| l.container.area())
165 .sum::<f32>();
166
167 let total_item_area = self
168 .layouts
169 .values()
170 .map(|l| l.placed_item_area(&self.instance))
171 .sum::<f32>();
172
173 total_item_area / total_bin_area
174 }
175
176 pub fn item_placed_qtys(&self) -> impl Iterator<Item = usize> {
177 self.item_demand_qtys
178 .iter()
179 .enumerate()
180 .map(|(i, demand)| self.instance.item_qty(i) - demand)
181 }
182
183 pub fn bin_used_qtys(&self) -> impl Iterator<Item = usize> {
184 self.bin_stock_qtys
185 .iter()
186 .enumerate()
187 .map(|(i, stock)| self.instance.bin_qty(i) - stock)
188 }
189
190 pub fn bin_cost(&self) -> u64 {
192 self.bin_used_qtys()
193 .enumerate()
194 .map(|(id, qty)| self.instance.bins[id].cost * qty as u64)
195 .sum()
196 }
197
198 fn register_layout(&mut self, layout: Layout) -> LayKey {
199 self.open_bin(layout.container.id);
200 layout
201 .placed_items()
202 .values()
203 .for_each(|pi| self.register_included_item(pi.item_id));
204 self.layouts.insert(layout)
205 }
206
207 fn deregister_layout(&mut self, key: LayKey) {
208 let layout = self.layouts.remove(key).expect("layout key not present");
209 self.close_bin(layout.container.id);
210 layout
211 .placed_items()
212 .values()
213 .for_each(|pi| self.deregister_included_item(pi.item_id));
214 }
215
216 fn register_included_item(&mut self, item_id: usize) {
217 self.item_demand_qtys[item_id] -= 1;
218 }
219
220 fn deregister_included_item(&mut self, item_id: usize) {
221 self.item_demand_qtys[item_id] += 1;
222 }
223
224 fn open_bin(&mut self, bin_id: usize) {
225 self.bin_stock_qtys[bin_id] -= 1
226 }
227
228 fn close_bin(&mut self, bin_id: usize) {
229 self.bin_stock_qtys[bin_id] += 1
230 }
231}
232
233#[derive(Clone, Debug, Copy)]
234pub struct BPPlacement {
236 pub layout_id: BPLayoutType,
238 pub item_id: usize,
240 pub d_transf: DTransformation,
242}
243
244impl BPPlacement {
245 pub fn from_placed_item(layout_id: BPLayoutType, placed_item: &PlacedItem) -> Self {
246 BPPlacement {
247 layout_id,
248 item_id: placed_item.item_id,
249 d_transf: placed_item.d_transf,
250 }
251 }
252}
253
254#[derive(Debug, Clone, Copy, PartialEq, Eq)]
256pub enum BPLayoutType {
257 Open(LayKey),
259 Closed { bin_id: usize },
261}