From 3b67afcd9bca508e97fe2ab13f5bd77a1af9071a Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Wed, 1 Dec 2021 11:07:13 +0100 Subject: [PATCH 1/9] Matrix market: Extend and reword documentation, rename some types --- nalgebra-sparse/src/io/matrix_market.rs | 213 ++++++++++++++---------- nalgebra-sparse/src/io/mod.rs | 34 +++- 2 files changed, 155 insertions(+), 92 deletions(-) diff --git a/nalgebra-sparse/src/io/matrix_market.rs b/nalgebra-sparse/src/io/matrix_market.rs index 6b008517..518675cd 100644 --- a/nalgebra-sparse/src/io/matrix_market.rs +++ b/nalgebra-sparse/src/io/matrix_market.rs @@ -27,11 +27,12 @@ pub struct MatrixMarketError { } /// Errors produced by functions that expect well-formed matrix market format data. -/// > _NOTE:_ Since the matrix market design didn't mention if multiple sparse entries with the same coordiantes are allowed or not, so, it's allowed here. #[non_exhaustive] #[derive(Copy, Clone, Debug, PartialEq)] pub enum MatrixMarketErrorKind { - /// Indicates that some word is not known to MM format + /// Parsing failure. + /// + /// Indicates that the parser failed, for example due to an unexpected string. /// /// Examples /// -------- @@ -39,17 +40,17 @@ pub enum MatrixMarketErrorKind { /// # use nalgebra_sparse::io::load_coo_from_matrix_market_str; /// # use nalgebra_sparse::io::MatrixMarketErrorKind; /// let str = r#" - /// %%MatrixMarket whatever whatever whatever whatever + /// %%MatrixMarket invalid invalid invalid invalid /// 1 1 1 /// 1 1 5 /// "#; - /// let matrix_error = load_coo_from_matrix_market_str::(str); - /// assert_eq!(matrix_error.is_err(), true); - /// assert_eq!(matrix_error.unwrap_err().kind(),MatrixMarketErrorKind::ParsingError); + /// let matrix_result = load_coo_from_matrix_market_str::(str); + /// assert_eq!(matrix_result.is_err(), true); + /// assert_eq!(matrix_result.unwrap_err().kind(), MatrixMarketErrorKind::ParsingError); /// ``` ParsingError, - /// Indicates that header is not valid + /// Indicates that the matrix market header is invalid. /// /// Examples /// -------- @@ -62,13 +63,13 @@ pub enum MatrixMarketErrorKind { /// 1 1 1 /// 1 1 5 /// "#; - /// let matrix_error = load_coo_from_matrix_market_str::(str); - /// assert_eq!(matrix_error.is_err(), true); - /// assert_eq!(matrix_error.unwrap_err().kind(),MatrixMarketErrorKind::InvalidHeader); + /// let matrix_result = load_coo_from_matrix_market_str::(str); + /// assert_eq!(matrix_result.is_err(), true); + /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::InvalidHeader); /// ``` InvalidHeader, - /// Indicates that the data entries in .mtx file are more or less than entries specified in .mtx file + /// Indicates that the number of data entries in the matrix market file does not match the header. /// /// Examples /// -------- @@ -82,13 +83,14 @@ pub enum MatrixMarketErrorKind { /// 2 2 2 /// 2 3 2 /// "#; - /// let matrix_error = load_coo_from_matrix_market_str::(str); - /// assert_eq!(matrix_error.is_err(), true); - /// assert_eq!(matrix_error.unwrap_err().kind(),MatrixMarketErrorKind::EntryNumUnmatched); + /// let matrix_result = load_coo_from_matrix_market_str::(str); + /// assert_eq!(matrix_result.is_err(), true); + /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::EntryMismatch); /// ``` - EntryNumUnmatched, + EntryMismatch, - /// Indicates that the type T is not matched with the function it called. + /// Indicates that the scalar type requested is not compatible with the scalar type stored + /// in the matrix market file. /// /// Examples /// -------- @@ -97,18 +99,20 @@ pub enum MatrixMarketErrorKind { /// # use nalgebra_sparse::io::MatrixMarketErrorKind; /// let str = r#" /// %%matrixmarket matrix coordinate real general - /// % it should be called by load_coo_from_matrix_market_str::(str), or f32; + /// % it should be loaded with load_coo_from_matrix_market_str::(str) (or f32) /// 3 3 2 /// 2 2 2.22 /// 2 3 2.22 /// "#; - /// let matrix_error = load_coo_from_matrix_market_str::(str); - /// assert_eq!(matrix_error.is_err(), true); - /// assert_eq!(matrix_error.unwrap_err().kind(),MatrixMarketErrorKind::TypeUnmatched); + /// let matrix_result = load_coo_from_matrix_market_str::(str); + /// assert_eq!(matrix_result.is_err(), true); + /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::TypeMismatch); /// ``` - TypeUnmatched, + TypeMismatch, - /// Indicates that zero has been used as an index in the data, or the shape of the matrix, which is not allowed. + /// Indicates that zero has been used as an index in the data. + /// + /// **Note**: The matrix market format uses 1-based indexing. /// /// Examples /// -------- @@ -120,13 +124,14 @@ pub enum MatrixMarketErrorKind { /// 1 1 1 /// 0 0 10 /// "#; - /// let matrix_error = load_coo_from_matrix_market_str::(str); - /// assert_eq!(matrix_error.is_err(), true); - /// assert_eq!(matrix_error.unwrap_err().kind(),MatrixMarketErrorKind::ZeroError); + /// let matrix_result = load_coo_from_matrix_market_str::(str); + /// assert_eq!(matrix_result.is_err(), true); + /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::ZeroError); /// ``` ZeroError, - /// Indicates [SparseFormatError], while creating the sparse matrix. + /// Indicates [SparseFormatError] while creating the sparse matrix. + /// /// /// Examples /// -------- @@ -139,13 +144,14 @@ pub enum MatrixMarketErrorKind { /// 1 1 1 /// 4 2 10 /// "#; - /// let matrix_error = load_coo_from_matrix_market_str::(str); - /// assert_eq!(matrix_error.is_err(), true); - /// assert_eq!(matrix_error.unwrap_err().kind(),MatrixMarketErrorKind::SparseFormatError(SparseFormatErrorKind::IndexOutOfBounds)); + /// let matrix_result = load_coo_from_matrix_market_str::(str); + /// assert_eq!(matrix_result.is_err(), true); + /// assert_eq!(matrix_result.unwrap_err().kind(), + /// MatrixMarketErrorKind::SparseFormatError(SparseFormatErrorKind::IndexOutOfBounds)); /// ``` SparseFormatError(SparseFormatErrorKind), - /// Indicates that a wrong diagonal element has been provieded to the matrix + /// Indicates that a wrong diagonal element has been provided to the matrix. /// /// Examples /// -------- @@ -160,9 +166,9 @@ pub enum MatrixMarketErrorKind { /// 1 1 10 /// 2 1 5 /// "#; - /// let matrix_error = load_coo_from_matrix_market_str::(str); - /// assert_eq!(matrix_error.is_err(), true); - /// assert_eq!(matrix_error.unwrap_err().kind(),MatrixMarketErrorKind::DiagonalError); + /// let matrix_result = load_coo_from_matrix_market_str::(str); + /// assert_eq!(matrix_result.is_err(), true); + /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::DiagonalError); /// /// let str = r#" /// %%matrixmarket matrix coordinate complex hermitian @@ -171,28 +177,27 @@ pub enum MatrixMarketErrorKind { /// 1 1 10 2 /// 2 1 5 2 /// "#; - /// let matrix_error = load_coo_from_matrix_market_str::>(str); - /// assert_eq!(matrix_error.is_err(), true); - /// assert_eq!(matrix_error.unwrap_err().kind(),MatrixMarketErrorKind::DiagonalError); + /// let matrix_result = load_coo_from_matrix_market_str::>(str); + /// assert_eq!(matrix_result.is_err(), true); + /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::DiagonalError); /// ``` - /// Here the skew matrix shouldn't have an element on the diagonal + /// Here the skew matrix shouldn't have an element on the diagonal. DiagonalError, - /// Indicates [io error](`std::io::Error`), while reading the data from file. + /// Indicates an [IO error](`std::io::Error`) while reading the data from file. /// /// Examples /// -------- /// ```rust /// # use nalgebra_sparse::io::load_coo_from_matrix_market_file; /// # use nalgebra_sparse::io::MatrixMarketErrorKind; - /// let file_name = "whatever.mtx"; - /// let matrix_error = load_coo_from_matrix_market_file::(file_name); - /// assert_eq!(matrix_error.is_err(), true); - /// assert_eq!(matrix_error.unwrap_err().kind(),MatrixMarketErrorKind::IOError(std::io::ErrorKind::NotFound)); + /// let matrix_result = load_coo_from_matrix_market_file::("matrix.mtx"); + /// assert_eq!(matrix_result.is_err(), true); + /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::IOError(std::io::ErrorKind::NotFound)); /// ``` IOError(std::io::ErrorKind), - /// Indicates (skew-)symmetric (or hermitian) matrix is not lower triangle matrix. + /// Indicates that a (skew-)symmetric (or hermitian) matrix is not a lower triangular matrix. /// /// Examples /// -------- @@ -205,13 +210,13 @@ pub enum MatrixMarketErrorKind { /// 1 1 10 /// 2 3 5 /// "#; - /// let matrix_error = load_coo_from_matrix_market_str::(str); - /// assert_eq!(matrix_error.is_err(), true); - /// assert_eq!(matrix_error.unwrap_err().kind(),MatrixMarketErrorKind::NotLowerTriangle); + /// let matrix_result = load_coo_from_matrix_market_str::(str); + /// assert_eq!(matrix_result.is_err(), true); + /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::NotLowerTriangle); /// ``` NotLowerTriangle, - /// Indicates (skew-)symmetric (or hermitian) matrix is not square matrix. + /// Indicates that a (skew-)symmetric (or hermitian) matrix is not a square matrix. /// /// Examples /// -------- @@ -224,11 +229,11 @@ pub enum MatrixMarketErrorKind { /// 1 1 10 /// 2 3 5 /// "#; - /// let matrix_error = load_coo_from_matrix_market_str::(str); - /// assert_eq!(matrix_error.is_err(), true); - /// assert_eq!(matrix_error.unwrap_err().kind(),MatrixMarketErrorKind::NotSquareMatrix); + /// let matrix_result = load_coo_from_matrix_market_str::(str); + /// assert_eq!(matrix_result.is_err(), true); + /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::NonSquare); /// ``` - NotSquareMatrix, + NonSquare, } impl MatrixMarketError { @@ -239,7 +244,7 @@ impl MatrixMarketError { } } - /// The operation error kind. + /// The matrix market error kind. #[must_use] pub fn kind(&self) -> MatrixMarketErrorKind { self.error_kind @@ -254,7 +259,7 @@ impl MatrixMarketError { impl fmt::Display for MatrixMarketError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "Matrix Market load error: ")?; + write!(f, "Matrix Market error: ")?; match self.kind() { MatrixMarketErrorKind::ParsingError => { write!(f, "ParsingError,")?; @@ -262,11 +267,11 @@ impl fmt::Display for MatrixMarketError { MatrixMarketErrorKind::InvalidHeader => { write!(f, "InvalidHeader,")?; } - MatrixMarketErrorKind::EntryNumUnmatched => { + MatrixMarketErrorKind::EntryMismatch => { write!(f, "EntryNumUnmatched,")?; } - MatrixMarketErrorKind::TypeUnmatched => { - write!(f, "TypeUnmatched,")?; + MatrixMarketErrorKind::TypeMismatch => { + write!(f, "TypeMismatch,")?; } MatrixMarketErrorKind::SparseFormatError(_) => { write!(f, "SparseFormatError,")?; @@ -283,11 +288,11 @@ impl fmt::Display for MatrixMarketError { MatrixMarketErrorKind::NotLowerTriangle => { write!(f, "NotLowerTriangle,")?; } - MatrixMarketErrorKind::NotSquareMatrix => { + MatrixMarketErrorKind::NonSquare => { write!(f, "NotSquareMatrix,")?; } } - write!(f, " Message: {}", self.message) + write!(f, " message: {}", self.message) } } @@ -334,9 +339,9 @@ impl From for MatrixMarketError { impl From for MatrixMarketError { fn from(err: TryFromIntError) -> Self { Self::from_kind_and_message( - MatrixMarketErrorKind::TypeUnmatched, + MatrixMarketErrorKind::TypeMismatch, format!( - "Please consider using a larger integery type. Error message: {}", + "Please consider using a larger integer type. Error message: {}", &err ), ) @@ -348,7 +353,7 @@ impl From for MatrixMarketError { impl From for MatrixMarketError { fn from(_err: Infallible) -> Self { Self::from_kind_and_message( - MatrixMarketErrorKind::TypeUnmatched, + MatrixMarketErrorKind::TypeMismatch, format!("This won't happen"), ) } @@ -479,9 +484,9 @@ fn typecode_precheck(tc: &Typecode) -> Result<(), MatrixMarketError> { } } -/// Base trait for matrix market types. +/// Scalar types supported by the matrix market parser. pub trait MatrixMarketScalar: Scalar { - /// When matrix is an Integer matrix, it will convert a [i128] number to this type. + /// When the matrix is an integer matrix, it will convert a [i128] number to this type. fn from_i128(i: i128) -> Result; /// When matrix is a Real matrix, it will convert a [f64] number to this type. fn from_f64(f: f64) -> Result; @@ -494,6 +499,7 @@ pub trait MatrixMarketScalar: Scalar { /// When matrix is a Hermitian matrix, it will convert itself to its conjugate. fn conjugate(self) -> Result; } + /// Implement MatrixMarketScalar for primitive integer types. macro_rules! mm_int_impl { ($T:ty) => { @@ -505,28 +511,28 @@ macro_rules! mm_int_impl { #[inline] fn from_f64(_f: f64) -> Result { Err(MatrixMarketError::from_kind_and_message( - MatrixMarketErrorKind::TypeUnmatched, + MatrixMarketErrorKind::TypeMismatch, format!("Int type can't be parsed from f64"), )) } #[inline] fn from_c64(_c: Complex) -> Result { Err(MatrixMarketError::from_kind_and_message( - MatrixMarketErrorKind::TypeUnmatched, + MatrixMarketErrorKind::TypeMismatch, format!("Int type can't be parsed from Complex"), )) } #[inline] fn from_pattern(_p: ()) -> Result { Err(MatrixMarketError::from_kind_and_message( - MatrixMarketErrorKind::TypeUnmatched, + MatrixMarketErrorKind::TypeMismatch, format!("Int type can't be parsed from ()"), )) } #[inline] fn conjugate(self) -> Result { Err(MatrixMarketError::from_kind_and_message( - MatrixMarketErrorKind::TypeUnmatched, + MatrixMarketErrorKind::TypeMismatch, format!("Int type has no conjugate"), )) } @@ -544,7 +550,7 @@ macro_rules! mm_real_impl { #[inline] fn from_i128(_i: i128) -> Result { Err(MatrixMarketError::from_kind_and_message( - MatrixMarketErrorKind::TypeUnmatched, + MatrixMarketErrorKind::TypeMismatch, format!("real type can't be parsed from i128"), )) } @@ -555,21 +561,21 @@ macro_rules! mm_real_impl { #[inline] fn from_c64(_c: Complex) -> Result { Err(MatrixMarketError::from_kind_and_message( - MatrixMarketErrorKind::TypeUnmatched, + MatrixMarketErrorKind::TypeMismatch, format!("real type can't be parsed from Complex"), )) } #[inline] fn from_pattern(_p: ()) -> Result { Err(MatrixMarketError::from_kind_and_message( - MatrixMarketErrorKind::TypeUnmatched, + MatrixMarketErrorKind::TypeMismatch, format!("real type can't be parsed from ()"), )) } #[inline] fn conjugate(self) -> Result { Err(MatrixMarketError::from_kind_and_message( - MatrixMarketErrorKind::TypeUnmatched, + MatrixMarketErrorKind::TypeMismatch, format!("real type has no conjugate"), )) } @@ -580,6 +586,7 @@ macro_rules! mm_real_impl { } }; } + /// Implement MatrixMarketScalar for primitive complex types. macro_rules! mm_complex_impl { ($T:ty) => { @@ -587,14 +594,14 @@ macro_rules! mm_complex_impl { #[inline] fn from_i128(_i: i128) -> Result { Err(MatrixMarketError::from_kind_and_message( - MatrixMarketErrorKind::TypeUnmatched, + MatrixMarketErrorKind::TypeMismatch, format!("Complex type can't be parsed from i128"), )) } #[inline] fn from_f64(_f: f64) -> Result { Err(MatrixMarketError::from_kind_and_message( - MatrixMarketErrorKind::TypeUnmatched, + MatrixMarketErrorKind::TypeMismatch, format!("Complex type can't be parsed from f64"), )) } @@ -608,7 +615,7 @@ macro_rules! mm_complex_impl { #[inline] fn from_pattern(_p: ()) -> Result { Err(MatrixMarketError::from_kind_and_message( - MatrixMarketErrorKind::TypeUnmatched, + MatrixMarketErrorKind::TypeMismatch, format!("Complex type can't be parsed from ()"), )) } @@ -630,21 +637,21 @@ macro_rules! mm_pattern_impl { #[inline] fn from_i128(_i: i128) -> Result { Err(MatrixMarketError::from_kind_and_message( - MatrixMarketErrorKind::TypeUnmatched, + MatrixMarketErrorKind::TypeMismatch, format!("Pattern type can't be parsed from i128"), )) } #[inline] fn from_f64(_f: f64) -> Result { Err(MatrixMarketError::from_kind_and_message( - MatrixMarketErrorKind::TypeUnmatched, + MatrixMarketErrorKind::TypeMismatch, format!("Pattern type can't be parsed from f64"), )) } #[inline] fn from_c64(_c: Complex) -> Result { Err(MatrixMarketError::from_kind_and_message( - MatrixMarketErrorKind::TypeUnmatched, + MatrixMarketErrorKind::TypeMismatch, format!("Pattern type can't be parsed from Complex"), )) } @@ -656,14 +663,14 @@ macro_rules! mm_pattern_impl { #[inline] fn conjugate(self) -> Result { Err(MatrixMarketError::from_kind_and_message( - MatrixMarketErrorKind::TypeUnmatched, + MatrixMarketErrorKind::TypeMismatch, format!("Pattern type has no conjugate"), )) } #[inline] fn negative(self) -> Result { Err(MatrixMarketError::from_kind_and_message( - MatrixMarketErrorKind::TypeUnmatched, + MatrixMarketErrorKind::TypeMismatch, format!("Pattern type has no negative"), )) } @@ -687,16 +694,31 @@ mm_pattern_impl!(()); #[derive(Parser)] #[grammar = "io/matrix_market.pest"] -struct MMParser; +struct MatrixMarketParser; -/// Parses a Matrix Market file at the given path, and returns the corresponding sparse matrix as CooMatrix format. +/// Parses a Matrix Market file at the given path as a `CooMatrix`. +/// +/// The matrix market format specification does not clarify whether duplicate entries are allowed. Our importer +/// assumes that this is permitted and produces a `CooMatrix` with possibly duplicate entries. +/// +/// **Note**: A current restriction of the importer is that you must use a compatible scalar type when importing. +/// For example, in order to import a matrix stored as `integer` in the matrix market format, you must +/// import it as an integer matrix, otherwise a [TypeMismatch](MatrixMarketErrorKind::TypeMismatch) error +/// will be returned. This restriction may be lifted in the future, and is +/// tracked by issue [#1038](https://github.com/dimforge/nalgebra/issues/1038). /// /// Errors /// -------- /// /// See [MatrixMarketErrorKind] for a list of possible error conditions. /// -/// > _NOTE:_ Here uses strong type requirements, which means if the matrix is an integer matrix, e.g. `%%matrixmarket matrix cooridnate integer general`, then you have to load it by `load_coo_from_matrix_market_file`, where T is an integer type. Trying `load_coo_from_matrix_market_file` will give [TypeUnmatched](MatrixMarketErrorKind::TypeUnmatched) Error. After loading it, you can cast it into a `f64` matrix, by calling [cast](`nalgebra::base::Matrix::cast()`), but be aware of accuracy lose. +/// Examples +/// -------- +/// ``` +/// use nalgebra_sparse::io::load_coo_from_matrix_market_file; +/// // Use e.g. `f64` for floating-point matrices +/// let matrix = load_coo_from_matrix_market_file::("path/to/matrix.mtx")?; +/// ``` pub fn load_coo_from_matrix_market_file>( path: P, ) -> Result, MatrixMarketError> @@ -707,21 +729,34 @@ where load_coo_from_matrix_market_str(&file) } -/// Parses a Matrix Market file described by the given string, and returns the corresponding as CooMatrix format. +/// Parses a Matrix Market file described by the given string as a `CooMatrix`. +/// +/// See [load_coo_from_matrix_market_file] for more information. /// /// Errors /// -------- /// /// See [MatrixMarketErrorKind] for a list of possible error conditions. /// -/// > _NOTE:_ Here uses strong type requirements, which means if the matrix is an integer matrix, e.g. `%%matrixmarket matrix cooridnate integer general`, then you have to load it by `load_coo_from_matrix_market_str`, where T is an integer type. Trying `load_coo_from_matrix_market_str` will give [TypeUnmatched](MatrixMarketErrorKind::TypeUnmatched) Error. After loading it, you can cast it into a `f64` matrix, by calling [cast](`nalgebra::base::Matrix::cast()`),but be aware of accuracy lose. - +/// Examples +/// -------- +/// ``` +/// # use nalgebra_sparse::io::load_coo_from_matrix_market_str; +/// # use nalgebra_sparse::io::MatrixMarketErrorKind; +/// let str = r#" +/// %%matrixmarket matrix coordinate integer symmetric +/// 5 4 2 +/// 1 1 10 +/// 2 3 5 +/// "#; +/// let matrix = load_coo_from_matrix_market_str::(str)?; +/// ``` pub fn load_coo_from_matrix_market_str(data: &str) -> Result, MatrixMarketError> where T: MatrixMarketScalar, { // unwrap() in this function are guaranteed by parsing the data - let file = MMParser::parse(Rule::Document, data)?.next().unwrap(); + let file = MatrixMarketParser::parse(Rule::Document, data)?.next().unwrap(); let mut rows: Vec = Vec::new(); let mut cols: Vec = Vec::new(); @@ -755,7 +790,7 @@ where let count = lines.clone().count(); if count != shape.2 { return Err(MatrixMarketError::from_kind_and_message( - MatrixMarketErrorKind::EntryNumUnmatched, + MatrixMarketErrorKind::EntryMismatch, format!( "{} entries required for the matrix, but {} was provided", shape.2, count, @@ -996,7 +1031,7 @@ fn parse_sparse_shape( // check for square matirx, when it's not a general matrix if *storagescheme != StorageScheme::General && r != c { - return Err(MatrixMarketError::from_kind_and_message(MatrixMarketErrorKind::NotSquareMatrix,format!("(Skew-)Symmetric or hermitian matrix should be square matrix, but it has dimension {} and {}",r,c))); + return Err(MatrixMarketError::from_kind_and_message(MatrixMarketErrorKind::NonSquare, format!("(Skew-)Symmetric or hermitian matrix should be square matrix, but it has dimension {} and {}", r, c))); } Ok((r, c, nnz)) @@ -1032,7 +1067,7 @@ fn parse_dense_shape( // check for square matirx, when it's not a general matrix if *storagescheme != StorageScheme::General && r != c { - return Err(MatrixMarketError::from_kind_and_message(MatrixMarketErrorKind::NotSquareMatrix,format!("(Skew-)Symmetric or hermitian matrix should be square matrix, but it has dimension {} and {}",r,c))); + return Err(MatrixMarketError::from_kind_and_message(MatrixMarketErrorKind::NonSquare, format!("(Skew-)Symmetric or hermitian matrix should be square matrix, but it has dimension {} and {}", r, c))); } let n: usize; diff --git a/nalgebra-sparse/src/io/mod.rs b/nalgebra-sparse/src/io/mod.rs index 2d74a35c..89b21ffb 100644 --- a/nalgebra-sparse/src/io/mod.rs +++ b/nalgebra-sparse/src/io/mod.rs @@ -1,7 +1,35 @@ -//! Parsers for various matrix formats. +//! Functionality for importing and exporting sparse matrices to and from files. //! -//! ## Matrix Market -//! See the [website](https://math.nist.gov/MatrixMarket/formats.html) or the [paper](https://www.researchgate.net/publication/2630533_The_Matrix_Market_Exchange_Formats_Initial_Design) for more details about matrix market. +//! **Available only when the `io` feature is enabled.** +//! +//! The following formats are currently supported: +//! +//! | Format | Import | Export | +//! | ------------------------------------------------|------------|------------| +//! | [Matrix market](#matrix-market-format) | Yes | No | +//! +//! [Matrix market]: https://math.nist.gov/MatrixMarket/formats.html +//! +//! ## Matrix Market format +//! +//! The Matrix Market format is a simple ASCII-based file format for sparse matrices, and was initially developed for +//! the [NIST Matrix Market](https://math.nist.gov/MatrixMarket/), a repository of example sparse matrices. +//! In later years it has largely been superseded by the +//! [SuiteSparse Matrix Collection](https://sparse.tamu.edu/) (formerly University of Florida Sparse Matrix Collection), +//! which also uses the Matrix Market file format. +//! +//! We currently offer functionality for importing a Matrix market file to an instance of a +//! [CooMatrix](crate::CooMatrix) through the function [load_coo_from_matrix_market_file]. It is also possible to load +//! a matrix stored in the matrix market format with the function [load_coo_from_matrix_market_str]. +//! +//! Export is currently not implemented, but [planned](https://github.com/dimforge/nalgebra/issues/1037). +//! +//! Our implementation is based on the [format description](https://math.nist.gov/MatrixMarket/formats.html) +//! on the Matrix Market website and the +//! [following NIST whitepaper](https://math.nist.gov/MatrixMarket/reports/MMformat.ps): +//! +//! > Boisvert, Ronald F., Roldan Pozo, and Karin A. Remington.
+//! > "*The Matrix Market Exchange Formats: Initial Design.*" (1996). pub use self::matrix_market::{ load_coo_from_matrix_market_file, load_coo_from_matrix_market_str, MatrixMarketError, From 1b73b2f9919e54cdaf870061fdc50c1c61a3d582 Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Wed, 1 Dec 2021 11:07:47 +0100 Subject: [PATCH 2/9] Link to matrix market IO in lib.rs --- nalgebra-sparse/Cargo.toml | 2 ++ nalgebra-sparse/src/lib.rs | 1 + 2 files changed, 3 insertions(+) diff --git a/nalgebra-sparse/Cargo.toml b/nalgebra-sparse/Cargo.toml index b85fea2c..7692984e 100644 --- a/nalgebra-sparse/Cargo.toml +++ b/nalgebra-sparse/Cargo.toml @@ -15,6 +15,8 @@ license = "Apache-2.0" [features] proptest-support = ["proptest", "nalgebra/proptest-support"] compare = [ "matrixcompare-core" ] + +# Enable matrix market I/O io = [ "pest", "pest_derive" ] # Enable to enable running some tests that take a lot of time to run diff --git a/nalgebra-sparse/src/lib.rs b/nalgebra-sparse/src/lib.rs index a588787f..edbf83bd 100644 --- a/nalgebra-sparse/src/lib.rs +++ b/nalgebra-sparse/src/lib.rs @@ -19,6 +19,7 @@ //! - Sparsity patterns in CSR and CSC matrices are explicitly represented by the //! [SparsityPattern](pattern::SparsityPattern) type, which encodes the invariants of the //! associated index data structures. +//! - [Matrix market format support](`io`) when the `io` feature is enabled. //! - [proptest strategies](`proptest`) for sparse matrices when the feature //! `proptest-support` is enabled. //! - [matrixcompare support](https://crates.io/crates/matrixcompare) for effortless From 4c039573f2c1e698cc06824662c4b1c580758d70 Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Wed, 1 Dec 2021 11:26:51 +0100 Subject: [PATCH 3/9] Make nalgebra-sparse unit tests require io feature --- nalgebra-sparse/tests/unit.rs | 11 +++++++++-- nalgebra-sparse/tests/unit_tests/mod.rs | 1 - 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/nalgebra-sparse/tests/unit.rs b/nalgebra-sparse/tests/unit.rs index 73a95cd7..1e61467c 100644 --- a/nalgebra-sparse/tests/unit.rs +++ b/nalgebra-sparse/tests/unit.rs @@ -1,6 +1,13 @@ //! Unit tests -#[cfg(any(not(feature = "proptest-support"), not(feature = "compare")))] -compile_error!("Tests must be run with features `proptest-support` and `compare`"); +#[cfg(not(all( +feature = "proptest-support", +feature = "compare", +feature = "io", +)))] +compile_error!( + "Please enable the `proptest-support`, `compare` and `io` features in order to compile and run the tests. + Example: `cargo test -p nalgebra-sparse --features proptest-support,compare,io`" +); mod unit_tests; diff --git a/nalgebra-sparse/tests/unit_tests/mod.rs b/nalgebra-sparse/tests/unit_tests/mod.rs index e97731e1..7090a493 100644 --- a/nalgebra-sparse/tests/unit_tests/mod.rs +++ b/nalgebra-sparse/tests/unit_tests/mod.rs @@ -3,7 +3,6 @@ mod convert_serial; mod coo; mod csc; mod csr; -#[cfg(feature = "io")] mod matrix_market; mod ops; mod pattern; From 4d0f401882648cceea0bf6e0ea88f496cafe0bfd Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Wed, 1 Dec 2021 11:27:03 +0100 Subject: [PATCH 4/9] Add (failing) test for empty matrix market matrix --- .../tests/unit_tests/matrix_market.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/nalgebra-sparse/tests/unit_tests/matrix_market.rs b/nalgebra-sparse/tests/unit_tests/matrix_market.rs index 316ac02f..f0a9d217 100644 --- a/nalgebra-sparse/tests/unit_tests/matrix_market.rs +++ b/nalgebra-sparse/tests/unit_tests/matrix_market.rs @@ -4,6 +4,24 @@ use nalgebra::Complex; use nalgebra_sparse::io::load_coo_from_matrix_market_str; use nalgebra_sparse::CooMatrix; +#[test] +#[rustfmt::skip] +fn test_matrixmarket_sparse_real_general_empty() { + // Test several valid zero-shapes of a matrix + let shapes = vec![ (0, 0), (1, 0), (0, 1) ]; + let strings: Vec = shapes + .into_iter() + .map(|(m, n)| format!("%%MatrixMarket matrix coordinate real general\n {} {} 0", m, n)) + .collect(); + + for string in &strings { + let sparse_mat = load_coo_from_matrix_market_str::(string).unwrap(); + assert_eq!(sparse_mat.nrows(), 0); + assert_eq!(sparse_mat.ncols(), 0); + assert_eq!(sparse_mat.nnz(), 0); + } +} + #[test] #[rustfmt::skip] fn test_matrixmarket_sparse_real_general() { From 93f3d600050f458298a6e89b0ccd17804a5658b5 Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Wed, 1 Dec 2021 11:33:03 +0100 Subject: [PATCH 5/9] Remove From for MatrixMarketError We want pest to remain an internal implementation detail, so it should not leak into the public API. --- nalgebra-sparse/src/io/matrix_market.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/nalgebra-sparse/src/io/matrix_market.rs b/nalgebra-sparse/src/io/matrix_market.rs index 518675cd..dd823bc9 100644 --- a/nalgebra-sparse/src/io/matrix_market.rs +++ b/nalgebra-sparse/src/io/matrix_market.rs @@ -298,13 +298,14 @@ impl fmt::Display for MatrixMarketError { impl std::error::Error for MatrixMarketError {} -impl From> - for MatrixMarketError -{ - fn from(err: pest::error::Error) -> Self { +impl MatrixMarketError { + fn from_pest_error(error: pest::error::Error) -> Self + where + T: fmt::Debug + std::hash::Hash + std::marker::Copy + Ord + { Self::from_kind_and_message( MatrixMarketErrorKind::ParsingError, - format!("Can't parse the data.\n Error: {}", err), + format!("Can't parse the data.\n Error: {}", error), ) } } @@ -756,7 +757,9 @@ where T: MatrixMarketScalar, { // unwrap() in this function are guaranteed by parsing the data - let file = MatrixMarketParser::parse(Rule::Document, data)?.next().unwrap(); + let file = MatrixMarketParser::parse(Rule::Document, data) + .map_err(MatrixMarketError::from_pest_error)? + .next().unwrap(); let mut rows: Vec = Vec::new(); let mut cols: Vec = Vec::new(); From e3d1119bffba0c6cd9de66ab6228566a6ace2d4b Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Wed, 1 Dec 2021 11:45:06 +0100 Subject: [PATCH 6/9] Hide MatrixMarketScalar implementation details for now The existing MatrixMarketScalar is relatively closely tied to the way oru parser is implemented. I've moved these internals into an internal trait and sealed the public trait. This makes it less flexible for users for now, but gives us more freedom to change internals in the future. --- nalgebra-sparse/src/io/matrix_market.rs | 54 ++++++++++++++++--------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/nalgebra-sparse/src/io/matrix_market.rs b/nalgebra-sparse/src/io/matrix_market.rs index dd823bc9..19434eb6 100644 --- a/nalgebra-sparse/src/io/matrix_market.rs +++ b/nalgebra-sparse/src/io/matrix_market.rs @@ -4,7 +4,6 @@ use crate::coo::CooMatrix; use crate::SparseFormatError; use crate::SparseFormatErrorKind; -use nalgebra::base::Scalar; use nalgebra::Complex; use pest::iterators::Pairs; use pest::Parser; @@ -486,25 +485,38 @@ fn typecode_precheck(tc: &Typecode) -> Result<(), MatrixMarketError> { } /// Scalar types supported by the matrix market parser. -pub trait MatrixMarketScalar: Scalar { - /// When the matrix is an integer matrix, it will convert a [i128] number to this type. - fn from_i128(i: i128) -> Result; - /// When matrix is a Real matrix, it will convert a [f64] number to this type. - fn from_f64(f: f64) -> Result; - /// When matrix is a Complx matrix, it will convert a [Complex] number to this type. - fn from_c64(c: Complex) -> Result; - /// When matrix is a Pattern matrix, it will convert a unit type [unit] to this type. - fn from_pattern(p: ()) -> Result; - /// When matrix is a Skew-symmetric matrix, it will convert itself to its negative. - fn negative(self) -> Result; - /// When matrix is a Hermitian matrix, it will convert itself to its conjugate. - fn conjugate(self) -> Result; +mod internal { + use crate::io::MatrixMarketError; + use na::{Complex, Scalar}; + + pub trait SupportedMatrixMarketScalar: Scalar { + /// When the matrix is an integer matrix, it will convert a [i128] number to this type. + fn from_i128(i: i128) -> Result; + /// When matrix is a Real matrix, it will convert a [f64] number to this type. + fn from_f64(f: f64) -> Result; + /// When matrix is a Complx matrix, it will convert a [Complex] number to this type. + fn from_c64(c: Complex) -> Result; + /// When matrix is a Pattern matrix, it will convert a unit type [unit] to this type. + fn from_pattern(p: ()) -> Result; + /// When matrix is a Skew-symmetric matrix, it will convert itself to its negative. + fn negative(self) -> Result; + /// When matrix is a Hermitian matrix, it will convert itself to its conjugate. + fn conjugate(self) -> Result; + } } +/// A marker trait for supported matrix market scalars. +/// +/// This is a sealed trait; it cannot be implemented by external crates. This is done in order to prevent leaking +/// some of the implementation details we currently rely on. We may relax this restriction in the future. +pub trait MatrixMarketScalar: internal::SupportedMatrixMarketScalar {} + /// Implement MatrixMarketScalar for primitive integer types. macro_rules! mm_int_impl { ($T:ty) => { - impl MatrixMarketScalar for $T { + impl MatrixMarketScalar for $T {} + + impl internal::SupportedMatrixMarketScalar for $T { #[inline] fn from_i128(i: i128) -> Result { Ok(Self::try_from(i)?) @@ -547,7 +559,9 @@ macro_rules! mm_int_impl { /// Implement MatrixMarketScalar for primitive real types. macro_rules! mm_real_impl { ($T:ty) => { - impl MatrixMarketScalar for $T { + impl MatrixMarketScalar for $T {} + + impl internal::SupportedMatrixMarketScalar for $T { #[inline] fn from_i128(_i: i128) -> Result { Err(MatrixMarketError::from_kind_and_message( @@ -591,7 +605,9 @@ macro_rules! mm_real_impl { /// Implement MatrixMarketScalar for primitive complex types. macro_rules! mm_complex_impl { ($T:ty) => { - impl MatrixMarketScalar for Complex<$T> { + impl MatrixMarketScalar for Complex<$T> {} + + impl internal::SupportedMatrixMarketScalar for Complex<$T> { #[inline] fn from_i128(_i: i128) -> Result { Err(MatrixMarketError::from_kind_and_message( @@ -634,7 +650,9 @@ macro_rules! mm_complex_impl { /// Implement MatrixMarketScalar for primitive unit types. macro_rules! mm_pattern_impl { ($T:ty) => { - impl MatrixMarketScalar for $T { + impl MatrixMarketScalar for $T {} + + impl internal::SupportedMatrixMarketScalar for $T { #[inline] fn from_i128(_i: i128) -> Result { Err(MatrixMarketError::from_kind_and_message( From 4569484aa027aaf9f5f90258f3f43c8546950115 Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Wed, 1 Dec 2021 12:08:42 +0100 Subject: [PATCH 7/9] Line breaks --- nalgebra-sparse/src/io/matrix_market.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nalgebra-sparse/src/io/matrix_market.rs b/nalgebra-sparse/src/io/matrix_market.rs index 19434eb6..e65620df 100644 --- a/nalgebra-sparse/src/io/matrix_market.rs +++ b/nalgebra-sparse/src/io/matrix_market.rs @@ -436,7 +436,11 @@ impl FromStr for StorageScheme { /// Precheck if it's a valid header. /// -/// For more details, please check Boisvert, Ronald F., Roldan Pozo, and Karin A. Remington. The matrix market formats: Initial design. Technical report, Applied and Computational Mathematics Division, NIST, 1996. Section 3. +/// For more details, please check +/// +/// Boisvert, Ronald F., Roldan Pozo, and Karin A. Remington. +/// The matrix market formats: Initial design. +/// Technical report, Applied and Computational Mathematics Division, NIST, 1996. Section 3. fn typecode_precheck(tc: &Typecode) -> Result<(), MatrixMarketError> { match tc { Typecode { From e2820316a8d991c42951e92d480aaad9448e6735 Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Wed, 1 Dec 2021 12:17:47 +0100 Subject: [PATCH 8/9] Fix typos --- nalgebra-sparse/src/io/matrix_market.rs | 102 ++++++++++++------------ 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/nalgebra-sparse/src/io/matrix_market.rs b/nalgebra-sparse/src/io/matrix_market.rs index e65620df..1429d814 100644 --- a/nalgebra-sparse/src/io/matrix_market.rs +++ b/nalgebra-sparse/src/io/matrix_market.rs @@ -498,7 +498,7 @@ mod internal { fn from_i128(i: i128) -> Result; /// When matrix is a Real matrix, it will convert a [f64] number to this type. fn from_f64(f: f64) -> Result; - /// When matrix is a Complx matrix, it will convert a [Complex] number to this type. + /// When matrix is a Complex matrix, it will convert a [Complex] number to this type. fn from_c64(c: Complex) -> Result; /// When matrix is a Pattern matrix, it will convert a unit type [unit] to this type. fn from_pattern(p: ()) -> Result; @@ -806,10 +806,10 @@ where // used when constructing dense matrix. // If it's sparse matrix, it has no effect. - let mut current_dense_coordiante: (usize, usize) = (0, 0); + let mut current_dense_coordinate: (usize, usize) = (0, 0); if header_type.storagescheme == StorageScheme::Skew { // for skew dense matrix, the first element starts from (1,0) - current_dense_coordiante = (1, 0); + current_dense_coordinate = (1, 0); } // count how many entries in the matrix data let count = lines.clone().count(); @@ -860,12 +860,12 @@ where .. } => { entry = ( - current_dense_coordiante.0, - current_dense_coordiante.1, + current_dense_coordinate.0, + current_dense_coordinate.1, parse_dense_complex::(&mut data_line.into_inner())?, ); - next_dense_coordiante( - &mut current_dense_coordiante, + next_dense_coordinate( + &mut current_dense_coordinate, shape, &header_type.storagescheme, ); @@ -876,13 +876,13 @@ where .. } => { entry = ( - current_dense_coordiante.0, - current_dense_coordiante.1, + current_dense_coordinate.0, + current_dense_coordinate.1, parse_dense_real::(&mut data_line.into_inner())?, ); - next_dense_coordiante( - &mut current_dense_coordiante, + next_dense_coordinate( + &mut current_dense_coordinate, shape, &header_type.storagescheme, ); @@ -893,12 +893,12 @@ where .. } => { entry = ( - current_dense_coordiante.0, - current_dense_coordiante.1, + current_dense_coordinate.0, + current_dense_coordinate.1, parse_dense_int::(&mut data_line.into_inner())?, ); - next_dense_coordiante( - &mut current_dense_coordiante, + next_dense_coordinate( + &mut current_dense_coordinate, shape, &header_type.storagescheme, ); @@ -1023,7 +1023,7 @@ fn parse_header(inner: &mut Pairs<'_, Rule>) -> Typecode { // Parse shape starts here------------------------------------------------- -/// Parse a pest structure to sparse shape information, including 3 int, which are number of rols, cols and non-zeros. +/// Parse a pest structure to sparse shape information, including 3 int, which are number of rows, cols and non-zeros. fn parse_sparse_shape( inner: &mut Pairs<'_, Rule>, storagescheme: &StorageScheme, @@ -1054,7 +1054,7 @@ fn parse_sparse_shape( )); } - // check for square matirx, when it's not a general matrix + // check for square matrix, when it's not a general matrix if *storagescheme != StorageScheme::General && r != c { return Err(MatrixMarketError::from_kind_and_message(MatrixMarketErrorKind::NonSquare, format!("(Skew-)Symmetric or hermitian matrix should be square matrix, but it has dimension {} and {}", r, c))); } @@ -1062,7 +1062,7 @@ fn parse_sparse_shape( Ok((r, c, nnz)) } -/// Parse a pest structure to dense shape information, including 2 int, which are number of rols, cols. +/// Parse a pest structure to dense shape information, including 2 int, which are number of rows, cols. fn parse_dense_shape( inner: &mut Pairs<'_, Rule>, storagescheme: &StorageScheme, @@ -1090,7 +1090,7 @@ fn parse_dense_shape( )); } - // check for square matirx, when it's not a general matrix + // check for square matrix, when it's not a general matrix if *storagescheme != StorageScheme::General && r != c { return Err(MatrixMarketError::from_kind_and_message(MatrixMarketErrorKind::NonSquare, format!("(Skew-)Symmetric or hermitian matrix should be square matrix, but it has dimension {} and {}", r, c))); } @@ -1121,7 +1121,7 @@ fn parse_dense_shape( // Parse entry starts here------------------------------------------------- -/// Parse a pest structure to sparse real entry, including 2 int, which are number of rols, cols, and a real number as data +/// Parse a pest structure to sparse real entry, including 2 int, which are number of rows, cols, and a real number as data fn parse_sparse_real(inner: &mut Pairs<'_, Rule>) -> Result<(usize, usize, T), MatrixMarketError> where T: MatrixMarketScalar, @@ -1130,17 +1130,17 @@ where let entry_inner = inner.next().unwrap(); if entry_inner.as_rule() != Rule::SparseReal { return Err(MatrixMarketError::from_kind_and_message(MatrixMarketErrorKind::ParsingError,format!(" - Spare real matrix requires 2 int number as coordiantes and 1 real number as data, but line {} was provided. + Spare real matrix requires 2 int number as coordinates and 1 real number as data, but line {} was provided. ",entry_inner.as_str() ))); } let mut inner = entry_inner.into_inner(); - let (r, c) = parse_sparse_coordiante(&mut inner)?; + let (r, c) = parse_sparse_coordinate(&mut inner)?; let d = inner.next().unwrap().as_str().parse::().unwrap(); Ok((r, c, T::from_f64(d)?)) } -/// Parse a pest structure to sparse integer entry, including 2 int, which are number of rols, cols, and a int number as data +/// Parse a pest structure to sparse integer entry, including 2 int, which are number of rows, cols, and a int number as data fn parse_sparse_int(inner: &mut Pairs<'_, Rule>) -> Result<(usize, usize, T), MatrixMarketError> where T: MatrixMarketScalar, @@ -1153,20 +1153,20 @@ where MatrixMarketErrorKind::ParsingError, format!( " - Spare real matrix requires 3 int number as coordiantes and data, but line {} was provided. + Spare real matrix requires 3 int number as coordinates and data, but line {} was provided. ", entry_inner.as_str() ), )); } let mut inner = entry_inner.into_inner(); - let (r, c) = parse_sparse_coordiante(&mut inner)?; + let (r, c) = parse_sparse_coordinate(&mut inner)?; // Here to guarantee it is an integer number let d = inner.next().unwrap().as_str().parse::()?; Ok((r, c, T::from_i128(d)?)) } -/// Parse a pest structure to sparse pattern entry, including 2 int, which are number of rols, cols +/// Parse a pest structure to sparse pattern entry, including 2 int, which are number of rows, cols fn parse_sparse_pattern( inner: &mut Pairs<'_, Rule>, ) -> Result<(usize, usize, T), MatrixMarketError> @@ -1180,18 +1180,18 @@ where MatrixMarketErrorKind::ParsingError, format!( " - Spare real matrix requires 2 int number as coordiantes, but line {} was provided. + Spare real matrix requires 2 int number as coordinates, but line {} was provided. ", entry_inner.as_str() ), )); } let mut inner = entry_inner.into_inner(); - let (r, c) = parse_sparse_coordiante(&mut inner)?; + let (r, c) = parse_sparse_coordinate(&mut inner)?; Ok((r, c, T::from_pattern(())?)) } -/// Parse a pest structure to sparse complex entry, including 2 int, which are number of rols, cols, and 2 real number as complex data +/// Parse a pest structure to sparse complex entry, including 2 int, which are number of rows, cols, and 2 real number as complex data fn parse_sparse_complex( inner: &mut Pairs<'_, Rule>, ) -> Result<(usize, usize, T), MatrixMarketError> @@ -1202,11 +1202,11 @@ where let entry_inner = inner.next().unwrap(); if entry_inner.as_rule() != Rule::SparseComplex { return Err(MatrixMarketError::from_kind_and_message(MatrixMarketErrorKind::ParsingError,format!(" - Spare real matrix requires 2 int number as coordiantes and 2 real number as complex data, but line {} was provided. + Spare real matrix requires 2 int number as coordinates and 2 real number as complex data, but line {} was provided. ",entry_inner.as_str() ))); } let mut inner = entry_inner.into_inner(); - let (r, c) = parse_sparse_coordiante(&mut inner)?; + let (r, c) = parse_sparse_coordinate(&mut inner)?; let real = inner.next().unwrap().as_str().parse::().unwrap(); let imag = inner.next().unwrap().as_str().parse::().unwrap(); let complex = Complex::::new(real, imag); @@ -1292,8 +1292,8 @@ where // Parse entry ends here------------------------------------------------- -/// Parse the coordiantes information used for sparse matrix -fn parse_sparse_coordiante( +/// Parse the coordinates information used for sparse matrix +fn parse_sparse_coordinate( inner: &mut Pairs<'_, Rule>, ) -> Result<(usize, usize), MatrixMarketError> { // unwrap() in this function are guaranteed by parsing the data @@ -1302,51 +1302,51 @@ fn parse_sparse_coordiante( if r * c == 0 { return Err(MatrixMarketError::from_kind_and_message( MatrixMarketErrorKind::ZeroError, - String::from("The data has to be one-indixed"), + String::from("The data has to be one-indexed"), )); } - // The coordiantes in matrix market is one-based, but in CooMatrix is zero-based. + // The coordinates in matrix market is one-based, but in CooMatrix is zero-based. Ok((r - 1, c - 1)) } -/// Calculate the next coordiantes used for dense matrix -fn next_dense_coordiante( - current_dense_coordiante: &mut (usize, usize), +/// Calculate the next coordinates used for dense matrix +fn next_dense_coordinate( + current_dense_coordinate: &mut (usize, usize), shape: (usize, usize, usize), storagescheme: &StorageScheme, ) { // matrix market is column based format. // so it follows the order (0,0) -> (1,0) -> ... -> (row, 0) -> (0,1) -> ... ->(row,col) - // current_dense_coordiante is (row, column) + // current_dense_coordinate is (row, column) match storagescheme { StorageScheme::General => { - if current_dense_coordiante.0 < shape.0 - 1 { - current_dense_coordiante.0 += 1 + if current_dense_coordinate.0 < shape.0 - 1 { + current_dense_coordinate.0 += 1 } else { // jump to next column, reset row to 1, column add 1 - current_dense_coordiante.0 = 0; - current_dense_coordiante.1 += 1; + current_dense_coordinate.0 = 0; + current_dense_coordinate.1 += 1; } } StorageScheme::Symmetric | StorageScheme::Hermitian => { - if current_dense_coordiante.0 < shape.0 - 1 { - current_dense_coordiante.0 += 1 + if current_dense_coordinate.0 < shape.0 - 1 { + current_dense_coordinate.0 += 1 } else { // jump to next column, column add 1, then set row equals to current column // for example (0,0) -> (1,0) -> ... -> (row, 0) -> (1,1) -> ... - current_dense_coordiante.1 += 1; - current_dense_coordiante.0 = current_dense_coordiante.1; + current_dense_coordinate.1 += 1; + current_dense_coordinate.0 = current_dense_coordinate.1; } } StorageScheme::Skew => { - if current_dense_coordiante.0 < shape.0 - 1 { - current_dense_coordiante.0 += 1; + if current_dense_coordinate.0 < shape.0 - 1 { + current_dense_coordinate.0 += 1; } else { // jump to next column, set row equals to current column, then column add 1 // skew matrix doesn't have element on diagonal // for example (1,0) -> (2,0) -> ... -> (row, 0) -> (2,1) -> ... - current_dense_coordiante.1 += 1; - current_dense_coordiante.0 = current_dense_coordiante.1 + 1; + current_dense_coordinate.1 += 1; + current_dense_coordinate.0 = current_dense_coordinate.1 + 1; } } } From 9ddd09017dc82ecfb4d1327ac71bb82ef5b0579f Mon Sep 17 00:00:00 2001 From: Andreas Longva Date: Wed, 1 Dec 2021 12:18:45 +0100 Subject: [PATCH 9/9] Update add IO feature to CI config for nalgebra-sparse --- .github/workflows/nalgebra-ci-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nalgebra-ci-build.yml b/.github/workflows/nalgebra-ci-build.yml index fd3ec273..b553deaf 100644 --- a/.github/workflows/nalgebra-ci-build.yml +++ b/.github/workflows/nalgebra-ci-build.yml @@ -65,10 +65,10 @@ jobs: - name: test nalgebra-sparse # Manifest-path is necessary because cargo otherwise won't correctly forward features # We increase number of proptest cases to hopefully catch more potential bugs - run: PROPTEST_CASES=10000 cargo test --manifest-path=nalgebra-sparse/Cargo.toml --features compare,proptest-support + run: PROPTEST_CASES=10000 cargo test --manifest-path=nalgebra-sparse/Cargo.toml --features compare,proptest-support,io - name: test nalgebra-sparse (slow tests) # Unfortunately, the "slow-tests" take so much time that we need to run them with --release - run: PROPTEST_CASES=10000 cargo test --release --manifest-path=nalgebra-sparse/Cargo.toml --features compare,proptest-support,slow-tests slow + run: PROPTEST_CASES=10000 cargo test --release --manifest-path=nalgebra-sparse/Cargo.toml --features compare,proptest-support,io,slow-tests slow test-nalgebra-macros: runs-on: ubuntu-latest steps: