Skip to content

Commit

Permalink
lbf improved readability
Browse files Browse the repository at this point in the history
  • Loading branch information
JeroenGar committed Feb 25, 2024
1 parent 5476c6f commit e3ed2d9
Show file tree
Hide file tree
Showing 10 changed files with 78 additions and 156,399 deletions.
8,826 changes: 0 additions & 8,826 deletions assets/Baldacci/Scarpa.json

This file was deleted.

71,615 changes: 0 additions & 71,615 deletions assets/Baldacci/Test10.json

This file was deleted.

1,781 changes: 0 additions & 1,781 deletions assets/Baldacci/Test7.json

This file was deleted.

764 changes: 0 additions & 764 deletions assets/Baldacci/Test8.json

This file was deleted.

73,353 changes: 0 additions & 73,353 deletions assets/Baldacci/Test9.json

This file was deleted.

8 changes: 6 additions & 2 deletions lbf/src/lbf_cost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ const X_MULTIPLIER: f64 = 10.0;
pub struct LBFPlacingCost(NotNan<f64>);

impl LBFPlacingCost {
pub fn from_shape(shape: &SimplePolygon) -> Self {
let cost = shape.bbox().x_max * X_MULTIPLIER + shape.bbox().y_max;
pub fn new(x_max: f64, y_max: f64) -> Self {
let cost = x_max * X_MULTIPLIER + y_max;
LBFPlacingCost(NotNan::new(cost).expect("cost is NaN"))
}

pub fn from_shape(shape: &SimplePolygon) -> Self {
LBFPlacingCost::new(shape.bbox().x_max, shape.bbox().y_max)
}
}
8 changes: 4 additions & 4 deletions lbf/src/lbf_optimizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,8 @@ pub fn sample_layout(
if worth_testing && !cde.shape_collides(&buffer, &irrel_hazards) {
//sample is valid and improves on the current best
let p_opt = PlacingOption::from_transform(l_index, item.id, transform);
sampler.tighten_x_bound(buffer.bbox().x_max);
debug!("[ls: {i}/{n_uni_samples}] better: {} ", &p_opt.d_transform);
sampler.tighten(cost);
debug!("[UNI: {i}/{n_uni_samples}] better: {} ", &p_opt.d_transform);

best = Some((p_opt, cost));
}
Expand Down Expand Up @@ -220,12 +220,12 @@ pub fn sample_layout(
//sample is valid and improves on the current best
let p_opt = PlacingOption::from_transform(l_index, item.id, transform);
ls_sampler.shift_mean(&p_opt.d_transform);
debug!("[ls: {i}/{n_ls_samples}] better: {}", p_opt.d_transform);
debug!("[LS: {i}/{n_ls_samples}] better: {}", p_opt.d_transform);
(*best_opt, *best_cost) = (p_opt, cost);
}
}
let progress_pct = i as f64 / n_ls_samples as f64;
ls_sampler.evolve_stddev(progress_pct);
ls_sampler.adjust_stddev(progress_pct);
}
}

Expand Down
99 changes: 59 additions & 40 deletions lbf/src/samplers/hpg_sampler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,58 +9,68 @@ use jagua_rs::geometry::geo_traits::Shape;
use jagua_rs::geometry::primitives::aa_rectangle::AARectangle;
use jagua_rs::geometry::transformation::Transformation;

use crate::lbf_cost::LBFPlacingCost;
use crate::samplers::uniform_rect_sampler::UniformAARectSampler;

/// Creates transformation samples for a given item.
/// Samples from the Hazard Proximity Grid, but only cells which could accommodate the item.
/// Creates `Transformation` samples for a given item.
/// Samples from the Hazard Proximity Grid uniformly, but only cells which could accommodate the item.
/// Cells were a collision is guaranteed are discarded.
pub struct HPGSampler<'a> {
pub item: &'a Item,
pub cell_samplers: Vec<UniformAARectSampler>,
pub x_bound: f64,
pub cost_bound: LBFPlacingCost,
pub pretransform: Transformation,
pub coverage_area: f64,
pub bin_bbox_area: f64,
}

impl<'a> HPGSampler<'a> {
pub fn new(item: &'a Item, layout: &Layout) -> Option<HPGSampler<'a>> {
let poi = &item.shape.poi;
let bin_bbox = layout.bin().bbox();

let hpg = layout.cde().haz_prox_grid().unwrap();

//create a pre-transformation which centers the shape around its Pole of Inaccessibility.
let pretransform = Transformation::from_translation((-poi.center.0, -poi.center.1));

//collect all eligible cells from the Hazard Proximity Grid
let cell_samplers = hpg
.grid
.cells
.iter()
.flatten()
.filter(|cell| cell.could_accommodate_item(item))
.map(|cell| {
let hpg = layout.cde().haz_prox_grid().unwrap();
let all_cells = hpg.grid.cells.iter().flatten();
let eligible_cells = all_cells.filter(|c| c.could_accommodate_item(item));

//create samplers for all eligible cells
let cell_samplers = eligible_cells
.map(|c| {
//map each eligible cell to a rectangle sampler, bounded by the layout's bbox.
//(with low densities, the cells can extend significantly beyond the layout's bbox)
let sampler_bbox = AARectangle::from_intersection(cell.bbox(), &bin_bbox)
.expect("cell should at least partially intersect with bin bbox");
UniformAARectSampler::new(sampler_bbox, item)
//(at low densities, the cells could extend significantly beyond the layout's bbox)
AARectangle::from_intersection(c.bbox(), &bin_bbox)
.expect("cell should at least partially intersect with bin bbox")
})
.map(|bbox| UniformAARectSampler::new(bbox, item))
.collect_vec();

let coverage_area = cell_samplers.iter().map(|s| s.bbox.area()).sum();

let x_bound = layout.bin().bbox().x_max;
let cost_bound = LBFPlacingCost::new(bin_bbox.x_max, bin_bbox.y_max);

match cell_samplers.is_empty() {
true => None,
false => Some(HPGSampler {
item,
cell_samplers,
x_bound,
pretransform,
coverage_area,
}),
true => {
debug!("[HPG] no eligible cells to sample from");
None
}
false => {
debug!(
"[HPGS] created sampler with {} eligible cells, coverage: {:.3}%",
cell_samplers.len(),
coverage_area / bin_bbox.area() * 100.0
);
Some(HPGSampler {
item,
cell_samplers,
cost_bound,
pretransform,
coverage_area,
bin_bbox_area: bin_bbox.area(),
})
}
}
}

Expand All @@ -70,32 +80,41 @@ impl<'a> HPGSampler<'a> {
let cell_sampler = self.cell_samplers.choose(rng).expect("no active samplers");

//from that cell, sample a transformation
let sample = cell_sampler.sample_x_bounded(rng, self.x_bound);
let sample = cell_sampler.sample(rng);

//combine the pretransform with the sampled transformation
self.pretransform.clone().transform_from_decomposed(&sample)
}

pub fn tighten_x_bound(&mut self, x_max: f64) {
/// Removes all cells that cannot possibly generate a `Transformation` which would be better than the current best solution.
/// LBF specific
pub fn tighten(&mut self, best: LBFPlacingCost) {
let poi_rad = self.item.shape.poi.radius;
let new_x_bound = x_max - poi_rad; //we need at least one POI radius of space to the left of the best solution

if new_x_bound < self.x_bound {
debug!(
"tightening x bound from {} to {}",
self.x_bound, new_x_bound
);
if best < self.cost_bound {
//remove all cells that are out of bounds, update the coverage area
self.cell_samplers.retain(|cell_sampler| {
let in_bounds = cell_sampler.bbox.x_min < new_x_bound;

if !in_bounds {
self.coverage_area -= cell_sampler.bbox.area();
//minimum cost that could be achieved by a cell
let min_cost = LBFPlacingCost::new(
cell_sampler.bbox.x_min + poi_rad,
cell_sampler.bbox.y_min + poi_rad,
);

match min_cost < best {
true => true,
false => {
self.coverage_area -= cell_sampler.bbox.area();
false
}
}
in_bounds
});

self.x_bound = new_x_bound
self.cost_bound = best;
debug!(
"[HPGS] tightened sampler to {} cells, coverage: {:.3}%",
self.cell_samplers.len(),
self.coverage_area / self.bin_bbox_area * 100.0
);
}
}
}
13 changes: 9 additions & 4 deletions lbf/src/samplers/ls_sampler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ impl LSSampler {
Self::new(item, ref_transform, stddev_transl_range, stddev_rot_range)
}

/// Shifts the mean of the normal distributions to the given reference transformation.
pub fn shift_mean(&mut self, ref_transform: &DTransformation) {
self.normal_x = Normal::new(ref_transform.translation().0, self.stddev_transl).unwrap();
self.normal_y = Normal::new(ref_transform.translation().1, self.stddev_transl).unwrap();
Expand All @@ -78,10 +79,14 @@ impl LSSampler {
self.normal_r.set_stddev(self.stddev_rot);
}

/// Changes the stddev according to the pct of samples that have passed.
/// `pct` is a value in [0, 1] that represents the percentage of samples that have passed.
/// 0 means the first sample, 1 means the last sample.
pub fn evolve_stddev(&mut self, progress_pct: f64) {
/// Adjusts the standard deviation according to the fraction of samples that have passed,
/// following an exponential decay curve.
/// `progress_pct` is a value in [0, 1].
///
/// f(0) = init;
/// f(1) = end;
/// f(x) = init * (end/init)^x;
pub fn adjust_stddev(&mut self, progress_pct: f64) {
let calc_stddev = |(init, end): (f64, f64), pct: f64| init * (end / init).powf(pct);
self.set_stddev(
calc_stddev(self.stddev_transl_range, progress_pct),
Expand Down
10 changes: 0 additions & 10 deletions lbf/src/samplers/uniform_rect_sampler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,4 @@ impl UniformAARectSampler {

DTransformation::new(r_sample, (x_sample, y_sample))
}

pub fn sample_x_bounded(&self, rng: &mut impl Rng, x_bound: f64) -> DTransformation {
let x_max = f64::min(self.bbox.x_max, x_bound);

let r_sample = self.uniform_r.sample(rng);
let x_sample = Uniform::new(self.bbox.x_min, x_max).sample(rng);
let y_sample = Uniform::new(self.bbox.y_min, self.bbox.y_max).sample(rng);

DTransformation::new(r_sample, (x_sample, y_sample))
}
}

0 comments on commit e3ed2d9

Please sign in to comment.