From e655fed4fa54486af2c3a9084c44aa0eb6f59d41 Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Tue, 19 Jan 2021 16:53:39 +0100 Subject: [PATCH] Replace Arc with SparsityPattern After much deliberation, I have come to the conclusion that the benefits do not really outweigh the added complexity. Even though the added complexity is relatively minor, it makes it somewhat more complicated to inter-op with other sparse linear algebra libraries in the future. --- nalgebra-sparse/src/cs.rs | 40 +++------ nalgebra-sparse/src/csc.rs | 9 +- nalgebra-sparse/src/csr.rs | 9 +- nalgebra-sparse/src/factorization/cholesky.rs | 15 ++-- nalgebra-sparse/src/ops/impl_std_ops.rs | 29 ++----- nalgebra-sparse/src/ops/serial/cs.rs | 85 ++++++++----------- nalgebra-sparse/src/proptest.rs | 5 +- nalgebra-sparse/tests/unit_tests/ops.rs | 30 +++---- 8 files changed, 86 insertions(+), 136 deletions(-) diff --git a/nalgebra-sparse/src/cs.rs b/nalgebra-sparse/src/cs.rs index e79557b2..645df265 100644 --- a/nalgebra-sparse/src/cs.rs +++ b/nalgebra-sparse/src/cs.rs @@ -1,6 +1,5 @@ use std::mem::replace; use std::ops::Range; -use std::sync::Arc; use num_traits::One; @@ -18,7 +17,7 @@ use crate::pattern::SparsityPattern; /// is obtained by associating columns with the major dimension. #[derive(Debug, Clone, PartialEq, Eq)] pub struct CsMatrix { - sparsity_pattern: Arc, + sparsity_pattern: SparsityPattern, values: Vec } @@ -27,13 +26,13 @@ impl CsMatrix { #[inline] pub fn new(major_dim: usize, minor_dim: usize) -> Self { Self { - sparsity_pattern: Arc::new(SparsityPattern::new(major_dim, minor_dim)), + sparsity_pattern: SparsityPattern::new(major_dim, minor_dim), values: vec![], } } #[inline] - pub fn pattern(&self) -> &Arc { + pub fn pattern(&self) -> &SparsityPattern { &self.sparsity_pattern } @@ -50,24 +49,24 @@ impl CsMatrix { /// Returns the raw data represented as a tuple `(major_offsets, minor_indices, values)`. #[inline] pub fn cs_data(&self) -> (&[usize], &[usize], &[T]) { - let pattern = self.pattern().as_ref(); + let pattern = self.pattern(); (pattern.major_offsets(), pattern.minor_indices(), &self.values) } /// Returns the raw data represented as a tuple `(major_offsets, minor_indices, values)`. #[inline] pub fn cs_data_mut(&mut self) -> (&[usize], &[usize], &mut [T]) { - let pattern = self.sparsity_pattern.as_ref(); + let pattern = &mut self.sparsity_pattern; (pattern.major_offsets(), pattern.minor_indices(), &mut self.values) } #[inline] - pub fn pattern_and_values_mut(&mut self) -> (&Arc, &mut [T]) { + pub fn pattern_and_values_mut(&mut self) -> (&SparsityPattern, &mut [T]) { (&self.sparsity_pattern, &mut self.values) } #[inline] - pub fn from_pattern_and_values(pattern: Arc, values: Vec) + pub fn from_pattern_and_values(pattern: SparsityPattern, values: Vec) -> Self { assert_eq!(pattern.nnz(), values.len(), "Internal error: consumers should verify shape compatibility."); Self { @@ -84,25 +83,14 @@ impl CsMatrix { Some(row_begin .. row_end) } - pub fn take_pattern_and_values(self) -> (Arc, Vec) { + pub fn take_pattern_and_values(self) -> (SparsityPattern, Vec) { (self.sparsity_pattern, self.values) } #[inline] pub fn disassemble(self) -> (Vec, Vec, Vec) { - // Take an Arc to the pattern, which might be the sole reference to the data after - // taking the values. This is important, because it might let us avoid cloning the data - // further below. - let pattern = self.sparsity_pattern; - let values = self.values; - - // Try to take the pattern out of the `Arc` if possible, - // otherwise clone the pattern. - let owned_pattern = Arc::try_unwrap(pattern) - .unwrap_or_else(|arc| SparsityPattern::clone(&*arc)); - let (offsets, indices) = owned_pattern.disassemble(); - - (offsets, indices, values) + let (offsets, indices) = self.sparsity_pattern.disassemble(); + (offsets, indices, self.values) } /// Returns an entry for the given major/minor indices, or `None` if the indices are out @@ -151,12 +139,12 @@ impl CsMatrix { #[inline] pub fn lane_iter(&self) -> CsLaneIter { - CsLaneIter::new(self.pattern().as_ref(), self.values()) + CsLaneIter::new(self.pattern(), self.values()) } #[inline] pub fn lane_iter_mut(&mut self) -> CsLaneIterMut { - CsLaneIterMut::new(self.sparsity_pattern.as_ref(), &mut self.values) + CsLaneIterMut::new(&self.sparsity_pattern, &mut self.values) } #[inline] @@ -190,7 +178,7 @@ impl CsMatrix { new_indices) .expect("Internal error: Sparsity pattern must always be valid."); - Self::from_pattern_and_values(Arc::new(new_pattern), new_values) + Self::from_pattern_and_values(new_pattern, new_values) } } @@ -205,7 +193,7 @@ impl CsMatrix { // TODO: We should skip checks here let pattern = SparsityPattern::try_from_offsets_and_indices(n, n, offsets, indices) .unwrap(); - Self::from_pattern_and_values(Arc::new(pattern), values) + Self::from_pattern_and_values(pattern, values) } } diff --git a/nalgebra-sparse/src/csc.rs b/nalgebra-sparse/src/csc.rs index c3d79508..d7883fa3 100644 --- a/nalgebra-sparse/src/csc.rs +++ b/nalgebra-sparse/src/csc.rs @@ -5,7 +5,6 @@ use crate::pattern::{SparsityPattern, SparsityPatternFormatError, SparsityPatter use crate::csr::CsrMatrix; use crate::cs::{CsMatrix, CsLane, CsLaneMut, CsLaneIter, CsLaneIterMut}; -use std::sync::Arc; use std::slice::{IterMut, Iter}; use num_traits::{One}; use nalgebra::Scalar; @@ -95,14 +94,14 @@ impl CscMatrix { let pattern = SparsityPattern::try_from_offsets_and_indices( num_cols, num_rows, col_offsets, row_indices) .map_err(pattern_format_error_to_csc_error)?; - Self::try_from_pattern_and_values(Arc::new(pattern), values) + Self::try_from_pattern_and_values(pattern, values) } /// Try to construct a CSC matrix from a sparsity pattern and associated non-zero values. /// /// Returns an error if the number of values does not match the number of minor indices /// in the pattern. - pub fn try_from_pattern_and_values(pattern: Arc, values: Vec) + pub fn try_from_pattern_and_values(pattern: SparsityPattern, values: Vec) -> Result { if pattern.nnz() == values.len() { Ok(Self { @@ -212,7 +211,7 @@ impl CscMatrix { /// An iterator over columns in the matrix. pub fn col_iter(&self) -> CscColIter { CscColIter { - lane_iter: CsLaneIter::new(self.pattern().as_ref(), self.values()) + lane_iter: CsLaneIter::new(self.pattern(), self.values()) } } @@ -258,7 +257,7 @@ impl CscMatrix { /// The sparsity pattern is stored internally inside an `Arc`. This allows users to re-use /// the same sparsity pattern for multiple matrices without storing the same pattern multiple /// times in memory. - pub fn pattern(&self) -> &Arc { + pub fn pattern(&self) -> &SparsityPattern { self.cs.pattern() } diff --git a/nalgebra-sparse/src/csr.rs b/nalgebra-sparse/src/csr.rs index 81f8191c..dceca614 100644 --- a/nalgebra-sparse/src/csr.rs +++ b/nalgebra-sparse/src/csr.rs @@ -7,7 +7,6 @@ use crate::cs::{CsMatrix, CsLaneIterMut, CsLaneIter, CsLane, CsLaneMut}; use nalgebra::Scalar; use num_traits::{One}; -use std::sync::Arc; use std::slice::{IterMut, Iter}; /// A CSR representation of a sparse matrix. @@ -97,14 +96,14 @@ impl CsrMatrix { let pattern = SparsityPattern::try_from_offsets_and_indices( num_rows, num_cols, row_offsets, col_indices) .map_err(pattern_format_error_to_csr_error)?; - Self::try_from_pattern_and_values(Arc::new(pattern), values) + Self::try_from_pattern_and_values(pattern, values) } /// Try to construct a CSR matrix from a sparsity pattern and associated non-zero values. /// /// Returns an error if the number of values does not match the number of minor indices /// in the pattern. - pub fn try_from_pattern_and_values(pattern: Arc, values: Vec) + pub fn try_from_pattern_and_values(pattern: SparsityPattern, values: Vec) -> Result { if pattern.nnz() == values.len() { Ok(Self { @@ -214,7 +213,7 @@ impl CsrMatrix { /// An iterator over rows in the matrix. pub fn row_iter(&self) -> CsrRowIter { CsrRowIter { - lane_iter: CsLaneIter::new(self.pattern().as_ref(), self.values()) + lane_iter: CsLaneIter::new(self.pattern(), self.values()) } } @@ -260,7 +259,7 @@ impl CsrMatrix { /// The sparsity pattern is stored internally inside an `Arc`. This allows users to re-use /// the same sparsity pattern for multiple matrices without storing the same pattern multiple /// times in memory. - pub fn pattern(&self) -> &Arc { + pub fn pattern(&self) -> &SparsityPattern { self.cs.pattern() } diff --git a/nalgebra-sparse/src/factorization/cholesky.rs b/nalgebra-sparse/src/factorization/cholesky.rs index 2639424c..8ffbbe3d 100644 --- a/nalgebra-sparse/src/factorization/cholesky.rs +++ b/nalgebra-sparse/src/factorization/cholesky.rs @@ -5,26 +5,25 @@ use crate::pattern::SparsityPattern; use crate::csc::CscMatrix; use core::{mem, iter}; use nalgebra::{Scalar, RealField, DMatrixSlice, DMatrixSliceMut, DMatrix}; -use std::sync::Arc; use std::fmt::{Display, Formatter}; use crate::ops::serial::spsolve_csc_lower_triangular; use crate::ops::Op; pub struct CscSymbolicCholesky { // Pattern of the original matrix that was decomposed - m_pattern: Arc, + m_pattern: SparsityPattern, l_pattern: SparsityPattern, // u in this context is L^T, so that M = L L^T u_pattern: SparsityPattern } impl CscSymbolicCholesky { - pub fn factor(pattern: &Arc) -> Self { + pub fn factor(pattern: SparsityPattern) -> Self { assert_eq!(pattern.major_dim(), pattern.minor_dim(), "Major and minor dimensions must be the same (square matrix)."); - let (l_pattern, u_pattern) = nonzero_pattern(&*pattern); + let (l_pattern, u_pattern) = nonzero_pattern(&pattern); Self { - m_pattern: Arc::clone(pattern), + m_pattern: pattern, l_pattern, u_pattern, } @@ -37,7 +36,7 @@ impl CscSymbolicCholesky { pub struct CscCholesky { // Pattern of the original matrix - m_pattern: Arc, + m_pattern: SparsityPattern, l_factor: CscMatrix, u_pattern: SparsityPattern, work_x: Vec, @@ -66,7 +65,7 @@ impl CscCholesky { let l_nnz = symbolic.l_pattern.nnz(); let l_values = vec![T::zero(); l_nnz]; - let l_factor = CscMatrix::try_from_pattern_and_values(Arc::new(symbolic.l_pattern), l_values) + let l_factor = CscMatrix::try_from_pattern_and_values(symbolic.l_pattern, l_values) .unwrap(); let (nrows, ncols) = (l_factor.nrows(), l_factor.ncols()); @@ -86,7 +85,7 @@ impl CscCholesky { } pub fn factor(matrix: &CscMatrix) -> Result { - let symbolic = CscSymbolicCholesky::factor(&*matrix.pattern()); + let symbolic = CscSymbolicCholesky::factor(matrix.pattern().clone()); Self::factor_numerical(symbolic, matrix.values()) } diff --git a/nalgebra-sparse/src/ops/impl_std_ops.rs b/nalgebra-sparse/src/ops/impl_std_ops.rs index 12db5264..1b337397 100644 --- a/nalgebra-sparse/src/ops/impl_std_ops.rs +++ b/nalgebra-sparse/src/ops/impl_std_ops.rs @@ -7,7 +7,6 @@ use crate::ops::serial::{spadd_csr_prealloc, spadd_csc_prealloc, spadd_pattern, use nalgebra::{ClosedAdd, ClosedMul, ClosedSub, ClosedDiv, Scalar, Matrix, Dim, DMatrixSlice, DMatrix, Dynamic}; use num_traits::{Zero, One}; -use std::sync::Arc; use crate::ops::{Op}; use nalgebra::base::storage::Storage; @@ -48,11 +47,7 @@ macro_rules! impl_sp_plus_minus { impl_bin_op!($trait, $method, <'a, T>(a: &'a $matrix_type, b: &'a $matrix_type) -> $matrix_type { // If both matrices have the same pattern, then we can immediately re-use it - let pattern = if Arc::ptr_eq(a.pattern(), b.pattern()) { - Arc::clone(a.pattern()) - } else { - Arc::new(spadd_pattern(a.pattern(), b.pattern())) - }; + let pattern = spadd_pattern(a.pattern(), b.pattern()); let values = vec![T::zero(); pattern.nnz()]; // We are giving data that is valid by definition, so it is safe to unwrap below let mut result = $matrix_type::try_from_pattern_and_values(pattern, values) @@ -64,24 +59,12 @@ macro_rules! impl_sp_plus_minus { impl_bin_op!($trait, $method, <'a, T>(a: $matrix_type, b: &'a $matrix_type) -> $matrix_type { - let mut a = a; - if Arc::ptr_eq(a.pattern(), b.pattern()) { - $spadd_fn(T::one(), &mut a, $factor * T::one(), Op::NoOp(b)).unwrap(); - a - } else { - &a $sign b - } + &a $sign b }); impl_bin_op!($trait, $method, <'a, T>(a: &'a $matrix_type, b: $matrix_type) -> $matrix_type { - let mut b = b; - if Arc::ptr_eq(a.pattern(), b.pattern()) { - $spadd_fn($factor * T::one(), &mut b, T::one(), Op::NoOp(a)).unwrap(); - b - } else { - a $sign &b - } + a $sign &b }); impl_bin_op!($trait, $method, (a: $matrix_type, b: $matrix_type) -> $matrix_type { a $sign &b @@ -107,7 +90,7 @@ macro_rules! impl_spmm { impl_mul!(<'a, T>(a: &'a $matrix_type, b: &'a $matrix_type) -> $matrix_type { let pattern = $pattern_fn(a.pattern(), b.pattern()); let values = vec![T::zero(); pattern.nnz()]; - let mut result = $matrix_type::try_from_pattern_and_values(Arc::new(pattern), values) + let mut result = $matrix_type::try_from_pattern_and_values(pattern, values) .unwrap(); $spmm_fn(T::zero(), &mut result, @@ -154,7 +137,7 @@ macro_rules! impl_scalar_mul { .iter() .map(|v_i| v_i.inlined_clone() * b.inlined_clone()) .collect(); - $matrix_type::try_from_pattern_and_values(Arc::clone(a.pattern()), values).unwrap() + $matrix_type::try_from_pattern_and_values(a.pattern().clone(), values).unwrap() }); impl_mul!(<'a, T>(a: &'a $matrix_type, b: T) -> $matrix_type { a * &b @@ -251,7 +234,7 @@ macro_rules! impl_div { .iter() .map(|v_i| v_i.inlined_clone() / scalar.inlined_clone()) .collect(); - $matrix_type::try_from_pattern_and_values(Arc::clone(matrix.pattern()), new_values) + $matrix_type::try_from_pattern_and_values(matrix.pattern().clone(), new_values) .unwrap() }); impl_bin_op!(Div, div, <'a, T: ClosedDiv>(matrix: &'a $matrix_type, scalar: &'a T) -> $matrix_type { diff --git a/nalgebra-sparse/src/ops/serial/cs.rs b/nalgebra-sparse/src/ops/serial/cs.rs index 22f3b524..9a4f7e34 100644 --- a/nalgebra-sparse/src/ops/serial/cs.rs +++ b/nalgebra-sparse/src/ops/serial/cs.rs @@ -4,7 +4,6 @@ use crate::ops::serial::{OperationErrorType, OperationError}; use nalgebra::{Scalar, ClosedAdd, ClosedMul, DMatrixSliceMut, DMatrixSlice}; use num_traits::{Zero, One}; use crate::SparseEntryMut; -use std::sync::Arc; fn spmm_cs_unexpected_entry() -> OperationError { OperationError::from_type_and_message( @@ -73,64 +72,52 @@ pub fn spadd_cs_prealloc(beta: T, where T: Scalar + ClosedAdd + ClosedMul + Zero + One { - - if Arc::ptr_eq(&c.pattern(), &a.inner_ref().pattern()) { - // Special fast path: The two matrices have *exactly* the same sparsity pattern, - // so we only need to sum the value arrays - // TODO: Test this fast path - for (c_ij, a_ij) in c.values_mut().iter_mut().zip(a.inner_ref().values()) { - let (alpha, beta) = (alpha.inlined_clone(), beta.inlined_clone()); - *c_ij = beta * c_ij.inlined_clone() + alpha * a_ij.inlined_clone(); - } - Ok(()) - } else { - match a { - Op::NoOp(a) => { - for (mut c_lane_i, a_lane_i) in c.lane_iter_mut().zip(a.lane_iter()) { - if beta != T::one() { - for c_ij in c_lane_i.values_mut() { - *c_ij *= beta.inlined_clone(); - } - } - - let (mut c_minors, mut c_vals) = c_lane_i.indices_and_values_mut(); - let (a_minors, a_vals) = (a_lane_i.minor_indices(), a_lane_i.values()); - - for (a_col, a_val) in a_minors.iter().zip(a_vals) { - // TODO: Use exponential search instead of linear search. - // If C has substantially more entries in the row than A, then a line search - // will needlessly visit many entries in C. - let (c_idx, _) = c_minors.iter() - .enumerate() - .find(|(_, c_col)| *c_col == a_col) - .ok_or_else(spadd_cs_unexpected_entry)?; - c_vals[c_idx] += alpha.inlined_clone() * a_val.inlined_clone(); - c_minors = &c_minors[c_idx ..]; - c_vals = &mut c_vals[c_idx ..]; - } - } - } - Op::Transpose(a) => { + match a { + Op::NoOp(a) => { + for (mut c_lane_i, a_lane_i) in c.lane_iter_mut().zip(a.lane_iter()) { if beta != T::one() { - for c_ij in c.values_mut() { + for c_ij in c_lane_i.values_mut() { *c_ij *= beta.inlined_clone(); } } - for (i, a_lane_i) in a.lane_iter().enumerate() { - for (&j, a_val) in a_lane_i.minor_indices().iter().zip(a_lane_i.values()) { - let a_val = a_val.inlined_clone(); - let alpha = alpha.inlined_clone(); - match c.get_entry_mut(j, i).unwrap() { - SparseEntryMut::NonZero(c_ji) => { *c_ji += alpha * a_val } - SparseEntryMut::Zero => return Err(spadd_cs_unexpected_entry()), - } + let (mut c_minors, mut c_vals) = c_lane_i.indices_and_values_mut(); + let (a_minors, a_vals) = (a_lane_i.minor_indices(), a_lane_i.values()); + + for (a_col, a_val) in a_minors.iter().zip(a_vals) { + // TODO: Use exponential search instead of linear search. + // If C has substantially more entries in the row than A, then a line search + // will needlessly visit many entries in C. + let (c_idx, _) = c_minors.iter() + .enumerate() + .find(|(_, c_col)| *c_col == a_col) + .ok_or_else(spadd_cs_unexpected_entry)?; + c_vals[c_idx] += alpha.inlined_clone() * a_val.inlined_clone(); + c_minors = &c_minors[c_idx ..]; + c_vals = &mut c_vals[c_idx ..]; + } + } + } + Op::Transpose(a) => { + if beta != T::one() { + for c_ij in c.values_mut() { + *c_ij *= beta.inlined_clone(); + } + } + + for (i, a_lane_i) in a.lane_iter().enumerate() { + for (&j, a_val) in a_lane_i.minor_indices().iter().zip(a_lane_i.values()) { + let a_val = a_val.inlined_clone(); + let alpha = alpha.inlined_clone(); + match c.get_entry_mut(j, i).unwrap() { + SparseEntryMut::NonZero(c_ji) => { *c_ji += alpha * a_val } + SparseEntryMut::Zero => return Err(spadd_cs_unexpected_entry()), } } } } - Ok(()) } + Ok(()) } /// Helper functionality for implementing CSR/CSC SPMM. diff --git a/nalgebra-sparse/src/proptest.rs b/nalgebra-sparse/src/proptest.rs index 80eb88a6..1115693a 100644 --- a/nalgebra-sparse/src/proptest.rs +++ b/nalgebra-sparse/src/proptest.rs @@ -11,7 +11,6 @@ use std::iter::{repeat}; use proptest::sample::{Index}; use crate::csr::CsrMatrix; use crate::pattern::SparsityPattern; -use std::sync::Arc; use crate::csc::CscMatrix; fn dense_row_major_coord_strategy(nrows: usize, ncols: usize, nnz: usize) @@ -291,7 +290,7 @@ where (Just(pattern), values) }) .prop_map(|(pattern, values)| { - CsrMatrix::try_from_pattern_and_values(Arc::new(pattern), values) + CsrMatrix::try_from_pattern_and_values(pattern, values) .expect("Internal error: Generated CsrMatrix is invalid") }) } @@ -313,7 +312,7 @@ pub fn csc(value_strategy: T, (Just(pattern), values) }) .prop_map(|(pattern, values)| { - CscMatrix::try_from_pattern_and_values(Arc::new(pattern), values) + CscMatrix::try_from_pattern_and_values(pattern, values) .expect("Internal error: Generated CscMatrix is invalid") }) } \ No newline at end of file diff --git a/nalgebra-sparse/tests/unit_tests/ops.rs b/nalgebra-sparse/tests/unit_tests/ops.rs index a814f094..5a9aafe7 100644 --- a/nalgebra-sparse/tests/unit_tests/ops.rs +++ b/nalgebra-sparse/tests/unit_tests/ops.rs @@ -17,12 +17,11 @@ use proptest::prelude::*; use matrixcompare::prop_assert_matrix_eq; use std::panic::catch_unwind; -use std::sync::Arc; /// Represents the sparsity pattern of a CSR matrix as a dense matrix with 0/1 fn dense_csr_pattern(pattern: &SparsityPattern) -> DMatrix { let boolean_csr = CsrMatrix::try_from_pattern_and_values( - Arc::new(pattern.clone()), + pattern.clone(), vec![1; pattern.nnz()]) .unwrap(); DMatrix::from(&boolean_csr) @@ -31,7 +30,7 @@ fn dense_csr_pattern(pattern: &SparsityPattern) -> DMatrix { /// Represents the sparsity pattern of a CSC matrix as a dense matrix with 0/1 fn dense_csc_pattern(pattern: &SparsityPattern) -> DMatrix { let boolean_csc = CscMatrix::try_from_pattern_and_values( - Arc::new(pattern.clone()), + pattern.clone(), vec![1; pattern.nnz()]) .unwrap(); DMatrix::from(&boolean_csc) @@ -137,8 +136,8 @@ fn spadd_csr_prealloc_args_strategy() -> impl Strategy> let beta = value_strategy.clone(); (Just(c_pattern), Just(a_pattern), c_values, a_values, alpha, beta, trans_strategy()) }).prop_map(|(c_pattern, a_pattern, c_values, a_values, alpha, beta, trans_a)| { - let c = CsrMatrix::try_from_pattern_and_values(Arc::new(c_pattern), c_values).unwrap(); - let a = CsrMatrix::try_from_pattern_and_values(Arc::new(a_pattern), a_values).unwrap(); + let c = CsrMatrix::try_from_pattern_and_values(c_pattern, c_values).unwrap(); + let a = CsrMatrix::try_from_pattern_and_values(a_pattern, a_values).unwrap(); let a = if trans_a { Op::Transpose(a.transpose()) } else { Op::NoOp(a) }; SpaddCsrArgs { c, beta, alpha, a } @@ -222,15 +221,12 @@ fn spmm_csr_prealloc_args_strategy() -> impl Strategy> { let b_values = vec![PROPTEST_I32_VALUE_STRATEGY; b_pattern.nnz()]; let c_pattern = spmm_pattern(&a_pattern, &b_pattern); let c_values = vec![PROPTEST_I32_VALUE_STRATEGY; c_pattern.nnz()]; - let a_pattern = Arc::new(a_pattern); - let b_pattern = Arc::new(b_pattern); - let c_pattern = Arc::new(c_pattern); let a = a_values.prop_map(move |values| - CsrMatrix::try_from_pattern_and_values(Arc::clone(&a_pattern), values).unwrap()); + CsrMatrix::try_from_pattern_and_values(a_pattern.clone(), values).unwrap()); let b = b_values.prop_map(move |values| - CsrMatrix::try_from_pattern_and_values(Arc::clone(&b_pattern), values).unwrap()); + CsrMatrix::try_from_pattern_and_values(b_pattern.clone(), values).unwrap()); let c = c_values.prop_map(move |values| - CsrMatrix::try_from_pattern_and_values(Arc::clone(&c_pattern), values).unwrap()); + CsrMatrix::try_from_pattern_and_values(c_pattern.clone(), values).unwrap()); let alpha = PROPTEST_I32_VALUE_STRATEGY; let beta = PROPTEST_I32_VALUE_STRATEGY; (c, beta, alpha, trans_strategy(), a, trans_strategy(), b) @@ -383,16 +379,16 @@ proptest! { // corresponding to a and b, and convert them to dense matrices. // The sum of these dense matrices will then have non-zeros in exactly the same locations // as the result of "adding" the sparsity patterns - let a_csr = CsrMatrix::try_from_pattern_and_values(Arc::new(a.clone()), vec![1; a.nnz()]) + let a_csr = CsrMatrix::try_from_pattern_and_values(a.clone(), vec![1; a.nnz()]) .unwrap(); let a_dense = DMatrix::from(&a_csr); - let b_csr = CsrMatrix::try_from_pattern_and_values(Arc::new(b.clone()), vec![1; b.nnz()]) + let b_csr = CsrMatrix::try_from_pattern_and_values(b.clone(), vec![1; b.nnz()]) .unwrap(); let b_dense = DMatrix::from(&b_csr); let c_dense = a_dense + b_dense; let c_csr = CsrMatrix::from(&c_dense); - prop_assert_eq!(&pattern_result, c_csr.pattern().as_ref()); + prop_assert_eq!(&pattern_result, c_csr.pattern()); } #[test] @@ -492,16 +488,16 @@ proptest! { // corresponding to a and b, and convert them to dense matrices. // The product of these dense matrices will then have non-zeros in exactly the same locations // as the result of "multiplying" the sparsity patterns - let a_csr = CsrMatrix::try_from_pattern_and_values(Arc::new(a.clone()), vec![1; a.nnz()]) + let a_csr = CsrMatrix::try_from_pattern_and_values(a.clone(), vec![1; a.nnz()]) .unwrap(); let a_dense = DMatrix::from(&a_csr); - let b_csr = CsrMatrix::try_from_pattern_and_values(Arc::new(b.clone()), vec![1; b.nnz()]) + let b_csr = CsrMatrix::try_from_pattern_and_values(b.clone(), vec![1; b.nnz()]) .unwrap(); let b_dense = DMatrix::from(&b_csr); let c_dense = a_dense * b_dense; let c_csr = CsrMatrix::from(&c_dense); - prop_assert_eq!(&c_pattern, c_csr.pattern().as_ref()); + prop_assert_eq!(&c_pattern, c_csr.pattern()); } #[test]