From 7e38e90631b231289264d7e7763789d2aeea3ef1 Mon Sep 17 00:00:00 2001 From: Hubert Hirtz Date: Tue, 28 Jun 2022 15:13:07 +0200 Subject: [PATCH 1/2] vn/first: remove unused trait bounds --- src/algorithms/vn/first.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/algorithms/vn/first.rs b/src/algorithms/vn/first.rs index 96156c41..64f15282 100644 --- a/src/algorithms/vn/first.rs +++ b/src/algorithms/vn/first.rs @@ -201,7 +201,7 @@ where pub trait VnFirstWeight where Self: Clone + Send + Sync, - Self: Sum + PartialOrd + num::FromPrimitive + num::Zero + num::One, + Self: Sum + PartialOrd + num::Zero, Self: Sub + AddAssign, { } @@ -209,7 +209,7 @@ where impl VnFirstWeight for T where Self: Clone + Send + Sync, - Self: Sum + PartialOrd + num::FromPrimitive + num::Zero + num::One, + Self: Sum + PartialOrd + num::Zero, Self: Sub + AddAssign, { } From f9a1e664281087f6332dfce485a3b0b0e7e73ab5 Mon Sep 17 00:00:00 2001 From: Hubert Hirtz Date: Thu, 24 Mar 2022 15:49:04 +0100 Subject: [PATCH 2/2] vn/first: multi-criteria support --- src/algorithms/vn/first.rs | 168 ++++++++----------------------------- 1 file changed, 34 insertions(+), 134 deletions(-) diff --git a/src/algorithms/vn/first.rs b/src/algorithms/vn/first.rs index 64f15282..05e8a5a5 100644 --- a/src/algorithms/vn/first.rs +++ b/src/algorithms/vn/first.rs @@ -1,14 +1,10 @@ use crate::Error; use itertools::Itertools as _; -use num::FromPrimitive; -use num::One; -use num::Zero; use rayon::iter::IntoParallelRefIterator as _; use rayon::iter::ParallelIterator as _; use std::iter::Sum; use std::ops::AddAssign; use std::ops::Sub; -use std::ops::SubAssign; fn vn_first_mono( partition: &mut [usize], @@ -39,18 +35,20 @@ where let (min_load, mut max_load) = part_loads.iter().cloned().minmax().into_option().unwrap(); let mut imbalance = max_load.clone() - min_load; - let mut i = weights.len(); + let mut i = 0; let mut i_last = 0; let mut algo_iterations = 0; - while i != i_last { - i = (i + 1) % weights.len(); - + loop { // loop through the weights. let p = partition[i]; if part_loads[p] < max_load { // weight #i is not in the heaviest partition, and thus the move // will not reduce the max imbalance. + i = (i + 1) % weights.len(); + if i == i_last { + break; + } continue; } @@ -66,135 +64,29 @@ where let (new_min_load, new_max_load) = part_loads.iter().cloned().minmax().into_option().unwrap(); let new_imbalance = new_max_load.clone() - new_min_load.clone(); - if imbalance < new_imbalance { + if new_imbalance <= imbalance { + // The move decreases the partition imbalance. + imbalance = new_imbalance; + max_load = new_max_load; + partition[i] = q; + i_last = i; + } else { // The move does not decrease the partition imbalance. part_loads[p] += weights[i].clone(); part_loads[q] = part_loads[p].clone() - weights[i].clone(); continue; } - imbalance = new_imbalance; - max_load = new_max_load; - partition[i] = q; - i_last = i; - } - - algo_iterations += 1; - } - - Ok(algo_iterations) -} - -#[allow(dead_code)] -pub fn vn_first( - partition: &mut [usize], - criteria: &[[T; C]], - num_parts: usize, -) -> usize -where - T: AddAssign + SubAssign + Sub + Sum, - T: Zero + One + FromPrimitive, - T: Copy + Ord, -{ - if num_parts < 2 || criteria.is_empty() || C == 0 { - return 0; - } - - assert_eq!(criteria.len(), partition.len()); - - let mut part_loads_per_criterion = { - let mut loads = vec![[T::zero(); C]; num_parts]; - for (w, weight) in criteria.iter().enumerate() { - for (part_load, criterion) in loads[partition[w]].iter_mut().zip(weight) { - *part_load += *criterion; - } - } - loads - }; - let total_weight_per_criterion = { - // TODO replace with .collect() once [_; C] implements FromIterator. - let mut ws = [T::zero(); C]; - for c in 0..C { - ws[c] = part_loads_per_criterion[c].iter().cloned().sum(); - } - ws - }; - if total_weight_per_criterion.contains(&T::zero()) { - return 0; - } - - let min_max_loads = |part_loads_per_criterion: &Vec<[T; C]>| -> [(T, T); C] { - // TODO replace with .collect() once [_; C] implements FromIterator. - let mut imbs = [(T::zero(), T::zero()); C]; - for c in 0..C { - imbs[c] = part_loads_per_criterion[c] - .iter() - .cloned() - .minmax() - .into_option() - .unwrap(); - } - imbs - }; - - let (global_min_load, mut global_max_load) = *min_max_loads(&part_loads_per_criterion) - .iter() - .max_by_key(|(min_load, max_load)| *max_load - *min_load) - .unwrap(); - let mut imbalance = global_max_load - global_min_load; - - let mut i = 0; - let mut i_last = 0; - let mut algo_iterations = 0; - while i != i_last { - i = (i + 1) % criteria.len(); - - // loop through the weights. - let p = partition[i]; - - if part_loads_per_criterion[p] - .iter() - .all(|criterion_load| *criterion_load < global_max_load) - { - // weight #i is not in the heaviest partition, and thus the move - // will not reduce the max imbalance. - continue; } - for q in 0..num_parts { - // loop through the parts. - if p == q { - // weight #i is already in partition #q. - continue; - } - - for c in 0..C { - part_loads_per_criterion[p][c] -= criteria[i][c]; - part_loads_per_criterion[q][c] += criteria[i][c]; - } - let (new_global_min_load, new_global_max_load) = - *min_max_loads(&part_loads_per_criterion) - .iter() - .max_by_key(|(min_load, max_load)| *max_load - *min_load) - .unwrap(); - let new_imbalance = new_global_max_load - new_global_min_load; - if imbalance < new_imbalance { - // The move does not decrease the partition imbalance. - for c in 0..C { - part_loads_per_criterion[p][c] += criteria[i][c]; - part_loads_per_criterion[q][c] -= criteria[i][c]; - } - continue; - } - imbalance = new_imbalance; - global_max_load = new_global_max_load; - partition[i] = q; - i_last = i; + i = (i + 1) % weights.len(); + if i == i_last { + break; } algo_iterations += 1; } - algo_iterations + Ok(algo_iterations) } /// Trait alias for values accepted as weights by [VnFirst]. @@ -306,18 +198,26 @@ mod tests { #[test] fn small() { const W: [[i32; 2]; 6] = [[1, 2], [2, 4], [3, 6], [8, 4], [10, 5], [12, 6]]; - let mut part = [0; W.len()]; + let w: Vec<_> = W + .iter() + .map(|w| nalgebra::SVector::::from(*w)) + .collect(); + let mut partition = [0; W.len()]; - vn_first(&mut part, &W, 1); + vn_first_mono(&mut partition, &w, 2).unwrap(); + println!("partition: {partition:?}"); let imbs_ini: Vec = (0..W[0].len()) - .map(|c| imbalance::max_imbalance(2, &part, W.par_iter().map(|w| w[c]))) + .map(|c| imbalance::max_imbalance(2, &partition, W.par_iter().map(|w| w[c]))) + .collect(); + vn_first_mono(&mut partition, &w, 2).unwrap(); + println!("partition: {partition:?}"); + let imbs_end: Vec = (0..W[0].len()) + .map(|c| imbalance::max_imbalance(2, &partition, W.par_iter().map(|w| w[c]))) .collect(); - vn_first(&mut part, &W, 2); - let imbs_end = - (0..W[0].len()).map(|c| imbalance::max_imbalance(2, &part, W.par_iter().map(|w| w[c]))); + println!("imbalances: {imbs_end:?} < {imbs_ini:?}"); for (imb_ini, imb_end) in imbs_ini.into_iter().zip(imbs_end) { + println!("imbalance: {imb_end} < {imb_ini}"); assert!(imb_end <= imb_ini); - println!("imbalance : {} < {}", imb_end, imb_ini); } } @@ -329,7 +229,7 @@ mod tests { (weights, mut partition) in (2..200_usize).prop_flat_map(|num_weights| { let weights = prop::collection::vec( - (1..1000_i32, 1..1000_i32).prop_map(|(a, b)| [a, b]), + (1..1000_i32, 1..1000_i32).prop_map(|(a, b)| nalgebra::SVector::::new(a, b)), num_weights ); let partition = prop::collection::vec(0..1_usize, num_weights); @@ -339,7 +239,7 @@ mod tests { let imbs_ini: Vec = (0..C) .map(|c| imbalance::max_imbalance(2, &partition, weights.par_iter().map(|w| w[c]))) .collect(); - vn_first(&mut partition, &weights, 2); + vn_first_mono(&mut partition, &weights, 2).unwrap(); let imbs_end: Vec = (0..C) .map(|c| imbalance::max_imbalance(2, &partition, weights.par_iter().map(|w| w[c]))) .collect();