From 45041949eec5d434191519df0a68f25a15ac74f0 Mon Sep 17 00:00:00 2001 From: Hubert Hirtz Date: Wed, 4 Jan 2023 10:52:53 +0100 Subject: [PATCH 01/13] cartesian: IterationResult->SplitTree + SVG display --- src/cartesian/mod.rs | 113 +++++++++++++++++++++++++++++++++++++++++++ src/cartesian/rcb.rs | 46 +++--------------- 2 files changed, 120 insertions(+), 39 deletions(-) diff --git a/src/cartesian/mod.rs b/src/cartesian/mod.rs index 9b50f33f..81c9e14e 100644 --- a/src/cartesian/mod.rs +++ b/src/cartesian/mod.rs @@ -6,6 +6,7 @@ use rayon::iter::IndexedParallelIterator; use rayon::iter::IntoParallelRefIterator; use rayon::iter::IntoParallelRefMutIterator; use rayon::iter::ParallelIterator; +use std::fmt; use std::iter::Sum; use std::marker::PhantomData; use std::num::NonZeroUsize; @@ -141,6 +142,7 @@ impl Grid<2> { iter_count, 1, ); + println!("{}", iters.fmt_svg(self, 1)); partition.par_iter_mut().enumerate().for_each(|(i, p)| { let pos = self.position_of(i); *p = iters.part_of(pos, 1); @@ -281,6 +283,117 @@ where } } +fn transpose(p: [T; 2]) -> [T; 2] { + let [a, b] = p; + [b, a] +} + +#[derive(Debug)] +pub enum SplitTree { + Whole, + Split { + position: usize, + left: Box, + right: Box, + }, +} + +impl SplitTree { + fn part_of(&self, pos: [usize; D], mut start_coord: usize) -> usize { + let mut it = self; + let mut part_id = 0; + while let Self::Split { + position, + left, + right, + } = it + { + if pos[start_coord] < *position { + part_id *= 2; + it = left; + } else { + part_id = 2 * part_id + 1; + it = right; + } + start_coord = (start_coord + 1) % D; + } + part_id + } + + pub fn fmt_svg(&self, grid: Grid<2>, start_coord: usize) -> impl fmt::Display + '_ { + struct ShowSvg<'a> { + tree: &'a SplitTree, + grid: Grid<2>, + start_coord: usize, + } + + fn print_splits( + f: &mut fmt::Formatter<'_>, + g: Grid<2>, + sg: SubGrid<2>, + tree: &SplitTree, + coord: usize, + iter: usize, + ) -> fmt::Result { + let SplitTree::Split { position, left, right } = tree + else { return Ok(()) }; + + // Recurse before so that lines from first iterations are shown + // above lines from the next ones. + let (sg_left, sg_right) = sg.split_at(coord, *position); + print_splits(f, g, sg_left, left, (coord + 1) % 2, iter + 1)?; + print_splits(f, g, sg_right, right, (coord + 1) % 2, iter + 1)?; + + let Range { start, end } = sg.axis(1 - coord); + let mut p1 = [*position, start]; + let mut p2 = [*position, end]; + if coord == 1 { + p1 = transpose(p1); + p2 = transpose(p2); + } + let color = match iter % 10 { + 0 => "maroon", + 1 => "green", + 2 => "red", + 3 => "lime", + 4 => "purple", + 5 => "olive", + 6 => "fuchsia", + 7 => "yellow", + 8 => "navy", + _ => "blue", + }; + writeln!( + f, + r#""#, + p1[0], p1[1], p2[0], p2[1], color, + ) + } + + impl fmt::Display for ShowSvg<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let [gwidth, gheight] = self.grid.size; + let sg = self.grid.into_subgrid(); + + writeln!( + f, + r#""# + )?; + + print_splits(f, self.grid, sg, self.tree, self.start_coord, 0)?; + + writeln!(f, "") + } + } + + ShowSvg { + tree: self, + grid, + start_coord, + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/cartesian/rcb.rs b/src/cartesian/rcb.rs index 5734ce1f..e678084a 100644 --- a/src/cartesian/rcb.rs +++ b/src/cartesian/rcb.rs @@ -1,5 +1,6 @@ use super::Grid; use super::SubGrid; +use super::SplitTree; use num_traits::AsPrimitive; use num_traits::Num; use rayon::iter::IndexedParallelIterator; @@ -8,39 +9,6 @@ use rayon::iter::IntoParallelRefIterator; use rayon::iter::ParallelIterator; use std::iter::Sum; -#[derive(Debug)] -pub enum IterationResult { - Whole, - Split { - position: usize, - left: Box, - right: Box, - }, -} - -impl IterationResult { - pub fn part_of(&self, pos: [usize; D], mut start_coord: usize) -> usize { - let mut it = self; - let mut part_id = 0; - while let Self::Split { - position, - left, - right, - } = it - { - if pos[start_coord] < *position { - part_id *= 2; - it = left; - } else { - part_id = 2 * part_id + 1; - it = right; - } - start_coord = (start_coord + 1) % D; - } - part_id - } -} - const TOLERANCE: f64 = 0.01; #[derive(Debug)] @@ -105,13 +73,13 @@ pub(super) fn recurse_2d( total_weight: W, iter_count: usize, coord: usize, -) -> IterationResult +) -> SplitTree where W: Send + Sync + PartialOrd + Num + Sum + AsPrimitive, f64: AsPrimitive, { if subgrid.size[coord] == 0 || iter_count == 0 { - return IterationResult::Whole; + return SplitTree::Whole; } let axis_weights: Vec = if coord == 0 { @@ -170,7 +138,7 @@ where }, ); - IterationResult::Split { + SplitTree::Split { position: split_position, left: Box::new(left), right: Box::new(right), @@ -184,13 +152,13 @@ pub(super) fn recurse_3d( total_weight: W, iter_count: usize, coord: usize, -) -> IterationResult +) -> SplitTree where W: Send + Sync + PartialOrd + Num + Sum + AsPrimitive, f64: AsPrimitive, { if subgrid.size[coord] == 0 || iter_count == 0 { - return IterationResult::Whole; + return SplitTree::Whole; } let axis_weights: Vec = if coord == 0 { @@ -273,7 +241,7 @@ where }, ); - IterationResult::Split { + SplitTree::Split { position: split_position, left: Box::new(left), right: Box::new(right), From f68fe49a02fe2dc9ae6fa2c520665cd975b29e99 Mon Sep 17 00:00:00 2001 From: Hubert Hirtz Date: Wed, 4 Jan 2023 15:10:59 +0100 Subject: [PATCH 02/13] cartesian: add a way to show joints in the partition --- src/cartesian/mod.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/cartesian/mod.rs b/src/cartesian/mod.rs index 81c9e14e..a87a71f5 100644 --- a/src/cartesian/mod.rs +++ b/src/cartesian/mod.rs @@ -6,6 +6,7 @@ use rayon::iter::IndexedParallelIterator; use rayon::iter::IntoParallelRefIterator; use rayon::iter::IntoParallelRefMutIterator; use rayon::iter::ParallelIterator; +use std::collections::HashMap; use std::fmt; use std::iter::Sum; use std::marker::PhantomData; @@ -382,6 +383,10 @@ impl SplitTree { print_splits(f, self.grid, sg, self.tree, self.start_coord, 0)?; + for [x, y] in self.tree.joints_2d(self.grid, self.start_coord) { + writeln!(f, r#""#)?; + } + writeln!(f, "") } } @@ -392,6 +397,40 @@ impl SplitTree { start_coord, } } + + pub fn joints_2d(&self, grid: Grid<2>, start_coord: usize) -> impl Iterator { + fn aux( + joints: &mut HashMap<[usize; 2], usize>, + tree: &SplitTree, + sg: SubGrid<2>, + coord: usize, + ) { + let SplitTree::Split { position, left, right } = tree + else { return }; + + let Range { start, end } = sg.axis(1 - coord); + let mut p1 = [*position, start]; + let mut p2 = [*position, end]; + if coord == 1 { + p1 = transpose(p1); + p2 = transpose(p2); + } + + *joints.entry(p1).or_default() += 1; + *joints.entry(p2).or_default() += 1; + + let (sg_left, sg_right) = sg.split_at(coord, *position); + aux(joints, left, sg_left, (coord + 1) % 2); + aux(joints, right, sg_right, (coord + 1) % 2); + } + + let mut joints = HashMap::new(); + + aux(&mut joints, self, grid.into_subgrid(), start_coord); + joints.retain(|_, occ| *occ > 1); + + joints.into_keys() + } } #[cfg(test)] From 6373e10068cf8d72cdbcaf4da6190cffda6b7f71 Mon Sep 17 00:00:00 2001 From: Hubert Hirtz Date: Wed, 4 Jan 2023 15:11:39 +0100 Subject: [PATCH 03/13] wip distribute weights in a way to have more intersting results --- src/main.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index ca4ac288..a43c0dd0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,9 @@ fn main() { eprintln!("grid size: ({x},{y}); rcb iters: {iter}"); let grid = coupe::Grid::new_2d(x, y); let n = usize::from(x) * usize::from(y); - let weights: Vec = (0..n).map(|i| i as f64).collect(); + let weights: Vec = (0..n) + .map(|i| if i % x < 50 && i / y < 50 { 2 } else { 3 } as f64) + .collect(); let mut partition = vec![0; n]; let domain = ittapi::Domain::new("MyIncredibleDomain"); From d9b8147cf0a9158f98ee528a795527442fbe9d05 Mon Sep 17 00:00:00 2001 From: Hubert Hirtz Date: Thu, 5 Jan 2023 10:49:00 +0100 Subject: [PATCH 04/13] compute initial set possible moves from the split tree and test some of these moves. we still need to test segment combinations --- src/cartesian/mod.rs | 240 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 239 insertions(+), 1 deletion(-) diff --git a/src/cartesian/mod.rs b/src/cartesian/mod.rs index a87a71f5..cfc5ec27 100644 --- a/src/cartesian/mod.rs +++ b/src/cartesian/mod.rs @@ -133,6 +133,7 @@ impl Grid<2> { where W: Send + Sync + PartialOrd + Num + Sum + AsPrimitive, f64: AsPrimitive, + W: std::ops::AddAssign, { let total_weight: W = weights.par_iter().cloned().sum(); let iters = rcb::recurse_2d( @@ -143,11 +144,163 @@ impl Grid<2> { iter_count, 1, ); - println!("{}", iters.fmt_svg(self, 1)); partition.par_iter_mut().enumerate().for_each(|(i, p)| { let pos = self.position_of(i); *p = iters.part_of(pos, 1); }); + + let part_count = usize::pow(2, iter_count as u32); + let mut part_loads = crate::imbalance::compute_parts_load( + partition, + part_count, + weights.par_iter().cloned(), + ); + let compute_imbalance = |part_loads: &[W]| { + part_loads + .iter() + .map(|pl| { + let ideal_part_weight = total_weight / (part_count as f64).as_(); + (*pl - ideal_part_weight) / ideal_part_weight + }) + .max_by(crate::partial_cmp) + .unwrap() + }; + let check_move_imb = |part_loads: &mut [W], src: usize, dst: usize, weight: W| { + part_loads[src] = part_loads[src] - weight; + part_loads[dst] = part_loads[dst] + weight; + let new_imbalance = compute_imbalance(&part_loads); + part_loads[src] = part_loads[src] + weight; + part_loads[dst] = part_loads[dst] - weight; + new_imbalance + }; + let imbalance = compute_imbalance(&part_loads); + + println!("{}", iters.fmt_svg(self, 1)); + let segs = iters.segments_2d(self, 1); + + // Testing horizontal segments for imbalance. + for seg in &segs.c[0] { + if seg.at != 0 { + // Test if we can move segment down. + let moved_cells = (seg.start..seg.end).map(|x| [x, seg.at - 1]); + + let src_part = partition[self.index_of([seg.start, seg.at - 1])]; + let dst_part = partition[self.index_of([seg.start, seg.at])]; + debug_assert_ne!(src_part, dst_part); + + let moved_weight = moved_cells.map(|pos| weights[self.index_of(pos)]).sum(); + let new_imbalance = + check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); + if new_imbalance < imbalance { + eprintln!("Found imb horiz move: {seg:?}"); + } + } + if seg.at + 1 >= usize::from(self.size[1]) { + // Test if we can move segment up. + let moved_cells = (seg.start..seg.end).map(|x| [x, seg.at]); + + let src_part = partition[self.index_of([seg.start, seg.at])]; + let dst_part = partition[self.index_of([seg.start, seg.at - 1])]; + debug_assert_ne!(src_part, dst_part); + + let moved_weight = moved_cells.map(|pos| weights[self.index_of(pos)]).sum(); + let new_imbalance = + check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); + if new_imbalance < imbalance { + eprintln!("Found imb horiz move: {seg:?}"); + } + } + } + + // Testing vertical segments for imbalance. + for seg in &segs.c[1] { + if seg.at != 0 { + // Test if we can move segment left. + let moved_cells = (seg.start..seg.end).map(|y| [seg.at - 1, y]); + + let src_part = partition[self.index_of([seg.at - 1, seg.start])]; + let dst_part = partition[self.index_of([seg.at, seg.start])]; + debug_assert_ne!(src_part, dst_part); + + let moved_weight = moved_cells.map(|pos| weights[self.index_of(pos)]).sum(); + let new_imbalance = + check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); + if new_imbalance < imbalance { + eprintln!("Found imb verti move: {seg:?}"); + } + } + if seg.at + 1 >= usize::from(self.size[0]) { + // Test if we can move segment right. + let moved_cells = (seg.start..seg.end).map(|y| [seg.at, y]); + + let src_part = partition[self.index_of([seg.at, seg.start])]; + let dst_part = partition[self.index_of([seg.at - 1, seg.start])]; + debug_assert_ne!(src_part, dst_part); + + let moved_weight = moved_cells.map(|pos| weights[self.index_of(pos)]).sum(); + let new_imbalance = + check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); + if new_imbalance < imbalance { + eprintln!("Found imb verti move: {seg:?}"); + } + } + } + + // Testing horizontal segments for lambda cut. + for seg in &segs.c[0] { + if seg.at >= 2 { + // Test if we can move segment down. + let a = (seg.start..seg.end).map(|x| [x, seg.at]); + let b = (seg.start..seg.end).map(|x| [x, seg.at - 2]); + + let a_weight: W = a.map(|pos| weights[self.index_of(pos)]).sum(); + let b_weight: W = b.map(|pos| weights[self.index_of(pos)]).sum(); + + if b_weight < a_weight { + eprintln!("Found lambda horiz move: {seg:?}"); + } + } + if seg.at + 2 >= usize::from(self.size[1]) { + // Test if we can move segment up. + let a = (seg.start..seg.end).map(|x| [x, seg.at - 1]); + let b = (seg.start..seg.end).map(|x| [x, seg.at + 1]); + + let a_weight: W = a.map(|pos| weights[self.index_of(pos)]).sum(); + let b_weight: W = b.map(|pos| weights[self.index_of(pos)]).sum(); + + if b_weight < a_weight { + eprintln!("Found lambda horiz move: {seg:?}"); + } + } + } + + // Testing vertical segments for lambda cut. + for seg in &segs.c[1] { + if seg.at >= 2 { + // Test if we can move segment down. + let a = (seg.start..seg.end).map(|y| [seg.at, y]); + let b = (seg.start..seg.end).map(|y| [seg.at - 2, y]); + + let a_weight: W = a.map(|pos| weights[self.index_of(pos)]).sum(); + let b_weight: W = b.map(|pos| weights[self.index_of(pos)]).sum(); + + if b_weight < a_weight { + eprintln!("Found lambda verti move: {seg:?}"); + } + } + if seg.at + 2 >= usize::from(self.size[0]) { + // Test if we can move segment up. + let a = (seg.start..seg.end).map(|y| [seg.at - 1, y]); + let b = (seg.start..seg.end).map(|y| [seg.at + 1, y]); + + let a_weight: W = a.map(|pos| weights[self.index_of(pos)]).sum(); + let b_weight: W = b.map(|pos| weights[self.index_of(pos)]).sum(); + + if b_weight < a_weight { + eprintln!("Found lambda verti move: {seg:?}"); + } + } + } } } @@ -431,8 +584,93 @@ impl SplitTree { joints.into_keys() } + + pub fn segments_2d(&self, grid: Grid<2>, start_coord: usize) -> Segments<2> { + fn aux(c: &mut [Vec; 2], tree: &SplitTree, sg: SubGrid<2>, coord: usize) { + let SplitTree::Split { position, left, right } = tree + else { return }; + + let Range { start, end } = sg.axis(1 - coord); + c[1 - coord].push(Segment { + start, + end, + at: *position, + }); + + let (sg_left, sg_right) = sg.split_at(coord, *position); + aux(c, left, sg_left, (coord + 1) % 2); + aux(c, right, sg_right, (coord + 1) % 2); + } + + let mut c = [Vec::new(), Vec::new()]; + aux(&mut c, self, grid.into_subgrid(), start_coord); + + for [x, y] in self.joints_2d(grid, start_coord) { + for i in 0..c[0].len() { + let seg = &c[0][i]; + if y != seg.at { + continue; + } + let Some((left, right)) = seg.split_at(x) + else { continue }; + c[0][i] = left; + c[0].push(right); + } + for i in 0..c[1].len() { + let seg = &c[1][i]; + if x != seg.at { + continue; + } + let Some((left, right)) = seg.split_at(y) + else { continue }; + c[1][i] = left; + c[1].push(right); + } + } + + Segments { c, grid } + } +} + +#[derive(Debug)] +pub struct Segment { + start: usize, + end: usize, + at: usize, +} + +impl Segment { + pub fn split_at(&self, pos: usize) -> Option<(Segment, Segment)> { + if pos <= self.start || self.end <= pos { + return None; + } + Some(( + Segment { + start: self.start, + end: pos, + at: self.at, + }, + Segment { + start: pos, + end: self.end, + at: self.at, + }, + )) + } +} + +#[derive(Debug)] +pub struct Segments { + c: [Vec; D], + grid: Grid, } +//impl Segments<2> { +// pub fn moves(&self) -> impl Iterator { +// // TODO +// } +//} + #[cfg(test)] mod tests { use super::*; From abf835ecbaafa67cd5dd19b3c3e82015e729e0b3 Mon Sep 17 00:00:00 2001 From: Hubert Hirtz Date: Thu, 5 Jan 2023 12:36:25 +0100 Subject: [PATCH 05/13] cargo fmt --- src/cartesian/rcb.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cartesian/rcb.rs b/src/cartesian/rcb.rs index e678084a..9382021e 100644 --- a/src/cartesian/rcb.rs +++ b/src/cartesian/rcb.rs @@ -1,6 +1,6 @@ use super::Grid; -use super::SubGrid; use super::SplitTree; +use super::SubGrid; use num_traits::AsPrimitive; use num_traits::Num; use rayon::iter::IndexedParallelIterator; From 23c96fff50ac293f5fc5a2b5bb485d1ee3ab30dc Mon Sep 17 00:00:00 2001 From: Hubert Hirtz Date: Thu, 5 Jan 2023 15:41:13 +0100 Subject: [PATCH 06/13] remove unused variable in SVG --- src/cartesian/mod.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/cartesian/mod.rs b/src/cartesian/mod.rs index cfc5ec27..09ab83d7 100644 --- a/src/cartesian/mod.rs +++ b/src/cartesian/mod.rs @@ -483,7 +483,6 @@ impl SplitTree { fn print_splits( f: &mut fmt::Formatter<'_>, - g: Grid<2>, sg: SubGrid<2>, tree: &SplitTree, coord: usize, @@ -495,8 +494,8 @@ impl SplitTree { // Recurse before so that lines from first iterations are shown // above lines from the next ones. let (sg_left, sg_right) = sg.split_at(coord, *position); - print_splits(f, g, sg_left, left, (coord + 1) % 2, iter + 1)?; - print_splits(f, g, sg_right, right, (coord + 1) % 2, iter + 1)?; + print_splits(f, sg_left, left, (coord + 1) % 2, iter + 1)?; + print_splits(f, sg_right, right, (coord + 1) % 2, iter + 1)?; let Range { start, end } = sg.axis(1 - coord); let mut p1 = [*position, start]; @@ -534,7 +533,7 @@ impl SplitTree { r#""# )?; - print_splits(f, self.grid, sg, self.tree, self.start_coord, 0)?; + print_splits(f, sg, self.tree, self.start_coord, 0)?; for [x, y] in self.tree.joints_2d(self.grid, self.start_coord) { writeln!(f, r#""#)?; From 68f2455ce6123f245d873d2e99a34e027ea64fa2 Mon Sep 17 00:00:00 2001 From: Hubert Hirtz Date: Thu, 5 Jan 2023 15:42:42 +0100 Subject: [PATCH 07/13] use addassign --- src/cartesian/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cartesian/mod.rs b/src/cartesian/mod.rs index 09ab83d7..93ddbcb6 100644 --- a/src/cartesian/mod.rs +++ b/src/cartesian/mod.rs @@ -133,7 +133,7 @@ impl Grid<2> { where W: Send + Sync + PartialOrd + Num + Sum + AsPrimitive, f64: AsPrimitive, - W: std::ops::AddAssign, + W: num_traits::NumAssign, { let total_weight: W = weights.par_iter().cloned().sum(); let iters = rcb::recurse_2d( @@ -166,11 +166,11 @@ impl Grid<2> { .unwrap() }; let check_move_imb = |part_loads: &mut [W], src: usize, dst: usize, weight: W| { - part_loads[src] = part_loads[src] - weight; - part_loads[dst] = part_loads[dst] + weight; + part_loads[src] -= weight; + part_loads[dst] += weight; let new_imbalance = compute_imbalance(&part_loads); - part_loads[src] = part_loads[src] + weight; - part_loads[dst] = part_loads[dst] - weight; + part_loads[src] += weight; + part_loads[dst] -= weight; new_imbalance }; let imbalance = compute_imbalance(&part_loads); From c296ac772199e76d7f7458724e925bece373681e Mon Sep 17 00:00:00 2001 From: Hubert Hirtz Date: Thu, 5 Jan 2023 15:43:29 +0100 Subject: [PATCH 08/13] remove useless borrow --- src/cartesian/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cartesian/mod.rs b/src/cartesian/mod.rs index 93ddbcb6..2c3d95a0 100644 --- a/src/cartesian/mod.rs +++ b/src/cartesian/mod.rs @@ -168,7 +168,7 @@ impl Grid<2> { let check_move_imb = |part_loads: &mut [W], src: usize, dst: usize, weight: W| { part_loads[src] -= weight; part_loads[dst] += weight; - let new_imbalance = compute_imbalance(&part_loads); + let new_imbalance = compute_imbalance(part_loads); part_loads[src] += weight; part_loads[dst] -= weight; new_imbalance From 641d9a42dd585fd7f13b05a734c17dd47f35c1c2 Mon Sep 17 00:00:00 2001 From: Hubert Hirtz Date: Mon, 9 Jan 2023 10:56:10 +0100 Subject: [PATCH 09/13] consider moving several segments at a time --- src/cartesian/mod.rs | 101 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 91 insertions(+), 10 deletions(-) diff --git a/src/cartesian/mod.rs b/src/cartesian/mod.rs index 2c3d95a0..599b0275 100644 --- a/src/cartesian/mod.rs +++ b/src/cartesian/mod.rs @@ -6,7 +6,9 @@ use rayon::iter::IndexedParallelIterator; use rayon::iter::IntoParallelRefIterator; use rayon::iter::IntoParallelRefMutIterator; use rayon::iter::ParallelIterator; +use std::collections::BTreeMap; use std::collections::HashMap; +use std::collections::VecDeque; use std::fmt; use std::iter::Sum; use std::marker::PhantomData; @@ -179,7 +181,7 @@ impl Grid<2> { let segs = iters.segments_2d(self, 1); // Testing horizontal segments for imbalance. - for seg in &segs.c[0] { + for seg in segs.moves(0) { if seg.at != 0 { // Test if we can move segment down. let moved_cells = (seg.start..seg.end).map(|x| [x, seg.at - 1]); @@ -213,7 +215,7 @@ impl Grid<2> { } // Testing vertical segments for imbalance. - for seg in &segs.c[1] { + for seg in segs.moves(1) { if seg.at != 0 { // Test if we can move segment left. let moved_cells = (seg.start..seg.end).map(|y| [seg.at - 1, y]); @@ -247,7 +249,7 @@ impl Grid<2> { } // Testing horizontal segments for lambda cut. - for seg in &segs.c[0] { + for seg in segs.moves(0) { if seg.at >= 2 { // Test if we can move segment down. let a = (seg.start..seg.end).map(|x| [x, seg.at]); @@ -275,7 +277,7 @@ impl Grid<2> { } // Testing vertical segments for lambda cut. - for seg in &segs.c[1] { + for seg in segs.moves(1) { if seg.at >= 2 { // Test if we can move segment down. let a = (seg.start..seg.end).map(|y| [seg.at, y]); @@ -631,7 +633,7 @@ impl SplitTree { } } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Segment { start: usize, end: usize, @@ -656,6 +658,22 @@ impl Segment { }, )) } + + pub fn p_start_2d(&self, coord: usize) -> [usize; 2] { + if coord == 0 { + [self.at, self.start] + } else { + [self.start, self.at] + } + } + + pub fn p_end_2d(&self, coord: usize) -> [usize; 2] { + if coord == 0 { + [self.at, self.end] + } else { + [self.end, self.at] + } + } } #[derive(Debug)] @@ -664,11 +682,74 @@ pub struct Segments { grid: Grid, } -//impl Segments<2> { -// pub fn moves(&self) -> impl Iterator { -// // TODO -// } -//} +impl Segments<2> { + pub fn moves(&self, coord: usize) -> impl IntoIterator { + let mut occs: BTreeMap<[usize; 2], Vec<&Segment>> = BTreeMap::new(); + + for seg in &self.c[coord] { + let p1 = seg.p_start_2d(coord); + let p2 = seg.p_end_2d(coord); + occs.entry(p1).or_default().push(seg); + occs.entry(p2).or_default().push(seg); + } + + occs.retain(|_, segs| segs.len() > 1); + + let mut moves = self.c[coord].clone(); + + while let Some((joint, segs)) = occs.pop_first() { + debug_assert_eq!(segs.len(), 2); + let seg1 = segs[0]; + let seg2 = segs[1]; + + let mut multi_seg = VecDeque::new(); + + if seg1.p_end_2d(coord) == joint { + // seg1 is before seg2 + multi_seg.push_back(seg1); + multi_seg.push_back(seg2); + } else { + // seg1 is after seg2 + multi_seg.push_back(seg2); + multi_seg.push_back(seg1); + } + + loop { + // Add segments to the left of the multi-segment. + let joint = multi_seg.front().unwrap().p_start_2d(coord); + let Some(segs) = occs.remove(&joint) else { break }; + let seg = *segs + .iter() + .find(|seg| seg.p_end_2d(coord) == joint) + .unwrap(); + multi_seg.push_front(seg); + } + loop { + // Add segments to the right of the multi-segment. + let joint = multi_seg.back().unwrap().p_end_2d(coord); + let Some(segs) = occs.remove(&joint) else { break }; + let seg = *segs + .iter() + .find(|seg| seg.p_start_2d(coord) == joint) + .unwrap(); + multi_seg.push_back(seg); + } + + for i in 0..multi_seg.len() { + // i+1 because "moves" already contains individual segments. + for j in i + 1..multi_seg.len() { + moves.push(Segment { + start: multi_seg[i].start, + end: multi_seg[j].end, + at: multi_seg[i].at, + }); + } + } + } + + moves + } +} #[cfg(test)] mod tests { From 7e7cfa5298a777b198823282683cb2c54624767a Mon Sep 17 00:00:00 2001 From: Hubert Hirtz Date: Mon, 9 Jan 2023 12:13:44 +0100 Subject: [PATCH 10/13] show gains --- src/cartesian/mod.rs | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/cartesian/mod.rs b/src/cartesian/mod.rs index 599b0275..a9f3d6cf 100644 --- a/src/cartesian/mod.rs +++ b/src/cartesian/mod.rs @@ -193,8 +193,9 @@ impl Grid<2> { let moved_weight = moved_cells.map(|pos| weights[self.index_of(pos)]).sum(); let new_imbalance = check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); - if new_imbalance < imbalance { - eprintln!("Found imb horiz move: {seg:?}"); + let gain = (imbalance - new_imbalance).as_(); + if gain > 0.0 { + eprintln!("Found imb horiz move: {seg:?} gain={gain}"); } } if seg.at + 1 >= usize::from(self.size[1]) { @@ -208,8 +209,9 @@ impl Grid<2> { let moved_weight = moved_cells.map(|pos| weights[self.index_of(pos)]).sum(); let new_imbalance = check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); - if new_imbalance < imbalance { - eprintln!("Found imb horiz move: {seg:?}"); + let gain = (imbalance - new_imbalance).as_(); + if gain > 0.0 { + eprintln!("Found imb horiz move: {seg:?}, gain={gain}"); } } } @@ -227,8 +229,9 @@ impl Grid<2> { let moved_weight = moved_cells.map(|pos| weights[self.index_of(pos)]).sum(); let new_imbalance = check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); - if new_imbalance < imbalance { - eprintln!("Found imb verti move: {seg:?}"); + let gain = (imbalance - new_imbalance).as_(); + if gain > 0.0 { + eprintln!("Found imb verti move: {seg:?}, gain={gain}"); } } if seg.at + 1 >= usize::from(self.size[0]) { @@ -242,8 +245,9 @@ impl Grid<2> { let moved_weight = moved_cells.map(|pos| weights[self.index_of(pos)]).sum(); let new_imbalance = check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); - if new_imbalance < imbalance { - eprintln!("Found imb verti move: {seg:?}"); + let gain = (imbalance - new_imbalance).as_(); + if gain > 0.0 { + eprintln!("Found imb verti move: {seg:?}, gain={gain}"); } } } @@ -258,8 +262,9 @@ impl Grid<2> { let a_weight: W = a.map(|pos| weights[self.index_of(pos)]).sum(); let b_weight: W = b.map(|pos| weights[self.index_of(pos)]).sum(); - if b_weight < a_weight { - eprintln!("Found lambda horiz move: {seg:?}"); + let gain = (a_weight - b_weight).as_(); + if gain > 0.0 { + eprintln!("Found lambda horiz move: {seg:?}, gain={gain}"); } } if seg.at + 2 >= usize::from(self.size[1]) { @@ -270,8 +275,9 @@ impl Grid<2> { let a_weight: W = a.map(|pos| weights[self.index_of(pos)]).sum(); let b_weight: W = b.map(|pos| weights[self.index_of(pos)]).sum(); - if b_weight < a_weight { - eprintln!("Found lambda horiz move: {seg:?}"); + let gain = (a_weight - b_weight).as_(); + if gain > 0.0 { + eprintln!("Found lambda horiz move: {seg:?}, gain={gain}"); } } } @@ -286,8 +292,9 @@ impl Grid<2> { let a_weight: W = a.map(|pos| weights[self.index_of(pos)]).sum(); let b_weight: W = b.map(|pos| weights[self.index_of(pos)]).sum(); - if b_weight < a_weight { - eprintln!("Found lambda verti move: {seg:?}"); + let gain = (a_weight - b_weight).as_(); + if gain > 0.0 { + eprintln!("Found lambda verti move: {seg:?}, gain={gain}"); } } if seg.at + 2 >= usize::from(self.size[0]) { @@ -298,8 +305,9 @@ impl Grid<2> { let a_weight: W = a.map(|pos| weights[self.index_of(pos)]).sum(); let b_weight: W = b.map(|pos| weights[self.index_of(pos)]).sum(); - if b_weight < a_weight { - eprintln!("Found lambda verti move: {seg:?}"); + let gain = (a_weight - b_weight).as_(); + if gain > 0.0 { + eprintln!("Found lambda verti move: {seg:?}, gain={gain}"); } } } From 4142f8448388e5b61de301df54623ebf84caccf3 Mon Sep 17 00:00:00 2001 From: Hubert Hirtz Date: Mon, 9 Jan 2023 12:40:59 +0100 Subject: [PATCH 11/13] find the best move for lambda cut --- src/cartesian/mod.rs | 258 ++++++++++++++++++++++++------------------- 1 file changed, 142 insertions(+), 116 deletions(-) diff --git a/src/cartesian/mod.rs b/src/cartesian/mod.rs index a9f3d6cf..8a263e57 100644 --- a/src/cartesian/mod.rs +++ b/src/cartesian/mod.rs @@ -180,136 +180,162 @@ impl Grid<2> { println!("{}", iters.fmt_svg(self, 1)); let segs = iters.segments_2d(self, 1); - // Testing horizontal segments for imbalance. - for seg in segs.moves(0) { - if seg.at != 0 { - // Test if we can move segment down. - let moved_cells = (seg.start..seg.end).map(|x| [x, seg.at - 1]); - - let src_part = partition[self.index_of([seg.start, seg.at - 1])]; - let dst_part = partition[self.index_of([seg.start, seg.at])]; - debug_assert_ne!(src_part, dst_part); - - let moved_weight = moved_cells.map(|pos| weights[self.index_of(pos)]).sum(); - let new_imbalance = - check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); - let gain = (imbalance - new_imbalance).as_(); - if gain > 0.0 { - eprintln!("Found imb horiz move: {seg:?} gain={gain}"); - } - } - if seg.at + 1 >= usize::from(self.size[1]) { - // Test if we can move segment up. - let moved_cells = (seg.start..seg.end).map(|x| [x, seg.at]); - - let src_part = partition[self.index_of([seg.start, seg.at])]; - let dst_part = partition[self.index_of([seg.start, seg.at - 1])]; - debug_assert_ne!(src_part, dst_part); - - let moved_weight = moved_cells.map(|pos| weights[self.index_of(pos)]).sum(); - let new_imbalance = - check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); - let gain = (imbalance - new_imbalance).as_(); - if gain > 0.0 { - eprintln!("Found imb horiz move: {seg:?}, gain={gain}"); - } + loop { + #[derive(Debug)] + enum Orientation { + Horizontal, + Vertical, } - } - // Testing vertical segments for imbalance. - for seg in segs.moves(1) { - if seg.at != 0 { - // Test if we can move segment left. - let moved_cells = (seg.start..seg.end).map(|y| [seg.at - 1, y]); - - let src_part = partition[self.index_of([seg.at - 1, seg.start])]; - let dst_part = partition[self.index_of([seg.at, seg.start])]; - debug_assert_ne!(src_part, dst_part); - - let moved_weight = moved_cells.map(|pos| weights[self.index_of(pos)]).sum(); - let new_imbalance = - check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); - let gain = (imbalance - new_imbalance).as_(); - if gain > 0.0 { - eprintln!("Found imb verti move: {seg:?}, gain={gain}"); + eprintln!("\nNew pass"); + let mut best_lambda_move = (Orientation::Horizontal, 0.0, segs.c[0][0]); + + // Testing horizontal segments for imbalance. + for seg in segs.moves(0) { + if seg.at != 0 { + // Test if we can move segment down. + let moved_cells = (seg.start..seg.end).map(|x| [x, seg.at - 1]); + + let src_part = partition[self.index_of([seg.start, seg.at - 1])]; + let dst_part = partition[self.index_of([seg.start, seg.at])]; + debug_assert_ne!(src_part, dst_part); + + let moved_weight = moved_cells.map(|pos| weights[self.index_of(pos)]).sum(); + let new_imbalance = + check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); + let gain = (imbalance - new_imbalance).as_(); + if gain > 0.0 { + eprintln!("Found imb horiz move: {seg:?} gain={gain}"); + } } - } - if seg.at + 1 >= usize::from(self.size[0]) { - // Test if we can move segment right. - let moved_cells = (seg.start..seg.end).map(|y| [seg.at, y]); - - let src_part = partition[self.index_of([seg.at, seg.start])]; - let dst_part = partition[self.index_of([seg.at - 1, seg.start])]; - debug_assert_ne!(src_part, dst_part); - - let moved_weight = moved_cells.map(|pos| weights[self.index_of(pos)]).sum(); - let new_imbalance = - check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); - let gain = (imbalance - new_imbalance).as_(); - if gain > 0.0 { - eprintln!("Found imb verti move: {seg:?}, gain={gain}"); + if seg.at + 1 >= usize::from(self.size[1]) { + // Test if we can move segment up. + let moved_cells = (seg.start..seg.end).map(|x| [x, seg.at]); + + let src_part = partition[self.index_of([seg.start, seg.at])]; + let dst_part = partition[self.index_of([seg.start, seg.at - 1])]; + debug_assert_ne!(src_part, dst_part); + + let moved_weight = moved_cells.map(|pos| weights[self.index_of(pos)]).sum(); + let new_imbalance = + check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); + let gain = (imbalance - new_imbalance).as_(); + if gain > 0.0 { + eprintln!("Found imb horiz move: {seg:?}, gain={gain}"); + } } } - } - - // Testing horizontal segments for lambda cut. - for seg in segs.moves(0) { - if seg.at >= 2 { - // Test if we can move segment down. - let a = (seg.start..seg.end).map(|x| [x, seg.at]); - let b = (seg.start..seg.end).map(|x| [x, seg.at - 2]); - let a_weight: W = a.map(|pos| weights[self.index_of(pos)]).sum(); - let b_weight: W = b.map(|pos| weights[self.index_of(pos)]).sum(); - - let gain = (a_weight - b_weight).as_(); - if gain > 0.0 { - eprintln!("Found lambda horiz move: {seg:?}, gain={gain}"); + // Testing vertical segments for imbalance. + for seg in segs.moves(1) { + if seg.at != 0 { + // Test if we can move segment left. + let moved_cells = (seg.start..seg.end).map(|y| [seg.at - 1, y]); + + let src_part = partition[self.index_of([seg.at - 1, seg.start])]; + let dst_part = partition[self.index_of([seg.at, seg.start])]; + debug_assert_ne!(src_part, dst_part); + + let moved_weight = moved_cells.map(|pos| weights[self.index_of(pos)]).sum(); + let new_imbalance = + check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); + let gain = (imbalance - new_imbalance).as_(); + if gain > 0.0 { + eprintln!("Found imb verti move: {seg:?}, gain={gain}"); + } } - } - if seg.at + 2 >= usize::from(self.size[1]) { - // Test if we can move segment up. - let a = (seg.start..seg.end).map(|x| [x, seg.at - 1]); - let b = (seg.start..seg.end).map(|x| [x, seg.at + 1]); - - let a_weight: W = a.map(|pos| weights[self.index_of(pos)]).sum(); - let b_weight: W = b.map(|pos| weights[self.index_of(pos)]).sum(); - - let gain = (a_weight - b_weight).as_(); - if gain > 0.0 { - eprintln!("Found lambda horiz move: {seg:?}, gain={gain}"); + if seg.at + 1 >= usize::from(self.size[0]) { + // Test if we can move segment right. + let moved_cells = (seg.start..seg.end).map(|y| [seg.at, y]); + + let src_part = partition[self.index_of([seg.at, seg.start])]; + let dst_part = partition[self.index_of([seg.at - 1, seg.start])]; + debug_assert_ne!(src_part, dst_part); + + let moved_weight = moved_cells.map(|pos| weights[self.index_of(pos)]).sum(); + let new_imbalance = + check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); + let gain = (imbalance - new_imbalance).as_(); + if gain > 0.0 { + eprintln!("Found imb verti move: {seg:?}, gain={gain}"); + } } } - } - - // Testing vertical segments for lambda cut. - for seg in segs.moves(1) { - if seg.at >= 2 { - // Test if we can move segment down. - let a = (seg.start..seg.end).map(|y| [seg.at, y]); - let b = (seg.start..seg.end).map(|y| [seg.at - 2, y]); - - let a_weight: W = a.map(|pos| weights[self.index_of(pos)]).sum(); - let b_weight: W = b.map(|pos| weights[self.index_of(pos)]).sum(); - let gain = (a_weight - b_weight).as_(); - if gain > 0.0 { - eprintln!("Found lambda verti move: {seg:?}, gain={gain}"); + // Testing horizontal segments for lambda cut. + for seg in segs.moves(0) { + if seg.at >= 2 { + // Test if we can move segment down. + let a = (seg.start..seg.end).map(|x| [x, seg.at]); + let b = (seg.start..seg.end).map(|x| [x, seg.at - 2]); + + let a_weight: W = a.map(|pos| weights[self.index_of(pos)]).sum(); + let b_weight: W = b.map(|pos| weights[self.index_of(pos)]).sum(); + + let gain = (a_weight - b_weight).as_(); + if gain > 0.0 { + eprintln!("Found lambda horiz move: {seg:?}, gain={gain}"); + if gain > best_lambda_move.1 { + best_lambda_move = (Orientation::Horizontal, gain, seg); + } + } + } + if seg.at + 2 >= usize::from(self.size[1]) { + // Test if we can move segment up. + let a = (seg.start..seg.end).map(|x| [x, seg.at - 1]); + let b = (seg.start..seg.end).map(|x| [x, seg.at + 1]); + + let a_weight: W = a.map(|pos| weights[self.index_of(pos)]).sum(); + let b_weight: W = b.map(|pos| weights[self.index_of(pos)]).sum(); + + let gain = (a_weight - b_weight).as_(); + if gain > 0.0 { + eprintln!("Found lambda horiz move: {seg:?}, gain={gain}"); + if gain > best_lambda_move.1 { + best_lambda_move = (Orientation::Horizontal, gain, seg); + } + } } } - if seg.at + 2 >= usize::from(self.size[0]) { - // Test if we can move segment up. - let a = (seg.start..seg.end).map(|y| [seg.at - 1, y]); - let b = (seg.start..seg.end).map(|y| [seg.at + 1, y]); - - let a_weight: W = a.map(|pos| weights[self.index_of(pos)]).sum(); - let b_weight: W = b.map(|pos| weights[self.index_of(pos)]).sum(); - let gain = (a_weight - b_weight).as_(); - if gain > 0.0 { - eprintln!("Found lambda verti move: {seg:?}, gain={gain}"); + // Testing vertical segments for lambda cut. + for seg in segs.moves(1) { + if seg.at >= 2 { + // Test if we can move segment down. + let a = (seg.start..seg.end).map(|y| [seg.at, y]); + let b = (seg.start..seg.end).map(|y| [seg.at - 2, y]); + + let a_weight: W = a.map(|pos| weights[self.index_of(pos)]).sum(); + let b_weight: W = b.map(|pos| weights[self.index_of(pos)]).sum(); + + let gain = (a_weight - b_weight).as_(); + if gain > 0.0 { + eprintln!("Found lambda verti move: {seg:?}, gain={gain}"); + if gain > best_lambda_move.1 { + best_lambda_move = (Orientation::Vertical, gain, seg); + } + } + } + if seg.at + 2 >= usize::from(self.size[0]) { + // Test if we can move segment up. + let a = (seg.start..seg.end).map(|y| [seg.at - 1, y]); + let b = (seg.start..seg.end).map(|y| [seg.at + 1, y]); + + let a_weight: W = a.map(|pos| weights[self.index_of(pos)]).sum(); + let b_weight: W = b.map(|pos| weights[self.index_of(pos)]).sum(); + + let gain = (a_weight - b_weight).as_(); + if gain > 0.0 { + eprintln!("Found lambda verti move: {seg:?}, gain={gain}"); + if gain > best_lambda_move.1 { + best_lambda_move = (Orientation::Vertical, gain, seg); + } + } } } + + eprintln!("Best lambda move: {best_lambda_move:?}"); + break; } } } @@ -641,7 +667,7 @@ impl SplitTree { } } -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub struct Segment { start: usize, end: usize, From 54124b88c2ad92dbd5d54154563283a8fd3454de Mon Sep 17 00:00:00 2001 From: Hubert Hirtz Date: Fri, 13 Jan 2023 10:18:14 +0100 Subject: [PATCH 12/13] do moves until no move has positive gain --- src/cartesian/mod.rs | 139 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 115 insertions(+), 24 deletions(-) diff --git a/src/cartesian/mod.rs b/src/cartesian/mod.rs index 8a263e57..6cec8c64 100644 --- a/src/cartesian/mod.rs +++ b/src/cartesian/mod.rs @@ -178,17 +178,36 @@ impl Grid<2> { let imbalance = compute_imbalance(&part_loads); println!("{}", iters.fmt_svg(self, 1)); - let segs = iters.segments_2d(self, 1); + let mut segs = iters.segments_2d(self, 1); loop { + #[derive(Copy, Clone, Debug)] + enum Direction { + Lower, + Higher, + } + + #[derive(Copy, Clone, Debug)] + enum Axis { + X = 0, + Y = 1, + } + #[derive(Debug)] - enum Orientation { - Horizontal, - Vertical, + struct Move { + orientation: Axis, + gain: f64, + seg: Segment, + direction: Direction, } eprintln!("\nNew pass"); - let mut best_lambda_move = (Orientation::Horizontal, 0.0, segs.c[0][0]); + let mut best_lambda_move = Move { + orientation: Axis::X, + gain: 0.0, + seg: segs.c[0][0], + direction: Direction::Lower, + }; // Testing horizontal segments for imbalance. for seg in segs.moves(0) { @@ -205,7 +224,7 @@ impl Grid<2> { check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); let gain = (imbalance - new_imbalance).as_(); if gain > 0.0 { - eprintln!("Found imb horiz move: {seg:?} gain={gain}"); + eprintln!(" Found imb horiz move: {seg:?} gain={gain}"); } } if seg.at + 1 >= usize::from(self.size[1]) { @@ -221,7 +240,7 @@ impl Grid<2> { check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); let gain = (imbalance - new_imbalance).as_(); if gain > 0.0 { - eprintln!("Found imb horiz move: {seg:?}, gain={gain}"); + eprintln!(" Found imb horiz move: {seg:?}, gain={gain}"); } } } @@ -241,7 +260,7 @@ impl Grid<2> { check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); let gain = (imbalance - new_imbalance).as_(); if gain > 0.0 { - eprintln!("Found imb verti move: {seg:?}, gain={gain}"); + eprintln!(" Found imb verti move: {seg:?}, gain={gain}"); } } if seg.at + 1 >= usize::from(self.size[0]) { @@ -257,7 +276,7 @@ impl Grid<2> { check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); let gain = (imbalance - new_imbalance).as_(); if gain > 0.0 { - eprintln!("Found imb verti move: {seg:?}, gain={gain}"); + eprintln!(" Found imb verti move: {seg:?}, gain={gain}"); } } } @@ -274,9 +293,14 @@ impl Grid<2> { let gain = (a_weight - b_weight).as_(); if gain > 0.0 { - eprintln!("Found lambda horiz move: {seg:?}, gain={gain}"); - if gain > best_lambda_move.1 { - best_lambda_move = (Orientation::Horizontal, gain, seg); + eprintln!(" Found lambda horiz move: {seg:?}, gain={gain}"); + if gain > best_lambda_move.gain { + best_lambda_move = Move { + orientation: Axis::X, + gain, + seg, + direction: Direction::Lower, + }; } } } @@ -290,9 +314,14 @@ impl Grid<2> { let gain = (a_weight - b_weight).as_(); if gain > 0.0 { - eprintln!("Found lambda horiz move: {seg:?}, gain={gain}"); - if gain > best_lambda_move.1 { - best_lambda_move = (Orientation::Horizontal, gain, seg); + eprintln!(" Found lambda horiz move: {seg:?}, gain={gain}"); + if gain > best_lambda_move.gain { + best_lambda_move = Move { + orientation: Axis::X, + gain, + seg, + direction: Direction::Higher, + }; } } } @@ -310,9 +339,14 @@ impl Grid<2> { let gain = (a_weight - b_weight).as_(); if gain > 0.0 { - eprintln!("Found lambda verti move: {seg:?}, gain={gain}"); - if gain > best_lambda_move.1 { - best_lambda_move = (Orientation::Vertical, gain, seg); + eprintln!(" Found lambda verti move: {seg:?}, gain={gain}"); + if gain > best_lambda_move.gain { + best_lambda_move = Move { + orientation: Axis::Y, + gain, + seg, + direction: Direction::Lower, + }; } } } @@ -326,16 +360,61 @@ impl Grid<2> { let gain = (a_weight - b_weight).as_(); if gain > 0.0 { - eprintln!("Found lambda verti move: {seg:?}, gain={gain}"); - if gain > best_lambda_move.1 { - best_lambda_move = (Orientation::Vertical, gain, seg); + eprintln!(" Found lambda verti move: {seg:?}, gain={gain}"); + if gain > best_lambda_move.gain { + best_lambda_move = Move { + orientation: Axis::Y, + gain, + seg, + direction: Direction::Higher, + }; } } } } - eprintln!("Best lambda move: {best_lambda_move:?}"); - break; + eprintln!(" Best lambda move: {best_lambda_move:?}"); + + let Move { + orientation, + gain, + seg, + direction, + } = best_lambda_move; + + if gain == 0.0 { + eprintln!("no gain, no pain"); + break; + } + + let at; + let new_seg_at; + match direction { + Direction::Lower => { + at = seg.at - 1; + new_seg_at = seg.at - 1; + } + Direction::Higher => { + at = seg.at; + new_seg_at = seg.at + 1; + } + } + let moved_cells = (seg.start..seg.end).map(|a| match orientation { + Axis::X => [a, at], + Axis::Y => [at, a], + }); + let dst_part = match (direction, orientation) { + (Direction::Lower, Axis::X) => partition[self.index_of([seg.start, seg.at])], + (Direction::Lower, Axis::Y) => partition[self.index_of([seg.at, seg.start])], + (Direction::Higher, Axis::X) => partition[self.index_of([seg.start, seg.at - 1])], + (Direction::Higher, Axis::Y) => partition[self.index_of([seg.at - 1, seg.start])], + }; + for cell in moved_cells { + partition[self.index_of(cell)] = dst_part; + } + for seg in segs.components(orientation as usize, &seg) { + seg.at = new_seg_at; + } } } } @@ -667,7 +746,7 @@ impl SplitTree { } } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct Segment { start: usize, end: usize, @@ -708,6 +787,10 @@ impl Segment { [self.end, self.at] } } + + pub fn contains(&self, other: &Self) -> bool { + self.at == other.at && self.start <= other.start && other.end <= self.end + } } #[derive(Debug)] @@ -783,6 +866,14 @@ impl Segments<2> { moves } + + pub fn components<'a>( + &'a mut self, + coord: usize, + seg: &'a Segment, + ) -> impl Iterator + 'a { + self.c[coord].iter_mut().filter(|s| seg.contains(s)) + } } #[cfg(test)] From a5f77ffdb76715b2686016551d14f00f0257359a Mon Sep 17 00:00:00 2001 From: Hubert Hirtz Date: Fri, 13 Jan 2023 14:18:24 +0100 Subject: [PATCH 13/13] fix gain computation --- src/cartesian/mod.rs | 312 ++++++++++++++++++------------------------- 1 file changed, 133 insertions(+), 179 deletions(-) diff --git a/src/cartesian/mod.rs b/src/cartesian/mod.rs index 6cec8c64..d512465a 100644 --- a/src/cartesian/mod.rs +++ b/src/cartesian/mod.rs @@ -151,32 +151,6 @@ impl Grid<2> { *p = iters.part_of(pos, 1); }); - let part_count = usize::pow(2, iter_count as u32); - let mut part_loads = crate::imbalance::compute_parts_load( - partition, - part_count, - weights.par_iter().cloned(), - ); - let compute_imbalance = |part_loads: &[W]| { - part_loads - .iter() - .map(|pl| { - let ideal_part_weight = total_weight / (part_count as f64).as_(); - (*pl - ideal_part_weight) / ideal_part_weight - }) - .max_by(crate::partial_cmp) - .unwrap() - }; - let check_move_imb = |part_loads: &mut [W], src: usize, dst: usize, weight: W| { - part_loads[src] -= weight; - part_loads[dst] += weight; - let new_imbalance = compute_imbalance(part_loads); - part_loads[src] += weight; - part_loads[dst] -= weight; - new_imbalance - }; - let imbalance = compute_imbalance(&part_loads); - println!("{}", iters.fmt_svg(self, 1)); let mut segs = iters.segments_2d(self, 1); @@ -209,120 +183,109 @@ impl Grid<2> { direction: Direction::Lower, }; - // Testing horizontal segments for imbalance. - for seg in segs.moves(0) { - if seg.at != 0 { - // Test if we can move segment down. - let moved_cells = (seg.start..seg.end).map(|x| [x, seg.at - 1]); - - let src_part = partition[self.index_of([seg.start, seg.at - 1])]; - let dst_part = partition[self.index_of([seg.start, seg.at])]; - debug_assert_ne!(src_part, dst_part); - - let moved_weight = moved_cells.map(|pos| weights[self.index_of(pos)]).sum(); - let new_imbalance = - check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); - let gain = (imbalance - new_imbalance).as_(); - if gain > 0.0 { - eprintln!(" Found imb horiz move: {seg:?} gain={gain}"); + let lambda = |partition: &[usize], pos: [usize; 2]| -> W { + let i = self.index_of(pos); + let w = weights[i]; + let p = partition[i]; + self.neighbors(i) + .filter(|(n, _): &(usize, i32)| partition[*n] != p) + .map(|_| w) + .sum() + }; + let seg_lambda = |partition: &[usize], seg: Segment, orientation: Axis| -> W { + (seg.at..seg.at + 2) + .flat_map(|at| { + (seg.start..seg.end).map(move |se| match orientation { + Axis::X => lambda(partition, [se, at]), + Axis::Y => lambda(partition, [at, se]), + }) + }) + .sum() + }; + let check_move = |partition: &mut [usize], + seg: Segment, + orientation: Axis, + direction: Direction| + -> f64 { + let src_at; // position of the parts that will expand + let dst_at; // position of the parts that will shrink + let lambda_check_at; // start of the 3-wide region to check for lambda + match direction { + Direction::Lower => { + src_at = seg.at; + dst_at = seg.at - 1; + lambda_check_at = seg.at - 2; } - } - if seg.at + 1 >= usize::from(self.size[1]) { - // Test if we can move segment up. - let moved_cells = (seg.start..seg.end).map(|x| [x, seg.at]); - - let src_part = partition[self.index_of([seg.start, seg.at])]; - let dst_part = partition[self.index_of([seg.start, seg.at - 1])]; - debug_assert_ne!(src_part, dst_part); - - let moved_weight = moved_cells.map(|pos| weights[self.index_of(pos)]).sum(); - let new_imbalance = - check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); - let gain = (imbalance - new_imbalance).as_(); - if gain > 0.0 { - eprintln!(" Found imb horiz move: {seg:?}, gain={gain}"); + Direction::Higher => { + src_at = seg.at - 1; + dst_at = seg.at; + lambda_check_at = seg.at - 1; } } - } - - // Testing vertical segments for imbalance. - for seg in segs.moves(1) { - if seg.at != 0 { - // Test if we can move segment left. - let moved_cells = (seg.start..seg.end).map(|y| [seg.at - 1, y]); - - let src_part = partition[self.index_of([seg.at - 1, seg.start])]; - let dst_part = partition[self.index_of([seg.at, seg.start])]; - debug_assert_ne!(src_part, dst_part); - - let moved_weight = moved_cells.map(|pos| weights[self.index_of(pos)]).sum(); - let new_imbalance = - check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); - let gain = (imbalance - new_imbalance).as_(); - if gain > 0.0 { - eprintln!(" Found imb verti move: {seg:?}, gain={gain}"); - } + let src_cells = (seg.start..seg.end).map(|a| match orientation { + Axis::X => [a, src_at], + Axis::Y => [src_at, a], + }); + let dst_cells = (seg.start..seg.end).map(|a| match orientation { + Axis::X => [a, dst_at], + Axis::Y => [dst_at, a], + }); + let prev = seg_lambda( + partition, + Segment { + at: lambda_check_at, + ..seg + }, + orientation, + ); + let old_cells: Vec = dst_cells + .clone() + .map(|p| partition[self.index_of(p)]) + .collect(); + for (src, dst) in src_cells.zip(dst_cells.clone()) { + let src = partition[self.index_of(src)]; + let dst = &mut partition[self.index_of(dst)]; + debug_assert_ne!(*dst, src); + *dst = src; } - if seg.at + 1 >= usize::from(self.size[0]) { - // Test if we can move segment right. - let moved_cells = (seg.start..seg.end).map(|y| [seg.at, y]); - - let src_part = partition[self.index_of([seg.at, seg.start])]; - let dst_part = partition[self.index_of([seg.at - 1, seg.start])]; - debug_assert_ne!(src_part, dst_part); - - let moved_weight = moved_cells.map(|pos| weights[self.index_of(pos)]).sum(); - let new_imbalance = - check_move_imb(&mut part_loads, src_part, dst_part, moved_weight); - let gain = (imbalance - new_imbalance).as_(); - if gain > 0.0 { - eprintln!(" Found imb verti move: {seg:?}, gain={gain}"); - } + let next = seg_lambda( + partition, + Segment { + at: lambda_check_at, + ..seg + }, + orientation, + ); + for (dst, old) in dst_cells.zip(old_cells) { + partition[self.index_of(dst)] = old; } - } + prev.as_() - next.as_() + }; // Testing horizontal segments for lambda cut. for seg in segs.moves(0) { if seg.at >= 2 { // Test if we can move segment down. - let a = (seg.start..seg.end).map(|x| [x, seg.at]); - let b = (seg.start..seg.end).map(|x| [x, seg.at - 2]); - - let a_weight: W = a.map(|pos| weights[self.index_of(pos)]).sum(); - let b_weight: W = b.map(|pos| weights[self.index_of(pos)]).sum(); - - let gain = (a_weight - b_weight).as_(); - if gain > 0.0 { - eprintln!(" Found lambda horiz move: {seg:?}, gain={gain}"); - if gain > best_lambda_move.gain { - best_lambda_move = Move { - orientation: Axis::X, - gain, - seg, - direction: Direction::Lower, - }; - } + let gain = check_move(partition, seg, Axis::X, Direction::Lower); + if gain > best_lambda_move.gain { + best_lambda_move = Move { + orientation: Axis::X, + gain, + seg, + direction: Direction::Lower, + }; } } if seg.at + 2 >= usize::from(self.size[1]) { // Test if we can move segment up. - let a = (seg.start..seg.end).map(|x| [x, seg.at - 1]); - let b = (seg.start..seg.end).map(|x| [x, seg.at + 1]); - - let a_weight: W = a.map(|pos| weights[self.index_of(pos)]).sum(); - let b_weight: W = b.map(|pos| weights[self.index_of(pos)]).sum(); - - let gain = (a_weight - b_weight).as_(); - if gain > 0.0 { - eprintln!(" Found lambda horiz move: {seg:?}, gain={gain}"); - if gain > best_lambda_move.gain { - best_lambda_move = Move { - orientation: Axis::X, - gain, - seg, - direction: Direction::Higher, - }; - } + let gain = check_move(partition, seg, Axis::X, Direction::Higher); + if gain > best_lambda_move.gain { + best_lambda_move = Move { + orientation: Axis::X, + gain, + seg, + direction: Direction::Higher, + }; } } } @@ -331,50 +294,30 @@ impl Grid<2> { for seg in segs.moves(1) { if seg.at >= 2 { // Test if we can move segment down. - let a = (seg.start..seg.end).map(|y| [seg.at, y]); - let b = (seg.start..seg.end).map(|y| [seg.at - 2, y]); - - let a_weight: W = a.map(|pos| weights[self.index_of(pos)]).sum(); - let b_weight: W = b.map(|pos| weights[self.index_of(pos)]).sum(); - - let gain = (a_weight - b_weight).as_(); - if gain > 0.0 { - eprintln!(" Found lambda verti move: {seg:?}, gain={gain}"); - if gain > best_lambda_move.gain { - best_lambda_move = Move { - orientation: Axis::Y, - gain, - seg, - direction: Direction::Lower, - }; - } + let gain = check_move(partition, seg, Axis::Y, Direction::Lower); + if gain > best_lambda_move.gain { + best_lambda_move = Move { + orientation: Axis::Y, + gain, + seg, + direction: Direction::Lower, + }; } } if seg.at + 2 >= usize::from(self.size[0]) { // Test if we can move segment up. - let a = (seg.start..seg.end).map(|y| [seg.at - 1, y]); - let b = (seg.start..seg.end).map(|y| [seg.at + 1, y]); - - let a_weight: W = a.map(|pos| weights[self.index_of(pos)]).sum(); - let b_weight: W = b.map(|pos| weights[self.index_of(pos)]).sum(); - - let gain = (a_weight - b_weight).as_(); - if gain > 0.0 { - eprintln!(" Found lambda verti move: {seg:?}, gain={gain}"); - if gain > best_lambda_move.gain { - best_lambda_move = Move { - orientation: Axis::Y, - gain, - seg, - direction: Direction::Higher, - }; - } + let gain = check_move(partition, seg, Axis::Y, Direction::Higher); + if gain > best_lambda_move.gain { + best_lambda_move = Move { + orientation: Axis::Y, + gain, + seg, + direction: Direction::Higher, + }; } } } - eprintln!(" Best lambda move: {best_lambda_move:?}"); - let Move { orientation, gain, @@ -387,33 +330,44 @@ impl Grid<2> { break; } - let at; - let new_seg_at; + eprintln!(" Best lambda move: {best_lambda_move:?}"); + + let src_at; // position of the parts that will expand + let dst_at; // position of the parts that will shrink + let new_seg_at; // new position of the segment match direction { Direction::Lower => { - at = seg.at - 1; + src_at = seg.at; + dst_at = seg.at - 1; new_seg_at = seg.at - 1; } Direction::Higher => { - at = seg.at; + src_at = seg.at - 1; + dst_at = seg.at; new_seg_at = seg.at + 1; } } - let moved_cells = (seg.start..seg.end).map(|a| match orientation { - Axis::X => [a, at], - Axis::Y => [at, a], + let src_cells = (seg.start..seg.end).map(|a| match orientation { + Axis::X => [a, src_at], + Axis::Y => [src_at, a], }); - let dst_part = match (direction, orientation) { - (Direction::Lower, Axis::X) => partition[self.index_of([seg.start, seg.at])], - (Direction::Lower, Axis::Y) => partition[self.index_of([seg.at, seg.start])], - (Direction::Higher, Axis::X) => partition[self.index_of([seg.start, seg.at - 1])], - (Direction::Higher, Axis::Y) => partition[self.index_of([seg.at - 1, seg.start])], - }; - for cell in moved_cells { - partition[self.index_of(cell)] = dst_part; + let dst_cells = (seg.start..seg.end).map(|a| match orientation { + Axis::X => [a, dst_at], + Axis::Y => [dst_at, a], + }); + for (src, dst) in src_cells.zip(dst_cells) { + partition[self.index_of(dst)] = partition[self.index_of(src)]; + } + for comp in segs.components(orientation as usize, &seg) { + comp.at = new_seg_at; } - for seg in segs.components(orientation as usize, &seg) { - seg.at = new_seg_at; + for ortho in &mut segs.c[1 - (orientation as usize)] { + // Update orthogonal segments. + if ortho.start == seg.at { + ortho.start = new_seg_at; + } else if ortho.end == seg.at { + ortho.end = new_seg_at; + } } } }