Initial COO <-> Dense conversion routines

This commit is contained in:
Andreas Longva 2020-11-18 14:14:38 +01:00
parent 7260f05b07
commit 54329146c9
7 changed files with 172 additions and 0 deletions

View File

@ -0,0 +1,26 @@
use crate::coo::CooMatrix;
use crate::convert::serial::{convert_dense_coo, convert_coo_dense};
use nalgebra::{Matrix, Scalar, Dim, ClosedAdd, DMatrix};
use nalgebra::storage::{Storage};
use num_traits::Zero;
impl<'a, T, R, C, S> From<&'a Matrix<T, R, C, S>> for CooMatrix<T>
where
T: Scalar + Zero,
R: Dim,
C: Dim,
S: Storage<T, R, C>
{
fn from(matrix: &'a Matrix<T, R, C, S>) -> Self {
convert_dense_coo(matrix)
}
}
impl<'a, T> From<&'a CooMatrix<T>> for DMatrix<T>
where
T: Scalar + Zero + ClosedAdd,
{
fn from(coo: &'a CooMatrix<T>) -> Self {
convert_coo_dense(coo)
}
}

View File

@ -0,0 +1,5 @@
//! TODO
pub mod serial;
mod impl_std_ops;

View File

@ -0,0 +1,48 @@
//! TODO
use nalgebra::{DMatrix, Scalar, Matrix, Dim};
use crate::coo::CooMatrix;
use crate::csr::CsrMatrix;
use num_traits::Zero;
use std::ops::{Add, AddAssign};
use nalgebra::storage::Storage;
/// TODO
pub fn convert_dense_coo<T, R, C, S>(dense: &Matrix<T, R, C, S>) -> CooMatrix<T>
where
T: Scalar + Zero,
R: Dim,
C: Dim,
S: Storage<T, R, C>
{
let mut coo = CooMatrix::new(dense.nrows(), dense.ncols());
for (index, v) in dense.iter().enumerate() {
if v != &T::zero() {
// We use the fact that matrix iteration is guaranteed to be column-major
let i = index % dense.nrows();
let j = index / dense.nrows();
coo.push(i, j, v.inlined_clone());
}
}
coo
}
/// TODO
///
/// TODO: What should the actual trait bounds be?
pub fn convert_coo_dense<T>(coo: &CooMatrix<T>) -> DMatrix<T>
where
T: Scalar + Zero + Add + AddAssign,
{
let mut output = DMatrix::repeat(coo.nrows(), coo.ncols(), T::zero());
for (i, j, v) in coo.triplet_iter() {
output[(i, j)] += v.inlined_clone();
}
output
}
/// TODO
pub fn convert_coo_csr<T>(_coo: &CooMatrix<T>) -> CsrMatrix<T> {
todo!()
}

View File

@ -186,6 +186,8 @@ where
/// Construct the dense representation of the COO matrix. /// Construct the dense representation of the COO matrix.
/// ///
/// Duplicate entries are summed together. /// Duplicate entries are summed together.
///
/// TODO: Remove?
pub fn to_dense(&self) -> DMatrix<T> pub fn to_dense(&self) -> DMatrix<T>
where where
T: ClosedAdd + Zero, T: ClosedAdd + Zero,

View File

@ -68,6 +68,11 @@
//! - Cholesky factorization (port existing factorization from nalgebra's sparse module) //! - Cholesky factorization (port existing factorization from nalgebra's sparse module)
//! //!
//! //!
//! TODO: Write docs on the following:
//!
//! - Overall design ("easy API" vs. "expert" API etc.)
//! - Conversions (From, explicit "expert" API etc.)
//! - Matrix ops design
#![deny(non_camel_case_types)] #![deny(non_camel_case_types)]
#![deny(unused_parens)] #![deny(unused_parens)]
#![deny(non_upper_case_globals)] #![deny(non_upper_case_globals)]
@ -80,6 +85,7 @@ pub mod csc;
pub mod csr; pub mod csr;
pub mod pattern; pub mod pattern;
pub mod ops; pub mod ops;
pub mod convert;
#[cfg(feature = "proptest-support")] #[cfg(feature = "proptest-support")]
pub mod proptest; pub mod proptest;

View File

@ -0,0 +1,84 @@
use nalgebra_sparse::coo::CooMatrix;
use nalgebra_sparse::convert::serial::{convert_coo_dense, convert_dense_coo};
use nalgebra_sparse::proptest::coo_with_duplicates;
use nalgebra::proptest::matrix;
use proptest::prelude::*;
use nalgebra::DMatrix;
#[test]
fn test_convert_dense_coo() {
// No duplicates
{
#[rustfmt::skip]
let entries = &[1, 0, 3,
0, 5, 0];
// The COO representation of a dense matrix is not unique.
// Here we implicitly test that the coo matrix is indeed constructed from column-major
// iteration of the dense matrix.
let dense = DMatrix::from_row_slice(2, 3, entries);
let coo = CooMatrix::try_from_triplets(2, 3, vec![0, 1, 0], vec![0, 1, 2], vec![1, 5, 3])
.unwrap();
assert_eq!(CooMatrix::from(&dense), coo);
assert_eq!(DMatrix::from(&coo), dense);
}
// Duplicates
// No duplicates
{
#[rustfmt::skip]
let entries = &[1, 0, 3,
0, 5, 0];
// The COO representation of a dense matrix is not unique.
// Here we implicitly test that the coo matrix is indeed constructed from column-major
// iteration of the dense matrix.
let dense = DMatrix::from_row_slice(2, 3, entries);
let coo_no_dup = CooMatrix::try_from_triplets(2, 3,
vec![0, 1, 0],
vec![0, 1, 2],
vec![1, 5, 3])
.unwrap();
let coo_dup = CooMatrix::try_from_triplets(2, 3,
vec![0, 1, 0, 1],
vec![0, 1, 2, 1],
vec![1, -2, 3, 7])
.unwrap();
assert_eq!(CooMatrix::from(&dense), coo_no_dup);
assert_eq!(DMatrix::from(&coo_dup), dense);
}
}
fn coo_strategy() -> impl Strategy<Value=CooMatrix<i32>> {
coo_with_duplicates(-5 ..= 5, 0..=6usize, 0..=6usize, 40, 2)
}
proptest! {
#[test]
fn convert_dense_coo_roundtrip(dense in matrix(-5 ..= 5, 0 ..=6, 0..=6)) {
let coo = convert_dense_coo(&dense);
let dense2 = convert_coo_dense(&coo);
prop_assert_eq!(&dense, &dense2);
}
#[test]
fn convert_coo_dense_coo_roundtrip(coo in coo_strategy()) {
// We cannot compare the result of the roundtrip coo -> dense -> coo directly for
// two reasons:
// 1. the COO matrices will generally have different ordering of elements
// 2. explicitly stored zero entries in the original matrix will be discarded
// when converting back to COO
// Therefore we instead compare the results of converting the COO matrix
// at the end of the roundtrip with its dense representation
let dense = convert_coo_dense(&coo);
let coo2 = convert_dense_coo(&dense);
let dense2 = convert_coo_dense(&coo2);
prop_assert_eq!(dense, dense2);
}
#[test]
fn from_dense_coo_roundtrip(dense in matrix(-5..=5, 0..=6, 0..=6)) {
prop_assert_eq!(&dense, &DMatrix::from(&CooMatrix::from(&dense)));
}
}

View File

@ -3,4 +3,5 @@ mod ops;
mod pattern; mod pattern;
mod csr; mod csr;
mod csc; mod csc;
mod convert_serial;
mod proptest; mod proptest;