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