diff --git a/Cargo.toml b/Cargo.toml index 4adf2ab1..36047af0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ sparse = [ ] debug = [ "approx/num-complex", "rand/std" ] alloc = [ ] io = [ "pest", "pest_derive" ] +compare = [ "matrixcompare-core" ] [dependencies] typenum = "1.11" @@ -50,6 +51,7 @@ mint = { version = "0.5", optional = true } quickcheck = { version = "0.9", optional = true } pest = { version = "2.0", optional = true } pest_derive = { version = "2.0", optional = true } +matrixcompare-core = { version = "0.1", optional = true } [dev-dependencies] serde_json = "1.0" @@ -61,6 +63,9 @@ rand_isaac = "0.2" ### https://github.com/rust-lang/cargo/issues/4866 #criterion = "0.2.10" +# For matrix comparison macro +matrixcompare = "0.1.3" + [workspace] members = [ "nalgebra-lapack", "nalgebra-glm" ] diff --git a/Makefile b/Makefile index 01707964..0af9f3f2 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ all: - cargo test --features "debug arbitrary serde-serialize abomonation-serialize" + cargo test --features "debug arbitrary serde-serialize abomonation-serialize compare" # cargo check --features "debug arbitrary serde-serialize" doc: @@ -9,4 +9,4 @@ bench: cargo bench test: - cargo test --features "debug arbitrary serde-serialize abomonation-serialize" + cargo test --features "debug arbitrary serde-serialize abomonation-serialize compare" diff --git a/examples/matrixcompare.rs b/examples/matrixcompare.rs new file mode 100644 index 00000000..713be25f --- /dev/null +++ b/examples/matrixcompare.rs @@ -0,0 +1,82 @@ +extern crate nalgebra as na; + +use matrixcompare::comparators::{AbsoluteElementwiseComparator, ExactElementwiseComparator}; +use matrixcompare::compare_matrices; +use na::{MatrixMN, U3, U4}; + +fn compare_integers_fail() { + println!("Comparing two integer matrices."); + + #[rustfmt::skip] + let a = MatrixMN::<_, U3, U4>::from_row_slice(&[ + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, -2, 11 + ]); + + #[rustfmt::skip] + let b = MatrixMN::<_, U3, U4>::from_row_slice(&[ + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11 + ]); + + if let Err(err) = compare_matrices(a, b, &ExactElementwiseComparator) { + println!("{}", err); + } +} + +fn compare_different_size() { + println!("Comparing matrices of different size."); + #[rustfmt::skip] + let a = MatrixMN::<_, U3, U3>::from_row_slice(&[ + 0, 1, 2, + 4, 5, 6, + 8, 9, 10, + ]); + + #[rustfmt::skip] + let b = MatrixMN::<_, U3, U4>::from_row_slice(&[ + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11 + ]); + + if let Err(err) = compare_matrices(a, b, &ExactElementwiseComparator) { + println!("{}", err); + } +} + +fn compare_f64_abs_tol_fail() { + println!("Comparing two f64 matrices."); + + #[rustfmt::skip] + let a = MatrixMN::::from_row_slice(&[ + 0.0, 1.0, 2.0 + 1e-10, + 4.0, 5.0, 6.0, + 8.0, 9.0, 10.0, + ]); + + #[rustfmt::skip] + let b = MatrixMN::<_, U3, U3>::from_row_slice(&[ + 0.0, 1.0, 2.0, + 4.0, 5.0, 6.0, + 8.0, 9.0, 10.0 + ]); + + let cmp = AbsoluteElementwiseComparator { tol: 1e-12 }; + if let Err(err) = compare_matrices(a, b, &cmp) { + println!("{}", err); + } +} + +fn main() { + // This example mostly serves the purpose of demonstrating the kind of error messages + // that are given upon comparison failure. + // The more typical use case is using `assert_matrix_eq!` in tests. + compare_integers_fail(); + println!("======================================================"); + compare_f64_abs_tol_fail(); + println!("======================================================"); + compare_different_size(); +} diff --git a/src/base/matrix.rs b/src/base/matrix.rs index d30773a3..6e6aaba5 100644 --- a/src/base/matrix.rs +++ b/src/base/matrix.rs @@ -159,6 +159,32 @@ impl Abomonation for Matrix> matrixcompare_core::Matrix + for Matrix +{ + fn rows(&self) -> usize { + self.nrows() + } + + fn cols(&self) -> usize { + self.ncols() + } + + fn access(&self) -> matrixcompare_core::Access { + matrixcompare_core::Access::Dense(self) + } +} + +#[cfg(feature = "compare")] +impl> matrixcompare_core::DenseAccess + for Matrix +{ + fn fetch_single(&self, row: usize, col: usize) -> N { + self.index((row, col)).clone() + } +} + impl Matrix { /// Creates a new matrix with the given data without statically checking that the matrix /// dimension matches the storage dimension. diff --git a/tests/core/matrixcompare.rs b/tests/core/matrixcompare.rs new file mode 100644 index 00000000..df112173 --- /dev/null +++ b/tests/core/matrixcompare.rs @@ -0,0 +1,85 @@ +//! Tests for `matrixcompare` integration. +//! +//! The `matrixcompare` crate itself is responsible for testing the actual comparison. +//! The tests here only check that the necessary trait implementations are correctly implemented, +//! in addition to some sanity checks with example input. + +use nalgebra::{DMatrix, MatrixMN, U4, U5}; + +use matrixcompare::{assert_matrix_eq, DenseAccess}; + +#[cfg(feature = "arbitrary")] +quickcheck! { + fn fetch_single_is_equivalent_to_index_f64(matrix: DMatrix) -> bool { + for i in 0 .. matrix.nrows() { + for j in 0 .. matrix.ncols() { + if matrix.fetch_single(i, j) != *matrix.index((i, j)) { + return false; + } + } + } + + true + } + + fn matrixcompare_shape_agrees_with_matrix(matrix: DMatrix) -> bool { + matrix.nrows() == as matrixcompare::Matrix>::rows(&matrix) + && + matrix.ncols() == as matrixcompare::Matrix>::cols(&matrix) + } +} + +#[test] +fn assert_matrix_eq_dense_positive_comparison() { + #[rustfmt::skip] + let a = MatrixMN::<_, U4, U5>::from_row_slice(&[ + 1210, 1320, 1430, 1540, 1650, + 2310, 2420, 2530, 2640, 2750, + 3410, 3520, 3630, 3740, 3850, + 4510, 4620, 4730, 4840, 4950, + ]); + + #[rustfmt::skip] + let b = MatrixMN::<_, U4, U5>::from_row_slice(&[ + 1210, 1320, 1430, 1540, 1650, + 2310, 2420, 2530, 2640, 2750, + 3410, 3520, 3630, 3740, 3850, + 4510, 4620, 4730, 4840, 4950, + ]); + + // Test matrices of static size + assert_matrix_eq!(a, b); + assert_matrix_eq!(&a, b); + assert_matrix_eq!(a, &b); + assert_matrix_eq!(&a, &b); + + // Test matrices of dynamic size + let a_dyn = a.index((0..4, 0..5)); + let b_dyn = b.index((0..4, 0..5)); + assert_matrix_eq!(a_dyn, b_dyn); + assert_matrix_eq!(a_dyn, &b_dyn); + assert_matrix_eq!(&a_dyn, b_dyn); + assert_matrix_eq!(&a_dyn, &b_dyn); +} + +#[test] +#[should_panic] +fn assert_matrix_eq_dense_negative_comparison() { + #[rustfmt::skip] + let a = MatrixMN::<_, U4, U5>::from_row_slice(&[ + 1210, 1320, 1430, 1540, 1650, + 2310, 2420, 2530, 2640, 2750, + 3410, 3520, 3630, 3740, 3850, + 4510, 4620, -4730, 4840, 4950, + ]); + + #[rustfmt::skip] + let b = MatrixMN::<_, U4, U5>::from_row_slice(&[ + 1210, 1320, 1430, 1540, 1650, + 2310, 2420, 2530, 2640, 2750, + 3410, 3520, 3630, 3740, 3850, + 4510, 4620, 4730, 4840, 4950, + ]); + + assert_matrix_eq!(a, b); +} diff --git a/tests/core/mod.rs b/tests/core/mod.rs index 7c738368..4f9bc745 100644 --- a/tests/core/mod.rs +++ b/tests/core/mod.rs @@ -11,5 +11,8 @@ mod matrix_slice; mod mint; mod serde; +#[cfg(feature = "compare")] +mod matrixcompare; + #[cfg(feature = "arbitrary")] pub mod helper;