diff --git a/README.md b/README.md
index 280c6d5..d010f8a 100644
--- a/README.md
+++ b/README.md
@@ -1,32 +1,46 @@
# Jagua-rs [![Rust CI](https://github.com/JeroenGar/jagua-rs/actions/workflows/rust.yml/badge.svg?branch=main)](https://github.com/JeroenGar/jagua-rs/actions/workflows/rust.yml)[![Docs](https://github.com/JeroenGar/jagua-rs/actions/workflows/doc.yml/badge.svg)](https://jeroengar.github.io/jagua-rs-docs/jagua_rs/)
+
### A fast and fearless Collision Detection Engine for 2D irregular Cutting and Packing problems
## Preamble
-2D irregular cutting and packing (C&P) problems are a class of optimization problems that involve placing irregular shaped items into containers in an efficient way.
+
+2D irregular cutting and packing (C&P) problems are a class of optimization problems that involve placing irregular
+shaped items into containers in an efficient way.
These problems contain two distinct challenges:
- * **Combinatorial**: deciding which items to place in which configuration in order to optimize some objective function.
- * **Geometric**: determining the feasibility of a placement. Does the item fit in the container? Does it not collide with other items?
-`jagua-rs` decouples these challenges by providing a Collision Detection Engine (CDE) that efficiently deals with the geometric challenge.
-By making use of `jagua-rs`, you can confidently focus on the combinatorial aspects without having to worry about sufficiently fast feasibility checks.
+* **Combinatorial**: deciding which items to place in which configuration in order to optimize some objective function.
+* **Geometric**: determining the feasibility of a placement. Does the item fit in the container? Does it not collide
+ with other items?
+
+This project aim to decouples these challenges by providing a Collision Detection Engine (CDE) that deals with the
+geometric aspects of the problem.
+`jagua-rs` enables you to confidently focus on the combinatorial aspects of the problem, without having to worry about
+the geometric feasibility of the placements.
+The speed at which these *feasibility checks* can be resolved is of paramount importance, defining the design
+constraints of the optimization algorithms that rely on it.
-A reference implementation of an optimization algorithm built on top of `jagua-rs` is also provided in the form of the `lbf` crate.
+`jagua-rs` can be used as-is (as a library) to build your own optimization algorithm on top of it.
+Or it can be used as the starting point for building a custom CDE tailored to your specific problem variant and use case.
+
+We also provide a reference implementation of an optimization algorithm built on top of `jagua-rs` in the `lbf` crate.
## `jagua-rs` 🐆
-`jagua-rs` incorporates all components required to create an **easily manipulable internal representation** of 2D irregular C&P problems.
-It also boasts a powerful **Collision Detection Engine (CDE)** that can efficiently determine whether an item can fit at a specific position without *colliding* with anything.
-The speed at which these *feasibility checks* can be resolved is of paramount importance, defining the design constraints of the optimization algorithms that rely on it.
+
+`jagua-rs` incorporates all components required to create an **easily manipulable internal representation** of 2D
+irregular C&P problems.
+It also boasts a powerful **Collision Detection Engine (CDE)** which determines whether an item can fit at a specific
+position without causing any *collisions*.
### Design Goals
-- **Currently supports:**
- - [x] Bin- and strip-packing problems
- - [x] Both irregular shaped items and bins
- - [x] Continuous rotation and translation
- - [x] Holes and quality zones in the bin
+
+- **Performant:**
+ - [x] Focus on maximum performance, both in terms of query resolution and update speed
+ - [x] Able to resolve millions of collision queries per second
+ - [x] Contains preprocessor to simplify polygons
- **Robust:**
- - [x] Uses the polygon representation of items and bins, and mimics the results of a simple trigonometric approach
+ - [x] Uses the polygon representation shapes and mimics the results of a basic trigonometric approach
- [x] Special care is taken to handle floating-point arithmetic edge cases
- [x] Written in pure Rust 🦀
- **Adaptable:**
@@ -34,18 +48,24 @@ The speed at which these *feasibility checks* can be resolved is of paramount im
- [x] Add extra constraints by creating new `Hazards` and `HazardFilters`
- [x] `Hazards`: consolidating all spatial constraints into a single model
- [x] `HazardFilters`: excluding specific `Hazards` on a per-query basis
-- **Performant:**
- - [x] Focus on maximum `query` and `update` performance
- - [x] Able to resolve millions of collision queries per second
- - [x] Contains preprocessor to simplify polygons
+- **Currently supports:**
+ - [x] Bin- & strip-packing problems
+ - [x] Irregular shaped items & bins
+ - [x] Continuous rotation & translation
+ - [x] Holes and quality zones in the bin
## `lbf` ↙️
+
The `lbf` crate contains a reference implementation of an optimization algorithm built on top of `jagua-rs`.
-It is a simple left-bottom-fill heuristic, which places the items one-by-one in the bin, each time at the left-bottom most position.
-It should provide a good starting point for anyone interested building their own optimization algorithm on top of `jagua-rs`.
+It is a simple left-bottom-fill heuristic, which places the items one-by-one in the bin, each time at the left-bottom
+most position.
+It should provide a good starting point for anyone interested building their own optimization algorithm on top
+of `jagua-rs`.
### How to run
+
General usage:
+
```bash
cd lbf
cargo run --release -- \
@@ -56,6 +76,7 @@ cargo run --release -- \
```
Concrete example:
+
```bash
cd lbf
cargo run --release -- \
@@ -69,7 +90,9 @@ cargo run --release -- \
The [assets](assets) folder contains a set of input files from the academic literature that were converted to the
same JSON structure.
-The files are also available in the [OR-Datasets repository](https://github.com/Oscar-Oliveira/OR-Datasets/tree/master/Cutting-and-Packing/2D-Irregular) by Oscar Oliveira.
+The files are also available in
+the [OR-Datasets repository](https://github.com/Oscar-Oliveira/OR-Datasets/tree/master/Cutting-and-Packing/2D-Irregular)
+by Oscar Oliveira.
### Solution
@@ -79,7 +102,8 @@ Two types of files are written in the solution folder:
#### JSON
The solution JSON is similar to the input JSON, but with the addition of the `Solution` key at the top level.
-It contains all information required to recreate the solution, including the containers used, the placements of the items and some additional stats.
+It contains all information required to recreate the solution, including the containers used, the placements of the
+items and some additional stats.
#### SVG
@@ -88,9 +112,8 @@ By default, just the container and placed inside it are drawn.
Optionally the quadtree, hazard proximity grid or fail-fast surrogates can be drawn on top.
This can be configured in the config file.
-See [docs](https://jeroengar.github.io/jagua-rs-docs/lbf/io/svg_util/struct.SvgDrawOptions.html) for all available options.
-
-
+See [docs](https://jeroengar.github.io/jagua-rs-docs/lbf/io/svg_util/struct.SvgDrawOptions.html) for all available
+options.
*Note: Unfortunately, the SVG standard does not support strokes drawn purely inside (or outside) shapes.
Items might therefore sometimes falsely appear to be (very slightly) colliding in the SVG visualizations.*
@@ -120,31 +143,38 @@ The configuration file is a JSON file with the following structure:
"tolerance": 0.001 //Polygons will be simplified until they deviate at most 0.1% from their original area.
}
},
- "deterministic_mode": true, //The heuristic will always produce the same solution for the same input and configuration
+ "prng_seed": 0, //Seed for the pseudo-random number generator. If not defined the outcome will be non-deterministic
"n_samples_per_item": 5000, //5000 placement samples will be queried per item.
"ls_samples_fraction": 0.2 //Of those 5000, 80% will be sampled at uniformly at random, 20% will be local search samples
}
```
-See [docs](https://jeroengar.github.io/jagua-rs-docs/lbf/config/struct.Config.html) for a detailed description of all available options.
+
+See [docs](https://jeroengar.github.io/jagua-rs-docs/lbf/config/struct.Config.html) for a detailed description of all
+available options.
### Important note
-Due to `lbf` being a one-pass constructive heuristic, the final solution quality is extremely *chaotic*. \
-Meaning that minute changes in the flow (sorting of the items, configuration, prng seed...) lead to solutions with drastically different quality. \
-Seemingly superior configurations (such as increased `n_samples_per_item`), for example, can result in worse solutions and vice versa. \
-Testing with `deterministic_mode` set to `false` will demonstrate this spread in solution quality.
+Due to `lbf` being a one-pass constructive heuristic, the final solution quality is very *chaotic*. \
+Meaning that minute changes in the flow of the algorithm (sorting of the items, configuration, prng seed...) lead to solutions with
+drastically different quality. \
+Seemingly superior configurations (such as increased `n_samples_per_item`), for example, can result in worse solutions
+and vice versa. \
+Setting `prng_seed: null` will demonstrate this spread in solution quality.
-**Once again, this heuristic should only serve as a reference implementation of how to use `jagua-rs` and not as a reliable optimization algorithm for any real-world problems.**
+**Once again, this heuristic should only serve as a reference implementation of how to use `jagua-rs` and not as a
+reliable optimization algorithm for any real-world problems.**
## Testing
The `jagua-rs` codebase contains a suite of assertion checks which verify the correctness of the engine.
-These `debug_asserts` are enabled by default in debug builds and tests but are omitted in release builds to maximize performance.
+These `debug_asserts` are enabled by default in debug builds and tests but are omitted in release builds to maximize
+performance.
Additionally, `lbf` contains some basic integration tests to validate the correctness of the engine on a macro level.
It basically runs the heuristic on a set of input files with multiple configurations with assertions enabled.
To run the tests, use:
+
```bash
cd lbf
cargo test
@@ -161,9 +191,12 @@ Alternatively, you can compile and view the docs locally with `cargo doc --open`
## Acknowledgements
-This project was funded by [Research Foundation - Flanders (FWO)](https://www.fwo.be/en/) (grant number: 1S71222N)
+This project and funded by [Research Foundation - Flanders (FWO)](https://www.fwo.be/en/) (grant number: 1S71222N) and
+developed at [KU Leuven](https://www.kuleuven.be/english/).
-
+
+
+
## License
diff --git a/assets/config_lbf.json b/assets/config_lbf.json
index c502577..cd87998 100644
--- a/assets/config_lbf.json
+++ b/assets/config_lbf.json
@@ -15,7 +15,7 @@
"tolerance": 0.001
}
},
- "deterministic_mode": true,
+ "prng_seed": 0,
"n_samples_per_item": 5000,
"ls_samples_fraction": 0.2
}
diff --git a/jagua-rs/src/entities/bin.rs b/jagua-rs/src/entities/bin.rs
index 6d1bcd5..d79ebbb 100644
--- a/jagua-rs/src/entities/bin.rs
+++ b/jagua-rs/src/entities/bin.rs
@@ -5,8 +5,8 @@ use itertools::Itertools;
use crate::collision_detection::cd_engine::CDEngine;
use crate::collision_detection::hazard::Hazard;
use crate::collision_detection::hazard::HazardEntity;
-use crate::entities::quality_zone::QualityZone;
use crate::entities::quality_zone::N_QUALITIES;
+use crate::entities::quality_zone::QualityZone;
use crate::geometry::geo_traits::Shape;
use crate::geometry::primitives::aa_rectangle::AARectangle;
use crate::geometry::primitives::simple_polygon::SimplePolygon;
diff --git a/jagua-rs/src/entities/problems/strip_packing.rs b/jagua-rs/src/entities/problems/strip_packing.rs
index 13b994a..2e2b58f 100644
--- a/jagua-rs/src/entities/problems/strip_packing.rs
+++ b/jagua-rs/src/entities/problems/strip_packing.rs
@@ -10,8 +10,8 @@ use crate::entities::instances::strip_packing::SPInstance;
use crate::entities::layout::Layout;
use crate::entities::placed_item::PlacedItemUID;
use crate::entities::placing_option::PlacingOption;
-use crate::entities::problems::problem_generic::private::ProblemGenericPrivate;
use crate::entities::problems::problem_generic::LayoutIndex;
+use crate::entities::problems::problem_generic::private::ProblemGenericPrivate;
use crate::entities::problems::problem_generic::ProblemGeneric;
use crate::entities::solution::Solution;
use crate::geometry::geo_traits::{Shape, Transformable};
diff --git a/lbf/benches/edge_sensitivity_bench.rs b/lbf/benches/edge_sensitivity_bench.rs
index 5e7facf..b7c2f58 100644
--- a/lbf/benches/edge_sensitivity_bench.rs
+++ b/lbf/benches/edge_sensitivity_bench.rs
@@ -5,22 +5,22 @@ use std::path::Path;
use criterion::measurement::WallTime;
use criterion::{criterion_group, criterion_main, BenchmarkGroup, BenchmarkId, Criterion};
use itertools::Itertools;
+use rand::prelude::SmallRng;
+use rand::SeedableRng;
+
use jagua_rs::entities::instances::bin_packing::BPInstance;
use jagua_rs::entities::instances::instance::Instance;
use jagua_rs::entities::instances::instance_generic::InstanceGeneric;
use jagua_rs::entities::instances::strip_packing::SPInstance;
-use rand::prelude::SmallRng;
-use rand::SeedableRng;
-
use jagua_rs::entities::item::Item;
use jagua_rs::entities::problems::problem_generic::{LayoutIndex, ProblemGeneric};
use jagua_rs::geometry::geo_traits::{Shape, TransformableFrom};
use jagua_rs::geometry::primitives::point::Point;
use jagua_rs::geometry::primitives::simple_polygon::SimplePolygon;
use jagua_rs::io::json_instance::JsonInstance;
-use lbf::config::Config;
use lbf::io;
use lbf::io::svg_util::SvgDrawOptions;
+use lbf::lbf_config::LBFConfig;
use lbf::samplers::hpg_sampler::HPGSampler;
use crate::util::{N_ITEMS_REMOVED, SWIM_PATH};
@@ -54,7 +54,7 @@ fn edge_sensitivity_bench_with_ff(c: &mut Criterion) {
edge_sensitivity_bench(config, group);
}
-fn edge_sensitivity_bench(config: Config, mut g: BenchmarkGroup) {
+fn edge_sensitivity_bench(config: LBFConfig, mut g: BenchmarkGroup) {
let json_instance: JsonInstance =
serde_json::from_reader(BufReader::new(File::open(SWIM_PATH).unwrap())).unwrap();
@@ -142,7 +142,7 @@ fn edge_sensitivity_bench(config: Config, mut g: BenchmarkGroup) {
g.finish();
}
-fn modify_instance(instance: &Instance, multiplier: usize, config: Config) -> Instance {
+fn modify_instance(instance: &Instance, multiplier: usize, config: LBFConfig) -> Instance {
let modified_items = instance
.items()
.iter()
diff --git a/lbf/benches/util.rs b/lbf/benches/util.rs
index 4d4b7e1..7f6158c 100644
--- a/lbf/benches/util.rs
+++ b/lbf/benches/util.rs
@@ -14,9 +14,9 @@ use jagua_rs::io::json_instance::JsonInstance;
use jagua_rs::io::parser::Parser;
use jagua_rs::util::config::{CDEConfig, SPSurrogateConfig};
use jagua_rs::util::polygon_simplification::PolySimplConfig;
-use lbf::config::Config;
use lbf::io;
use lbf::io::svg_util::SvgDrawOptions;
+use lbf::lbf_config::LBFConfig;
use lbf::lbf_optimizer::LBFOptimizer;
pub const SWIM_PATH: &str = "../assets/swim.json";
@@ -36,7 +36,7 @@ pub fn create_instance(
/// Simulates a common scenario in iterative optimization algorithms: dense packing with a few items removed
pub fn create_blf_problem(
instance: Instance,
- config: Config,
+ config: LBFConfig,
n_items_removed: usize,
) -> (SPProblem, Vec) {
assert!(matches!(&instance, &Instance::SP(_)));
@@ -90,8 +90,8 @@ pub fn create_blf_problem(
(problem, removed_pi_uids)
}
-pub fn create_base_config() -> Config {
- Config {
+pub fn create_base_config() -> LBFConfig {
+ LBFConfig {
cde_config: CDEConfig {
quadtree_depth: 5,
hpg_n_cells: 2000,
@@ -103,7 +103,7 @@ pub fn create_base_config() -> Config {
},
},
poly_simpl_config: PolySimplConfig::Disabled,
- deterministic_mode: true,
+ prng_seed: Some(0),
n_samples_per_item: 5000,
ls_samples_fraction: 0.2,
svg_draw_options: Default::default(),
diff --git a/lbf/src/io/json_output.rs b/lbf/src/io/json_output.rs
index ed6a102..48443c4 100644
--- a/lbf/src/io/json_output.rs
+++ b/lbf/src/io/json_output.rs
@@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
use jagua_rs::io::json_instance::JsonInstance;
use jagua_rs::io::json_solution::JsonSolution;
-use crate::config::Config;
+use crate::lbf_config::LBFConfig;
#[derive(Serialize, Deserialize, Clone)]
#[serde(rename_all = "PascalCase")]
@@ -11,5 +11,5 @@ pub struct JsonOutput {
#[serde(flatten)]
pub instance: JsonInstance,
pub solution: JsonSolution,
- pub config: Config,
+ pub config: LBFConfig,
}
diff --git a/lbf/src/config.rs b/lbf/src/lbf_config.rs
similarity index 78%
rename from lbf/src/config.rs
rename to lbf/src/lbf_config.rs
index 1a48dba..8f732e8 100644
--- a/lbf/src/config.rs
+++ b/lbf/src/lbf_config.rs
@@ -7,11 +7,13 @@ use crate::io::svg_util::SvgDrawOptions;
/// Configuration for the LBF optimizer
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
-pub struct Config {
+pub struct LBFConfig {
+ /// Configuration of the Collision Detection Engine
pub cde_config: CDEConfig,
+ /// Configuration of the polygon simplification in preprocessing
pub poly_simpl_config: PolySimplConfig,
- /// Fixes the seed for the random number generator, resulting in deterministic behavior.
- pub deterministic_mode: bool,
+ /// Seed for the PRNG. If not defined, the algorithm will run in non-deterministic mode using entropy
+ pub prng_seed: Option,
/// Total number of samples per item
pub n_samples_per_item: usize,
/// Fraction of the samples used for the local search sampler
@@ -20,7 +22,7 @@ pub struct Config {
pub svg_draw_options: SvgDrawOptions,
}
-impl Default for Config {
+impl Default for LBFConfig {
fn default() -> Self {
Self {
cde_config: CDEConfig {
@@ -34,7 +36,7 @@ impl Default for Config {
},
},
poly_simpl_config: PolySimplConfig::Enabled { tolerance: 0.001 },
- deterministic_mode: true,
+ prng_seed: Some(0),
n_samples_per_item: 5000,
ls_samples_fraction: 0.2,
svg_draw_options: SvgDrawOptions::default(),
diff --git a/lbf/src/lbf_cost.rs b/lbf/src/lbf_cost.rs
index fee89ca..5e5ee26 100644
--- a/lbf/src/lbf_cost.rs
+++ b/lbf/src/lbf_cost.rs
@@ -5,11 +5,11 @@ use jagua_rs::geometry::primitives::simple_polygon::SimplePolygon;
const X_MULTIPLIER: f64 = 10.0;
-/// The cost LBF assigns to a placing option.
-/// A pure lexicographic comparison would lead to weird results due to continuous values,
-// instead we opted for a weighted sum of the x_max and y_max of the shape,
-// with the horizontal dimension being more important.
-#[derive(PartialEq, PartialOrd, Copy, Clone, Debug)]
+/// The cost LBF assigned to a placing option.
+/// Weighted sum of the x_max and y_max of the shape, with the horizontal dimension being more important.
+///
+/// A pure lexicographic comparison (always prioritizing x-axis) would lead to undesirable results due to the continuous nature of the values.
+#[derive(PartialEq, PartialOrd, Copy, Clone, Debug, Eq, Ord)]
pub struct LBFPlacingCost(NotNan);
impl LBFPlacingCost {
diff --git a/lbf/src/lbf_optimizer.rs b/lbf/src/lbf_optimizer.rs
index e145245..7debf90 100644
--- a/lbf/src/lbf_optimizer.rs
+++ b/lbf/src/lbf_optimizer.rs
@@ -22,7 +22,7 @@ use jagua_rs::geometry::convex_hull::convex_hull_from_points;
use jagua_rs::geometry::geo_traits::{Shape, TransformableFrom};
use jagua_rs::geometry::primitives::simple_polygon::SimplePolygon;
-use crate::config::Config;
+use crate::lbf_config::LBFConfig;
use crate::lbf_cost::LBFPlacingCost;
use crate::samplers::hpg_sampler::HPGSampler;
use crate::samplers::ls_sampler::LSSampler;
@@ -33,13 +33,13 @@ pub const ITEM_LIMIT: usize = usize::MAX;
pub struct LBFOptimizer {
pub instance: Instance,
pub problem: Problem,
- pub config: Config,
+ pub config: LBFConfig,
/// SmallRng is a fast, non-cryptographic PRNG
pub rng: SmallRng,
}
impl LBFOptimizer {
- pub fn new(instance: Instance, config: Config, rng: SmallRng) -> Self {
+ pub fn new(instance: Instance, config: LBFConfig, rng: SmallRng) -> Self {
assert!(config.n_samples_per_item > 0);
let problem = match instance.clone() {
Instance::BP(bpi) => BPProblem::new(bpi.clone()).into(),
@@ -127,7 +127,7 @@ impl LBFOptimizer {
pub fn find_lbf_placement(
problem: &Problem,
item: &Item,
- config: &Config,
+ config: &LBFConfig,
rng: &mut impl Rng,
) -> Option {
//search all existing layouts and template layouts with remaining stock
@@ -147,7 +147,7 @@ pub fn sample_layout(
problem: &Problem,
l_index: LayoutIndex,
item: &Item,
- config: &Config,
+ config: &LBFConfig,
rng: &mut impl Rng,
) -> Option {
let layout: &Layout = problem.get_layout(&l_index);
@@ -209,7 +209,7 @@ pub fn sample_layout(
*/
let mut ls_sampler =
- LSSampler::from_default_stddevs(item, &best_opt.d_transform, &layout.bin().bbox());
+ LSSampler::from_defaults(item, &best_opt.d_transform, &layout.bin().bbox());
for i in 0..n_ls_samples {
let transform = ls_sampler.sample(rng);
diff --git a/lbf/src/lib.rs b/lbf/src/lib.rs
index e965ce7..ca8fe52 100644
--- a/lbf/src/lib.rs
+++ b/lbf/src/lib.rs
@@ -3,8 +3,8 @@ use std::time::Instant;
use mimalloc::MiMalloc;
use once_cell::sync::Lazy;
-pub mod config;
pub mod io;
+pub mod lbf_config;
pub mod lbf_cost;
pub mod lbf_optimizer;
pub mod samplers;
diff --git a/lbf/src/main.rs b/lbf/src/main.rs
index f243acd..ecc2721 100644
--- a/lbf/src/main.rs
+++ b/lbf/src/main.rs
@@ -10,10 +10,10 @@ use rand::SeedableRng;
use jagua_rs::io::parser;
use jagua_rs::io::parser::Parser;
-use lbf::config::Config;
use lbf::io::cli::Cli;
use lbf::io::json_output::JsonOutput;
use lbf::io::layout_to_svg::s_layout_to_svg;
+use lbf::lbf_config::LBFConfig;
use lbf::lbf_optimizer::LBFOptimizer;
use lbf::{io, EPOCH};
@@ -26,9 +26,9 @@ fn main() {
warn!("No config file provided, use --config-file to provide a custom config");
warn!(
"Falling back default config:\n{}",
- serde_json::to_string(&Config::default()).unwrap()
+ serde_json::to_string(&LBFConfig::default()).unwrap()
);
- Config::default()
+ LBFConfig::default()
}
Some(config_file) => {
let file = File::open(config_file).unwrap_or_else(|err| {
@@ -47,9 +47,9 @@ fn main() {
let parser = Parser::new(config.poly_simpl_config, config.cde_config, true);
let instance = parser.parse(&json_instance);
- let rng = match config.deterministic_mode {
- true => SmallRng::seed_from_u64(0),
- false => SmallRng::from_entropy(),
+ let rng = match config.prng_seed {
+ Some(seed) => SmallRng::seed_from_u64(seed),
+ None => SmallRng::from_entropy(),
};
let mut optimizer = LBFOptimizer::new(instance.clone(), config, rng);
diff --git a/lbf/src/samplers/ls_sampler.rs b/lbf/src/samplers/ls_sampler.rs
index bdb00fa..2247aee 100644
--- a/lbf/src/samplers/ls_sampler.rs
+++ b/lbf/src/samplers/ls_sampler.rs
@@ -11,30 +11,31 @@ use jagua_rs::geometry::transformation::Transformation;
use crate::samplers::rotation_distr::NormalRotDistr;
-const TRANSL_START_FRAC: f64 = 0.01;
-const TRANSL_END_FRAC: f64 = 0.001;
-const ROT_START_FRAC: f64 = 2.0 * (PI / 180.0);
-const ROT_END_FRAC: f64 = 0.5 * (PI / 180.0);
+/// The stddev of translation starts at 1% and ends at 0.05% of the largest dimension of the bounding box.
+pub const SD_TRANSL: (f64, f64) = (0.01, 0.0005);
+
+/// The stddev of rotation starts at 2° and ends at 0.5°.
+pub const SD_ROT: (f64, f64) = (2.0 * PI / 180.0, 0.5 * PI / 180.0);
pub struct LSSampler {
normal_x: Normal,
normal_y: Normal,
normal_r: NormalRotDistr,
- stddev_transl: f64,
- stddev_rot: f64,
- stddev_transl_range: (f64, f64),
- stddev_rot_range: (f64, f64),
+ sd_transl: f64,
+ sd_rot: f64,
+ sd_transl_range: (f64, f64),
+ sd_rot_range: (f64, f64),
}
impl LSSampler {
pub fn new(
item: &Item,
ref_transform: &DTransformation,
- stddev_transl_range: (f64, f64),
- stddev_rot_range: (f64, f64),
+ sd_transl_range: (f64, f64),
+ sd_rot_range: (f64, f64),
) -> Self {
- let stddev_transl = stddev_transl_range.0;
- let stddev_rot = stddev_rot_range.0;
+ let stddev_transl = sd_transl_range.0;
+ let stddev_rot = sd_rot_range.0;
let normal_x = Normal::new(ref_transform.translation().0, stddev_transl).unwrap();
let normal_y = Normal::new(ref_transform.translation().1, stddev_transl).unwrap();
@@ -44,39 +45,36 @@ impl LSSampler {
normal_x,
normal_y,
normal_r,
- stddev_transl,
- stddev_rot,
- stddev_transl_range,
- stddev_rot_range,
+ sd_transl: stddev_transl,
+ sd_rot: stddev_rot,
+ sd_transl_range,
+ sd_rot_range,
}
}
- pub fn from_default_stddevs(
- item: &Item,
- ref_transform: &DTransformation,
- bbox: &AARectangle,
- ) -> Self {
+ /// Creates a new sampler with default standard deviation ranges: [SD_TRANSL] and [SD_ROT].
+ pub fn from_defaults(item: &Item, ref_transform: &DTransformation, bbox: &AARectangle) -> Self {
let max_dim = f64::max(bbox.width(), bbox.height());
- let stddev_transl_range = (max_dim * TRANSL_START_FRAC, max_dim * TRANSL_END_FRAC);
- let stddev_rot_range = (ROT_START_FRAC, ROT_END_FRAC);
- Self::new(item, ref_transform, stddev_transl_range, stddev_rot_range)
+ let stddev_transl_range = (SD_TRANSL.0 * max_dim, SD_TRANSL.1 * max_dim);
+ Self::new(item, ref_transform, stddev_transl_range, SD_ROT)
}
/// 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();
+ self.normal_x = Normal::new(ref_transform.translation().0, self.sd_transl).unwrap();
+ self.normal_y = Normal::new(ref_transform.translation().1, self.sd_transl).unwrap();
self.normal_r.set_mean(ref_transform.rotation());
}
+ /// Sets the standard deviation of the normal distributions.
pub fn set_stddev(&mut self, stddev_transl: f64, stddev_rot: f64) {
assert!(stddev_transl >= 0.0 && stddev_rot >= 0.0);
- self.stddev_transl = stddev_transl;
- self.stddev_rot = stddev_rot;
- self.normal_x = Normal::new(self.normal_x.mean(), self.stddev_transl).unwrap();
- self.normal_y = Normal::new(self.normal_y.mean(), self.stddev_transl).unwrap();
- self.normal_r.set_stddev(self.stddev_rot);
+ self.sd_transl = stddev_transl;
+ self.sd_rot = stddev_rot;
+ self.normal_x = Normal::new(self.normal_x.mean(), self.sd_transl).unwrap();
+ self.normal_y = Normal::new(self.normal_y.mean(), self.sd_transl).unwrap();
+ self.normal_r.set_stddev(self.sd_rot);
}
/// Adjusts the standard deviation according to the fraction of samples that have passed,
@@ -89,11 +87,12 @@ impl LSSampler {
pub fn decay_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),
- calc_stddev(self.stddev_rot_range, progress_pct),
+ calc_stddev(self.sd_transl_range, progress_pct),
+ calc_stddev(self.sd_rot_range, progress_pct),
);
}
+ /// Samples a transformation from the distribution.
pub fn sample(&self, rng: &mut impl Rng) -> Transformation {
Transformation::from_rotation(self.normal_r.sample(rng))
.translate((self.normal_x.sample(rng), self.normal_y.sample(rng)))
diff --git a/lbf/tests/tests.rs b/lbf/tests/tests.rs
index cd7559f..a6b1882 100644
--- a/lbf/tests/tests.rs
+++ b/lbf/tests/tests.rs
@@ -10,8 +10,8 @@ mod tests {
use jagua_rs::entities::problems::problem_generic::LayoutIndex;
use jagua_rs::entities::problems::problem_generic::ProblemGeneric;
use jagua_rs::io::parser::Parser;
- use lbf::config::Config;
use lbf::io;
+ use lbf::lbf_config::LBFConfig;
use lbf::lbf_optimizer::LBFOptimizer;
const N_ITEMS_TO_REMOVE: usize = 5;
@@ -29,7 +29,7 @@ mod tests {
fn test_instance(instance_path: &str) {
let instance = Path::new(instance_path);
// parse the instance
- let mut config = Config::default();
+ let mut config = LBFConfig::default();
config.n_samples_per_item = 100;
let json_instance = io::read_json_instance(&instance);
let parser = Parser::new(config.poly_simpl_config, config.cde_config, true);