nalgebra-sparse: Move slow tests and imports into 'slow' child module

This way it's easier to prevent accidental unused imports when
running tests without the slow-tests feature.
This commit is contained in:
Andreas Longva 2020-11-23 10:16:18 +01:00
parent 54329146c9
commit f20e764edc

View File

@ -1,134 +1,134 @@
use nalgebra_sparse::proptest::{coo_with_duplicates, coo_no_duplicates}; #[test]
use nalgebra::DMatrix; #[ignore]
fn coo_no_duplicates_generates_admissible_matrices() {
use proptest::prelude::*; //TODO
use itertools::Itertools; }
use std::collections::HashSet;
use std::iter::repeat;
#[cfg(feature = "slow-tests")] #[cfg(feature = "slow-tests")]
use { mod slow {
proptest::test_runner::TestRunner, use nalgebra_sparse::proptest::{coo_with_duplicates, coo_no_duplicates};
proptest::strategy::ValueTree use nalgebra::DMatrix;
};
use std::ops::RangeInclusive;
#[cfg(feature = "slow-tests")] use proptest::test_runner::TestRunner;
fn generate_all_possible_matrices(value_range: RangeInclusive<i32>, use proptest::strategy::ValueTree;
rows_range: RangeInclusive<usize>, use itertools::Itertools;
cols_range: RangeInclusive<usize>)
-> HashSet<DMatrix<i32>>
{
// Enumerate all possible combinations
let mut all_combinations = HashSet::new();
for nrows in rows_range {
for ncols in cols_range.clone() {
// For the given number of rows and columns
let n_values = nrows * ncols;
if n_values == 0 { use proptest::prelude::*;
// If we have zero rows or columns, the set of matrices with the given
// rows and columns is a single element: an empty matrix use std::collections::HashSet;
all_combinations.insert(DMatrix::from_row_slice(nrows, ncols, &[])); use std::iter::repeat;
} else { use std::ops::RangeInclusive;
// Otherwise, we need to sample all possible matrices.
// To do this, we generate the values as the (multi) Cartesian product fn generate_all_possible_matrices(value_range: RangeInclusive<i32>,
// of the value sets. For example, for a 2x2 matrices, we consider rows_range: RangeInclusive<usize>,
// all possible 4-element arrays that the matrices can take by cols_range: RangeInclusive<usize>)
// considering all elements in the cartesian product -> HashSet<DMatrix<i32>>
// V x V x V x V {
// where V is the set of eligible values, e.g. V := -1 ..= 1 // Enumerate all possible combinations
let values_iter = repeat(value_range.clone()) let mut all_combinations = HashSet::new();
.take(n_values) for nrows in rows_range {
.multi_cartesian_product(); for ncols in cols_range.clone() {
for matrix_values in values_iter { // For the given number of rows and columns
all_combinations.insert(DMatrix::from_row_slice(nrows, ncols, &matrix_values)); let n_values = nrows * ncols;
if n_values == 0 {
// If we have zero rows or columns, the set of matrices with the given
// rows and columns is a single element: an empty matrix
all_combinations.insert(DMatrix::from_row_slice(nrows, ncols, &[]));
} else {
// Otherwise, we need to sample all possible matrices.
// To do this, we generate the values as the (multi) Cartesian product
// of the value sets. For example, for a 2x2 matrices, we consider
// all possible 4-element arrays that the matrices can take by
// considering all elements in the cartesian product
// V x V x V x V
// where V is the set of eligible values, e.g. V := -1 ..= 1
let values_iter = repeat(value_range.clone())
.take(n_values)
.multi_cartesian_product();
for matrix_values in values_iter {
all_combinations.insert(DMatrix::from_row_slice(nrows, ncols, &matrix_values));
}
} }
} }
} }
} all_combinations
all_combinations
}
#[cfg(feature = "slow-tests")]
#[test]
fn coo_no_duplicates_samples_all_admissible_outputs() {
// Note: This test basically mirrors a similar test for `matrix` in the `nalgebra` repo.
// 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();
// This number needs to be high enough so that we with high probability sample
// all possible cases
let num_generated_matrices = 500000;
let values = -1..=1;
let rows = 0..=2;
let cols = 0..=3;
let strategy = coo_no_duplicates(values.clone(), rows.clone(), cols.clone(), 2 * 3);
// Enumerate all possible combinations
let all_combinations = generate_all_possible_matrices(values, rows, cols);
let mut visited_combinations = HashSet::new();
for _ in 0..num_generated_matrices {
let tree = strategy
.new_tree(&mut runner)
.expect("Tree generation should not fail");
let matrix = tree.current();
visited_combinations.insert(DMatrix::from(&matrix));
} }
assert_eq!(visited_combinations.len(), all_combinations.len()); #[cfg(feature = "slow-tests")]
assert_eq!(visited_combinations, all_combinations, "Did not sample all possible values."); #[test]
} fn coo_no_duplicates_samples_all_admissible_outputs() {
// Note: This test basically mirrors a similar test for `matrix` in the `nalgebra` repo.
#[cfg(feature = "slow-tests")] // Test that the proptest generation covers all possible outputs for a small space of inputs
#[test] // given enough samples.
fn coo_with_duplicates_samples_all_admissible_outputs() {
// This is almost the same as the test for coo_no_duplicates, except that we need
// a different "success" criterion, since coo_with_duplicates is able to generate
// matrices with values outside of the value constraints. See below for details.
// We use a deterministic test runner to make the test "stable". // We use a deterministic test runner to make the test "stable".
let mut runner = TestRunner::deterministic(); let mut runner = TestRunner::deterministic();
// This number needs to be high enough so that we with high probability sample // This number needs to be high enough so that we with high probability sample
// all possible cases // all possible cases
let num_generated_matrices = 500000; let num_generated_matrices = 500000;
let values = -1..=1; let values = -1..=1;
let rows = 0..=2; let rows = 0..=2;
let cols = 0..=3; let cols = 0..=3;
let strategy = coo_with_duplicates(values.clone(), rows.clone(), cols.clone(), 2 * 3, 2); let strategy = coo_no_duplicates(values.clone(), rows.clone(), cols.clone(), 2 * 3);
// Enumerate all possible combinations that fit the constraints // Enumerate all possible combinations
// (note: this is only a subset of the matrices that can be generated by let all_combinations = generate_all_possible_matrices(values, rows, cols);
// `coo_with_duplicates`)
let all_combinations = generate_all_possible_matrices(values, rows, cols);
let mut visited_combinations = HashSet::new(); let mut visited_combinations = HashSet::new();
for _ in 0..num_generated_matrices { for _ in 0..num_generated_matrices {
let tree = strategy let tree = strategy
.new_tree(&mut runner) .new_tree(&mut runner)
.expect("Tree generation should not fail"); .expect("Tree generation should not fail");
let matrix = tree.current(); let matrix = tree.current();
visited_combinations.insert(DMatrix::from(&matrix)); visited_combinations.insert(DMatrix::from(&matrix));
}
assert_eq!(visited_combinations.len(), all_combinations.len());
assert_eq!(visited_combinations, all_combinations, "Did not sample all possible values.");
} }
// Here we cannot verify that the set of visited combinations is *equal* to #[cfg(feature = "slow-tests")]
// all possible outcomes with the given constraints, however the #[test]
// strategy should be able to generate all matrices that fit the constraints. fn coo_with_duplicates_samples_all_admissible_outputs() {
// In other words, we need to determine that set of all admissible matrices // This is almost the same as the test for coo_no_duplicates, except that we need
// is contained in the set of visited matrices // a different "success" criterion, since coo_with_duplicates is able to generate
assert!(all_combinations.is_subset(&visited_combinations)); // matrices with values outside of the value constraints. See below for details.
}
#[test] // We use a deterministic test runner to make the test "stable".
fn coo_no_duplicates_generates_admissible_matrices() { let mut runner = TestRunner::deterministic();
// This number needs to be high enough so that we with high probability sample
// all possible cases
let num_generated_matrices = 500000;
let values = -1..=1;
let rows = 0..=2;
let cols = 0..=3;
let strategy = coo_with_duplicates(values.clone(), rows.clone(), cols.clone(), 2 * 3, 2);
// Enumerate all possible combinations that fit the constraints
// (note: this is only a subset of the matrices that can be generated by
// `coo_with_duplicates`)
let all_combinations = generate_all_possible_matrices(values, rows, cols);
let mut visited_combinations = HashSet::new();
for _ in 0..num_generated_matrices {
let tree = strategy
.new_tree(&mut runner)
.expect("Tree generation should not fail");
let matrix = tree.current();
visited_combinations.insert(DMatrix::from(&matrix));
}
// Here we cannot verify that the set of visited combinations is *equal* to
// all possible outcomes with the given constraints, however the
// strategy should be able to generate all matrices that fit the constraints.
// In other words, we need to determine that set of all admissible matrices
// is contained in the set of visited matrices
assert!(all_combinations.is_subset(&visited_combinations));
}
} }