Skip to content

Commit 0e047b0

Browse files
authored
Merge pull request #107 from JeroenGar/streamline
Every Algorithm from the paper is referenced to its corresponding function
2 parents 5b05c53 + a77e2a4 commit 0e047b0

17 files changed

Lines changed: 56 additions & 42 deletions

Cargo.toml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ name = "bench"
1414
path = "src/bench.rs"
1515

1616
[dependencies]
17-
jagua-rs = { features = ["spp"], git = "https://github.com/JeroenGar/jagua-rs.git", rev = "1fb0824457cea336e65cebdd8b3dfb8c9d88487a"}
17+
jagua-rs = { features = ["spp"], git = "https://github.com/JeroenGar/jagua-rs.git", rev = "8ad43e489a2d24f63cf9a15feae268af9569d65c"}
1818
#jagua-rs = { features = ["spp"], path = "../jagua-rs/jagua-rs" }
1919
rand = "0.9"
2020
rand_distr = "0.5"
@@ -24,19 +24,19 @@ log = { version = "0.4", features = ["release_max_level_info"] }
2424
fern = "0.7"
2525
serde = "1.0"
2626
serde_json = "1.0"
27-
tap = "1.0.1"
27+
tap = "1.0"
2828
slotmap = "1.0"
29-
float-cmp = "0.10.0"
30-
ordered-float = "5.0.0"
31-
rayon = "1.10.0"
32-
numfmt = "1.1.1"
33-
num_cpus = "1.16.0"
29+
float-cmp = "0.10"
30+
ordered-float = "5.0"
31+
rayon = "1.10"
32+
numfmt = "1.1"
33+
num_cpus = "1.17"
3434
jiff = "0.2"
3535
test-case = "3.3"
3636
clap = { version = "4.5", features = ["derive"] }
3737
anyhow = "1.0"
3838
ndarray = "0.16"
39-
rand_xoshiro = "0.7.0"
39+
rand_xoshiro = "0.7"
4040

4141
getrandom = { version = "0.3", features = ["wasm_js"] }
4242

src/eval/lbf_evaluator.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ impl<'a> LBFEvaluator<'a> {
3131
}
3232

3333
impl<'a> SampleEvaluator for LBFEvaluator<'a> {
34-
fn eval(&mut self, dt: DTransformation, _upper_bound: Option<SampleEval>) -> SampleEval {
34+
fn evaluate_sample(&mut self, dt: DTransformation, _upper_bound: Option<SampleEval>) -> SampleEval {
3535
self.n_evals += 1;
3636
let cde = self.layout.cde();
3737
let transf = dt.into();

src/eval/sample_eval.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ impl Eq for SampleEval {}
3838

3939
/// Simple trait for types that can evaluate samples
4040
pub trait SampleEvaluator {
41-
fn eval(&mut self, dt: DTransformation, upper_bound: Option<SampleEval>) -> SampleEval;
41+
fn evaluate_sample(&mut self, dt: DTransformation, upper_bound: Option<SampleEval>) -> SampleEval;
4242

4343
fn n_evals(&self) -> usize;
4444
}

src/eval/sep_evaluator.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,21 @@ impl<'a> SeparationEvaluator<'a> {
3737

3838
impl<'a> SampleEvaluator for SeparationEvaluator<'a> {
3939
/// Evaluates a transformation. An upper bound can be provided to early terminate the process.
40-
fn eval(&mut self, dt: DTransformation, upper_bound: Option<SampleEval>) -> SampleEval {
40+
/// Algorithm 7 from https://doi.org/10.48550/arXiv.2509.13329
41+
fn evaluate_sample(&mut self, dt: DTransformation, upper_bound: Option<SampleEval>) -> SampleEval {
4142
self.n_evals += 1;
4243
let cde = self.layout.cde();
4344

44-
// evals with higher loss than this will always be rejected
45+
//samples which evaluate higher than this will certainly be rejected
4546
let loss_bound = match upper_bound {
4647
Some(SampleEval::Collision { loss }) => loss,
4748
Some(SampleEval::Clear { .. }) => 0.0,
4849
_ => f32::INFINITY,
4950
};
50-
// Reload the detection map for the new query and update the loss bound
51+
//reload the hazard collector to prepare for a new query
5152
self.collector.reload(loss_bound);
5253

53-
// Query the CDE, all colliding hazards will be stored in the detection map
54+
//query the CDE, all colliding hazards will be stored in the detection map
5455
collect_poly_collisions_in_detector_custom(cde, &dt, &mut self.shape_buff, self.item.shape_cd.as_ref(), &mut self.collector);
5556

5657
if self.collector.early_terminate(&self.shape_buff) {

src/eval/specialized_jaguars_pipeline.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,18 @@ pub fn collect_poly_collisions_in_detector_custom(
3838
#[cfg(feature = "simd")]
3939
collector.poles_soa.load(&shape.surrogate().poles);
4040

41-
// Start off by checking a few poles to detect obvious collisions quickly
41+
4242
{
43-
//TODO: clean this up
43+
// We start off by checking a few poles in order to detect obvious collisions quickly and quickly raise the loss.
44+
// Potentially allows us to fail fast (early terminate) without checking all edges.
45+
// We check poles until the area of the poles checked exceeds 50% of the shape.
4446
let area_threshold = shape.area * 0.5 / PI;
4547
let mut area_sum = 0.0;
4648
for pole in shape.surrogate().poles.iter() {
4749
cde.quadtree.collect_collisions(pole, collector);
4850
if collector.early_terminate(shape) { return; }
4951
area_sum += pole.radius * pole.radius;
5052
if area_sum > area_threshold {
51-
// If the area of the poles exceeds 80% of the shape's area, we can stop early.
5253
break;
5354
}
5455
}
@@ -57,7 +58,7 @@ pub fn collect_poly_collisions_in_detector_custom(
5758
// Find the virtual root of the quadtree for the shape's bounding box. So we do not have to start from the root every time.
5859
let v_quadtree = cde.get_virtual_root(shape.bbox);
5960

60-
// Collect collisions for all edges.
61+
// Collect collisions for each edge of the polygon.
6162
// Iterate over them in a bit-reversed order to maximize detecting new hazards early.
6263
let custom_edge_iter = BitReversalIterator::new(shape.n_vertices())
6364
.map(|i| shape.edge(i));
@@ -66,7 +67,7 @@ pub fn collect_poly_collisions_in_detector_custom(
6667
if collector.early_terminate(shape) { return; }
6768
}
6869

69-
//Check if there are any other collisions due to containment
70+
// Check if there are any other collisions due to containment
7071
for qt_haz in v_quadtree.hazards.iter() {
7172
match &qt_haz.presence {
7273
// No need to check these, guaranteed to be detected by edge intersection
@@ -188,7 +189,8 @@ impl<'a> HazardCollector for SpecializedHazardCollector<'a> {
188189
}
189190

190191
fn remove_by_key(&mut self, hkey: HazKey) {
191-
let (_, idx) = self.detected.remove(hkey).expect("key should be present in the collector");
192+
let (_, idx) = self.detected.remove(hkey)
193+
.expect("key should be present in the collector");
192194
if idx < self.loss_cache.0 {
193195
//wipe the cache if a hazard was removed that was in it
194196
self.loss_cache = (0, 0.0);

src/optimizer/compress.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::optimizer::separator::Separator;
77
use crate::util::listener::{ReportType, SolutionListener};
88
use crate::util::terminator::Terminator;
99

10+
/// Algorithm 13 from https://doi.org/10.48550/arXiv.2509.13329
1011
pub fn compression_phase(
1112
instance: &SPInstance,
1213
sep: &mut Separator,

src/optimizer/explore.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use crate::sample::uniform_sampler::convert_sample_to_closest_feasible;
1717
use crate::util::listener::{ReportType, SolutionListener};
1818
use crate::util::terminator::Terminator;
1919

20+
/// Algorithm 12 from https://doi.org/10.48550/arXiv.2509.13329
2021
pub fn exploration_phase(instance: &SPInstance, sep: &mut Separator, sol_listener: &mut impl SolutionListener, term: &impl Terminator, config: &ExplorationConfig) -> Vec<SPSolution> {
2122
let mut current_width = sep.prob.strip_width();
2223
let mut best_width = current_width;

src/optimizer/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mod worker;
1717
pub mod explore;
1818
pub mod compress;
1919

20+
///Algorithm 11 from https://doi.org/10.48550/arXiv.2509.13329
2021
pub fn optimize(instance: SPInstance, mut rng: Xoshiro256PlusPlus, sol_listener: &mut impl SolutionListener, terminator: &mut impl Terminator, expl_config: &ExplorationConfig, cmpr_config: &CompressionConfig) -> SPSolution {
2122
let mut next_rng = || Xoshiro256PlusPlus::seed_from_u64(rng.next_u64());
2223
let builder = LBFBuilder::new(instance.clone(), next_rng(), LBF_SAMPLE_CONFIG).construct();

src/optimizer/separator.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ impl Separator {
6868
}
6969
}
7070

71+
/// Algorithm 9 from https://doi.org/10.48550/arXiv.2509.13329
7172
pub fn separate(&mut self, term: &impl Terminator, sol_listener: &mut impl SolutionListener) -> (SPSolution, CTSnapshot) {
7273
let mut min_loss_sol = (self.prob.save(), self.ct.save());
7374
let mut min_loss = self.ct.get_total_loss();
@@ -89,7 +90,7 @@ impl Separator {
8990
self.ct.get_total_loss(),
9091
self.ct.get_total_weighted_loss(),
9192
);
92-
sep_stats += self.move_colliding_items();
93+
sep_stats += self.move_items_multi();
9394
let (loss, w_loss) = (
9495
self.ct.get_total_loss(),
9596
self.ct.get_total_weighted_loss(),
@@ -117,7 +118,7 @@ impl Separator {
117118
n_iter_no_improvement += 1;
118119
}
119120

120-
self.ct.increment_weights();
121+
self.ct.update_weights();
121122
n_iter += 1;
122123
}
123124

@@ -141,15 +142,16 @@ impl Separator {
141142
(min_loss_sol.0, min_loss_sol.1)
142143
}
143144

144-
fn move_colliding_items(&mut self) -> SepStats {
145+
/// Algorithm 10 from https://doi.org/10.48550/arXiv.2509.13329
146+
fn move_items_multi(&mut self) -> SepStats {
145147
let master_sol = self.prob.save();
146148

147149
let mut separate_multi = || -> SepStats {
148150
self.workers.par_iter_mut().map(|worker| {
149151
// Sync the workers with the master
150152
worker.load(&master_sol, &self.ct);
151153
// Let them modify
152-
worker.move_colliding_items()
154+
worker.move_items()
153155
}).sum()
154156
};
155157

src/optimizer/worker.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ impl SeparatorWorker {
3131
self.ct = ct.clone();
3232
}
3333

34-
pub fn move_colliding_items(&mut self) -> SepStats {
35-
//collect all colliding items and shuffle them
34+
/// Algorithm 5 from https://doi.org/10.48550/arXiv.2509.13329
35+
pub fn move_items(&mut self) -> SepStats {
36+
//collect all colliding items and order them randomly
3637
let candidates = self.prob.layout.placed_items.keys()
3738
.filter(|pk| self.ct.get_loss(*pk) > 0.0)
3839
.collect_vec()
@@ -41,23 +42,23 @@ impl SeparatorWorker {
4142
let mut total_moves = 0;
4243
let mut total_evals = 0;
4344

44-
//give each item a chance to move to a better (eval) position
45+
//give each item the opportunity to move to a better (eval) position
4546
for &pk in candidates.iter() {
4647
//check if the item is still colliding
4748
if self.ct.get_loss(pk) > 0.0 {
4849
let item_id = self.prob.layout.placed_items[pk].item_id;
4950
let item = self.instance.item(item_id);
5051

51-
// create an evaluator to evaluate the samples during the search
52+
//create an evaluator to evaluate the samples during the search
5253
let evaluator = SeparationEvaluator::new(&self.prob.layout, item, pk, &self.ct);
5354

54-
// search for a better position for the item
55+
//search for a better position for the item
5556
let (best_sample, n_evals) =
5657
search::search_placement(&self.prob.layout, item, Some(pk), evaluator, self.sample_config, &mut self.rng);
5758

5859
let (new_dt, _eval) = best_sample.expect("search_placement should always return a sample");
5960

60-
// move the item to the new position
61+
//move the item to the new position
6162
self.move_item(pk, new_dt);
6263
total_moves += 1;
6364
total_evals += n_evals;

0 commit comments

Comments
 (0)