1use crate::config::LBFConfig;
2use crate::opt::loss::LBFLoss;
3use crate::samplers::ls_sampler::LSSampler;
4use crate::samplers::uniform_rect_sampler::UniformRectSampler;
5use itertools::Itertools;
6use jagua_rs::collision_detection::CDEngine;
7use jagua_rs::collision_detection::hazards::filter::HazardFilter;
8use jagua_rs::entities::{Instance, Item};
9use jagua_rs::geometry::DTransformation;
10use jagua_rs::geometry::geo_traits::TransformableFrom;
11use log::debug;
12use ordered_float::OrderedFloat;
13use rand::Rng;
14use std::cmp::{Ordering, Reverse};
15
16pub fn search(
18 cde: &CDEngine,
19 item: &Item,
20 config: &LBFConfig,
21 rng: &mut impl Rng,
22 sample_counter: &mut usize,
23 filter: &impl HazardFilter,
24) -> Option<(DTransformation, LBFLoss)> {
25 let surrogate = item.shape_cd.surrogate();
26 let mut buffer = {
28 let mut buffer = (*item.shape_cd).clone();
29 buffer.surrogate = None; buffer
31 };
32
33 let mut best: Option<(DTransformation, LBFLoss)> = None;
34
35 let ls_sample_budget = (config.n_samples as f32 * config.ls_frac) as usize;
37 let uni_sample_budget = config.n_samples - ls_sample_budget;
38
39 let mut bin_sampler = UniformRectSampler::new(cde.bbox(), item);
40
41 for i in 0..uni_sample_budget {
42 let d_transf = bin_sampler.sample(rng);
43 let transf = d_transf.compose();
44 if !cde.surrogate_collides(surrogate, &transf, filter) {
45 buffer.transform_from(&item.shape_cd, &transf);
47 let cost = LBFLoss::from_shape(&buffer);
48
49 let worth_testing = match (best.as_ref(), &cost) {
51 (Some((_, best_cost)), cost) => {
52 cost.partial_cmp(best_cost).unwrap() == Ordering::Less
53 }
54 (None, _) => true,
55 };
56
57 if worth_testing && !cde.poly_collides(&buffer, filter) {
58 debug!("[UNI: {i}/{uni_sample_budget}] better: {} ", &d_transf);
60
61 best = Some((d_transf, cost));
62
63 let tightened_sampling_bbox = cost.tighten_sample_bbox(bin_sampler.bbox);
64 bin_sampler = UniformRectSampler::new(tightened_sampling_bbox, item);
65 }
66 }
67 }
68
69 *sample_counter += uni_sample_budget;
70
71 let (best_sample, best_cost) = best.as_mut()?;
73
74 let mut ls_sampler = LSSampler::from_defaults(item, *best_sample, cde.bbox());
81
82 for i in 0..ls_sample_budget {
83 let d_transf = ls_sampler.sample(rng);
84 let transf = d_transf.compose();
85 if !cde.surrogate_collides(surrogate, &transf, filter) {
86 buffer.transform_from(&item.shape_cd, &transf);
87 let cost = LBFLoss::from_shape(&buffer);
88
89 let worth_testing = cost < *best_cost;
91
92 if worth_testing && !cde.poly_collides(&buffer, filter) {
93 ls_sampler.shift_mean(d_transf);
95 debug!("[LS: {i}/{ls_sample_budget}] better: {}", &d_transf);
96 (*best_sample, *best_cost) = (d_transf, cost);
97 }
98 }
99 let progress_pct = i as f32 / ls_sample_budget as f32;
100 ls_sampler.decay_stddev(progress_pct);
101 }
102
103 *sample_counter += ls_sampler.n_samples;
104
105 best
106}
107
108pub fn item_placement_order(instance: &impl Instance) -> Vec<usize> {
109 instance
111 .items()
112 .sorted_by_key(|item| Reverse(OrderedFloat(item.shape_cd.diameter)))
113 .map(|item| item.id)
114 .collect_vec()
115}