use crate::entities::Instance;
use crate::entities::Layout;
use crate::entities::{PItemKey, PlacedItem};
use crate::geometry::DTransformation;
use crate::probs::bpp::entities::BPInstance;
use crate::probs::bpp::entities::BPSolution;
use crate::probs::bpp::util::assertions::problem_matches_solution;
use itertools::Itertools;
use slotmap::{SlotMap, new_key_type};
use std::time::Instant;
new_key_type! {
pub struct LayKey;
}
#[derive(Clone)]
pub struct BPProblem {
pub instance: BPInstance,
pub layouts: SlotMap<LayKey, Layout>,
pub item_demand_qtys: Vec<usize>,
pub bin_stock_qtys: Vec<usize>,
}
impl BPProblem {
pub fn new(instance: BPInstance) -> Self {
let item_demand_qtys = instance.items.iter().map(|(_, qty)| *qty).collect_vec();
let bin_stock_qtys = instance.bins.iter().map(|bin| bin.stock).collect_vec();
Self {
instance,
layouts: SlotMap::with_key(),
item_demand_qtys,
bin_stock_qtys,
}
}
pub fn remove_layout(&mut self, key: LayKey) {
self.deregister_layout(key);
}
pub fn place_item(&mut self, p_opt: BPPlacement) -> (LayKey, PItemKey) {
let lkey = match p_opt.layout_id {
BPLayoutType::Open(lkey) => lkey,
BPLayoutType::Closed { bin_id } => {
let bin = &self.instance.bins[bin_id];
let layout = Layout::new(bin.container.clone());
self.register_layout(layout)
}
};
let layout = &mut self.layouts[lkey];
let item = self.instance.item(p_opt.item_id);
let pik = layout.place_item(item, p_opt.d_transf);
self.register_included_item(p_opt.item_id);
(lkey, pik)
}
pub fn remove_item(&mut self, lkey: LayKey, pik: PItemKey) -> BPPlacement {
let pi = self.layouts[lkey].remove_item(pik);
self.deregister_included_item(pi.item_id);
if self.layouts[lkey].is_empty() {
let bin_id = self.layouts[lkey].container.id;
self.deregister_layout(lkey);
BPPlacement::from_placed_item(BPLayoutType::Closed { bin_id }, &pi)
} else {
BPPlacement::from_placed_item(BPLayoutType::Open(lkey), &pi)
}
}
pub fn save(&mut self) -> BPSolution {
let layout_snapshots = self
.layouts
.iter_mut()
.map(|(lkey, l)| (lkey, l.save()))
.collect();
let solution = BPSolution {
layout_snapshots,
time_stamp: Instant::now(),
};
debug_assert!(problem_matches_solution(self, &solution));
solution
}
pub fn restore(&mut self, solution: &BPSolution) {
let mut layouts_to_remove = vec![];
for (lkey, layout) in self.layouts.iter_mut() {
match solution.layout_snapshots.get(lkey) {
Some(ls) => match layout.container.id == ls.container.id {
true => layout.restore(ls),
false => layouts_to_remove.push(lkey),
},
None => {
layouts_to_remove.push(lkey);
}
}
}
for lkey in layouts_to_remove {
self.layouts.remove(lkey);
}
for (lkey, ls) in solution.layout_snapshots.iter() {
if !self.layouts.contains_key(lkey) {
self.layouts.insert(Layout::from_snapshot(ls));
}
}
{
self.item_demand_qtys
.iter_mut()
.enumerate()
.for_each(|(id, demand)| {
*demand = self.instance.item_qty(id);
});
self.bin_stock_qtys
.iter_mut()
.enumerate()
.for_each(|(id, stock)| {
*stock = self.instance.bin_qty(id);
});
self.layouts.values().for_each(|layout| {
self.bin_stock_qtys[layout.container.id] -= 1;
layout
.placed_items
.values()
.for_each(|pi| self.item_demand_qtys[pi.item_id] -= 1);
});
}
debug_assert!(problem_matches_solution(self, solution));
}
pub fn density(&self) -> f32 {
let total_bin_area = self
.layouts
.values()
.map(|l| l.container.area())
.sum::<f32>();
let total_item_area = self
.layouts
.values()
.map(|l| l.placed_item_area(&self.instance))
.sum::<f32>();
total_item_area / total_bin_area
}
pub fn item_placed_qtys(&self) -> impl Iterator<Item = usize> {
self.item_demand_qtys
.iter()
.enumerate()
.map(|(i, demand)| self.instance.item_qty(i) - demand)
}
pub fn bin_used_qtys(&self) -> impl Iterator<Item = usize> {
self.bin_stock_qtys
.iter()
.enumerate()
.map(|(i, stock)| self.instance.bin_qty(i) - stock)
}
pub fn bin_cost(&self) -> u64 {
self.bin_used_qtys()
.enumerate()
.map(|(id, qty)| self.instance.bins[id].cost * qty as u64)
.sum()
}
fn register_layout(&mut self, layout: Layout) -> LayKey {
self.open_bin(layout.container.id);
layout
.placed_items
.values()
.for_each(|pi| self.register_included_item(pi.item_id));
self.layouts.insert(layout)
}
fn deregister_layout(&mut self, key: LayKey) {
let layout = self.layouts.remove(key).expect("layout key not present");
self.close_bin(layout.container.id);
layout
.placed_items
.values()
.for_each(|pi| self.deregister_included_item(pi.item_id));
}
fn register_included_item(&mut self, item_id: usize) {
self.item_demand_qtys[item_id] -= 1;
}
fn deregister_included_item(&mut self, item_id: usize) {
self.item_demand_qtys[item_id] += 1;
}
fn open_bin(&mut self, bin_id: usize) {
self.bin_stock_qtys[bin_id] -= 1
}
fn close_bin(&mut self, bin_id: usize) {
self.bin_stock_qtys[bin_id] += 1
}
}
#[derive(Clone, Debug, Copy)]
pub struct BPPlacement {
pub layout_id: BPLayoutType,
pub item_id: usize,
pub d_transf: DTransformation,
}
impl BPPlacement {
pub fn from_placed_item(layout_id: BPLayoutType, placed_item: &PlacedItem) -> Self {
BPPlacement {
layout_id,
item_id: placed_item.item_id,
d_transf: placed_item.d_transf,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BPLayoutType {
Open(LayKey),
Closed { bin_id: usize },
}