Test SparsityPattern and CSR try_* constructors

This commit is contained in:
Andreas Longva 2020-09-25 14:48:10 +02:00
parent d98f2d2ad7
commit df1ef991f3
6 changed files with 381 additions and 7 deletions

View File

@ -62,7 +62,7 @@ impl<T> CsrMatrix<T> {
/// The column indices defining part of the CSR format. /// The column indices defining part of the CSR format.
#[inline] #[inline]
pub fn column_indices(&self) -> &[usize] { pub fn col_indices(&self) -> &[usize] {
self.sparsity_pattern.minor_indices() self.sparsity_pattern.minor_indices()
} }
@ -520,7 +520,7 @@ where
type Item = CsrRowMut<'a, T>; type Item = CsrRowMut<'a, T>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let lane = self.pattern.lane(self.current_row_idx); let lane = self.pattern.get_lane(self.current_row_idx);
let ncols = self.pattern.minor_dim(); let ncols = self.pattern.minor_dim();
if let Some(col_indices) = lane { if let Some(col_indices) = lane {

View File

@ -103,7 +103,7 @@ impl SparseFormatError {
} }
/// The type of format error described by a [SparseFormatError](struct.SparseFormatError.html). /// The type of format error described by a [SparseFormatError](struct.SparseFormatError.html).
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum SparseFormatErrorKind { pub enum SparseFormatErrorKind {
/// Indicates that the index data associated with the format contains at least one index /// Indicates that the index data associated with the format contains at least one index
/// out of bounds. /// out of bounds.

View File

@ -66,8 +66,19 @@ impl SparsityPattern {
} }
/// Get the lane at the given index. /// Get the lane at the given index.
///
/// Panics
/// ------
///
/// Panics if `major_index` is out of bounds.
#[inline] #[inline]
pub fn lane(&self, major_index: usize) -> Option<&[usize]> { pub fn lane(&self, major_index: usize) -> &[usize] {
self.get_lane(major_index).unwrap()
}
/// Get the lane at the given index, or `None` if out of bounds.
#[inline]
pub fn get_lane(&self, major_index: usize) -> Option<&[usize]> {
let offset_begin = *self.major_offsets().get(major_index)?; let offset_begin = *self.major_offsets().get(major_index)?;
let offset_end = *self.major_offsets().get(major_index + 1)?; let offset_end = *self.major_offsets().get(major_index + 1)?;
Some(&self.minor_indices()[offset_begin..offset_end]) Some(&self.minor_indices()[offset_begin..offset_end])
@ -124,7 +135,7 @@ impl SparsityPattern {
let mut prev = None; let mut prev = None;
while let Some(next) = iter.next().copied() { while let Some(next) = iter.next().copied() {
if next > minor_dim { if next >= minor_dim {
return Err(MinorIndexOutOfBounds); return Err(MinorIndexOutOfBounds);
} }
@ -197,7 +208,7 @@ impl SparsityPattern {
/// Error type for `SparsityPattern` format errors. /// Error type for `SparsityPattern` format errors.
#[non_exhaustive] #[non_exhaustive]
#[derive(Debug)] #[derive(Debug, PartialEq, Eq)]
pub enum SparsityPatternFormatError { pub enum SparsityPatternFormatError {
/// Indicates an invalid number of offsets. /// Indicates an invalid number of offsets.
/// ///

View File

@ -0,0 +1,231 @@
use nalgebra_sparse::csr::CsrMatrix;
use nalgebra_sparse::SparseFormatErrorKind;
#[test]
fn csr_matrix_valid_data() {
// Construct matrix from valid data and check that selected methods return results
// that agree with expectations.
{
// A CSR matrix with zero explicitly stored entries
let offsets = vec![0, 0, 0, 0];
let indices = vec![];
let values = Vec::<i32>::new();
let mut matrix = CsrMatrix::try_from_csr_data(3, 2, offsets, indices, values).unwrap();
assert_eq!(matrix, CsrMatrix::new(3, 2));
assert_eq!(matrix.nrows(), 3);
assert_eq!(matrix.ncols(), 2);
assert_eq!(matrix.nnz(), 0);
assert_eq!(matrix.row_offsets(), &[0, 0, 0, 0]);
assert_eq!(matrix.col_indices(), &[]);
assert_eq!(matrix.values(), &[]);
assert!(matrix.triplet_iter().next().is_none());
assert!(matrix.triplet_iter_mut().next().is_none());
assert_eq!(matrix.row(0).ncols(), 2);
assert_eq!(matrix.row(0).nnz(), 0);
assert_eq!(matrix.row(0).col_indices(), &[]);
assert_eq!(matrix.row(0).values(), &[]);
assert_eq!(matrix.row_mut(0).ncols(), 2);
assert_eq!(matrix.row_mut(0).nnz(), 0);
assert_eq!(matrix.row_mut(0).col_indices(), &[]);
assert_eq!(matrix.row_mut(0).values(), &[]);
assert_eq!(matrix.row_mut(0).values_mut(), &[]);
assert_eq!(matrix.row_mut(0).cols_and_values_mut(), ([].as_ref(), [].as_mut()));
assert_eq!(matrix.row(1).ncols(), 2);
assert_eq!(matrix.row(1).nnz(), 0);
assert_eq!(matrix.row(1).col_indices(), &[]);
assert_eq!(matrix.row(1).values(), &[]);
assert_eq!(matrix.row_mut(1).ncols(), 2);
assert_eq!(matrix.row_mut(1).nnz(), 0);
assert_eq!(matrix.row_mut(1).col_indices(), &[]);
assert_eq!(matrix.row_mut(1).values(), &[]);
assert_eq!(matrix.row_mut(1).values_mut(), &[]);
assert_eq!(matrix.row_mut(1).cols_and_values_mut(), ([].as_ref(), [].as_mut()));
assert_eq!(matrix.row(2).ncols(), 2);
assert_eq!(matrix.row(2).nnz(), 0);
assert_eq!(matrix.row(2).col_indices(), &[]);
assert_eq!(matrix.row(2).values(), &[]);
assert_eq!(matrix.row_mut(2).ncols(), 2);
assert_eq!(matrix.row_mut(2).nnz(), 0);
assert_eq!(matrix.row_mut(2).col_indices(), &[]);
assert_eq!(matrix.row_mut(2).values(), &[]);
assert_eq!(matrix.row_mut(2).values_mut(), &[]);
assert_eq!(matrix.row_mut(2).cols_and_values_mut(), ([].as_ref(), [].as_mut()));
assert!(matrix.get_row(3).is_none());
assert!(matrix.get_row_mut(3).is_none());
let (offsets, indices, values) = matrix.disassemble();
assert_eq!(offsets, vec![0, 0, 0, 0]);
assert_eq!(indices, vec![]);
assert_eq!(values, vec![]);
}
{
// An arbitrary CSR matrix
let offsets = vec![0, 2, 2, 5];
let indices = vec![0, 5, 1, 2, 3];
let values = vec![0, 1, 2, 3, 4];
let mut matrix = CsrMatrix::try_from_csr_data(3,
6,
offsets.clone(),
indices.clone(),
values.clone()).unwrap();
assert_eq!(matrix.nrows(), 3);
assert_eq!(matrix.ncols(), 6);
assert_eq!(matrix.nnz(), 5);
assert_eq!(matrix.row_offsets(), &[0, 2, 2, 5]);
assert_eq!(matrix.col_indices(), &[0, 5, 1, 2, 3]);
assert_eq!(matrix.values(), &[0, 1, 2, 3, 4]);
let expected_triplets = vec![(0, 0, 0), (0, 5, 1), (2, 1, 2), (2, 2, 3), (2, 3, 4)];
assert_eq!(matrix.triplet_iter().map(|(i, j, v)| (i, j, *v)).collect::<Vec<_>>(),
expected_triplets);
assert_eq!(matrix.triplet_iter_mut().map(|(i, j, v)| (i, j, *v)).collect::<Vec<_>>(),
expected_triplets);
assert_eq!(matrix.row(0).ncols(), 6);
assert_eq!(matrix.row(0).nnz(), 2);
assert_eq!(matrix.row(0).col_indices(), &[0, 5]);
assert_eq!(matrix.row(0).values(), &[0, 1]);
assert_eq!(matrix.row_mut(0).ncols(), 6);
assert_eq!(matrix.row_mut(0).nnz(), 2);
assert_eq!(matrix.row_mut(0).col_indices(), &[0, 5]);
assert_eq!(matrix.row_mut(0).values(), &[0, 1]);
assert_eq!(matrix.row_mut(0).values_mut(), &[0, 1]);
assert_eq!(matrix.row_mut(0).cols_and_values_mut(), ([0, 5].as_ref(), [0, 1].as_mut()));
assert_eq!(matrix.row(1).ncols(), 6);
assert_eq!(matrix.row(1).nnz(), 0);
assert_eq!(matrix.row(1).col_indices(), &[]);
assert_eq!(matrix.row(1).values(), &[]);
assert_eq!(matrix.row_mut(1).ncols(), 6);
assert_eq!(matrix.row_mut(1).nnz(), 0);
assert_eq!(matrix.row_mut(1).col_indices(), &[]);
assert_eq!(matrix.row_mut(1).values(), &[]);
assert_eq!(matrix.row_mut(1).values_mut(), &[]);
assert_eq!(matrix.row_mut(1).cols_and_values_mut(), ([].as_ref(), [].as_mut()));
assert_eq!(matrix.row(2).ncols(), 6);
assert_eq!(matrix.row(2).nnz(), 3);
assert_eq!(matrix.row(2).col_indices(), &[1, 2, 3]);
assert_eq!(matrix.row(2).values(), &[2, 3, 4]);
assert_eq!(matrix.row_mut(2).ncols(), 6);
assert_eq!(matrix.row_mut(2).nnz(), 3);
assert_eq!(matrix.row_mut(2).col_indices(), &[1, 2, 3]);
assert_eq!(matrix.row_mut(2).values(), &[2, 3, 4]);
assert_eq!(matrix.row_mut(2).values_mut(), &[2, 3, 4]);
assert_eq!(matrix.row_mut(2).cols_and_values_mut(), ([1, 2, 3].as_ref(), [2, 3, 4].as_mut()));
assert!(matrix.get_row(3).is_none());
assert!(matrix.get_row_mut(3).is_none());
let (offsets2, indices2, values2) = matrix.disassemble();
assert_eq!(offsets2, offsets);
assert_eq!(indices2, indices);
assert_eq!(values2, values);
}
}
#[test]
fn csr_matrix_try_from_invalid_csr_data() {
{
// Empty offset array (invalid length)
let matrix = CsrMatrix::try_from_csr_data(0, 0, Vec::new(), Vec::new(), Vec::<u32>::new());
assert_eq!(matrix.unwrap_err().kind(), &SparseFormatErrorKind::InvalidStructure);
}
{
// Offset array invalid length for arbitrary data
let offsets = vec![0, 3, 5];
let indices = vec![0, 1, 2, 3, 5];
let values = vec![0, 1, 2, 3, 4];
let matrix = CsrMatrix::try_from_csr_data(3, 6, offsets, indices, values);
assert_eq!(matrix.unwrap_err().kind(), &SparseFormatErrorKind::InvalidStructure);
}
{
// Invalid first entry in offsets array
let offsets = vec![1, 2, 2, 5];
let indices = vec![0, 5, 1, 2, 3];
let values = vec![0, 1, 2, 3, 4];
let matrix = CsrMatrix::try_from_csr_data(3, 6, offsets, indices, values);
assert_eq!(matrix.unwrap_err().kind(), &SparseFormatErrorKind::InvalidStructure);
}
{
// Invalid last entry in offsets array
let offsets = vec![0, 2, 2, 4];
let indices = vec![0, 5, 1, 2, 3];
let values = vec![0, 1, 2, 3, 4];
let matrix = CsrMatrix::try_from_csr_data(3, 6, offsets, indices, values);
assert_eq!(matrix.unwrap_err().kind(), &SparseFormatErrorKind::InvalidStructure);
}
{
// Invalid length of offsets array
let offsets = vec![0, 2, 2];
let indices = vec![0, 5, 1, 2, 3];
let values = vec![0, 1, 2, 3, 4];
let matrix = CsrMatrix::try_from_csr_data(3, 6, offsets, indices, values);
assert_eq!(matrix.unwrap_err().kind(), &SparseFormatErrorKind::InvalidStructure);
}
{
// Nonmonotonic offsets
let offsets = vec![0, 3, 2, 5];
let indices = vec![0, 1, 2, 3, 4];
let values = vec![0, 1, 2, 3, 4];
let matrix = CsrMatrix::try_from_csr_data(3, 6, offsets, indices, values);
assert_eq!(matrix.unwrap_err().kind(), &SparseFormatErrorKind::InvalidStructure);
}
{
// Nonmonotonic minor indices
let offsets = vec![0, 2, 2, 5];
let indices = vec![0, 2, 3, 1, 4];
let values = vec![0, 1, 2, 3, 4];
let matrix = CsrMatrix::try_from_csr_data(3, 6, offsets, indices, values);
assert_eq!(matrix.unwrap_err().kind(), &SparseFormatErrorKind::InvalidStructure);
}
{
// Minor index out of bounds
let offsets = vec![0, 2, 2, 5];
let indices = vec![0, 6, 1, 2, 3];
let values = vec![0, 1, 2, 3, 4];
let matrix = CsrMatrix::try_from_csr_data(3, 6, offsets, indices, values);
assert_eq!(matrix.unwrap_err().kind(), &SparseFormatErrorKind::IndexOutOfBounds);
}
{
// Duplicate entry
let offsets = vec![0, 2, 2, 5];
let indices = vec![0, 5, 2, 2, 3];
let values = vec![0, 1, 2, 3, 4];
let matrix = CsrMatrix::try_from_csr_data(3, 6, offsets, indices, values);
assert_eq!(matrix.unwrap_err().kind(), &SparseFormatErrorKind::DuplicateEntry);
}
}
#[test]
fn csr_matrix_get_index() {
// TODO: Implement tests for ::get() and index()
}
#[test]
fn csr_matrix_row_iter() {
}

View File

@ -1,2 +1,4 @@
mod coo; mod coo;
mod ops; mod ops;
mod pattern;
mod csr;

View File

@ -0,0 +1,130 @@
use nalgebra_sparse::pattern::{SparsityPattern, SparsityPatternFormatError};
#[test]
fn sparsity_pattern_valid_data() {
// Construct pattern from valid data and check that selected methods return results
// that agree with expectations.
{
// A pattern with zero explicitly stored entries
let pattern = SparsityPattern::try_from_offsets_and_indices(3,
2,
vec![0, 0, 0, 0],
Vec::new())
.unwrap();
assert_eq!(pattern.major_dim(), 3);
assert_eq!(pattern.minor_dim(), 2);
assert_eq!(pattern.nnz(), 0);
assert_eq!(pattern.major_offsets(), &[0, 0, 0, 0]);
assert_eq!(pattern.minor_indices(), &[]);
assert_eq!(pattern.lane(0), &[]);
assert_eq!(pattern.lane(1), &[]);
assert_eq!(pattern.lane(2), &[]);
assert!(pattern.entries().next().is_none());
assert_eq!(pattern, SparsityPattern::new(3, 2));
let (offsets, indices) = pattern.disassemble();
assert_eq!(offsets, vec![0, 0, 0, 0]);
assert_eq!(indices, vec![]);
}
{
// Arbitrary pattern
let offsets = vec![0, 2, 2, 5];
let indices = vec![0, 5, 1, 2, 3];
let pattern =
SparsityPattern::try_from_offsets_and_indices(3, 6, offsets.clone(), indices.clone())
.unwrap();
assert_eq!(pattern.major_dim(), 3);
assert_eq!(pattern.minor_dim(), 6);
assert_eq!(pattern.major_offsets(), offsets.as_slice());
assert_eq!(pattern.minor_indices(), indices.as_slice());
assert_eq!(pattern.nnz(), 5);
assert_eq!(pattern.lane(0), &[0, 5]);
assert_eq!(pattern.lane(1), &[]);
assert_eq!(pattern.lane(2), &[1, 2, 3]);
assert_eq!(pattern.entries().collect::<Vec<_>>(),
vec![(0, 0), (0, 5), (2, 1), (2, 2), (2, 3)]);
let (offsets2, indices2) = pattern.disassemble();
assert_eq!(offsets2, offsets);
assert_eq!(indices2, indices);
}
}
#[test]
fn sparsity_pattern_try_from_invalid_data() {
{
// Empty offset array (invalid length)
let pattern = SparsityPattern::try_from_offsets_and_indices(0, 0, Vec::new(), Vec::new());
assert_eq!(pattern, Err(SparsityPatternFormatError::InvalidOffsetArrayLength));
}
{
// Offset array invalid length for arbitrary data
let offsets = vec![0, 3, 5];
let indices = vec![0, 1, 2, 3, 5];
let pattern = SparsityPattern::try_from_offsets_and_indices(3, 6, offsets, indices);
assert!(matches!(pattern, Err(SparsityPatternFormatError::InvalidOffsetArrayLength)));
}
{
// Invalid first entry in offsets array
let offsets = vec![1, 2, 2, 5];
let indices = vec![0, 5, 1, 2, 3];
let pattern = SparsityPattern::try_from_offsets_and_indices(3, 6, offsets, indices);
assert!(matches!(pattern, Err(SparsityPatternFormatError::InvalidOffsetFirstLast)));
}
{
// Invalid last entry in offsets array
let offsets = vec![0, 2, 2, 4];
let indices = vec![0, 5, 1, 2, 3];
let pattern = SparsityPattern::try_from_offsets_and_indices(3, 6, offsets, indices);
assert!(matches!(pattern, Err(SparsityPatternFormatError::InvalidOffsetFirstLast)));
}
{
// Invalid length of offsets array
let offsets = vec![0, 2, 2];
let indices = vec![0, 5, 1, 2, 3];
let pattern = SparsityPattern::try_from_offsets_and_indices(3, 6, offsets, indices);
assert!(matches!(pattern, Err(SparsityPatternFormatError::InvalidOffsetArrayLength)));
}
{
// Nonmonotonic offsets
let offsets = vec![0, 3, 2, 5];
let indices = vec![0, 1, 2, 3, 4];
let pattern = SparsityPattern::try_from_offsets_and_indices(3, 6, offsets, indices);
assert_eq!(pattern, Err(SparsityPatternFormatError::NonmonotonicOffsets));
}
{
// Nonmonotonic minor indices
let offsets = vec![0, 2, 2, 5];
let indices = vec![0, 2, 3, 1, 4];
let pattern = SparsityPattern::try_from_offsets_and_indices(3, 6, offsets, indices);
assert_eq!(pattern, Err(SparsityPatternFormatError::NonmonotonicMinorIndices));
}
{
// Minor index out of bounds
let offsets = vec![0, 2, 2, 5];
let indices = vec![0, 6, 1, 2, 3];
let pattern = SparsityPattern::try_from_offsets_and_indices(3, 6, offsets, indices);
assert_eq!(pattern, Err(SparsityPatternFormatError::MinorIndexOutOfBounds));
}
{
// Duplicate entry
let offsets = vec![0, 2, 2, 5];
let indices = vec![0, 5, 2, 2, 3];
let pattern = SparsityPattern::try_from_offsets_and_indices(3, 6, offsets, indices);
assert_eq!(pattern, Err(SparsityPatternFormatError::DuplicateEntry));
}
}