Merge pull request #1 from Andlon/matrixmarket-io-fixes

Matrixmarket IO changes
This commit is contained in:
huihantao 2021-12-01 15:00:44 +01:00 committed by GitHub
commit 6cd009988c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 285 additions and 170 deletions

View File

@ -65,10 +65,10 @@ jobs:
- name: test nalgebra-sparse - name: test nalgebra-sparse
# Manifest-path is necessary because cargo otherwise won't correctly forward features # Manifest-path is necessary because cargo otherwise won't correctly forward features
# We increase number of proptest cases to hopefully catch more potential bugs # 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) - name: test nalgebra-sparse (slow tests)
# Unfortunately, the "slow-tests" take so much time that we need to run them with --release # 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: test-nalgebra-macros:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

View File

@ -15,6 +15,8 @@ license = "Apache-2.0"
[features] [features]
proptest-support = ["proptest", "nalgebra/proptest-support"] proptest-support = ["proptest", "nalgebra/proptest-support"]
compare = [ "matrixcompare-core" ] compare = [ "matrixcompare-core" ]
# Enable matrix market I/O
io = [ "pest", "pest_derive" ] io = [ "pest", "pest_derive" ]
# Enable to enable running some tests that take a lot of time to run # Enable to enable running some tests that take a lot of time to run

View File

@ -4,7 +4,6 @@
use crate::coo::CooMatrix; use crate::coo::CooMatrix;
use crate::SparseFormatError; use crate::SparseFormatError;
use crate::SparseFormatErrorKind; use crate::SparseFormatErrorKind;
use nalgebra::base::Scalar;
use nalgebra::Complex; use nalgebra::Complex;
use pest::iterators::Pairs; use pest::iterators::Pairs;
use pest::Parser; use pest::Parser;
@ -27,11 +26,12 @@ pub struct MatrixMarketError {
} }
/// Errors produced by functions that expect well-formed matrix market format data. /// 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] #[non_exhaustive]
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub enum MatrixMarketErrorKind { 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 /// Examples
/// -------- /// --------
@ -39,17 +39,17 @@ pub enum MatrixMarketErrorKind {
/// # use nalgebra_sparse::io::load_coo_from_matrix_market_str; /// # use nalgebra_sparse::io::load_coo_from_matrix_market_str;
/// # use nalgebra_sparse::io::MatrixMarketErrorKind; /// # use nalgebra_sparse::io::MatrixMarketErrorKind;
/// let str = r#" /// let str = r#"
/// %%MatrixMarket whatever whatever whatever whatever /// %%MatrixMarket invalid invalid invalid invalid
/// 1 1 1 /// 1 1 1
/// 1 1 5 /// 1 1 5
/// "#; /// "#;
/// let matrix_error = load_coo_from_matrix_market_str::<f64>(str); /// let matrix_result = load_coo_from_matrix_market_str::<f64>(str);
/// assert_eq!(matrix_error.is_err(), true); /// assert_eq!(matrix_result.is_err(), true);
/// assert_eq!(matrix_error.unwrap_err().kind(),MatrixMarketErrorKind::ParsingError); /// assert_eq!(matrix_result.unwrap_err().kind(), MatrixMarketErrorKind::ParsingError);
/// ``` /// ```
ParsingError, ParsingError,
/// Indicates that header is not valid /// Indicates that the matrix market header is invalid.
/// ///
/// Examples /// Examples
/// -------- /// --------
@ -62,13 +62,13 @@ pub enum MatrixMarketErrorKind {
/// 1 1 1 /// 1 1 1
/// 1 1 5 /// 1 1 5
/// "#; /// "#;
/// let matrix_error = load_coo_from_matrix_market_str::<f64>(str); /// let matrix_result = load_coo_from_matrix_market_str::<f64>(str);
/// assert_eq!(matrix_error.is_err(), true); /// assert_eq!(matrix_result.is_err(), true);
/// assert_eq!(matrix_error.unwrap_err().kind(),MatrixMarketErrorKind::InvalidHeader); /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::InvalidHeader);
/// ``` /// ```
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 /// Examples
/// -------- /// --------
@ -82,13 +82,14 @@ pub enum MatrixMarketErrorKind {
/// 2 2 2 /// 2 2 2
/// 2 3 2 /// 2 3 2
/// "#; /// "#;
/// let matrix_error = load_coo_from_matrix_market_str::<f64>(str); /// let matrix_result = load_coo_from_matrix_market_str::<f64>(str);
/// assert_eq!(matrix_error.is_err(), true); /// assert_eq!(matrix_result.is_err(), true);
/// assert_eq!(matrix_error.unwrap_err().kind(),MatrixMarketErrorKind::EntryNumUnmatched); /// 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 /// Examples
/// -------- /// --------
@ -97,18 +98,20 @@ pub enum MatrixMarketErrorKind {
/// # use nalgebra_sparse::io::MatrixMarketErrorKind; /// # use nalgebra_sparse::io::MatrixMarketErrorKind;
/// let str = r#" /// let str = r#"
/// %%matrixmarket matrix coordinate real general /// %%matrixmarket matrix coordinate real general
/// % it should be called by load_coo_from_matrix_market_str::<f64>(str), or f32; /// % it should be loaded with load_coo_from_matrix_market_str::<f64>(str) (or f32)
/// 3 3 2 /// 3 3 2
/// 2 2 2.22 /// 2 2 2.22
/// 2 3 2.22 /// 2 3 2.22
/// "#; /// "#;
/// let matrix_error = load_coo_from_matrix_market_str::<i32>(str); /// let matrix_result = load_coo_from_matrix_market_str::<i32>(str);
/// assert_eq!(matrix_error.is_err(), true); /// assert_eq!(matrix_result.is_err(), true);
/// assert_eq!(matrix_error.unwrap_err().kind(),MatrixMarketErrorKind::TypeUnmatched); /// 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 /// Examples
/// -------- /// --------
@ -120,13 +123,14 @@ pub enum MatrixMarketErrorKind {
/// 1 1 1 /// 1 1 1
/// 0 0 10 /// 0 0 10
/// "#; /// "#;
/// let matrix_error = load_coo_from_matrix_market_str::<f64>(str); /// let matrix_result = load_coo_from_matrix_market_str::<f64>(str);
/// assert_eq!(matrix_error.is_err(), true); /// assert_eq!(matrix_result.is_err(), true);
/// assert_eq!(matrix_error.unwrap_err().kind(),MatrixMarketErrorKind::ZeroError); /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::ZeroError);
/// ``` /// ```
ZeroError, ZeroError,
/// Indicates [SparseFormatError], while creating the sparse matrix. /// Indicates [SparseFormatError] while creating the sparse matrix.
///
/// ///
/// Examples /// Examples
/// -------- /// --------
@ -139,13 +143,14 @@ pub enum MatrixMarketErrorKind {
/// 1 1 1 /// 1 1 1
/// 4 2 10 /// 4 2 10
/// "#; /// "#;
/// let matrix_error = load_coo_from_matrix_market_str::<f64>(str); /// let matrix_result = load_coo_from_matrix_market_str::<f64>(str);
/// assert_eq!(matrix_error.is_err(), true); /// assert_eq!(matrix_result.is_err(), true);
/// assert_eq!(matrix_error.unwrap_err().kind(),MatrixMarketErrorKind::SparseFormatError(SparseFormatErrorKind::IndexOutOfBounds)); /// assert_eq!(matrix_result.unwrap_err().kind(),
/// MatrixMarketErrorKind::SparseFormatError(SparseFormatErrorKind::IndexOutOfBounds));
/// ``` /// ```
SparseFormatError(SparseFormatErrorKind), 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 /// Examples
/// -------- /// --------
@ -160,9 +165,9 @@ pub enum MatrixMarketErrorKind {
/// 1 1 10 /// 1 1 10
/// 2 1 5 /// 2 1 5
/// "#; /// "#;
/// let matrix_error = load_coo_from_matrix_market_str::<f64>(str); /// let matrix_result = load_coo_from_matrix_market_str::<f64>(str);
/// assert_eq!(matrix_error.is_err(), true); /// assert_eq!(matrix_result.is_err(), true);
/// assert_eq!(matrix_error.unwrap_err().kind(),MatrixMarketErrorKind::DiagonalError); /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::DiagonalError);
/// ///
/// let str = r#" /// let str = r#"
/// %%matrixmarket matrix coordinate complex hermitian /// %%matrixmarket matrix coordinate complex hermitian
@ -171,28 +176,27 @@ pub enum MatrixMarketErrorKind {
/// 1 1 10 2 /// 1 1 10 2
/// 2 1 5 2 /// 2 1 5 2
/// "#; /// "#;
/// let matrix_error = load_coo_from_matrix_market_str::<Complex<f64>>(str); /// let matrix_result = load_coo_from_matrix_market_str::<Complex<f64>>(str);
/// assert_eq!(matrix_error.is_err(), true); /// assert_eq!(matrix_result.is_err(), true);
/// assert_eq!(matrix_error.unwrap_err().kind(),MatrixMarketErrorKind::DiagonalError); /// 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, 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 /// Examples
/// -------- /// --------
/// ```rust /// ```rust
/// # use nalgebra_sparse::io::load_coo_from_matrix_market_file; /// # use nalgebra_sparse::io::load_coo_from_matrix_market_file;
/// # use nalgebra_sparse::io::MatrixMarketErrorKind; /// # use nalgebra_sparse::io::MatrixMarketErrorKind;
/// let file_name = "whatever.mtx"; /// let matrix_result = load_coo_from_matrix_market_file::<f64,_>("matrix.mtx");
/// let matrix_error = load_coo_from_matrix_market_file::<f64,_>(file_name); /// assert_eq!(matrix_result.is_err(), true);
/// assert_eq!(matrix_error.is_err(), true); /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::IOError(std::io::ErrorKind::NotFound));
/// assert_eq!(matrix_error.unwrap_err().kind(),MatrixMarketErrorKind::IOError(std::io::ErrorKind::NotFound));
/// ``` /// ```
IOError(std::io::ErrorKind), 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 /// Examples
/// -------- /// --------
@ -205,13 +209,13 @@ pub enum MatrixMarketErrorKind {
/// 1 1 10 /// 1 1 10
/// 2 3 5 /// 2 3 5
/// "#; /// "#;
/// let matrix_error = load_coo_from_matrix_market_str::<i32>(str); /// let matrix_result = load_coo_from_matrix_market_str::<i32>(str);
/// assert_eq!(matrix_error.is_err(), true); /// assert_eq!(matrix_result.is_err(), true);
/// assert_eq!(matrix_error.unwrap_err().kind(),MatrixMarketErrorKind::NotLowerTriangle); /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::NotLowerTriangle);
/// ``` /// ```
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 /// Examples
/// -------- /// --------
@ -224,11 +228,11 @@ pub enum MatrixMarketErrorKind {
/// 1 1 10 /// 1 1 10
/// 2 3 5 /// 2 3 5
/// "#; /// "#;
/// let matrix_error = load_coo_from_matrix_market_str::<i32>(str); /// let matrix_result = load_coo_from_matrix_market_str::<i32>(str);
/// assert_eq!(matrix_error.is_err(), true); /// assert_eq!(matrix_result.is_err(), true);
/// assert_eq!(matrix_error.unwrap_err().kind(),MatrixMarketErrorKind::NotSquareMatrix); /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::NonSquare);
/// ``` /// ```
NotSquareMatrix, NonSquare,
} }
impl MatrixMarketError { impl MatrixMarketError {
@ -239,7 +243,7 @@ impl MatrixMarketError {
} }
} }
/// The operation error kind. /// The matrix market error kind.
#[must_use] #[must_use]
pub fn kind(&self) -> MatrixMarketErrorKind { pub fn kind(&self) -> MatrixMarketErrorKind {
self.error_kind self.error_kind
@ -254,7 +258,7 @@ impl MatrixMarketError {
impl fmt::Display for MatrixMarketError { impl fmt::Display for MatrixMarketError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "Matrix Market load error: ")?; write!(f, "Matrix Market error: ")?;
match self.kind() { match self.kind() {
MatrixMarketErrorKind::ParsingError => { MatrixMarketErrorKind::ParsingError => {
write!(f, "ParsingError,")?; write!(f, "ParsingError,")?;
@ -262,11 +266,11 @@ impl fmt::Display for MatrixMarketError {
MatrixMarketErrorKind::InvalidHeader => { MatrixMarketErrorKind::InvalidHeader => {
write!(f, "InvalidHeader,")?; write!(f, "InvalidHeader,")?;
} }
MatrixMarketErrorKind::EntryNumUnmatched => { MatrixMarketErrorKind::EntryMismatch => {
write!(f, "EntryNumUnmatched,")?; write!(f, "EntryNumUnmatched,")?;
} }
MatrixMarketErrorKind::TypeUnmatched => { MatrixMarketErrorKind::TypeMismatch => {
write!(f, "TypeUnmatched,")?; write!(f, "TypeMismatch,")?;
} }
MatrixMarketErrorKind::SparseFormatError(_) => { MatrixMarketErrorKind::SparseFormatError(_) => {
write!(f, "SparseFormatError,")?; write!(f, "SparseFormatError,")?;
@ -283,23 +287,24 @@ impl fmt::Display for MatrixMarketError {
MatrixMarketErrorKind::NotLowerTriangle => { MatrixMarketErrorKind::NotLowerTriangle => {
write!(f, "NotLowerTriangle,")?; write!(f, "NotLowerTriangle,")?;
} }
MatrixMarketErrorKind::NotSquareMatrix => { MatrixMarketErrorKind::NonSquare => {
write!(f, "NotSquareMatrix,")?; write!(f, "NotSquareMatrix,")?;
} }
} }
write!(f, " Message: {}", self.message) write!(f, " message: {}", self.message)
} }
} }
impl std::error::Error for MatrixMarketError {} impl std::error::Error for MatrixMarketError {}
impl<T: fmt::Debug + std::hash::Hash + std::marker::Copy + Ord> From<pest::error::Error<T>> impl MatrixMarketError {
for MatrixMarketError fn from_pest_error<T>(error: pest::error::Error<T>) -> Self
{ where
fn from(err: pest::error::Error<T>) -> Self { T: fmt::Debug + std::hash::Hash + std::marker::Copy + Ord
{
Self::from_kind_and_message( Self::from_kind_and_message(
MatrixMarketErrorKind::ParsingError, MatrixMarketErrorKind::ParsingError,
format!("Can't parse the data.\n Error: {}", err), format!("Can't parse the data.\n Error: {}", error),
) )
} }
} }
@ -334,9 +339,9 @@ impl From<std::io::Error> for MatrixMarketError {
impl From<TryFromIntError> for MatrixMarketError { impl From<TryFromIntError> for MatrixMarketError {
fn from(err: TryFromIntError) -> Self { fn from(err: TryFromIntError) -> Self {
Self::from_kind_and_message( Self::from_kind_and_message(
MatrixMarketErrorKind::TypeUnmatched, MatrixMarketErrorKind::TypeMismatch,
format!( format!(
"Please consider using a larger integery type. Error message: {}", "Please consider using a larger integer type. Error message: {}",
&err &err
), ),
) )
@ -348,7 +353,7 @@ impl From<TryFromIntError> for MatrixMarketError {
impl From<Infallible> for MatrixMarketError { impl From<Infallible> for MatrixMarketError {
fn from(_err: Infallible) -> Self { fn from(_err: Infallible) -> Self {
Self::from_kind_and_message( Self::from_kind_and_message(
MatrixMarketErrorKind::TypeUnmatched, MatrixMarketErrorKind::TypeMismatch,
format!("This won't happen"), format!("This won't happen"),
) )
} }
@ -431,7 +436,11 @@ impl FromStr for StorageScheme {
/// Precheck if it's a valid header. /// 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> { fn typecode_precheck(tc: &Typecode) -> Result<(), MatrixMarketError> {
match tc { match tc {
Typecode { Typecode {
@ -479,25 +488,39 @@ 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 { mod internal {
/// When matrix is an Integer matrix, it will convert a [i128] number to this type. use crate::io::MatrixMarketError;
fn from_i128(i: i128) -> Result<Self, MatrixMarketError>; use na::{Complex, Scalar};
/// When matrix is a Real matrix, it will convert a [f64] number to this type.
fn from_f64(f: f64) -> Result<Self, MatrixMarketError>; pub trait SupportedMatrixMarketScalar: Scalar {
/// When matrix is a Complx matrix, it will convert a [Complex<f64>] number to this type. /// When the matrix is an integer matrix, it will convert a [i128] number to this type.
fn from_c64(c: Complex<f64>) -> Result<Self, MatrixMarketError>; fn from_i128(i: i128) -> Result<Self, MatrixMarketError>;
/// When matrix is a Pattern matrix, it will convert a unit type [unit] to this type. /// When matrix is a Real matrix, it will convert a [f64] number to this type.
fn from_pattern(p: ()) -> Result<Self, MatrixMarketError>; fn from_f64(f: f64) -> Result<Self, MatrixMarketError>;
/// When matrix is a Skew-symmetric matrix, it will convert itself to its negative. /// When matrix is a Complex matrix, it will convert a [Complex<f64>] number to this type.
fn negative(self) -> Result<Self, MatrixMarketError>; fn from_c64(c: Complex<f64>) -> Result<Self, MatrixMarketError>;
/// When matrix is a Hermitian matrix, it will convert itself to its conjugate. /// When matrix is a Pattern matrix, it will convert a unit type [unit] to this type.
fn conjugate(self) -> Result<Self, MatrixMarketError>; fn from_pattern(p: ()) -> Result<Self, MatrixMarketError>;
/// When matrix is a Skew-symmetric matrix, it will convert itself to its negative.
fn negative(self) -> Result<Self, MatrixMarketError>;
/// When matrix is a Hermitian matrix, it will convert itself to its conjugate.
fn conjugate(self) -> Result<Self, MatrixMarketError>;
}
} }
/// 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. /// Implement MatrixMarketScalar for primitive integer types.
macro_rules! mm_int_impl { macro_rules! mm_int_impl {
($T:ty) => { ($T:ty) => {
impl MatrixMarketScalar for $T { impl MatrixMarketScalar for $T {}
impl internal::SupportedMatrixMarketScalar for $T {
#[inline] #[inline]
fn from_i128(i: i128) -> Result<Self, MatrixMarketError> { fn from_i128(i: i128) -> Result<Self, MatrixMarketError> {
Ok(Self::try_from(i)?) Ok(Self::try_from(i)?)
@ -505,28 +528,28 @@ macro_rules! mm_int_impl {
#[inline] #[inline]
fn from_f64(_f: f64) -> Result<Self, MatrixMarketError> { fn from_f64(_f: f64) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message( Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeUnmatched, MatrixMarketErrorKind::TypeMismatch,
format!("Int type can't be parsed from f64"), format!("Int type can't be parsed from f64"),
)) ))
} }
#[inline] #[inline]
fn from_c64(_c: Complex<f64>) -> Result<Self, MatrixMarketError> { fn from_c64(_c: Complex<f64>) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message( Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeUnmatched, MatrixMarketErrorKind::TypeMismatch,
format!("Int type can't be parsed from Complex<f64>"), format!("Int type can't be parsed from Complex<f64>"),
)) ))
} }
#[inline] #[inline]
fn from_pattern(_p: ()) -> Result<Self, MatrixMarketError> { fn from_pattern(_p: ()) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message( Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeUnmatched, MatrixMarketErrorKind::TypeMismatch,
format!("Int type can't be parsed from ()"), format!("Int type can't be parsed from ()"),
)) ))
} }
#[inline] #[inline]
fn conjugate(self) -> Result<Self, MatrixMarketError> { fn conjugate(self) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message( Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeUnmatched, MatrixMarketErrorKind::TypeMismatch,
format!("Int type has no conjugate"), format!("Int type has no conjugate"),
)) ))
} }
@ -540,11 +563,13 @@ macro_rules! mm_int_impl {
/// Implement MatrixMarketScalar for primitive real types. /// Implement MatrixMarketScalar for primitive real types.
macro_rules! mm_real_impl { macro_rules! mm_real_impl {
($T:ty) => { ($T:ty) => {
impl MatrixMarketScalar for $T { impl MatrixMarketScalar for $T {}
impl internal::SupportedMatrixMarketScalar for $T {
#[inline] #[inline]
fn from_i128(_i: i128) -> Result<Self, MatrixMarketError> { fn from_i128(_i: i128) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message( Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeUnmatched, MatrixMarketErrorKind::TypeMismatch,
format!("real type can't be parsed from i128"), format!("real type can't be parsed from i128"),
)) ))
} }
@ -555,21 +580,21 @@ macro_rules! mm_real_impl {
#[inline] #[inline]
fn from_c64(_c: Complex<f64>) -> Result<Self, MatrixMarketError> { fn from_c64(_c: Complex<f64>) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message( Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeUnmatched, MatrixMarketErrorKind::TypeMismatch,
format!("real type can't be parsed from Complex<f64>"), format!("real type can't be parsed from Complex<f64>"),
)) ))
} }
#[inline] #[inline]
fn from_pattern(_p: ()) -> Result<Self, MatrixMarketError> { fn from_pattern(_p: ()) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message( Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeUnmatched, MatrixMarketErrorKind::TypeMismatch,
format!("real type can't be parsed from ()"), format!("real type can't be parsed from ()"),
)) ))
} }
#[inline] #[inline]
fn conjugate(self) -> Result<Self, MatrixMarketError> { fn conjugate(self) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message( Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeUnmatched, MatrixMarketErrorKind::TypeMismatch,
format!("real type has no conjugate"), format!("real type has no conjugate"),
)) ))
} }
@ -580,21 +605,24 @@ macro_rules! mm_real_impl {
} }
}; };
} }
/// Implement MatrixMarketScalar for primitive complex types. /// Implement MatrixMarketScalar for primitive complex types.
macro_rules! mm_complex_impl { macro_rules! mm_complex_impl {
($T:ty) => { ($T:ty) => {
impl MatrixMarketScalar for Complex<$T> { impl MatrixMarketScalar for Complex<$T> {}
impl internal::SupportedMatrixMarketScalar for Complex<$T> {
#[inline] #[inline]
fn from_i128(_i: i128) -> Result<Self, MatrixMarketError> { fn from_i128(_i: i128) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message( Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeUnmatched, MatrixMarketErrorKind::TypeMismatch,
format!("Complex type can't be parsed from i128"), format!("Complex type can't be parsed from i128"),
)) ))
} }
#[inline] #[inline]
fn from_f64(_f: f64) -> Result<Self, MatrixMarketError> { fn from_f64(_f: f64) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message( Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeUnmatched, MatrixMarketErrorKind::TypeMismatch,
format!("Complex type can't be parsed from f64"), format!("Complex type can't be parsed from f64"),
)) ))
} }
@ -608,7 +636,7 @@ macro_rules! mm_complex_impl {
#[inline] #[inline]
fn from_pattern(_p: ()) -> Result<Self, MatrixMarketError> { fn from_pattern(_p: ()) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message( Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeUnmatched, MatrixMarketErrorKind::TypeMismatch,
format!("Complex type can't be parsed from ()"), format!("Complex type can't be parsed from ()"),
)) ))
} }
@ -626,25 +654,27 @@ macro_rules! mm_complex_impl {
/// Implement MatrixMarketScalar for primitive unit types. /// Implement MatrixMarketScalar for primitive unit types.
macro_rules! mm_pattern_impl { macro_rules! mm_pattern_impl {
($T:ty) => { ($T:ty) => {
impl MatrixMarketScalar for $T { impl MatrixMarketScalar for $T {}
impl internal::SupportedMatrixMarketScalar for $T {
#[inline] #[inline]
fn from_i128(_i: i128) -> Result<Self, MatrixMarketError> { fn from_i128(_i: i128) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message( Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeUnmatched, MatrixMarketErrorKind::TypeMismatch,
format!("Pattern type can't be parsed from i128"), format!("Pattern type can't be parsed from i128"),
)) ))
} }
#[inline] #[inline]
fn from_f64(_f: f64) -> Result<Self, MatrixMarketError> { fn from_f64(_f: f64) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message( Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeUnmatched, MatrixMarketErrorKind::TypeMismatch,
format!("Pattern type can't be parsed from f64"), format!("Pattern type can't be parsed from f64"),
)) ))
} }
#[inline] #[inline]
fn from_c64(_c: Complex<f64>) -> Result<Self, MatrixMarketError> { fn from_c64(_c: Complex<f64>) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message( Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeUnmatched, MatrixMarketErrorKind::TypeMismatch,
format!("Pattern type can't be parsed from Complex<f64>"), format!("Pattern type can't be parsed from Complex<f64>"),
)) ))
} }
@ -656,14 +686,14 @@ macro_rules! mm_pattern_impl {
#[inline] #[inline]
fn conjugate(self) -> Result<Self, MatrixMarketError> { fn conjugate(self) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message( Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeUnmatched, MatrixMarketErrorKind::TypeMismatch,
format!("Pattern type has no conjugate"), format!("Pattern type has no conjugate"),
)) ))
} }
#[inline] #[inline]
fn negative(self) -> Result<Self, MatrixMarketError> { fn negative(self) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message( Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeUnmatched, MatrixMarketErrorKind::TypeMismatch,
format!("Pattern type has no negative"), format!("Pattern type has no negative"),
)) ))
} }
@ -687,16 +717,31 @@ mm_pattern_impl!(());
#[derive(Parser)] #[derive(Parser)]
#[grammar = "io/matrix_market.pest"] #[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 /// Errors
/// -------- /// --------
/// ///
/// See [MatrixMarketErrorKind] for a list of possible error conditions. /// 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<T>`, where T is an integer type. Trying `load_coo_from_matrix_market_file<f64>` 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::<i32>("path/to/matrix.mtx")?;
/// ```
pub fn load_coo_from_matrix_market_file<T, P: AsRef<Path>>( pub fn load_coo_from_matrix_market_file<T, P: AsRef<Path>>(
path: P, path: P,
) -> Result<CooMatrix<T>, MatrixMarketError> ) -> Result<CooMatrix<T>, MatrixMarketError>
@ -707,21 +752,36 @@ where
load_coo_from_matrix_market_str(&file) 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 /// Errors
/// -------- /// --------
/// ///
/// See [MatrixMarketErrorKind] for a list of possible error conditions. /// 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<T>`, where T is an integer type. Trying `load_coo_from_matrix_market_str<f64>` 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::<i32>(str)?;
/// ```
pub fn load_coo_from_matrix_market_str<T>(data: &str) -> Result<CooMatrix<T>, MatrixMarketError> pub fn load_coo_from_matrix_market_str<T>(data: &str) -> Result<CooMatrix<T>, MatrixMarketError>
where where
T: MatrixMarketScalar, T: MatrixMarketScalar,
{ {
// unwrap() in this function are guaranteed by parsing the data // 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)
.map_err(MatrixMarketError::from_pest_error)?
.next().unwrap();
let mut rows: Vec<usize> = Vec::new(); let mut rows: Vec<usize> = Vec::new();
let mut cols: Vec<usize> = Vec::new(); let mut cols: Vec<usize> = Vec::new();
@ -746,16 +806,16 @@ where
// used when constructing dense matrix. // used when constructing dense matrix.
// If it's sparse matrix, it has no effect. // 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 { if header_type.storagescheme == StorageScheme::Skew {
// for skew dense matrix, the first element starts from (1,0) // 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 // count how many entries in the matrix data
let count = lines.clone().count(); let count = lines.clone().count();
if count != shape.2 { if count != shape.2 {
return Err(MatrixMarketError::from_kind_and_message( return Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::EntryNumUnmatched, MatrixMarketErrorKind::EntryMismatch,
format!( format!(
"{} entries required for the matrix, but {} was provided", "{} entries required for the matrix, but {} was provided",
shape.2, count, shape.2, count,
@ -800,12 +860,12 @@ where
.. ..
} => { } => {
entry = ( entry = (
current_dense_coordiante.0, current_dense_coordinate.0,
current_dense_coordiante.1, current_dense_coordinate.1,
parse_dense_complex::<T>(&mut data_line.into_inner())?, parse_dense_complex::<T>(&mut data_line.into_inner())?,
); );
next_dense_coordiante( next_dense_coordinate(
&mut current_dense_coordiante, &mut current_dense_coordinate,
shape, shape,
&header_type.storagescheme, &header_type.storagescheme,
); );
@ -816,13 +876,13 @@ where
.. ..
} => { } => {
entry = ( entry = (
current_dense_coordiante.0, current_dense_coordinate.0,
current_dense_coordiante.1, current_dense_coordinate.1,
parse_dense_real::<T>(&mut data_line.into_inner())?, parse_dense_real::<T>(&mut data_line.into_inner())?,
); );
next_dense_coordiante( next_dense_coordinate(
&mut current_dense_coordiante, &mut current_dense_coordinate,
shape, shape,
&header_type.storagescheme, &header_type.storagescheme,
); );
@ -833,12 +893,12 @@ where
.. ..
} => { } => {
entry = ( entry = (
current_dense_coordiante.0, current_dense_coordinate.0,
current_dense_coordiante.1, current_dense_coordinate.1,
parse_dense_int::<T>(&mut data_line.into_inner())?, parse_dense_int::<T>(&mut data_line.into_inner())?,
); );
next_dense_coordiante( next_dense_coordinate(
&mut current_dense_coordiante, &mut current_dense_coordinate,
shape, shape,
&header_type.storagescheme, &header_type.storagescheme,
); );
@ -963,7 +1023,7 @@ fn parse_header(inner: &mut Pairs<'_, Rule>) -> Typecode {
// Parse shape starts here------------------------------------------------- // 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( fn parse_sparse_shape(
inner: &mut Pairs<'_, Rule>, inner: &mut Pairs<'_, Rule>,
storagescheme: &StorageScheme, storagescheme: &StorageScheme,
@ -994,15 +1054,15 @@ 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 { 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)) 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( fn parse_dense_shape(
inner: &mut Pairs<'_, Rule>, inner: &mut Pairs<'_, Rule>,
storagescheme: &StorageScheme, storagescheme: &StorageScheme,
@ -1030,9 +1090,9 @@ 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 { 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; let n: usize;
@ -1061,7 +1121,7 @@ fn parse_dense_shape(
// Parse entry starts here------------------------------------------------- // 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<T>(inner: &mut Pairs<'_, Rule>) -> Result<(usize, usize, T), MatrixMarketError> fn parse_sparse_real<T>(inner: &mut Pairs<'_, Rule>) -> Result<(usize, usize, T), MatrixMarketError>
where where
T: MatrixMarketScalar, T: MatrixMarketScalar,
@ -1070,17 +1130,17 @@ where
let entry_inner = inner.next().unwrap(); let entry_inner = inner.next().unwrap();
if entry_inner.as_rule() != Rule::SparseReal { if entry_inner.as_rule() != Rule::SparseReal {
return Err(MatrixMarketError::from_kind_and_message(MatrixMarketErrorKind::ParsingError,format!(" 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() ))); ",entry_inner.as_str() )));
} }
let mut inner = entry_inner.into_inner(); 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::<f64>().unwrap(); let d = inner.next().unwrap().as_str().parse::<f64>().unwrap();
Ok((r, c, T::from_f64(d)?)) 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<T>(inner: &mut Pairs<'_, Rule>) -> Result<(usize, usize, T), MatrixMarketError> fn parse_sparse_int<T>(inner: &mut Pairs<'_, Rule>) -> Result<(usize, usize, T), MatrixMarketError>
where where
T: MatrixMarketScalar, T: MatrixMarketScalar,
@ -1093,20 +1153,20 @@ where
MatrixMarketErrorKind::ParsingError, MatrixMarketErrorKind::ParsingError,
format!( 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() entry_inner.as_str()
), ),
)); ));
} }
let mut inner = entry_inner.into_inner(); 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 // Here to guarantee it is an integer number
let d = inner.next().unwrap().as_str().parse::<i128>()?; let d = inner.next().unwrap().as_str().parse::<i128>()?;
Ok((r, c, T::from_i128(d)?)) 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<T>( fn parse_sparse_pattern<T>(
inner: &mut Pairs<'_, Rule>, inner: &mut Pairs<'_, Rule>,
) -> Result<(usize, usize, T), MatrixMarketError> ) -> Result<(usize, usize, T), MatrixMarketError>
@ -1120,18 +1180,18 @@ where
MatrixMarketErrorKind::ParsingError, MatrixMarketErrorKind::ParsingError,
format!( 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() entry_inner.as_str()
), ),
)); ));
} }
let mut inner = entry_inner.into_inner(); 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(())?)) 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<T>( fn parse_sparse_complex<T>(
inner: &mut Pairs<'_, Rule>, inner: &mut Pairs<'_, Rule>,
) -> Result<(usize, usize, T), MatrixMarketError> ) -> Result<(usize, usize, T), MatrixMarketError>
@ -1142,11 +1202,11 @@ where
let entry_inner = inner.next().unwrap(); let entry_inner = inner.next().unwrap();
if entry_inner.as_rule() != Rule::SparseComplex { if entry_inner.as_rule() != Rule::SparseComplex {
return Err(MatrixMarketError::from_kind_and_message(MatrixMarketErrorKind::ParsingError,format!(" 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() ))); ",entry_inner.as_str() )));
} }
let mut inner = entry_inner.into_inner(); 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::<f64>().unwrap(); let real = inner.next().unwrap().as_str().parse::<f64>().unwrap();
let imag = inner.next().unwrap().as_str().parse::<f64>().unwrap(); let imag = inner.next().unwrap().as_str().parse::<f64>().unwrap();
let complex = Complex::<f64>::new(real, imag); let complex = Complex::<f64>::new(real, imag);
@ -1232,8 +1292,8 @@ where
// Parse entry ends here------------------------------------------------- // Parse entry ends here-------------------------------------------------
/// Parse the coordiantes information used for sparse matrix /// Parse the coordinates information used for sparse matrix
fn parse_sparse_coordiante( fn parse_sparse_coordinate(
inner: &mut Pairs<'_, Rule>, inner: &mut Pairs<'_, Rule>,
) -> Result<(usize, usize), MatrixMarketError> { ) -> Result<(usize, usize), MatrixMarketError> {
// unwrap() in this function are guaranteed by parsing the data // unwrap() in this function are guaranteed by parsing the data
@ -1242,51 +1302,51 @@ fn parse_sparse_coordiante(
if r * c == 0 { if r * c == 0 {
return Err(MatrixMarketError::from_kind_and_message( return Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::ZeroError, 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)) Ok((r - 1, c - 1))
} }
/// Calculate the next coordiantes used for dense matrix /// Calculate the next coordinates used for dense matrix
fn next_dense_coordiante( fn next_dense_coordinate(
current_dense_coordiante: &mut (usize, usize), current_dense_coordinate: &mut (usize, usize),
shape: (usize, usize, usize), shape: (usize, usize, usize),
storagescheme: &StorageScheme, storagescheme: &StorageScheme,
) { ) {
// matrix market is column based format. // matrix market is column based format.
// so it follows the order (0,0) -> (1,0) -> ... -> (row, 0) -> (0,1) -> ... ->(row,col) // 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 { match storagescheme {
StorageScheme::General => { StorageScheme::General => {
if current_dense_coordiante.0 < shape.0 - 1 { if current_dense_coordinate.0 < shape.0 - 1 {
current_dense_coordiante.0 += 1 current_dense_coordinate.0 += 1
} else { } else {
// jump to next column, reset row to 1, column add 1 // jump to next column, reset row to 1, column add 1
current_dense_coordiante.0 = 0; current_dense_coordinate.0 = 0;
current_dense_coordiante.1 += 1; current_dense_coordinate.1 += 1;
} }
} }
StorageScheme::Symmetric | StorageScheme::Hermitian => { StorageScheme::Symmetric | StorageScheme::Hermitian => {
if current_dense_coordiante.0 < shape.0 - 1 { if current_dense_coordinate.0 < shape.0 - 1 {
current_dense_coordiante.0 += 1 current_dense_coordinate.0 += 1
} else { } else {
// jump to next column, column add 1, then set row equals to current column // jump to next column, column add 1, then set row equals to current column
// for example (0,0) -> (1,0) -> ... -> (row, 0) -> (1,1) -> ... // for example (0,0) -> (1,0) -> ... -> (row, 0) -> (1,1) -> ...
current_dense_coordiante.1 += 1; current_dense_coordinate.1 += 1;
current_dense_coordiante.0 = current_dense_coordiante.1; current_dense_coordinate.0 = current_dense_coordinate.1;
} }
} }
StorageScheme::Skew => { StorageScheme::Skew => {
if current_dense_coordiante.0 < shape.0 - 1 { if current_dense_coordinate.0 < shape.0 - 1 {
current_dense_coordiante.0 += 1; current_dense_coordinate.0 += 1;
} else { } else {
// jump to next column, set row equals to current column, then column add 1 // jump to next column, set row equals to current column, then column add 1
// skew matrix doesn't have element on diagonal // skew matrix doesn't have element on diagonal
// for example (1,0) -> (2,0) -> ... -> (row, 0) -> (2,1) -> ... // for example (1,0) -> (2,0) -> ... -> (row, 0) -> (2,1) -> ...
current_dense_coordiante.1 += 1; current_dense_coordinate.1 += 1;
current_dense_coordiante.0 = current_dense_coordiante.1 + 1; current_dense_coordinate.0 = current_dense_coordinate.1 + 1;
} }
} }
} }

View File

@ -1,7 +1,35 @@
//! Parsers for various matrix formats. //! Functionality for importing and exporting sparse matrices to and from files.
//! //!
//! ## Matrix Market //! **Available only when the `io` feature is enabled.**
//! 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. //!
//! 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.<br/>
//! > "*The Matrix Market Exchange Formats: Initial Design.*" (1996).
pub use self::matrix_market::{ pub use self::matrix_market::{
load_coo_from_matrix_market_file, load_coo_from_matrix_market_str, MatrixMarketError, load_coo_from_matrix_market_file, load_coo_from_matrix_market_str, MatrixMarketError,

View File

@ -19,6 +19,7 @@
//! - Sparsity patterns in CSR and CSC matrices are explicitly represented by the //! - Sparsity patterns in CSR and CSC matrices are explicitly represented by the
//! [SparsityPattern](pattern::SparsityPattern) type, which encodes the invariants of the //! [SparsityPattern](pattern::SparsityPattern) type, which encodes the invariants of the
//! associated index data structures. //! 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 strategies](`proptest`) for sparse matrices when the feature
//! `proptest-support` is enabled. //! `proptest-support` is enabled.
//! - [matrixcompare support](https://crates.io/crates/matrixcompare) for effortless //! - [matrixcompare support](https://crates.io/crates/matrixcompare) for effortless

View File

@ -1,6 +1,13 @@
//! Unit tests //! Unit tests
#[cfg(any(not(feature = "proptest-support"), not(feature = "compare")))] #[cfg(not(all(
compile_error!("Tests must be run with features `proptest-support` and `compare`"); 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; mod unit_tests;

View File

@ -4,6 +4,24 @@ use nalgebra::Complex;
use nalgebra_sparse::io::load_coo_from_matrix_market_str; use nalgebra_sparse::io::load_coo_from_matrix_market_str;
use nalgebra_sparse::CooMatrix; 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<String> = 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::<f32>(string).unwrap();
assert_eq!(sparse_mat.nrows(), 0);
assert_eq!(sparse_mat.ncols(), 0);
assert_eq!(sparse_mat.nnz(), 0);
}
}
#[test] #[test]
#[rustfmt::skip] #[rustfmt::skip]
fn test_matrixmarket_sparse_real_general() { fn test_matrixmarket_sparse_real_general() {

View File

@ -3,7 +3,6 @@ mod convert_serial;
mod coo; mod coo;
mod csc; mod csc;
mod csr; mod csr;
#[cfg(feature = "io")]
mod matrix_market; mod matrix_market;
mod ops; mod ops;
mod pattern; mod pattern;