nalgebra_sparse/io/
matrix_market.rs

1//! Implementation of matrix market io code.
2//!
3//! 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.
4use crate::SparseFormatError;
5use crate::SparseFormatErrorKind;
6use crate::{CooMatrix, CscMatrix, CsrMatrix};
7use nalgebra::Complex;
8use pest::iterators::Pairs;
9use pest::Parser;
10use std::cmp::PartialEq;
11use std::convert::Infallible;
12use std::convert::TryFrom;
13use std::fmt;
14use std::fmt::Formatter;
15use std::fs::{self, File};
16use std::io::{BufWriter, Write};
17use std::num::ParseIntError;
18use std::num::TryFromIntError;
19use std::path::Path;
20use std::str::FromStr;
21
22/// A description of the error that occurred during importing a matrix from a matrix market format data.
23#[derive(Debug)]
24pub struct MatrixMarketError {
25    error_kind: MatrixMarketErrorKind,
26    message: String,
27}
28
29/// Errors produced by functions that expect well-formed matrix market format data.
30#[non_exhaustive]
31#[derive(Copy, Clone, Debug, PartialEq)]
32pub enum MatrixMarketErrorKind {
33    /// Parsing failure.
34    ///
35    /// Indicates that the parser failed, for example due to an unexpected string.
36    ///
37    /// Examples
38    /// --------
39    /// ```rust
40    /// # use nalgebra_sparse::io::load_coo_from_matrix_market_str;
41    /// # use nalgebra_sparse::io::MatrixMarketErrorKind;
42    /// let str = r#"
43    /// %%MatrixMarket invalid invalid invalid invalid
44    /// 1 1 1
45    /// 1 1 5
46    /// "#;
47    /// let matrix_result = load_coo_from_matrix_market_str::<f64>(str);
48    /// assert_eq!(matrix_result.is_err(), true);
49    /// assert_eq!(matrix_result.unwrap_err().kind(), MatrixMarketErrorKind::ParsingError);
50    /// ```
51    ParsingError,
52
53    /// Indicates that the matrix market header is invalid.
54    ///
55    /// Examples
56    /// --------
57    /// ```rust
58    /// # use nalgebra_sparse::io::load_coo_from_matrix_market_str;
59    /// # use nalgebra_sparse::io::MatrixMarketErrorKind;
60    /// let str = r#"
61    /// %%MatrixMarket matrix coordinate real hermitian
62    /// % a real matrix can't be hermitian
63    /// 1 1 1
64    /// 1 1 5
65    /// "#;
66    /// let matrix_result = load_coo_from_matrix_market_str::<f64>(str);
67    /// assert_eq!(matrix_result.is_err(), true);
68    /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::InvalidHeader);
69    /// ```
70    InvalidHeader,
71
72    /// Indicates that the number of data entries in the matrix market file does not match the header.
73    ///
74    /// Examples
75    /// --------
76    /// ```rust
77    /// # use nalgebra_sparse::io::load_coo_from_matrix_market_str;
78    /// # use nalgebra_sparse::io::MatrixMarketErrorKind;
79    /// let str = r#"
80    /// %%matrixmarket matrix coordinate real general
81    /// % it has one more data entry than specified.
82    /// 3 3 1
83    /// 2 2 2
84    /// 2 3 2
85    /// "#;
86    /// let matrix_result = load_coo_from_matrix_market_str::<f64>(str);
87    /// assert_eq!(matrix_result.is_err(), true);
88    /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::EntryMismatch);
89    /// ```
90    EntryMismatch,
91
92    /// Indicates that the scalar type requested is not compatible with the scalar type stored
93    /// in the matrix market file.
94    ///
95    /// Examples
96    /// --------
97    /// ```rust
98    /// # use nalgebra_sparse::io::load_coo_from_matrix_market_str;
99    /// # use nalgebra_sparse::io::MatrixMarketErrorKind;
100    /// let str = r#"
101    /// %%matrixmarket matrix coordinate real general
102    /// % it should be loaded with load_coo_from_matrix_market_str::<f64>(str) (or f32)
103    /// 3 3 2
104    /// 2 2 2.22
105    /// 2 3 2.22
106    /// "#;
107    /// let matrix_result = load_coo_from_matrix_market_str::<i32>(str);
108    /// assert_eq!(matrix_result.is_err(), true);
109    /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::TypeMismatch);
110    /// ```
111    TypeMismatch,
112
113    /// Indicates that zero has been used as an index in the data.
114    ///
115    /// **Note**: The matrix market format uses 1-based indexing.
116    ///
117    /// Examples
118    /// --------
119    /// ```rust
120    /// # use nalgebra_sparse::io::load_coo_from_matrix_market_str;
121    /// # use nalgebra_sparse::io::MatrixMarketErrorKind;
122    /// let str = r#"
123    /// %%matrixmarket matrix coordinate real general
124    /// 1 1 1
125    /// 0 0 10
126    /// "#;
127    /// let matrix_result = load_coo_from_matrix_market_str::<f64>(str);
128    /// assert_eq!(matrix_result.is_err(), true);
129    /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::ZeroError);
130    /// ```
131    ZeroError,
132
133    /// Indicates [SparseFormatError] while creating the sparse matrix.
134    ///
135    ///
136    /// Examples
137    /// --------
138    /// ```rust
139    /// # use nalgebra_sparse::io::load_coo_from_matrix_market_str;
140    /// # use nalgebra_sparse::io::MatrixMarketErrorKind;
141    /// # use nalgebra_sparse::SparseFormatErrorKind;
142    /// let str = r#"
143    /// %%matrixmarket matrix coordinate real general
144    /// 1 1 1
145    /// 4 2 10
146    /// "#;
147    /// let matrix_result = load_coo_from_matrix_market_str::<f64>(str);
148    /// assert_eq!(matrix_result.is_err(), true);
149    /// assert_eq!(matrix_result.unwrap_err().kind(),
150    ///     MatrixMarketErrorKind::SparseFormatError(SparseFormatErrorKind::IndexOutOfBounds));
151    /// ```
152    SparseFormatError(SparseFormatErrorKind),
153
154    /// Indicates that a wrong diagonal element has been provided to the matrix.
155    ///
156    /// Examples
157    /// --------
158    /// ```rust
159    /// # use nalgebra_sparse::io::load_coo_from_matrix_market_str;
160    /// # use nalgebra_sparse::io::MatrixMarketErrorKind;
161    /// # use nalgebra::Complex;
162    /// let str = r#"
163    /// %%matrixmarket matrix coordinate real skew-symmetric
164    /// % skew-symmetric matrix can't have element on diagonal
165    /// 5 5 2
166    /// 1 1 10
167    /// 2 1 5
168    /// "#;
169    /// let matrix_result = load_coo_from_matrix_market_str::<f64>(str);
170    /// assert_eq!(matrix_result.is_err(), true);
171    /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::DiagonalError);
172    ///
173    /// let str = r#"
174    /// %%matrixmarket matrix coordinate complex hermitian
175    /// % hermitian matrix diagonal element must be a real number
176    /// 5 5 2
177    /// 1 1 10 2
178    /// 2 1 5 2
179    /// "#;
180    /// let matrix_result = load_coo_from_matrix_market_str::<Complex<f64>>(str);
181    /// assert_eq!(matrix_result.is_err(), true);
182    /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::DiagonalError);
183    /// ```
184    /// Here the skew matrix shouldn't have an element on the diagonal.
185    DiagonalError,
186
187    /// Indicates an [IO error](`std::io::Error`) while reading the data from file.
188    ///
189    /// Examples
190    /// --------
191    /// ```rust
192    /// # use nalgebra_sparse::io::load_coo_from_matrix_market_file;
193    /// # use nalgebra_sparse::io::MatrixMarketErrorKind;
194    /// let matrix_result = load_coo_from_matrix_market_file::<f64,_>("matrix.mtx");
195    /// assert_eq!(matrix_result.is_err(), true);
196    /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::IOError(std::io::ErrorKind::NotFound));
197    /// ```
198    IOError(std::io::ErrorKind),
199
200    /// Indicates that a (skew-)symmetric (or hermitian) matrix is not a lower triangular matrix.
201    ///
202    /// Examples
203    /// --------
204    /// ```rust
205    /// # use nalgebra_sparse::io::load_coo_from_matrix_market_str;
206    /// # use nalgebra_sparse::io::MatrixMarketErrorKind;
207    /// let str = r#"
208    /// %%matrixmarket matrix coordinate integer symmetric
209    /// 5 5 2
210    /// 1 1 10
211    /// 2 3 5
212    /// "#;
213    /// let matrix_result = load_coo_from_matrix_market_str::<i32>(str);
214    /// assert_eq!(matrix_result.is_err(), true);
215    /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::NotLowerTriangle);
216    /// ```
217    NotLowerTriangle,
218
219    /// Indicates that a (skew-)symmetric (or hermitian) matrix is not a square matrix.
220    ///
221    /// Examples
222    /// --------
223    /// ```rust
224    /// # use nalgebra_sparse::io::load_coo_from_matrix_market_str;
225    /// # use nalgebra_sparse::io::MatrixMarketErrorKind;
226    /// let str = r#"
227    /// %%matrixmarket matrix coordinate integer symmetric
228    /// 5 4 2
229    /// 1 1 10
230    /// 3 2 5
231    /// "#;
232    /// let matrix_result = load_coo_from_matrix_market_str::<i32>(str);
233    /// assert_eq!(matrix_result.is_err(), true);
234    /// assert_eq!(matrix_result.unwrap_err().kind(),MatrixMarketErrorKind::NonSquare);
235    /// ```
236    NonSquare,
237}
238
239impl MatrixMarketError {
240    fn from_kind_and_message(error_type: MatrixMarketErrorKind, message: String) -> Self {
241        Self {
242            error_kind: error_type,
243            message,
244        }
245    }
246
247    /// The matrix market error kind.
248    #[must_use]
249    pub fn kind(&self) -> MatrixMarketErrorKind {
250        self.error_kind
251    }
252
253    /// The underlying error message.
254    #[must_use]
255    pub fn message(&self) -> &str {
256        self.message.as_str()
257    }
258}
259
260impl fmt::Display for MatrixMarketError {
261    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
262        write!(f, "Matrix Market error: ")?;
263        match self.kind() {
264            MatrixMarketErrorKind::ParsingError => {
265                write!(f, "ParsingError,")?;
266            }
267            MatrixMarketErrorKind::InvalidHeader => {
268                write!(f, "InvalidHeader,")?;
269            }
270            MatrixMarketErrorKind::EntryMismatch => {
271                write!(f, "EntryMismatch,")?;
272            }
273            MatrixMarketErrorKind::TypeMismatch => {
274                write!(f, "TypeMismatch,")?;
275            }
276            MatrixMarketErrorKind::SparseFormatError(_) => {
277                write!(f, "SparseFormatError,")?;
278            }
279            MatrixMarketErrorKind::ZeroError => {
280                write!(f, "ZeroError,")?;
281            }
282            MatrixMarketErrorKind::IOError(_) => {
283                write!(f, "IOError,")?;
284            }
285            MatrixMarketErrorKind::DiagonalError => {
286                write!(f, "DiagonalError,")?;
287            }
288            MatrixMarketErrorKind::NotLowerTriangle => {
289                write!(f, "NotLowerTriangle,")?;
290            }
291            MatrixMarketErrorKind::NonSquare => {
292                write!(f, "NonSquare,")?;
293            }
294        }
295        write!(f, " message: {}", self.message)
296    }
297}
298
299impl std::error::Error for MatrixMarketError {}
300
301impl MatrixMarketError {
302    fn from_pest_error<T>(error: pest::error::Error<T>) -> Self
303    where
304        T: fmt::Debug + std::hash::Hash + std::marker::Copy + Ord,
305    {
306        Self::from_kind_and_message(
307            MatrixMarketErrorKind::ParsingError,
308            format!("Can't parse the data.\n Error: {}", error),
309        )
310    }
311}
312
313impl From<ParseIntError> for MatrixMarketError {
314    fn from(err: ParseIntError) -> Self {
315        Self::from_kind_and_message(
316            MatrixMarketErrorKind::ParsingError,
317            format!("Can't parse data as i128.\n Error: {}", err),
318        )
319    }
320}
321
322impl From<SparseFormatError> for MatrixMarketError {
323    fn from(err: SparseFormatError) -> Self {
324        Self::from_kind_and_message(
325            MatrixMarketErrorKind::SparseFormatError(*err.kind()),
326            format!("{}", &err),
327        )
328    }
329}
330
331impl From<std::io::Error> for MatrixMarketError {
332    fn from(err: std::io::Error) -> Self {
333        Self::from_kind_and_message(
334            MatrixMarketErrorKind::IOError(err.kind()),
335            format!("{}", &err),
336        )
337    }
338}
339
340impl From<TryFromIntError> for MatrixMarketError {
341    fn from(err: TryFromIntError) -> Self {
342        Self::from_kind_and_message(
343            MatrixMarketErrorKind::TypeMismatch,
344            format!(
345                "Please consider using a larger integer type. Error message: {}",
346                &err
347            ),
348        )
349    }
350}
351
352// This is needed when calling `i128::try_from(i: i128)`
353// but it won't happen
354impl From<Infallible> for MatrixMarketError {
355    fn from(_err: Infallible) -> Self {
356        Self::from_kind_and_message(
357            MatrixMarketErrorKind::TypeMismatch,
358            format!("This won't happen"),
359        )
360    }
361}
362
363#[derive(Debug, PartialEq)]
364enum Sparsity {
365    Sparse,
366    Dense,
367}
368#[derive(Debug, PartialEq)]
369enum DataType {
370    Real,
371    Complex,
372    Pattern,
373    Integer,
374}
375#[derive(Debug, PartialEq)]
376enum StorageScheme {
377    Symmetric,
378    General,
379    Skew,
380    Hermitian,
381}
382#[derive(Debug, PartialEq)]
383struct Typecode {
384    sparsity: Sparsity,
385    datatype: DataType,
386    storagescheme: StorageScheme,
387}
388
389impl FromStr for Sparsity {
390    type Err = MatrixMarketError;
391    /// Assumes that `word` is already lower case.
392    fn from_str(word: &str) -> Result<Self, Self::Err> {
393        match word {
394            "coordinate" => Ok(Sparsity::Sparse),
395            "array" => Ok(Sparsity::Dense),
396            _ => Err(MatrixMarketError::from_kind_and_message(
397                MatrixMarketErrorKind::ParsingError,
398                format!("keyword {} is unknown", word),
399            )),
400        }
401    }
402}
403
404impl FromStr for DataType {
405    type Err = MatrixMarketError;
406    /// Assumes that `word` is already lower case.
407    fn from_str(word: &str) -> Result<Self, Self::Err> {
408        match word {
409            "real" => Ok(DataType::Real),
410            "complex" => Ok(DataType::Complex),
411            "integer" => Ok(DataType::Integer),
412            "pattern" => Ok(DataType::Pattern),
413            _ => Err(MatrixMarketError::from_kind_and_message(
414                MatrixMarketErrorKind::ParsingError,
415                format!("keyword {} is unknown", word),
416            )),
417        }
418    }
419}
420
421impl FromStr for StorageScheme {
422    type Err = MatrixMarketError;
423    /// Assumes that `word` is already lower case.
424    fn from_str(word: &str) -> Result<Self, Self::Err> {
425        match word {
426            "skew-symmetric" => Ok(StorageScheme::Skew),
427            "general" => Ok(StorageScheme::General),
428            "symmetric" => Ok(StorageScheme::Symmetric),
429            "hermitian" => Ok(StorageScheme::Hermitian),
430            _ => Err(MatrixMarketError::from_kind_and_message(
431                MatrixMarketErrorKind::ParsingError,
432                format!("keyword {} is unknown", word),
433            )),
434        }
435    }
436}
437
438/// Precheck if it's a valid header.
439///
440/// For more details, please check
441///
442/// Boisvert, Ronald F., Roldan Pozo, and Karin A. Remington.
443/// The matrix market formats: Initial design.
444/// Technical report, Applied and Computational Mathematics Division, NIST, 1996.  Section 3.
445fn typecode_precheck(tc: &Typecode) -> Result<(), MatrixMarketError> {
446    match tc {
447        Typecode {
448            datatype: DataType::Real,
449            storagescheme: StorageScheme::Hermitian,
450            ..
451        } => Err(MatrixMarketError::from_kind_and_message(
452            MatrixMarketErrorKind::InvalidHeader,
453            String::from("Real matrix can't be hermitian."),
454        )),
455        Typecode {
456            datatype: DataType::Integer,
457            storagescheme: StorageScheme::Hermitian,
458            ..
459        } => Err(MatrixMarketError::from_kind_and_message(
460            MatrixMarketErrorKind::InvalidHeader,
461            String::from("Integer matrix can't be hermitian."),
462        )),
463        Typecode {
464            datatype: DataType::Pattern,
465            storagescheme: StorageScheme::Hermitian,
466            ..
467        } => Err(MatrixMarketError::from_kind_and_message(
468            MatrixMarketErrorKind::InvalidHeader,
469            String::from("Pattern matrix can't be hermitian."),
470        )),
471        Typecode {
472            datatype: DataType::Pattern,
473            storagescheme: StorageScheme::Skew,
474            ..
475        } => Err(MatrixMarketError::from_kind_and_message(
476            MatrixMarketErrorKind::InvalidHeader,
477            String::from("Pattern matrix can't be skew-symmetric."),
478        )),
479        Typecode {
480            datatype: DataType::Pattern,
481            sparsity: Sparsity::Dense,
482            ..
483        } => Err(MatrixMarketError::from_kind_and_message(
484            MatrixMarketErrorKind::InvalidHeader,
485            String::from("Dense matrix can't be pattern matrix."),
486        )),
487        // precheck success
488        _ => Ok(()),
489    }
490}
491
492/// Scalar types supported by the matrix market parser.
493mod internal {
494    use crate::io::MatrixMarketError;
495    use na::{Complex, Scalar};
496
497    pub trait SupportedMatrixMarketScalar: Scalar {
498        /// When the matrix is an integer matrix, it will convert a [i128] number to this type.
499        fn from_i128(i: i128) -> Result<Self, MatrixMarketError>;
500        /// When matrix is a Real matrix, it will convert a [f64] number to this type.
501        fn from_f64(f: f64) -> Result<Self, MatrixMarketError>;
502        /// When matrix is a Complex matrix, it will convert a [Complex<f64>] number to this type.
503        fn from_c64(c: Complex<f64>) -> Result<Self, MatrixMarketError>;
504        /// When matrix is a Pattern matrix, it will convert a unit type [unit] to this type.
505        fn from_pattern(p: ()) -> Result<Self, MatrixMarketError>;
506        /// When matrix is a Skew-symmetric matrix, it will convert itself to its negative.
507        fn negative(self) -> Result<Self, MatrixMarketError>;
508        /// When matrix is a Hermitian matrix, it will convert itself to its conjugate.
509        fn conjugate(self) -> Result<Self, MatrixMarketError>;
510        /// Returns the name of SupportedMatrixMarketScalar, used when write the matrix
511        fn typename() -> &'static str;
512        /// Write the data self to w
513        fn write_matrix_market<W: std::fmt::Write>(&self, w: W) -> Result<(), std::fmt::Error>;
514    }
515
516    pub trait SupportedMatrixMarketExport<T: SupportedMatrixMarketScalar> {
517        /// iterate over triplets
518        fn triplet_iter(&self) -> Box<dyn Iterator<Item = (usize, usize, &T)> + '_>;
519        /// number of rows
520        fn nrows(&self) -> usize;
521        /// number of columns
522        fn ncols(&self) -> usize;
523        /// number of non-zeros
524        fn nnz(&self) -> usize;
525    }
526}
527
528/// A marker trait for supported matrix market scalars.
529///
530/// This is a sealed trait; it cannot be implemented by external crates. This is done in order to prevent leaking
531/// some of the implementation details we currently rely on. We may relax this restriction in the future.
532pub trait MatrixMarketScalar: internal::SupportedMatrixMarketScalar {}
533
534/// Implement MatrixMarketScalar for primitive integer types.
535macro_rules! mm_int_impl {
536    ($T:ty) => {
537        impl MatrixMarketScalar for $T {}
538
539        impl internal::SupportedMatrixMarketScalar for $T {
540            #[inline]
541            fn from_i128(i: i128) -> Result<Self, MatrixMarketError> {
542                Ok(Self::try_from(i)?)
543            }
544            #[inline]
545            fn from_f64(_f: f64) -> Result<Self, MatrixMarketError> {
546                Err(MatrixMarketError::from_kind_and_message(
547                    MatrixMarketErrorKind::TypeMismatch,
548                    format!("Int type can't be parsed from f64"),
549                ))
550            }
551            #[inline]
552            fn from_c64(_c: Complex<f64>) -> Result<Self, MatrixMarketError> {
553                Err(MatrixMarketError::from_kind_and_message(
554                    MatrixMarketErrorKind::TypeMismatch,
555                    format!("Int type can't be parsed from Complex<f64>"),
556                ))
557            }
558            #[inline]
559            fn from_pattern(_p: ()) -> Result<Self, MatrixMarketError> {
560                Err(MatrixMarketError::from_kind_and_message(
561                    MatrixMarketErrorKind::TypeMismatch,
562                    format!("Int type can't be parsed from ()"),
563                ))
564            }
565            #[inline]
566            fn conjugate(self) -> Result<Self, MatrixMarketError> {
567                Err(MatrixMarketError::from_kind_and_message(
568                    MatrixMarketErrorKind::TypeMismatch,
569                    format!("Int type has no conjugate"),
570                ))
571            }
572            #[inline]
573            fn negative(self) -> Result<Self, MatrixMarketError> {
574                Ok(-self)
575            }
576            #[inline]
577            fn typename() -> &'static str {
578                "integer"
579            }
580            #[inline]
581            fn write_matrix_market<W: std::fmt::Write>(
582                &self,
583                mut w: W,
584            ) -> Result<(), std::fmt::Error> {
585                write!(w, "{}", self)
586            }
587        }
588    };
589}
590/// Implement MatrixMarketScalar for primitive real types.
591macro_rules! mm_real_impl {
592    ($T:ty) => {
593        impl MatrixMarketScalar for $T {}
594
595        impl internal::SupportedMatrixMarketScalar for $T {
596            #[inline]
597            fn from_i128(_i: i128) -> Result<Self, MatrixMarketError> {
598                Err(MatrixMarketError::from_kind_and_message(
599                    MatrixMarketErrorKind::TypeMismatch,
600                    format!("real type can't be parsed from i128"),
601                ))
602            }
603            #[inline]
604            fn from_f64(f: f64) -> Result<Self, MatrixMarketError> {
605                Ok(f as Self)
606            }
607            #[inline]
608            fn from_c64(_c: Complex<f64>) -> Result<Self, MatrixMarketError> {
609                Err(MatrixMarketError::from_kind_and_message(
610                    MatrixMarketErrorKind::TypeMismatch,
611                    format!("real type can't be parsed from Complex<f64>"),
612                ))
613            }
614            #[inline]
615            fn from_pattern(_p: ()) -> Result<Self, MatrixMarketError> {
616                Err(MatrixMarketError::from_kind_and_message(
617                    MatrixMarketErrorKind::TypeMismatch,
618                    format!("real type can't be parsed from ()"),
619                ))
620            }
621            #[inline]
622            fn conjugate(self) -> Result<Self, MatrixMarketError> {
623                Err(MatrixMarketError::from_kind_and_message(
624                    MatrixMarketErrorKind::TypeMismatch,
625                    format!("real type has no conjugate"),
626                ))
627            }
628            #[inline]
629            fn negative(self) -> Result<Self, MatrixMarketError> {
630                Ok(-self)
631            }
632            #[inline]
633            fn typename() -> &'static str {
634                "real"
635            }
636            #[inline]
637            fn write_matrix_market<W: std::fmt::Write>(
638                &self,
639                mut w: W,
640            ) -> Result<(), std::fmt::Error> {
641                write!(w, "{}", self)
642            }
643        }
644    };
645}
646
647/// Implement MatrixMarketScalar for primitive complex types.
648macro_rules! mm_complex_impl {
649    ($T:ty) => {
650        impl MatrixMarketScalar for Complex<$T> {}
651
652        impl internal::SupportedMatrixMarketScalar for Complex<$T> {
653            #[inline]
654            fn from_i128(_i: i128) -> Result<Self, MatrixMarketError> {
655                Err(MatrixMarketError::from_kind_and_message(
656                    MatrixMarketErrorKind::TypeMismatch,
657                    format!("Complex type can't be parsed from i128"),
658                ))
659            }
660            #[inline]
661            fn from_f64(_f: f64) -> Result<Self, MatrixMarketError> {
662                Err(MatrixMarketError::from_kind_and_message(
663                    MatrixMarketErrorKind::TypeMismatch,
664                    format!("Complex type can't be parsed from f64"),
665                ))
666            }
667            #[inline]
668            fn from_c64(c: Complex<f64>) -> Result<Self, MatrixMarketError> {
669                Ok(Self {
670                    re: c.re as $T,
671                    im: c.im as $T,
672                })
673            }
674            #[inline]
675            fn from_pattern(_p: ()) -> Result<Self, MatrixMarketError> {
676                Err(MatrixMarketError::from_kind_and_message(
677                    MatrixMarketErrorKind::TypeMismatch,
678                    format!("Complex type can't be parsed from ()"),
679                ))
680            }
681            #[inline]
682            fn conjugate(self) -> Result<Self, MatrixMarketError> {
683                Ok(self.conj())
684            }
685            #[inline]
686            fn negative(self) -> Result<Self, MatrixMarketError> {
687                Ok(-self)
688            }
689            #[inline]
690            fn typename() -> &'static str {
691                "complex"
692            }
693            #[inline]
694            fn write_matrix_market<W: std::fmt::Write>(
695                &self,
696                mut w: W,
697            ) -> Result<(), std::fmt::Error> {
698                write!(w, "{} {}", self.re, self.im)
699            }
700        }
701    };
702}
703/// Implement MatrixMarketScalar for primitive unit types.
704macro_rules! mm_pattern_impl {
705    ($T:ty) => {
706        impl MatrixMarketScalar for $T {}
707
708        impl internal::SupportedMatrixMarketScalar for $T {
709            #[inline]
710            fn from_i128(_i: i128) -> Result<Self, MatrixMarketError> {
711                Err(MatrixMarketError::from_kind_and_message(
712                    MatrixMarketErrorKind::TypeMismatch,
713                    format!("Pattern type can't be parsed from i128"),
714                ))
715            }
716            #[inline]
717            fn from_f64(_f: f64) -> Result<Self, MatrixMarketError> {
718                Err(MatrixMarketError::from_kind_and_message(
719                    MatrixMarketErrorKind::TypeMismatch,
720                    format!("Pattern type can't be parsed from f64"),
721                ))
722            }
723            #[inline]
724            fn from_c64(_c: Complex<f64>) -> Result<Self, MatrixMarketError> {
725                Err(MatrixMarketError::from_kind_and_message(
726                    MatrixMarketErrorKind::TypeMismatch,
727                    format!("Pattern type can't be parsed from Complex<f64>"),
728                ))
729            }
730            #[inline]
731            fn from_pattern(p: ()) -> Result<Self, MatrixMarketError> {
732                Ok(p)
733            }
734
735            #[inline]
736            fn conjugate(self) -> Result<Self, MatrixMarketError> {
737                Err(MatrixMarketError::from_kind_and_message(
738                    MatrixMarketErrorKind::TypeMismatch,
739                    format!("Pattern type has no conjugate"),
740                ))
741            }
742            #[inline]
743            fn negative(self) -> Result<Self, MatrixMarketError> {
744                Err(MatrixMarketError::from_kind_and_message(
745                    MatrixMarketErrorKind::TypeMismatch,
746                    format!("Pattern type has no negative"),
747                ))
748            }
749            #[inline]
750            fn typename() -> &'static str {
751                "pattern"
752            }
753            #[inline]
754            fn write_matrix_market<W: std::fmt::Write>(
755                &self,
756                mut _w: W,
757            ) -> Result<(), std::fmt::Error> {
758                Ok(())
759            }
760        }
761    };
762}
763
764mm_int_impl!(i8);
765mm_int_impl!(i16);
766mm_int_impl!(i32);
767mm_int_impl!(i64);
768mm_int_impl!(i128);
769
770mm_real_impl!(f32);
771mm_real_impl!(f64);
772
773mm_complex_impl!(f32);
774mm_complex_impl!(f64);
775
776mm_pattern_impl!(());
777
778/// A marker trait for sparse matrix types that can be exported to the matrix market format.
779///
780/// This is a sealed trait; it cannot be implemented by external crates. This is done in order to prevent leaking
781/// some of the implementation details we currently rely on. We may relax this restriction in the future.
782pub trait MatrixMarketExport<T: MatrixMarketScalar>:
783    internal::SupportedMatrixMarketExport<T>
784{
785}
786
787macro_rules! mm_matrix_impl {
788    ($T_MATRIX:ty) => {
789        impl<T: MatrixMarketScalar> MatrixMarketExport<T> for $T_MATRIX {}
790
791        impl<T: internal::SupportedMatrixMarketScalar> internal::SupportedMatrixMarketExport<T>
792            for $T_MATRIX
793        {
794            #[inline]
795            fn triplet_iter(&self) -> Box<dyn Iterator<Item = (usize, usize, &T)> + '_> {
796                Box::new(self.triplet_iter())
797            }
798            #[inline]
799            fn nrows(&self) -> usize {
800                self.nrows()
801            }
802            #[inline]
803            fn ncols(&self) -> usize {
804                self.ncols()
805            }
806            #[inline]
807            fn nnz(&self) -> usize {
808                self.nnz()
809            }
810        }
811    };
812}
813
814mm_matrix_impl!(CooMatrix<T>);
815mm_matrix_impl!(CsrMatrix<T>);
816mm_matrix_impl!(CscMatrix<T>);
817
818#[derive(Parser)]
819#[grammar = "io/matrix_market.pest"]
820struct MatrixMarketParser;
821
822/// Parses a Matrix Market file at the given path as a `CooMatrix`.
823///
824/// The matrix market format specification does not clarify whether duplicate entries are allowed. Our importer
825/// assumes that this is permitted and produces a `CooMatrix` with possibly duplicate entries.
826///
827/// **Note**: A current restriction of the importer is that you must use a compatible scalar type when importing.
828/// For example, in order to import a matrix stored as `integer` in the matrix market format, you must
829/// import it as an integer matrix, otherwise a [TypeMismatch](MatrixMarketErrorKind::TypeMismatch) error
830/// will be returned. This restriction may be lifted in the future, and is
831/// tracked by issue [#1038](https://github.com/dimforge/nalgebra/issues/1038).
832///
833/// Errors
834/// --------
835///
836/// See [MatrixMarketErrorKind] for a list of possible error conditions.
837///
838/// Examples
839/// --------
840/// ```no_run
841/// use nalgebra_sparse::io::load_coo_from_matrix_market_file;
842/// // Use e.g. `i32` for integer matrices
843/// let matrix = load_coo_from_matrix_market_file::<i32,_>("path/to/matrix.mtx").unwrap();
844/// ```
845pub fn load_coo_from_matrix_market_file<T, P: AsRef<Path>>(
846    path: P,
847) -> Result<CooMatrix<T>, MatrixMarketError>
848where
849    T: MatrixMarketScalar,
850{
851    let file = fs::read_to_string(path)?;
852    load_coo_from_matrix_market_str(&file)
853}
854
855/// Parses a Matrix Market file described by the given string as a `CooMatrix`.
856///
857/// See [load_coo_from_matrix_market_file] for more information.
858///
859/// Errors
860/// --------
861///
862/// See [MatrixMarketErrorKind] for a list of possible error conditions.
863///
864/// Examples
865/// --------
866/// ```
867/// use nalgebra_sparse::io::load_coo_from_matrix_market_str;
868/// let str = r#"
869/// %%matrixmarket matrix coordinate integer general
870/// 5 4 2
871/// 1 1 10
872/// 2 3 5
873/// "#;
874/// // Use e.g. `i32` for integer matrices
875/// let matrix = load_coo_from_matrix_market_str::<i32>(str).unwrap();
876/// ```
877pub fn load_coo_from_matrix_market_str<T>(data: &str) -> Result<CooMatrix<T>, MatrixMarketError>
878where
879    T: MatrixMarketScalar,
880{
881    // unwrap() in this function are guaranteed by parsing the data
882    let file = MatrixMarketParser::parse(Rule::Document, data)
883        .map_err(MatrixMarketError::from_pest_error)?
884        .next()
885        .unwrap();
886
887    let mut rows: Vec<usize> = Vec::new();
888    let mut cols: Vec<usize> = Vec::new();
889    let mut data: Vec<T> = Vec::new();
890    let mut lines = file.into_inner();
891
892    let header_line = lines.next().unwrap();
893    let header_type = parse_header(&mut header_line.into_inner());
894    typecode_precheck(&header_type)?;
895
896    let shape_line = lines.next().unwrap();
897    // shape here is number of rows, columns, non-zeros
898    let shape: (usize, usize, usize);
899    match header_type.sparsity {
900        Sparsity::Sparse => {
901            shape = parse_sparse_shape(&mut shape_line.into_inner(), &header_type.storagescheme)?;
902        }
903        Sparsity::Dense => {
904            shape = parse_dense_shape(&mut shape_line.into_inner(), &header_type.storagescheme)?;
905        }
906    }
907
908    // used when constructing dense matrix.
909    // If it's sparse matrix, it has no effect.
910    let mut current_dense_coordinate: (usize, usize) = (0, 0);
911    if header_type.storagescheme == StorageScheme::Skew {
912        // for skew dense matrix, the first element starts from (1,0)
913        current_dense_coordinate = (1, 0);
914    }
915    // count how many entries in the matrix data
916    let count = lines.clone().count();
917    if count != shape.2 {
918        return Err(MatrixMarketError::from_kind_and_message(
919            MatrixMarketErrorKind::EntryMismatch,
920            format!(
921                "{} entries required for the matrix, but {} was provided",
922                shape.2, count,
923            ),
924        ));
925    }
926
927    for data_line in lines {
928        let entry: (usize, usize, T);
929        match header_type {
930            Typecode {
931                sparsity: Sparsity::Sparse,
932                datatype: DataType::Real,
933                ..
934            } => {
935                entry = parse_sparse_real::<T>(&mut data_line.into_inner())?;
936            }
937            Typecode {
938                sparsity: Sparsity::Sparse,
939                datatype: DataType::Integer,
940                ..
941            } => {
942                entry = parse_sparse_int::<T>(&mut data_line.into_inner())?;
943            }
944            Typecode {
945                sparsity: Sparsity::Sparse,
946                datatype: DataType::Pattern,
947                ..
948            } => {
949                entry = parse_sparse_pattern::<T>(&mut data_line.into_inner())?;
950            }
951            Typecode {
952                sparsity: Sparsity::Sparse,
953                datatype: DataType::Complex,
954                ..
955            } => {
956                entry = parse_sparse_complex::<T>(&mut data_line.into_inner())?;
957            }
958            Typecode {
959                sparsity: Sparsity::Dense,
960                datatype: DataType::Complex,
961                ..
962            } => {
963                entry = (
964                    current_dense_coordinate.0,
965                    current_dense_coordinate.1,
966                    parse_dense_complex::<T>(&mut data_line.into_inner())?,
967                );
968                next_dense_coordinate(
969                    &mut current_dense_coordinate,
970                    shape,
971                    &header_type.storagescheme,
972                );
973            }
974            Typecode {
975                sparsity: Sparsity::Dense,
976                datatype: DataType::Real,
977                ..
978            } => {
979                entry = (
980                    current_dense_coordinate.0,
981                    current_dense_coordinate.1,
982                    parse_dense_real::<T>(&mut data_line.into_inner())?,
983                );
984
985                next_dense_coordinate(
986                    &mut current_dense_coordinate,
987                    shape,
988                    &header_type.storagescheme,
989                );
990            }
991            Typecode {
992                sparsity: Sparsity::Dense,
993                datatype: DataType::Integer,
994                ..
995            } => {
996                entry = (
997                    current_dense_coordinate.0,
998                    current_dense_coordinate.1,
999                    parse_dense_int::<T>(&mut data_line.into_inner())?,
1000                );
1001                next_dense_coordinate(
1002                    &mut current_dense_coordinate,
1003                    shape,
1004                    &header_type.storagescheme,
1005                );
1006            }
1007            _ => {
1008                // it shouldn't happen here, because dense matrix can't be pattern. And it will give InvalidHeader error beforehand.
1009                entry = (1, 1, T::from_i128(1)?)
1010            }
1011        }
1012
1013        let (r, c, d) = entry;
1014
1015        match header_type.storagescheme {
1016            StorageScheme::General => {
1017                rows.push(r);
1018                cols.push(c);
1019                data.push(d);
1020            }
1021            StorageScheme::Symmetric => {
1022                check_lower_triangle(r, c)?;
1023                rows.push(r);
1024                cols.push(c);
1025                data.push(d.clone());
1026                // don't need to add twice if the element in on diagonal
1027                if r != c {
1028                    rows.push(c);
1029                    cols.push(r);
1030                    data.push(d);
1031                }
1032            }
1033            StorageScheme::Skew => {
1034                check_lower_triangle(r, c)?;
1035                rows.push(r);
1036                cols.push(c);
1037                data.push(d.clone());
1038                // skew-symmetric matrix shouldn't have diagonal element
1039                if r == c {
1040                    return Err(MatrixMarketError::from_kind_and_message(
1041                        MatrixMarketErrorKind::DiagonalError,
1042                        format!(
1043                            "There is a diagonal element in skew matrix, in row(and column) {}",
1044                            r + 1
1045                        ),
1046                    ));
1047                }
1048                rows.push(c);
1049                cols.push(r);
1050                data.push(d.negative()?);
1051            }
1052            StorageScheme::Hermitian => {
1053                check_lower_triangle(r, c)?;
1054                rows.push(r);
1055                cols.push(c);
1056                data.push(d.clone());
1057
1058                if r == c && d != d.clone().conjugate()? {
1059                    return Err(MatrixMarketError::from_kind_and_message(
1060                        MatrixMarketErrorKind::DiagonalError,
1061                        format!(
1062                            "There is a diagonal element in hermitian matrix, which is not a real number, in row(and column) {}",
1063                            r + 1
1064                        ),
1065                    ));
1066                }
1067                // don't need to add twice if the element in on diagonal
1068                if r != c {
1069                    rows.push(c);
1070                    cols.push(r);
1071                    data.push(d.conjugate()?);
1072                }
1073            }
1074        }
1075    }
1076    Ok(CooMatrix::try_from_triplets(
1077        shape.0, shape.1, rows, cols, data,
1078    )?)
1079}
1080
1081#[inline]
1082/// do a quick check it the entry is in the lower triangle part of the matrix
1083fn check_lower_triangle(r: usize, c: usize) -> Result<(), MatrixMarketError> {
1084    if c > r {
1085        return Err(MatrixMarketError::from_kind_and_message(
1086            MatrixMarketErrorKind::NotLowerTriangle,
1087            format!(
1088                "Entry: row {} col {} should be put into lower triangle",
1089                r, c
1090            ),
1091        ));
1092    }
1093    Ok(())
1094}
1095
1096#[inline]
1097/// Parse a pest structure to a Typecode of the matrix.
1098fn parse_header(inner: &mut Pairs<'_, Rule>) -> Typecode {
1099    // unwrap() in this function are guaranteed by parsing the data
1100    Typecode {
1101        sparsity: inner
1102            .next()
1103            .unwrap()
1104            .as_str()
1105            .to_ascii_lowercase()
1106            .parse::<Sparsity>()
1107            .unwrap(),
1108        datatype: inner
1109            .next()
1110            .unwrap()
1111            .as_str()
1112            .to_ascii_lowercase()
1113            .parse::<DataType>()
1114            .unwrap(),
1115        storagescheme: inner
1116            .next()
1117            .unwrap()
1118            .as_str()
1119            .to_ascii_lowercase()
1120            .parse::<StorageScheme>()
1121            .unwrap(),
1122    }
1123}
1124
1125// Parse shape starts here-------------------------------------------------
1126
1127/// Parse a pest structure to sparse shape information, including 3 int, which are number of rows, cols and non-zeros.
1128fn parse_sparse_shape(
1129    inner: &mut Pairs<'_, Rule>,
1130    storagescheme: &StorageScheme,
1131) -> Result<(usize, usize, usize), MatrixMarketError> {
1132    // unwrap() in this function are guaranteed by parsing the data
1133    let shape_inner = inner.next().unwrap();
1134    if shape_inner.as_rule() != Rule::SparseShape {
1135        return Err(MatrixMarketError::from_kind_and_message(MatrixMarketErrorKind::ParsingError,format!("
1136        Shape shape line requires 3 int numbers as number of rows, columns and non-zeros, but line {} was provided here.
1137        ",shape_inner.as_str())));
1138    }
1139
1140    let mut inner = shape_inner.into_inner();
1141
1142    let r = inner.next().unwrap().as_str().parse::<usize>().unwrap();
1143    let c = inner.next().unwrap().as_str().parse::<usize>().unwrap();
1144    let nnz = inner.next().unwrap().as_str().parse::<usize>().unwrap();
1145
1146    // check for square matrix, when it's not a general matrix
1147    if *storagescheme != StorageScheme::General && r != c {
1148        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)));
1149    }
1150
1151    Ok((r, c, nnz))
1152}
1153
1154/// Parse a pest structure to dense shape information, including 2 int, which are number of rows, cols.
1155fn parse_dense_shape(
1156    inner: &mut Pairs<'_, Rule>,
1157    storagescheme: &StorageScheme,
1158) -> Result<(usize, usize, usize), MatrixMarketError> {
1159    // unwrap() in this function are guaranteed by parsing the data
1160    let shape_inner = inner.next().unwrap();
1161    if shape_inner.as_rule() != Rule::DenseShape {
1162        return Err(MatrixMarketError::from_kind_and_message(MatrixMarketErrorKind::ParsingError,format!("
1163        Shape shape line requires 2 int numbers as number of rows, columns, but line {} was provided here.
1164        ",shape_inner.as_str())));
1165    }
1166
1167    let mut inner = shape_inner.into_inner();
1168    let r = inner.next().unwrap().as_str().parse::<usize>().unwrap();
1169    let c = inner.next().unwrap().as_str().parse::<usize>().unwrap();
1170
1171    // check for square matrix, when it's not a general matrix
1172    if *storagescheme != StorageScheme::General && r != c {
1173        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)));
1174    }
1175
1176    let n: usize;
1177    // Calculate the number of entries in the dense matrix
1178    match storagescheme {
1179        StorageScheme::General => {
1180            // general matrix should contain r*c entries
1181            n = r * c;
1182        }
1183        StorageScheme::Symmetric | StorageScheme::Hermitian => {
1184            // it must be square matrix, so r==c is true here
1185            // Symmetric or Hermitian should contain 1+2...+r  = r*(r+1)/2 entries
1186            n = r * (r + 1) / 2;
1187        }
1188        StorageScheme::Skew => {
1189            // it must be square matrix, so r==c is true here
1190            // Skew-Symmetric should contain 1+2...+r-1  = r*(r-1)/2 entries
1191            n = r * (r - 1) / 2;
1192        }
1193    }
1194
1195    Ok((r, c, n))
1196}
1197
1198// Parse shape ends here-------------------------------------------------
1199
1200// Parse entry starts here-------------------------------------------------
1201
1202/// Parse a pest structure to sparse real entry, including 2 int, which are number of rows, cols, and a real number as data
1203fn parse_sparse_real<T>(inner: &mut Pairs<'_, Rule>) -> Result<(usize, usize, T), MatrixMarketError>
1204where
1205    T: MatrixMarketScalar,
1206{
1207    // unwrap() in this function are guaranteed by parsing the data
1208    let entry_inner = inner.next().unwrap();
1209    if entry_inner.as_rule() != Rule::SparseReal {
1210        return Err(MatrixMarketError::from_kind_and_message(MatrixMarketErrorKind::ParsingError,format!("
1211        Spare real matrix requires 2 int number as coordinates and 1 real number as data, but line {} was provided.  
1212        ",entry_inner.as_str() )));
1213    }
1214
1215    let mut inner = entry_inner.into_inner();
1216    let (r, c) = parse_sparse_coordinate(&mut inner)?;
1217    let d = inner.next().unwrap().as_str().parse::<f64>().unwrap();
1218    Ok((r, c, T::from_f64(d)?))
1219}
1220
1221/// Parse a pest structure to sparse integer entry, including 2 int, which are number of rows, cols, and a int number as data
1222fn parse_sparse_int<T>(inner: &mut Pairs<'_, Rule>) -> Result<(usize, usize, T), MatrixMarketError>
1223where
1224    T: MatrixMarketScalar,
1225{
1226    // unwrap() in this function are guaranteed by parsing the data
1227    let entry_inner = inner.next().unwrap();
1228    // Because integer numbers can also be parsed as float numbers, it will be checked again in `parse::<i128>()?`
1229    if entry_inner.as_rule() != Rule::SparseReal {
1230        return Err(MatrixMarketError::from_kind_and_message(
1231            MatrixMarketErrorKind::ParsingError,
1232            format!(
1233                "
1234        Spare real matrix requires 3 int number as coordinates and data, but line {} was provided.  
1235        ",
1236                entry_inner.as_str()
1237            ),
1238        ));
1239    }
1240    let mut inner = entry_inner.into_inner();
1241    let (r, c) = parse_sparse_coordinate(&mut inner)?;
1242    // Here to guarantee it is an integer number
1243    let d = inner.next().unwrap().as_str().parse::<i128>()?;
1244    Ok((r, c, T::from_i128(d)?))
1245}
1246
1247/// Parse a pest structure to sparse pattern entry, including 2 int, which are number of rows, cols
1248fn parse_sparse_pattern<T>(
1249    inner: &mut Pairs<'_, Rule>,
1250) -> Result<(usize, usize, T), MatrixMarketError>
1251where
1252    T: MatrixMarketScalar,
1253{
1254    // unwrap() in this function are guaranteed by parsing the data
1255    let entry_inner = inner.next().unwrap();
1256    if entry_inner.as_rule() != Rule::SparsePattern {
1257        return Err(MatrixMarketError::from_kind_and_message(
1258            MatrixMarketErrorKind::ParsingError,
1259            format!(
1260                "
1261        Spare real matrix requires 2 int number as coordinates, but line {} was provided.  
1262        ",
1263                entry_inner.as_str()
1264            ),
1265        ));
1266    }
1267    let mut inner = entry_inner.into_inner();
1268    let (r, c) = parse_sparse_coordinate(&mut inner)?;
1269    Ok((r, c, T::from_pattern(())?))
1270}
1271
1272/// Parse a pest structure to sparse complex entry, including 2 int, which are number of rows, cols, and 2 real number as complex data
1273fn parse_sparse_complex<T>(
1274    inner: &mut Pairs<'_, Rule>,
1275) -> Result<(usize, usize, T), MatrixMarketError>
1276where
1277    T: MatrixMarketScalar,
1278{
1279    // unwrap() in this function are guaranteed by parsing the data
1280    let entry_inner = inner.next().unwrap();
1281    if entry_inner.as_rule() != Rule::SparseComplex {
1282        return Err(MatrixMarketError::from_kind_and_message(MatrixMarketErrorKind::ParsingError,format!("
1283        Spare real matrix requires 2 int number as coordinates and 2 real number as complex data, but line {} was provided.  
1284        ",entry_inner.as_str() )));
1285    }
1286    let mut inner = entry_inner.into_inner();
1287    let (r, c) = parse_sparse_coordinate(&mut inner)?;
1288    let real = inner.next().unwrap().as_str().parse::<f64>().unwrap();
1289    let imag = inner.next().unwrap().as_str().parse::<f64>().unwrap();
1290    let complex = Complex::<f64>::new(real, imag);
1291    Ok((r, c, T::from_c64(complex)?))
1292}
1293
1294/// Parse a pest structure to dense real entry, including a real number as data
1295fn parse_dense_real<T>(inner: &mut Pairs<'_, Rule>) -> Result<T, MatrixMarketError>
1296where
1297    T: MatrixMarketScalar,
1298{
1299    // unwrap() in this function are guaranteed by parsing the data
1300    let entry_inner = inner.next().unwrap();
1301    if entry_inner.as_rule() != Rule::DenseReal {
1302        return Err(MatrixMarketError::from_kind_and_message(
1303            MatrixMarketErrorKind::ParsingError,
1304            format!(
1305                "
1306        Dense real matrix requires 1 real number as data, but line {} was provided.  
1307        ",
1308                entry_inner.as_str()
1309            ),
1310        ));
1311    }
1312    let mut inner = entry_inner.into_inner();
1313    let d = inner.next().unwrap().as_str().parse::<f64>().unwrap();
1314    Ok(T::from_f64(d)?)
1315}
1316
1317/// Parse a pest structure to dense integer entry, including a integer number as data
1318fn parse_dense_int<T>(inner: &mut Pairs<'_, Rule>) -> Result<T, MatrixMarketError>
1319where
1320    T: MatrixMarketScalar,
1321{
1322    // unwrap() in this function are guaranteed by parsing the data
1323    let entry_inner = inner.next().unwrap();
1324    // Because integer numbers can also be parsed as float numbers, it will be checked again in `parse::<i128>()?`
1325    if entry_inner.as_rule() != Rule::DenseReal {
1326        return Err(MatrixMarketError::from_kind_and_message(
1327            MatrixMarketErrorKind::ParsingError,
1328            format!(
1329                "
1330        Dense real matrix requires 1 int number as data, but line {} was provided.  
1331        ",
1332                entry_inner.as_str()
1333            ),
1334        ));
1335    }
1336    let mut inner = entry_inner.into_inner();
1337    // Here to guarantee it is an integer number
1338    let d = inner.next().unwrap().as_str().parse::<i128>()?;
1339    Ok(T::from_i128(d)?)
1340}
1341
1342/// Parse a pest structure to dense complex entry, including 2 real number as complex data
1343fn parse_dense_complex<T>(inner: &mut Pairs<'_, Rule>) -> Result<T, MatrixMarketError>
1344where
1345    T: MatrixMarketScalar,
1346{
1347    // unwrap() in this function are guaranteed by parsing the data
1348    let entry_inner = inner.next().unwrap();
1349    // Note: theoretically, 2 positive integers could also become the complex number,
1350    // but it would be parsed as SparsePattern, because SparsePattern has higher priority.
1351    // But DenseComplex can't have higher priority,
1352    // because, it's more often to deal with "normal" SparsePattern, rather than "unnormal" DenseComplex
1353    if entry_inner.as_rule() != Rule::DenseComplex && entry_inner.as_rule() != Rule::SparsePattern {
1354        return Err(MatrixMarketError::from_kind_and_message(
1355            MatrixMarketErrorKind::ParsingError,
1356            format!(
1357                "
1358        Dense real matrix requires 2 real number as complex data, but line {} was provided.  
1359        ",
1360                entry_inner.as_str()
1361            ),
1362        ));
1363    }
1364    let mut inner = entry_inner.into_inner();
1365    let real = inner.next().unwrap().as_str().parse::<f64>().unwrap();
1366    let imag = inner.next().unwrap().as_str().parse::<f64>().unwrap();
1367    let complex = Complex::<f64>::new(real, imag);
1368    Ok(T::from_c64(complex)?)
1369}
1370
1371// Parse entry ends here-------------------------------------------------
1372
1373/// Parse the coordinates information used for sparse matrix
1374fn parse_sparse_coordinate(
1375    inner: &mut Pairs<'_, Rule>,
1376) -> Result<(usize, usize), MatrixMarketError> {
1377    // unwrap() in this function are guaranteed by parsing the data
1378    let r = inner.next().unwrap().as_str().parse::<usize>().unwrap();
1379    let c = inner.next().unwrap().as_str().parse::<usize>().unwrap();
1380    if r * c == 0 {
1381        return Err(MatrixMarketError::from_kind_and_message(
1382            MatrixMarketErrorKind::ZeroError,
1383            String::from("The data has to be one-indexed"),
1384        ));
1385    }
1386    // The coordinates in matrix market is one-based, but in CooMatrix is zero-based.
1387    Ok((r - 1, c - 1))
1388}
1389
1390/// Calculate the next coordinates used for dense matrix
1391fn next_dense_coordinate(
1392    current_dense_coordinate: &mut (usize, usize),
1393    shape: (usize, usize, usize),
1394    storagescheme: &StorageScheme,
1395) {
1396    // matrix market is column based format.
1397    // so it follows the order (0,0) -> (1,0) -> ... -> (row, 0) -> (0,1) -> ... ->(row,col)
1398    // current_dense_coordinate is (row, column)
1399    match storagescheme {
1400        StorageScheme::General => {
1401            if current_dense_coordinate.0 < shape.0 - 1 {
1402                current_dense_coordinate.0 += 1
1403            } else {
1404                // jump to next column, reset row to 1, column add 1
1405                current_dense_coordinate.0 = 0;
1406                current_dense_coordinate.1 += 1;
1407            }
1408        }
1409        StorageScheme::Symmetric | StorageScheme::Hermitian => {
1410            if current_dense_coordinate.0 < shape.0 - 1 {
1411                current_dense_coordinate.0 += 1
1412            } else {
1413                // jump to next column, column add 1, then set row equals to current column
1414                // for example   (0,0) -> (1,0) -> ... -> (row, 0) -> (1,1) -> ...
1415                current_dense_coordinate.1 += 1;
1416                current_dense_coordinate.0 = current_dense_coordinate.1;
1417            }
1418        }
1419        StorageScheme::Skew => {
1420            if current_dense_coordinate.0 < shape.0 - 1 {
1421                current_dense_coordinate.0 += 1;
1422            } else {
1423                // jump to next column, set row equals to current column, then column add 1
1424                // skew matrix doesn't have element on diagonal
1425                // for example  (1,0) -> (2,0) -> ... -> (row, 0) -> (2,1) -> ...
1426                current_dense_coordinate.1 += 1;
1427                current_dense_coordinate.0 = current_dense_coordinate.1 + 1;
1428            }
1429        }
1430    }
1431}
1432
1433/// Save a sparse matrix as a Matrix Market format string.
1434///
1435/// The exporter only writes the matrix into `coordinate` and `general` format.
1436///
1437///
1438/// Examples
1439/// --------
1440/// ```
1441/// # use nalgebra_sparse::CooMatrix;
1442/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1443/// use nalgebra_sparse::io::{save_to_matrix_market_str};
1444/// let expected_str = r#"%%matrixmarket matrix coordinate integer general
1445/// % matrixmarket file generated by nalgebra-sparse.
1446/// 5 4 2
1447/// 1 1 10
1448/// 2 3 5
1449/// "#;
1450/// let row_indices = vec![0,1];
1451/// let col_indices = vec![0,2];
1452/// let values = vec![10,5];
1453/// let matrix = CooMatrix::try_from_triplets(5,4,row_indices,col_indices,values)?;
1454/// let generated_matrixmarket_str = save_to_matrix_market_str(&matrix);
1455/// assert_eq!(expected_str,generated_matrixmarket_str);
1456/// # Ok(()) }
1457/// ```
1458pub fn save_to_matrix_market_str<T, S>(sparse_matrix: &S) -> String
1459where
1460    T: MatrixMarketScalar,
1461    S: MatrixMarketExport<T>,
1462{
1463    let mut bytes = Vec::<u8>::new();
1464    // This will call impl<A: Allocator> Write for Vec<u8, A>
1465    // The vector will grow as needed.
1466    // So, unwrap here won't cause any issue.
1467    save_to_matrix_market(&mut bytes, sparse_matrix).unwrap();
1468
1469    String::from_utf8(bytes)
1470        .expect("Unexpected non UTF-8 data was generated when export to matrix market string")
1471}
1472
1473/// Save a sparse matrix to a Matrix Market format file.
1474///
1475/// The exporter only saves the matrix with the `coordinate` and `general` matrix market formats.
1476///
1477/// Errors
1478/// --------
1479///
1480/// See [MatrixMarketErrorKind] for a list of possible error conditions.
1481///
1482/// Examples
1483/// --------
1484/// ```no_run
1485/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1486/// use nalgebra_sparse::io::{save_to_matrix_market_file,load_coo_from_matrix_market_str};
1487/// let str = r#"
1488/// %%matrixmarket matrix coordinate integer general
1489/// 5 4 2
1490/// 1 1 10
1491/// 2 3 5
1492/// "#;
1493/// let matrix = load_coo_from_matrix_market_str::<i32>(&str)?;
1494/// save_to_matrix_market_file(&matrix,"path/to/matrix.mtx")?;
1495/// # Ok(()) }
1496/// ```
1497pub fn save_to_matrix_market_file<T, S, P>(sparse_matrix: &S, path: P) -> Result<(), std::io::Error>
1498where
1499    T: MatrixMarketScalar,
1500    S: MatrixMarketExport<T>,
1501    P: AsRef<Path>,
1502{
1503    let file = File::create(path)?;
1504    let mut file = BufWriter::new(file);
1505    save_to_matrix_market(&mut file, sparse_matrix)?;
1506    // Quote from BufWriter doc.
1507    // > It is critical to call flush before BufWriter<W> is dropped. Though dropping will attempt to flush the contents of the buffer, any errors that happen in the process of dropping will be ignored. Calling flush ensures that the buffer is empty and thus dropping will not even attempt file operations.
1508    file.flush()
1509        .expect("Unexpected error when flushing the buffer data to File");
1510    Ok(())
1511}
1512
1513/// Save a sparse matrix to an [std::io::Write] instance.
1514///
1515/// This is the most general save functionality. See [save_to_matrix_market_file] and
1516/// [save_to_matrix_market_str] for higher-level functionality.
1517pub fn save_to_matrix_market<T, S, W>(mut w: W, sparse_matrix: &S) -> Result<(), std::io::Error>
1518where
1519    T: MatrixMarketScalar,
1520    S: MatrixMarketExport<T>,
1521    W: Write,
1522{
1523    // write header
1524    writeln!(
1525        w,
1526        "%%matrixmarket matrix coordinate {} general",
1527        T::typename()
1528    )?;
1529
1530    //write comment
1531    writeln!(w, "% matrixmarket file generated by nalgebra-sparse.")?;
1532
1533    // write shape information
1534    writeln!(
1535        w,
1536        "{} {} {}",
1537        sparse_matrix.nrows(),
1538        sparse_matrix.ncols(),
1539        sparse_matrix.nnz()
1540    )?;
1541
1542    //write triplets
1543    let mut buffer = String::new();
1544    for (r, c, d) in sparse_matrix.triplet_iter() {
1545        buffer.clear();
1546        d.write_matrix_market(&mut buffer)
1547            .expect("Unexpected format error was generated when write to String");
1548        writeln!(w, "{} {} {}", r + 1, c + 1, buffer)?;
1549    }
1550    Ok(())
1551}