diff --git a/src/coverage.rs b/src/coverage.rs index e5ea938..854d60d 100644 --- a/src/coverage.rs +++ b/src/coverage.rs @@ -34,9 +34,9 @@ impl Coverage { /// find a single uncovered (row, col) pair. Iterates in col, row order. #[inline] pub fn find_uncovered_col_row(&self, mut f: F) -> Option<(usize, usize)> - where F: FnMut((usize, usize)) -> bool + where + F: FnMut((usize, usize)) -> bool, { - let n = self.n(); for col in 0..n { @@ -61,7 +61,8 @@ impl Coverage { /// iterates over all uncovered (row, col) pairs in row, col order #[inline] pub fn iter_uncovered_row_col(&self, mut f: F) - where F: FnMut((usize, usize)) + where + F: FnMut((usize, usize)), { let n = self.n(); @@ -83,7 +84,8 @@ impl Coverage { /// iterates over all uncovered (row, col) pairs in row, col order, and set covered if f returns true. #[inline] pub fn iter_uncovered_row_col_and_cover(&mut self, mut f: F) - where F: FnMut((usize, usize)) -> bool + where + F: FnMut((usize, usize)) -> bool, { let n = self.n(); diff --git a/src/lib.rs b/src/lib.rs index afb625c..4fc61ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,13 +8,10 @@ /// This code is derived from a port of the Python version found here: /// https://github.com/bmc/munkres/blob/master/munkres.py /// which is Copyright (c) 2008 Brian M. Clapper. - - // TODO: // * Cleanup // * More test cases // * Non-square matrices - extern crate fixedbitset; extern crate ndarray; @@ -36,21 +33,73 @@ mod coverage; mod mark_matrix; pub mod weight_matrix; -pub trait WeightNum: PartialOrd + Copy + Sub + Add { +pub trait WeightNum: PartialOrd + Copy + Sub + Add { fn is_zero(&self) -> bool; - fn is_disallowed(&self) -> bool { false } + fn is_disallowed(&self) -> bool { + false + } } -impl WeightNum for usize { #[inline(always)] fn is_zero(&self) -> bool { *self == 0 } } -impl WeightNum for isize { #[inline(always)] fn is_zero(&self) -> bool { *self == 0 } } -impl WeightNum for u64 { #[inline(always)] fn is_zero(&self) -> bool { *self == 0 } } -impl WeightNum for i64 { #[inline(always)] fn is_zero(&self) -> bool { *self == 0 } } -impl WeightNum for u32 { #[inline(always)] fn is_zero(&self) -> bool { *self == 0 } } -impl WeightNum for i32 { #[inline(always)] fn is_zero(&self) -> bool { *self == 0 } } -impl WeightNum for u16 { #[inline(always)] fn is_zero(&self) -> bool { *self == 0 } } -impl WeightNum for i16 { #[inline(always)] fn is_zero(&self) -> bool { *self == 0 } } -impl WeightNum for u8 { #[inline(always)] fn is_zero(&self) -> bool { *self == 0 } } -impl WeightNum for i8 { #[inline(always)] fn is_zero(&self) -> bool { *self == 0 } } +impl WeightNum for usize { + #[inline(always)] + fn is_zero(&self) -> bool { + *self == 0 + } +} +impl WeightNum for isize { + #[inline(always)] + fn is_zero(&self) -> bool { + *self == 0 + } +} +impl WeightNum for u64 { + #[inline(always)] + fn is_zero(&self) -> bool { + *self == 0 + } +} +impl WeightNum for i64 { + #[inline(always)] + fn is_zero(&self) -> bool { + *self == 0 + } +} +impl WeightNum for u32 { + #[inline(always)] + fn is_zero(&self) -> bool { + *self == 0 + } +} +impl WeightNum for i32 { + #[inline(always)] + fn is_zero(&self) -> bool { + *self == 0 + } +} +impl WeightNum for u16 { + #[inline(always)] + fn is_zero(&self) -> bool { + *self == 0 + } +} +impl WeightNum for i16 { + #[inline(always)] + fn is_zero(&self) -> bool { + *self == 0 + } +} +impl WeightNum for u8 { + #[inline(always)] + fn is_zero(&self) -> bool { + *self == 0 + } +} +impl WeightNum for i8 { + #[inline(always)] + fn is_zero(&self) -> bool { + *self == 0 + } +} impl WeightNum for f64 { #[inline(always)] @@ -105,7 +154,8 @@ enum Step { /// For each row of the matrix, find the smallest element and /// subtract it from every element in its row. Go to Step 2. fn step1(c: &mut W) -> Step - where W: Weights +where + W: Weights, { c.sub_min_of_each_row(); return Step::Step2; @@ -115,9 +165,9 @@ fn step1(c: &mut W) -> Step /// zero in its row or column, star Z. Repeat for each element in the /// matrix. Go to Step 3. fn step2(c: &W, marks: &mut MarkMatrix, cov: &mut Coverage) -> Step - where W: Weights +where + W: Weights, { - let n = c.n(); assert!(marks.n() == n); @@ -143,9 +193,9 @@ fn step2(c: &W, marks: &mut MarkMatrix, cov: &mut Coverage) -> Step /// covered, the starred zeros describe a complete set of unique /// assignments. In this case, Go to DONE, otherwise, Go to Step 4. fn step3(c: &W, marks: &MarkMatrix, cov: &mut Coverage) -> Step - where W: Weights +where + W: Weights, { - let n = c.n(); assert!(marks.n() == n); @@ -172,16 +222,15 @@ fn step3(c: &W, marks: &MarkMatrix, cov: &mut Coverage) -> Step /// zero. Continue in this manner until there are no uncovered zeros /// left. Save the smallest uncovered value and Go to Step 6. fn step4(c: &W, marks: &mut MarkMatrix, cov: &mut Coverage) -> Step - where W: Weights +where + W: Weights, { - let n = c.n(); assert!(marks.n() == n); assert!(cov.n() == n); loop { - // find uncovered zero element match cov.find_uncovered_col_row(|pos| c.is_element_zero(pos)) { None => { @@ -202,7 +251,6 @@ fn step4(c: &W, marks: &mut MarkMatrix, cov: &mut Coverage) -> Step } } } - } /// Construct a series of alternating primed and starred zeros as @@ -213,7 +261,12 @@ fn step4(c: &W, marks: &mut MarkMatrix, cov: &mut Coverage) -> Step /// that has no starred zero in its column. Unstar each starred zero /// of the series, star each primed zero of the series, erase all /// primes and uncover every line in the matrix. Return to Step 3 -fn step5(marks: &mut MarkMatrix, cov: &mut Coverage, z0: (usize, usize), path: &mut Vec<(usize, usize)>) -> Step { +fn step5( + marks: &mut MarkMatrix, + cov: &mut Coverage, + z0: (usize, usize), + path: &mut Vec<(usize, usize)>, +) -> Step { let n = cov.n(); assert!(marks.n() == n); @@ -252,15 +305,14 @@ fn step5(marks: &mut MarkMatrix, cov: &mut Coverage, z0: (usize, usize), path: & return Step::Step3; } - /// Add the value found in Step 4 to every element of each covered /// row, and subtract it from every element of each uncovered column. /// Return to Step 4 without altering any stars, primes, or covered /// lines. fn step6(c: &mut W, cov: &Coverage) -> Step - where W: Weights +where + W: Weights, { - let n = c.n(); assert!(cov.n() == n); @@ -276,9 +328,7 @@ fn step6(c: &mut W, cov: &Coverage) -> Step elm } } - None => { - elm - } + None => elm, }); }); @@ -298,7 +348,8 @@ fn step6(c: &mut W, cov: &Coverage) -> Step } pub fn solve_assignment(weights: &mut W) -> Vec<(usize, usize)> - where W: Weights +where + W: Weights, { if !weights.is_solvable() { panic!("Matrix can not be solved"); @@ -313,9 +364,7 @@ pub fn solve_assignment(weights: &mut W) -> Vec<(usize, usize)> let mut step = Step::Step1; loop { match step { - Step::Step1 => { - step = step1(weights) - } + Step::Step1 => step = step1(weights), Step::Step2 => { step = step2(weights, &mut marks, &mut coverage); } @@ -365,7 +414,6 @@ fn test_step1() { assert_eq!(exp, weights.as_slice()); } - #[test] fn test_step2() { let c = vec![0, 150, 100, 50, 250, 0, 0, 200, 50]; @@ -569,7 +617,6 @@ fn test_step5() { assert_eq!(true, marks.is_none((2, 2))); } - #[test] fn test_solve() { let c = vec![250, 400, 350, // row 1 @@ -586,10 +633,7 @@ fn test_solve() { #[test] fn test_solve_equal_rows_stepwise() { const N: usize = 2; - let c = vec![ - 1, 1, - 2, 2, - ]; + let c = vec![1, 1, 2, 2]; let mut weights: WeightMatrix = WeightMatrix::from_row_vec(N, c); @@ -612,10 +656,10 @@ fn test_solve_equal_rows_stepwise() { assert_eq!(Step::Step3, next_step); assert!(coverage.is_clear()); - assert!(marks.is_star((0,0))); - assert!(marks.is_star((1,1))); - assert!(marks.is_none((0,1))); - assert!(marks.is_none((1,0))); + assert!(marks.is_star((0, 0))); + assert!(marks.is_star((1, 1))); + assert!(marks.is_none((0, 1))); + assert!(marks.is_none((1, 0))); // step 3 let next_step = step3(&weights, &mut marks, &mut coverage); @@ -625,10 +669,7 @@ fn test_solve_equal_rows_stepwise() { #[test] fn test_solve_equal_rows2() { const N: usize = 2; - let c = vec![ - 1, 1, - 2, 2, - ]; + let c = vec![1, 1, 2, 2]; let mut weights: WeightMatrix = WeightMatrix::from_row_vec(N, c.clone()); let matching = solve_assignment(&mut weights); @@ -647,11 +688,7 @@ fn test_solve_equal_rows2() { fn test_solve_equal_rows5() { const N: usize = 5; let c = vec![ - 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ]; let mut weights: WeightMatrix = WeightMatrix::from_row_vec(N, c.clone()); @@ -671,11 +708,8 @@ fn test_solve_equal_rows5() { fn test_solve_equal_rows5_float() { const N: usize = 5; let c = vec![ - 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, - 1.0, 1.0, 1.0, 1.0, 1.0, - 1.0, 1.0, 1.0, 1.0, 1.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, ]; let mut weights: WeightMatrix = WeightMatrix::from_row_vec(N, c.clone()); @@ -695,11 +729,8 @@ fn test_solve_equal_rows5_float() { fn test_solve_equal_rows5_float2() { const N: usize = 5; let c = vec![ - 1.0, 1.0, 1.0, 1.0, 1.0, - 1.0, 1.0, 1.0, 1.0, 1.0, - 1.0, 1.0, 1.0, 1.0, 1.0, - 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, ]; let mut weights: WeightMatrix = WeightMatrix::from_row_vec(N, c.clone()); @@ -719,16 +750,12 @@ fn test_solve_equal_rows5_float2() { fn test_solve_random10() { const N: usize = 10; let c = vec![ - 612, 643, 717, 2, 946, 534, 242, 235, 376, 839, - 224, 141, 799, 180, 386, 745, 592, 822, 421, 42, - 241, 369, 831, 67, 258, 549, 615, 529, 458, 524, - 231, 649, 287, 910, 12, 820, 31, 92, 217, 555, - 912, 81, 568, 241, 292, 653, 417, 652, 630, 788, - 32, 822, 788, 166, 122, 690, 304, 568, 449, 214, - 441, 469, 584, 633, 213, 414, 498, 500, 317, 391, - 798, 581, 183, 420, 16, 748, 35, 516, 639, 356, - 351, 921, 67, 33, 592, 775, 780, 335, 464, 788, - 771, 455, 950, 25, 22, 576, 969, 122, 86, 74, + 612, 643, 717, 2, 946, 534, 242, 235, 376, 839, 224, 141, 799, 180, 386, 745, 592, 822, + 421, 42, 241, 369, 831, 67, 258, 549, 615, 529, 458, 524, 231, 649, 287, 910, 12, 820, 31, + 92, 217, 555, 912, 81, 568, 241, 292, 653, 417, 652, 630, 788, 32, 822, 788, 166, 122, 690, + 304, 568, 449, 214, 441, 469, 584, 633, 213, 414, 498, 500, 317, 391, 798, 581, 183, 420, + 16, 748, 35, 516, 639, 356, 351, 921, 67, 33, 592, 775, 780, 335, 464, 788, 771, 455, 950, + 25, 22, 576, 969, 122, 86, 74, ]; let mut weights: WeightMatrix = WeightMatrix::from_row_vec(N, c.clone()); @@ -743,7 +770,18 @@ fn test_solve_random10() { assert_eq!(1071, cost); - let exp = &[(0, 7), (1, 9), (2, 3), (3, 4), (4, 1), (5, 0), (6, 5), (7, 6), (8, 2), (9, 8)]; + let exp = &[ + (0, 7), + (1, 9), + (2, 3), + (3, 4), + (4, 1), + (5, 0), + (6, 5), + (7, 6), + (8, 2), + (9, 8), + ]; assert_eq!(exp, &matching[..]); } @@ -751,9 +789,15 @@ fn test_solve_random10() { #[test] fn test_disallowed() { let c = vec![ - 250.0, 400.0, 350.0, - 400.0, 600.0, f32::INFINITY, - 200.0, 400.0, 250.0 + 250.0, + 400.0, + 350.0, + 400.0, + 600.0, + f32::INFINITY, + 200.0, + 400.0, + 250.0, ]; let mut weights: WeightMatrix = WeightMatrix::from_row_vec(3, c); @@ -767,9 +811,15 @@ fn test_disallowed() { fn test_unsolvable() { const N: usize = 3; let c = vec![ - 1.0, 1.0, 1.0, - f32::INFINITY, f32::INFINITY, f32::INFINITY, - 1.0, 1.0, 1.0, + 1.0, + 1.0, + 1.0, + f32::INFINITY, + f32::INFINITY, + f32::INFINITY, + 1.0, + 1.0, + 1.0, ]; let mut weights: WeightMatrix = WeightMatrix::from_row_vec(N, c.clone()); diff --git a/src/mark_matrix.rs b/src/mark_matrix.rs index b02e77c..6fb42c7 100644 --- a/src/mark_matrix.rs +++ b/src/mark_matrix.rs @@ -16,7 +16,7 @@ pub struct MarkMatrix { impl MarkMatrix { pub fn new(n: usize) -> MarkMatrix { MarkMatrix { - marks: SquareMatrix::from_shape_fn((n, n), |_| Mark::None), + marks: SquareMatrix::from_shape_fn((n, n), |_| Mark::None), } } @@ -69,7 +69,8 @@ impl MarkMatrix { #[inline] pub fn each_star(&self, mut f: F) - where F: FnMut((usize, usize)) + where + F: FnMut((usize, usize)), { let n = self.n(); diff --git a/src/weight_matrix.rs b/src/weight_matrix.rs index 39f3499..52db07c 100644 --- a/src/weight_matrix.rs +++ b/src/weight_matrix.rs @@ -31,24 +31,22 @@ impl Weights for WeightMatrix { // Add `val` to every element in row `row`. fn add_row(&mut self, row: usize, val: T) { - self.c.row_mut(row).mapv_inplace(|cur| { - if cur.is_disallowed() { - cur - } else { - cur + val - } - }); + self.c + .row_mut(row) + .mapv_inplace(|cur| if cur.is_disallowed() { cur } else { cur + val }); } // Subtract `val` from every element in column `col`. fn sub_col(&mut self, col: usize, val: T) { - self.c.column_mut(col).mapv_inplace(|cur| { - if cur.is_disallowed() { - cur - } else { - cur - val - } - }); + self.c.column_mut(col).mapv_inplace( + |cur| { + if cur.is_disallowed() { + cur + } else { + cur - val + } + }, + ); } fn is_solvable(&self) -> bool { @@ -63,12 +61,16 @@ impl Weights for WeightMatrix { impl WeightMatrix { pub fn from_row_vec(n: usize, data: Vec) -> WeightMatrix { - WeightMatrix { c: SquareMatrix::from_shape_vec((n, n), data).unwrap() } + WeightMatrix { + c: SquareMatrix::from_shape_vec((n, n), data).unwrap(), + } } pub fn from_fn T>(n: usize, f: F) -> WeightMatrix { assert!(n > 0); - WeightMatrix { c: SquareMatrix::from_shape_fn((n, n), f) } + WeightMatrix { + c: SquareMatrix::from_shape_fn((n, n), f), + } } /// Return the minimum element of row `row`. @@ -85,13 +87,9 @@ impl WeightMatrix { // Subtract `val` from every element in row `row`. fn sub_row(&mut self, row: usize, val: T) { - self.c.row_mut(row).mapv_inplace(|cur| { - if cur.is_disallowed() { - cur - } else { - cur - val - } - }); + self.c + .row_mut(row) + .mapv_inplace(|cur| if cur.is_disallowed() { cur } else { cur - val }); } pub fn as_slice(&self) -> &[T] { @@ -103,7 +101,10 @@ impl WeightMatrix { fn test_weight_matrix() { assert_eq!(0, WeightMatrix::from_row_vec(1, vec![0]).min_of_row(0)); assert_eq!(1, WeightMatrix::from_row_vec(1, vec![1]).min_of_row(0)); - assert_eq!(1, WeightMatrix::from_row_vec(2, vec![5, 1, 0, 0]).min_of_row(0)); + assert_eq!( + 1, + WeightMatrix::from_row_vec(2, vec![5, 1, 0, 0]).min_of_row(0) + ); let mut mat = WeightMatrix::from_row_vec(2, vec![0, 1, 2, 3]); mat.sub_row(1, 1);