diff --git a/Cargo.toml b/Cargo.toml index 4adf2ab1..fe347ed0 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" + [workspace] members = [ "nalgebra-lapack", "nalgebra-glm" ] diff --git a/examples/matrixcompare.rs b/examples/matrixcompare.rs new file mode 100644 index 00000000..60c632d3 --- /dev/null +++ b/examples/matrixcompare.rs @@ -0,0 +1,79 @@ +extern crate nalgebra as na; + +#[macro_use] +extern crate quickcheck; + +use na::{U3, U4, MatrixMN}; +use matrixcompare::compare_matrices; +use matrixcompare::comparators::{ExactElementwiseComparator, AbsoluteElementwiseComparator}; + +fn compare_integers_fail() { + println!("Comparing two integer matrices."); + + let a = MatrixMN::<_, U3, U4>::from_row_slice(&[ + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, -2, 11 + ]); + + let b = MatrixMN::<_, U3, U4>::from_row_slice(&[ + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11 + ]); + + if let Some(msg) = compare_matrices(a, b, &ExactElementwiseComparator).panic_message() { + println!("{}", msg); + } +} + +fn compare_different_size() { + println!("Comparing matrices of different size."); + let a = MatrixMN::<_, U3, U3>::from_row_slice(&[ + 0, 1, 2, + 4, 5, 6, + 8, 9, 10, + ]); + + let b = MatrixMN::<_, U3, U4>::from_row_slice(&[ + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11 + ]); + + if let Some(msg) = compare_matrices(a, b, &ExactElementwiseComparator).panic_message() { + println!("{}", msg); + } +} + +fn compare_f64_abs_tol_fail() { + println!("Comparing two f64 matrices."); + + 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, + ]); + + 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 Some(msg) = compare_matrices(a, b, &cmp).panic_message() { + println!("{}", msg); + } +} + +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(); +} \ No newline at end of file diff --git a/src/base/matrix.rs b/src/base/matrix.rs index d30773a3..8107a1dc 100644 --- a/src/base/matrix.rs +++ b/src/base/matrix.rs @@ -159,6 +159,30 @@ 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..d4b6f142 --- /dev/null +++ b/tests/core/matrixcompare.rs @@ -0,0 +1,81 @@ +//! 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::{U4, U5, MatrixMN, DMatrix}; + +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() { + 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, + ]); + + 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() { + 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, + ]); + + 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); +} \ No newline at end of file diff --git a/tests/core/mod.rs b/tests/core/mod.rs index ec1c4e3e..1b3c7861 100644 --- a/tests/core/mod.rs +++ b/tests/core/mod.rs @@ -10,5 +10,8 @@ mod matrix_slice; mod mint; mod serde; +#[cfg(feature = "compare")] +mod matrixcompare; + #[cfg(feature = "arbitrary")] pub mod helper;