1use std::time::Instant;
2
3use crate::ITEM_LIMIT;
4use crate::config::LBFConfig;
5use crate::opt::search::{item_placement_order, search};
6use crate::util::assertions::strip_width_is_in_check;
7use jagua_rs::collision_detection::hazards::filter::{HazKeyFilter, NoFilter};
8use jagua_rs::entities::Instance;
9use jagua_rs::probs::spp::entities::{SPInstance, SPPlacement, SPProblem, SPSolution};
10use log::info;
11use rand::prelude::SmallRng;
12use thousands::Separable;
13
14pub struct LBFOptimizerSP {
16 pub instance: SPInstance,
17 pub problem: SPProblem,
18 pub config: LBFConfig,
19 pub rng: SmallRng,
21 pub sample_counter: usize,
22}
23
24impl LBFOptimizerSP {
25 pub fn new(instance: SPInstance, config: LBFConfig, rng: SmallRng) -> Self {
26 assert!(config.n_samples > 0);
27 let problem = SPProblem::new(instance.clone());
28 Self {
29 instance,
30 problem,
31 config,
32 rng,
33 sample_counter: 0,
34 }
35 }
36
37 pub fn solve(&mut self) -> SPSolution {
38 let start = Instant::now();
39
40 'outer: for item_id in item_placement_order(&self.instance) {
41 let item = self.instance.item(item_id);
42 while self.problem.item_demand_qtys[item_id] > 0 {
44 let cde = self.problem.layout.cde();
45 let placement = match &item.min_quality {
46 None => search(
47 cde,
48 item,
49 &self.config,
50 &mut self.rng,
51 &mut self.sample_counter,
52 &NoFilter,
53 ),
54 Some(min_quality) => {
55 let filter =
56 HazKeyFilter::from_irrelevant_qzones(*min_quality, &cde.hazards_map);
57 search(
58 cde,
59 item,
60 &self.config,
61 &mut self.rng,
62 &mut self.sample_counter,
63 &filter,
64 )
65 }
66 };
67
68 match placement {
69 Some((d_transf, _)) => {
70 self.problem.place_item(SPPlacement {
71 item_id: item.id,
72 d_transf,
73 });
74 info!(
75 "[LBF] placing item {}/{} with id {} at [{}]",
76 self.problem.layout.placed_items.len(),
77 self.instance.total_item_qty(),
78 item.id,
79 d_transf,
80 );
81 #[allow(clippy::absurd_extreme_comparisons)]
82 if self.problem.layout.placed_items.len() >= ITEM_LIMIT {
83 break 'outer;
84 }
85 }
86 None => {
87 self.problem
89 .change_strip_width(self.problem.strip.width * 1.1);
90 info!(
91 "[LBF] no placement found, extended strip by 10% to {:.3}",
92 self.problem.strip.width
93 );
94 assert!(
95 strip_width_is_in_check(&self.problem),
96 " strip width is running away, check if all items fit in the height of the strip"
97 )
98 }
99 }
100 }
101 }
102
103 self.problem.fit_strip();
104 info!(
105 "[LBF] fitted strip width to {:.3}",
106 self.problem.strip.width
107 );
108
109 let solution = self.problem.save();
110
111 info!(
112 "[LBF] optimization finished in {:.3}ms ({} samples)",
113 start.elapsed().as_secs_f64() * 1000.0,
114 self.sample_counter.separate_with_commas()
115 );
116
117 info!(
118 "[LBF] solution contains {} items with a density of {:.3}%",
119 solution.layout_snapshot.placed_items.len(),
120 solution.density(&self.instance) * 100.0
121 );
122 solution
123 }
124}