forked from M-Labs/nalgebra
185 lines
6.8 KiB
Rust
185 lines
6.8 KiB
Rust
|
//! Tests for proptest-related functionality.
|
||
|
use nalgebra::base::dimension::*;
|
||
|
use nalgebra::proptest::{matrix, DimRange, MatrixStrategy};
|
||
|
use nalgebra::{DMatrix, DVector, Dim, Matrix3, Matrix4, MatrixMN, Vector3};
|
||
|
use proptest::prelude::*;
|
||
|
use proptest::strategy::ValueTree;
|
||
|
use proptest::test_runner::TestRunner;
|
||
|
|
||
|
/// Generate a proptest that tests that all matrices generated with the
|
||
|
/// provided rows and columns conform to the constraints defined by the
|
||
|
/// input.
|
||
|
macro_rules! generate_matrix_sanity_test {
|
||
|
($test_name:ident, $rows:expr, $cols:expr) => {
|
||
|
proptest! {
|
||
|
#[test]
|
||
|
fn $test_name(a in matrix(-5 ..= 5i32, $rows, $cols)) {
|
||
|
// let a: MatrixMN<_, $rows, $cols> = a;
|
||
|
let rows_range = DimRange::from($rows);
|
||
|
let cols_range = DimRange::from($cols);
|
||
|
prop_assert!(a.nrows() >= rows_range.lower_bound().value()
|
||
|
&& a.nrows() <= rows_range.upper_bound().value());
|
||
|
prop_assert!(a.ncols() >= cols_range.lower_bound().value()
|
||
|
&& a.ncols() <= cols_range.upper_bound().value());
|
||
|
prop_assert!(a.iter().all(|x_ij| *x_ij >= -5 && *x_ij <= 5));
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
// Test all fixed-size matrices with row/col dimensions up to 3
|
||
|
generate_matrix_sanity_test!(test_matrix_u0_u0, U0, U0);
|
||
|
generate_matrix_sanity_test!(test_matrix_u1_u0, U1, U0);
|
||
|
generate_matrix_sanity_test!(test_matrix_u0_u1, U0, U1);
|
||
|
generate_matrix_sanity_test!(test_matrix_u1_u1, U1, U1);
|
||
|
generate_matrix_sanity_test!(test_matrix_u2_u1, U2, U1);
|
||
|
generate_matrix_sanity_test!(test_matrix_u1_u2, U1, U2);
|
||
|
generate_matrix_sanity_test!(test_matrix_u2_u2, U2, U2);
|
||
|
generate_matrix_sanity_test!(test_matrix_u3_u2, U3, U2);
|
||
|
generate_matrix_sanity_test!(test_matrix_u2_u3, U2, U3);
|
||
|
generate_matrix_sanity_test!(test_matrix_u3_u3, U3, U3);
|
||
|
|
||
|
// Similarly test all heap-allocated but fixed dim ranges
|
||
|
generate_matrix_sanity_test!(test_matrix_0_0, 0, 0);
|
||
|
generate_matrix_sanity_test!(test_matrix_0_1, 0, 1);
|
||
|
generate_matrix_sanity_test!(test_matrix_1_0, 1, 0);
|
||
|
generate_matrix_sanity_test!(test_matrix_1_1, 1, 1);
|
||
|
generate_matrix_sanity_test!(test_matrix_2_1, 2, 1);
|
||
|
generate_matrix_sanity_test!(test_matrix_1_2, 1, 2);
|
||
|
generate_matrix_sanity_test!(test_matrix_2_2, 2, 2);
|
||
|
generate_matrix_sanity_test!(test_matrix_3_2, 3, 2);
|
||
|
generate_matrix_sanity_test!(test_matrix_2_3, 2, 3);
|
||
|
generate_matrix_sanity_test!(test_matrix_3_3, 3, 3);
|
||
|
|
||
|
// Test arbitrary inputs
|
||
|
generate_matrix_sanity_test!(test_matrix_input_1, U5, 1..=5);
|
||
|
generate_matrix_sanity_test!(test_matrix_input_2, 3..=4, 1..=5);
|
||
|
generate_matrix_sanity_test!(test_matrix_input_3, 1..=2, U3);
|
||
|
generate_matrix_sanity_test!(test_matrix_input_4, 3, U4);
|
||
|
|
||
|
#[test]
|
||
|
fn test_matrix_output_types() {
|
||
|
// Test that the dimension types are correct for the given inputs
|
||
|
let _: MatrixStrategy<_, U3, U4> = matrix(-5..5, U3, U4);
|
||
|
let _: MatrixStrategy<_, U3, U3> = matrix(-5..5, U3, U3);
|
||
|
let _: MatrixStrategy<_, U3, Dynamic> = matrix(-5..5, U3, 1..=5);
|
||
|
let _: MatrixStrategy<_, Dynamic, U3> = matrix(-5..5, 1..=5, U3);
|
||
|
let _: MatrixStrategy<_, Dynamic, Dynamic> = matrix(-5..5, 1..=5, 1..=5);
|
||
|
}
|
||
|
|
||
|
// Below we have some tests to ensure that specific instances of MatrixMN are usable
|
||
|
// in a typical proptest scenario where we (implicitly) use the `Arbitrary` trait
|
||
|
proptest! {
|
||
|
#[test]
|
||
|
fn ensure_arbitrary_test_compiles_matrix3(_: Matrix3<i32>) {}
|
||
|
|
||
|
#[test]
|
||
|
fn ensure_arbitrary_test_compiles_matrixmn_u3_dynamic(_: MatrixMN<i32, U3, Dynamic>) {}
|
||
|
|
||
|
#[test]
|
||
|
fn ensure_arbitrary_test_compiles_matrixmn_dynamic_u3(_: MatrixMN<i32, Dynamic, U3>) {}
|
||
|
|
||
|
#[test]
|
||
|
fn ensure_arbitrary_test_compiles_dmatrix(_: DMatrix<i32>) {}
|
||
|
|
||
|
#[test]
|
||
|
fn ensure_arbitrary_test_compiles_vector3(_: Vector3<i32>) {}
|
||
|
|
||
|
#[test]
|
||
|
fn ensure_arbitrary_test_compiles_dvector(_: DVector<i32>) {}
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn matrix_samples_all_possible_outputs() {
|
||
|
// Test that the proptest generation covers all possible outputs for a small space of inputs
|
||
|
// given enough samples.
|
||
|
|
||
|
// We use a deterministic test runner to make the test "stable".
|
||
|
let mut runner = TestRunner::deterministic();
|
||
|
|
||
|
let strategy = matrix(0..=2usize, 0..=3, 0..=3);
|
||
|
|
||
|
// We use flags to record whether values and combinations of dimensions were encountered.
|
||
|
// For example, if we encounter value 1, we set the value flag of 1 to true,
|
||
|
// and if we encounted matrix dimensions 4x3, we set the flag of [4, 3] to true.
|
||
|
let mut value_encountered = Vector3::new(false, false, false);
|
||
|
let mut dimensions_encountered = Matrix4::repeat(false);
|
||
|
|
||
|
for _ in 0..1000 {
|
||
|
let tree = strategy
|
||
|
.new_tree(&mut runner)
|
||
|
.expect("Tree generation should not fail");
|
||
|
let matrix = tree.current();
|
||
|
|
||
|
dimensions_encountered[(matrix.nrows(), matrix.ncols())] = true;
|
||
|
|
||
|
for &value in matrix.iter() {
|
||
|
value_encountered[value] = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
assert!(
|
||
|
value_encountered.iter().all(|v| *v),
|
||
|
"Did not sample all possible values."
|
||
|
);
|
||
|
assert!(
|
||
|
dimensions_encountered.iter().all(|v| *v),
|
||
|
"Did not sample all possible matrix dimensions."
|
||
|
);
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn matrix_shrinking_satisfies_constraints() {
|
||
|
// We use a deterministic test runner to make the test "stable".
|
||
|
let mut runner = TestRunner::deterministic();
|
||
|
|
||
|
let strategy = matrix(-1..=2, 1..=3, 2..=4);
|
||
|
|
||
|
let num_matrices = 25;
|
||
|
|
||
|
macro_rules! maybeprintln {
|
||
|
($($arg:tt)*) => {
|
||
|
// Uncomment the below line to enable printing of matrix sequences. This is handy
|
||
|
// for manually inspecting the sequences of simplified matrices.
|
||
|
// println!($($arg)*)
|
||
|
};
|
||
|
}
|
||
|
|
||
|
maybeprintln!("========================== (begin generation process)");
|
||
|
|
||
|
for _ in 0..num_matrices {
|
||
|
let mut tree = strategy
|
||
|
.new_tree(&mut runner)
|
||
|
.expect("Tree generation should not fail.");
|
||
|
|
||
|
let mut current = Some(tree.current());
|
||
|
|
||
|
maybeprintln!("------------------");
|
||
|
|
||
|
while let Some(matrix) = current {
|
||
|
maybeprintln!("{}", matrix);
|
||
|
|
||
|
assert!(
|
||
|
matrix.iter().all(|&v| v >= -1 && v <= 2),
|
||
|
"All matrix elements must satisfy constraints"
|
||
|
);
|
||
|
assert!(
|
||
|
matrix.nrows() >= 1 && matrix.nrows() <= 3,
|
||
|
"Number of rows in matrix must satisfy constraints."
|
||
|
);
|
||
|
assert!(
|
||
|
matrix.ncols() >= 2 && matrix.ncols() <= 4,
|
||
|
"Number of columns in matrix must satisfy constraints."
|
||
|
);
|
||
|
|
||
|
current = if tree.simplify() {
|
||
|
Some(tree.current())
|
||
|
} else {
|
||
|
None
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
maybeprintln!("========================== (end of generation process)");
|
||
|
}
|