Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
This commit is contained in:
104
vendor/smawk/tests/agreement.rs
vendored
Normal file
104
vendor/smawk/tests/agreement.rs
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
#![cfg(feature = "ndarray")]
|
||||
|
||||
use ndarray::{s, Array2};
|
||||
use rand::SeedableRng;
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
use smawk::{brute_force, online_column_minima, recursive};
|
||||
|
||||
mod random_monge;
|
||||
use random_monge::random_monge_matrix;
|
||||
|
||||
/// Check that the brute force, recursive, and SMAWK functions
|
||||
/// give identical results on a large number of randomly generated
|
||||
/// Monge matrices.
|
||||
#[test]
|
||||
fn column_minima_agree() {
|
||||
let sizes = vec![1, 2, 3, 4, 5, 10, 15, 20, 30];
|
||||
let mut rng = ChaCha20Rng::seed_from_u64(0);
|
||||
for _ in 0..4 {
|
||||
for m in sizes.clone().iter() {
|
||||
for n in sizes.clone().iter() {
|
||||
let matrix: Array2<i32> = random_monge_matrix(*m, *n, &mut rng);
|
||||
|
||||
// Compute and test row minima.
|
||||
let brute_force = brute_force::row_minima(&matrix);
|
||||
let recursive = recursive::row_minima(&matrix);
|
||||
let smawk = smawk::row_minima(&matrix);
|
||||
assert_eq!(
|
||||
brute_force, recursive,
|
||||
"recursive and brute force differs on:\n{:?}",
|
||||
matrix
|
||||
);
|
||||
assert_eq!(
|
||||
brute_force, smawk,
|
||||
"SMAWK and brute force differs on:\n{:?}",
|
||||
matrix
|
||||
);
|
||||
|
||||
// Do the same for the column minima.
|
||||
let brute_force = brute_force::column_minima(&matrix);
|
||||
let recursive = recursive::column_minima(&matrix);
|
||||
let smawk = smawk::column_minima(&matrix);
|
||||
assert_eq!(
|
||||
brute_force, recursive,
|
||||
"recursive and brute force differs on:\n{:?}",
|
||||
matrix
|
||||
);
|
||||
assert_eq!(
|
||||
brute_force, smawk,
|
||||
"SMAWK and brute force differs on:\n{:?}",
|
||||
matrix
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check that the brute force and online SMAWK functions give
|
||||
/// identical results on a large number of randomly generated
|
||||
/// Monge matrices.
|
||||
#[test]
|
||||
fn online_agree() {
|
||||
let sizes = vec![1, 2, 3, 4, 5, 10, 15, 20, 30, 50];
|
||||
let mut rng = ChaCha20Rng::seed_from_u64(0);
|
||||
for _ in 0..5 {
|
||||
for &size in &sizes {
|
||||
// Random totally monotone square matrix of the
|
||||
// desired size.
|
||||
let mut matrix: Array2<i32> = random_monge_matrix(size, size, &mut rng);
|
||||
|
||||
// Adjust matrix so the column minima are above the
|
||||
// diagonal. The brute_force::column_minima will still
|
||||
// work just fine on such a mangled Monge matrix.
|
||||
let max = *matrix.iter().max().unwrap_or(&0);
|
||||
for idx in 0..(size as isize) {
|
||||
// Using the maximum value of the matrix instead
|
||||
// of i32::max_value() makes for prettier matrices
|
||||
// in case we want to print them.
|
||||
matrix.slice_mut(s![idx..idx + 1, ..idx + 1]).fill(max);
|
||||
}
|
||||
|
||||
// The online algorithm always returns the initial
|
||||
// value for the left-most column -- without
|
||||
// inspecting the column at all. So we fill the
|
||||
// left-most column with this value to have the brute
|
||||
// force algorithm do the same.
|
||||
let initial = 42;
|
||||
matrix.slice_mut(s![0.., ..1]).fill(initial);
|
||||
|
||||
// Brute-force computation of column minima, returned
|
||||
// in the same form as online_column_minima.
|
||||
let brute_force = brute_force::column_minima(&matrix)
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(j, &i)| (i, matrix[[i, j]]))
|
||||
.collect::<Vec<_>>();
|
||||
let online = online_column_minima(initial, size, |_, i, j| matrix[[i, j]]);
|
||||
assert_eq!(
|
||||
brute_force, online,
|
||||
"brute force and online differ on:\n{:3?}",
|
||||
matrix
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
83
vendor/smawk/tests/complexity.rs
vendored
Normal file
83
vendor/smawk/tests/complexity.rs
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
#![cfg(feature = "ndarray")]
|
||||
|
||||
use ndarray::{Array1, Array2};
|
||||
use rand::SeedableRng;
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
use smawk::online_column_minima;
|
||||
|
||||
mod random_monge;
|
||||
use random_monge::random_monge_matrix;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct LinRegression {
|
||||
alpha: f64,
|
||||
beta: f64,
|
||||
r_squared: f64,
|
||||
}
|
||||
|
||||
/// Square an expression. Works equally well for floats and matrices.
|
||||
macro_rules! squared {
|
||||
($x:expr) => {
|
||||
$x * $x
|
||||
};
|
||||
}
|
||||
|
||||
/// Compute the mean of a 1-dimensional array.
|
||||
macro_rules! mean {
|
||||
($a:expr) => {
|
||||
$a.mean().expect("Mean of empty array")
|
||||
};
|
||||
}
|
||||
|
||||
/// Compute a simple linear regression from the list of values.
|
||||
///
|
||||
/// See <https://en.wikipedia.org/wiki/Simple_linear_regression>.
|
||||
fn linear_regression(values: &[(usize, i32)]) -> LinRegression {
|
||||
let xs = values.iter().map(|&(x, _)| x as f64).collect::<Array1<_>>();
|
||||
let ys = values.iter().map(|&(_, y)| y as f64).collect::<Array1<_>>();
|
||||
|
||||
let xs_mean = mean!(&xs);
|
||||
let ys_mean = mean!(&ys);
|
||||
let xs_ys_mean = mean!(&xs * &ys);
|
||||
|
||||
let cov_xs_ys = ((&xs - xs_mean) * (&ys - ys_mean)).sum();
|
||||
let var_xs = squared!(&xs - xs_mean).sum();
|
||||
|
||||
let beta = cov_xs_ys / var_xs;
|
||||
let alpha = ys_mean - beta * xs_mean;
|
||||
let r_squared = squared!(xs_ys_mean - xs_mean * ys_mean)
|
||||
/ ((mean!(&xs * &xs) - squared!(xs_mean)) * (mean!(&ys * &ys) - squared!(ys_mean)));
|
||||
|
||||
LinRegression {
|
||||
alpha: alpha,
|
||||
beta: beta,
|
||||
r_squared: r_squared,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check that the number of matrix accesses in `online_column_minima`
|
||||
/// grows as O(*n*) for *n* ✕ *n* matrix.
|
||||
#[test]
|
||||
fn online_linear_complexity() {
|
||||
let mut rng = ChaCha20Rng::seed_from_u64(0);
|
||||
let mut data = vec![];
|
||||
|
||||
for &size in &[1, 2, 3, 4, 5, 10, 15, 20, 30, 40, 50, 60, 70, 80, 90, 100] {
|
||||
let matrix: Array2<i32> = random_monge_matrix(size, size, &mut rng);
|
||||
let count = std::cell::RefCell::new(0);
|
||||
online_column_minima(0, size, |_, i, j| {
|
||||
*count.borrow_mut() += 1;
|
||||
matrix[[i, j]]
|
||||
});
|
||||
data.push((size, count.into_inner()));
|
||||
}
|
||||
|
||||
let lin_reg = linear_regression(&data);
|
||||
assert!(
|
||||
lin_reg.r_squared > 0.95,
|
||||
"r² = {:.4} is lower than expected for a linear fit\nData points: {:?}\n{:?}",
|
||||
lin_reg.r_squared,
|
||||
data,
|
||||
lin_reg
|
||||
);
|
||||
}
|
83
vendor/smawk/tests/monge.rs
vendored
Normal file
83
vendor/smawk/tests/monge.rs
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
#![cfg(feature = "ndarray")]
|
||||
|
||||
use ndarray::{arr2, Array, Array2};
|
||||
use rand::SeedableRng;
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
use smawk::monge::is_monge;
|
||||
|
||||
mod random_monge;
|
||||
use random_monge::{random_monge_matrix, MongePrim};
|
||||
|
||||
#[test]
|
||||
fn random_monge() {
|
||||
let mut rng = ChaCha20Rng::seed_from_u64(0);
|
||||
let matrix: Array2<u8> = random_monge_matrix(5, 5, &mut rng);
|
||||
|
||||
assert!(is_monge(&matrix));
|
||||
assert_eq!(
|
||||
matrix,
|
||||
arr2(&[
|
||||
[2, 3, 4, 4, 5],
|
||||
[5, 5, 6, 6, 7],
|
||||
[3, 3, 4, 4, 5],
|
||||
[5, 2, 3, 3, 4],
|
||||
[5, 2, 3, 3, 4]
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn monge_constant_rows() {
|
||||
let mut rng = ChaCha20Rng::seed_from_u64(0);
|
||||
let matrix: Array2<u8> = MongePrim::ConstantRows.to_matrix(5, 4, &mut rng);
|
||||
assert!(is_monge(&matrix));
|
||||
for row in matrix.rows() {
|
||||
let elem = row[0];
|
||||
assert_eq!(row, Array::from_elem(matrix.ncols(), elem));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn monge_constant_cols() {
|
||||
let mut rng = ChaCha20Rng::seed_from_u64(0);
|
||||
let matrix: Array2<u8> = MongePrim::ConstantCols.to_matrix(5, 4, &mut rng);
|
||||
assert!(is_monge(&matrix));
|
||||
for column in matrix.columns() {
|
||||
let elem = column[0];
|
||||
assert_eq!(column, Array::from_elem(matrix.nrows(), elem));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn monge_upper_right_ones() {
|
||||
let mut rng = ChaCha20Rng::seed_from_u64(1);
|
||||
let matrix: Array2<u8> = MongePrim::UpperRightOnes.to_matrix(5, 4, &mut rng);
|
||||
assert!(is_monge(&matrix));
|
||||
assert_eq!(
|
||||
matrix,
|
||||
arr2(&[
|
||||
[0, 0, 1, 1],
|
||||
[0, 0, 1, 1],
|
||||
[0, 0, 1, 1],
|
||||
[0, 0, 0, 0],
|
||||
[0, 0, 0, 0]
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn monge_lower_left_ones() {
|
||||
let mut rng = ChaCha20Rng::seed_from_u64(1);
|
||||
let matrix: Array2<u8> = MongePrim::LowerLeftOnes.to_matrix(5, 4, &mut rng);
|
||||
assert!(is_monge(&matrix));
|
||||
assert_eq!(
|
||||
matrix,
|
||||
arr2(&[
|
||||
[0, 0, 0, 0],
|
||||
[0, 0, 0, 0],
|
||||
[1, 1, 0, 0],
|
||||
[1, 1, 0, 0],
|
||||
[1, 1, 0, 0]
|
||||
])
|
||||
);
|
||||
}
|
83
vendor/smawk/tests/random_monge/mod.rs
vendored
Normal file
83
vendor/smawk/tests/random_monge/mod.rs
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
//! Test functionality for generating random Monge matrices.
|
||||
|
||||
// The code is put here so we can reuse it in different integration
|
||||
// tests, without Cargo finding it when `cargo test` is run. See the
|
||||
// section on "Submodules in Integration Tests" in
|
||||
// https://doc.rust-lang.org/book/ch11-03-test-organization.html
|
||||
|
||||
use ndarray::{s, Array2};
|
||||
use num_traits::PrimInt;
|
||||
use rand::distributions::{Distribution, Standard};
|
||||
use rand::Rng;
|
||||
|
||||
/// A Monge matrix can be decomposed into one of these primitive
|
||||
/// building blocks.
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum MongePrim {
|
||||
ConstantRows,
|
||||
ConstantCols,
|
||||
UpperRightOnes,
|
||||
LowerLeftOnes,
|
||||
}
|
||||
|
||||
impl MongePrim {
|
||||
/// Generate a Monge matrix from a primitive.
|
||||
pub fn to_matrix<T: PrimInt, R: Rng>(&self, m: usize, n: usize, rng: &mut R) -> Array2<T>
|
||||
where
|
||||
Standard: Distribution<T>,
|
||||
{
|
||||
let mut matrix = Array2::from_elem((m, n), T::zero());
|
||||
// Avoid panic in UpperRightOnes and LowerLeftOnes below.
|
||||
if m == 0 || n == 0 {
|
||||
return matrix;
|
||||
}
|
||||
|
||||
match *self {
|
||||
MongePrim::ConstantRows => {
|
||||
for mut row in matrix.rows_mut() {
|
||||
if rng.gen::<bool>() {
|
||||
row.fill(T::one())
|
||||
}
|
||||
}
|
||||
}
|
||||
MongePrim::ConstantCols => {
|
||||
for mut col in matrix.columns_mut() {
|
||||
if rng.gen::<bool>() {
|
||||
col.fill(T::one())
|
||||
}
|
||||
}
|
||||
}
|
||||
MongePrim::UpperRightOnes => {
|
||||
let i = rng.gen_range(0..(m + 1) as isize);
|
||||
let j = rng.gen_range(0..(n + 1) as isize);
|
||||
matrix.slice_mut(s![..i, -j..]).fill(T::one());
|
||||
}
|
||||
MongePrim::LowerLeftOnes => {
|
||||
let i = rng.gen_range(0..(m + 1) as isize);
|
||||
let j = rng.gen_range(0..(n + 1) as isize);
|
||||
matrix.slice_mut(s![-i.., ..j]).fill(T::one());
|
||||
}
|
||||
}
|
||||
|
||||
matrix
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a random Monge matrix.
|
||||
pub fn random_monge_matrix<R: Rng, T: PrimInt>(m: usize, n: usize, rng: &mut R) -> Array2<T>
|
||||
where
|
||||
Standard: Distribution<T>,
|
||||
{
|
||||
let monge_primitives = [
|
||||
MongePrim::ConstantRows,
|
||||
MongePrim::ConstantCols,
|
||||
MongePrim::LowerLeftOnes,
|
||||
MongePrim::UpperRightOnes,
|
||||
];
|
||||
let mut matrix = Array2::from_elem((m, n), T::zero());
|
||||
for _ in 0..(m + n) {
|
||||
let monge = monge_primitives[rng.gen_range(0..monge_primitives.len())];
|
||||
matrix = matrix + monge.to_matrix(m, n, rng);
|
||||
}
|
||||
matrix
|
||||
}
|
9
vendor/smawk/tests/version-numbers.rs
vendored
Normal file
9
vendor/smawk/tests/version-numbers.rs
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
#[test]
|
||||
fn test_readme_deps() {
|
||||
version_sync::assert_markdown_deps_updated!("README.md");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_html_root_url() {
|
||||
version_sync::assert_html_root_url_updated!("src/lib.rs");
|
||||
}
|
Reference in New Issue
Block a user