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::Parser;
9use pest::iterators::Pairs;
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(
1149            MatrixMarketErrorKind::NonSquare,
1150            format!(
1151                "(Skew-)Symmetric or hermitian matrix should be square matrix, but it has dimension {} and {}",
1152                r, c
1153            ),
1154        ));
1155    }
1156
1157    Ok((r, c, nnz))
1158}
1159
1160/// Parse a pest structure to dense shape information, including 2 int, which are number of rows, cols.
1161fn parse_dense_shape(
1162    inner: &mut Pairs<'_, Rule>,
1163    storagescheme: &StorageScheme,
1164) -> Result<(usize, usize, usize), MatrixMarketError> {
1165    // unwrap() in this function are guaranteed by parsing the data
1166    let shape_inner = inner.next().unwrap();
1167    if shape_inner.as_rule() != Rule::DenseShape {
1168        return Err(MatrixMarketError::from_kind_and_message(MatrixMarketErrorKind::ParsingError,format!("
1169        Shape shape line requires 2 int numbers as number of rows, columns, but line {} was provided here.
1170        ",shape_inner.as_str())));
1171    }
1172
1173    let mut inner = shape_inner.into_inner();
1174    let r = inner.next().unwrap().as_str().parse::<usize>().unwrap();
1175    let c = inner.next().unwrap().as_str().parse::<usize>().unwrap();
1176
1177    // check for square matrix, when it's not a general matrix
1178    if *storagescheme != StorageScheme::General && r != c {
1179        return Err(MatrixMarketError::from_kind_and_message(
1180            MatrixMarketErrorKind::NonSquare,
1181            format!(
1182                "(Skew-)Symmetric or hermitian matrix should be square matrix, but it has dimension {} and {}",
1183                r, c
1184            ),
1185        ));
1186    }
1187
1188    let n: usize;
1189    // Calculate the number of entries in the dense matrix
1190    match storagescheme {
1191        StorageScheme::General => {
1192            // general matrix should contain r*c entries
1193            n = r * c;
1194        }
1195        StorageScheme::Symmetric | StorageScheme::Hermitian => {
1196            // it must be square matrix, so r==c is true here
1197            // Symmetric or Hermitian should contain 1+2...+r  = r*(r+1)/2 entries
1198            n = r * (r + 1) / 2;
1199        }
1200        StorageScheme::Skew => {
1201            // it must be square matrix, so r==c is true here
1202            // Skew-Symmetric should contain 1+2...+r-1  = r*(r-1)/2 entries
1203            n = r * (r - 1) / 2;
1204        }
1205    }
1206
1207    Ok((r, c, n))
1208}
1209
1210// Parse shape ends here-------------------------------------------------
1211
1212// Parse entry starts here-------------------------------------------------
1213
1214/// Parse a pest structure to sparse real entry, including 2 int, which are number of rows, cols, and a real number as data
1215fn parse_sparse_real<T>(inner: &mut Pairs<'_, Rule>) -> Result<(usize, usize, T), MatrixMarketError>
1216where
1217    T: MatrixMarketScalar,
1218{
1219    // unwrap() in this function are guaranteed by parsing the data
1220    let entry_inner = inner.next().unwrap();
1221    if entry_inner.as_rule() != Rule::SparseReal {
1222        return Err(MatrixMarketError::from_kind_and_message(MatrixMarketErrorKind::ParsingError,format!("
1223        Spare real matrix requires 2 int number as coordinates and 1 real number as data, but line {} was provided.  
1224        ",entry_inner.as_str() )));
1225    }
1226
1227    let mut inner = entry_inner.into_inner();
1228    let (r, c) = parse_sparse_coordinate(&mut inner)?;
1229    let d = inner.next().unwrap().as_str().parse::<f64>().unwrap();
1230    Ok((r, c, T::from_f64(d)?))
1231}
1232
1233/// Parse a pest structure to sparse integer entry, including 2 int, which are number of rows, cols, and a int number as data
1234fn parse_sparse_int<T>(inner: &mut Pairs<'_, Rule>) -> Result<(usize, usize, T), MatrixMarketError>
1235where
1236    T: MatrixMarketScalar,
1237{
1238    // unwrap() in this function are guaranteed by parsing the data
1239    let entry_inner = inner.next().unwrap();
1240    // Because integer numbers can also be parsed as float numbers, it will be checked again in `parse::<i128>()?`
1241    if entry_inner.as_rule() != Rule::SparseReal {
1242        return Err(MatrixMarketError::from_kind_and_message(
1243            MatrixMarketErrorKind::ParsingError,
1244            format!(
1245                "
1246        Spare real matrix requires 3 int number as coordinates and data, but line {} was provided.  
1247        ",
1248                entry_inner.as_str()
1249            ),
1250        ));
1251    }
1252    let mut inner = entry_inner.into_inner();
1253    let (r, c) = parse_sparse_coordinate(&mut inner)?;
1254    // Here to guarantee it is an integer number
1255    let d = inner.next().unwrap().as_str().parse::<i128>()?;
1256    Ok((r, c, T::from_i128(d)?))
1257}
1258
1259/// Parse a pest structure to sparse pattern entry, including 2 int, which are number of rows, cols
1260fn parse_sparse_pattern<T>(
1261    inner: &mut Pairs<'_, Rule>,
1262) -> Result<(usize, usize, T), MatrixMarketError>
1263where
1264    T: MatrixMarketScalar,
1265{
1266    // unwrap() in this function are guaranteed by parsing the data
1267    let entry_inner = inner.next().unwrap();
1268    if entry_inner.as_rule() != Rule::SparsePattern {
1269        return Err(MatrixMarketError::from_kind_and_message(
1270            MatrixMarketErrorKind::ParsingError,
1271            format!(
1272                "
1273        Spare real matrix requires 2 int number as coordinates, but line {} was provided.  
1274        ",
1275                entry_inner.as_str()
1276            ),
1277        ));
1278    }
1279    let mut inner = entry_inner.into_inner();
1280    let (r, c) = parse_sparse_coordinate(&mut inner)?;
1281    Ok((r, c, T::from_pattern(())?))
1282}
1283
1284/// Parse a pest structure to sparse complex entry, including 2 int, which are number of rows, cols, and 2 real number as complex data
1285fn parse_sparse_complex<T>(
1286    inner: &mut Pairs<'_, Rule>,
1287) -> Result<(usize, usize, T), MatrixMarketError>
1288where
1289    T: MatrixMarketScalar,
1290{
1291    // unwrap() in this function are guaranteed by parsing the data
1292    let entry_inner = inner.next().unwrap();
1293    if entry_inner.as_rule() != Rule::SparseComplex {
1294        return Err(MatrixMarketError::from_kind_and_message(MatrixMarketErrorKind::ParsingError,format!("
1295        Spare real matrix requires 2 int number as coordinates and 2 real number as complex data, but line {} was provided.  
1296        ",entry_inner.as_str() )));
1297    }
1298    let mut inner = entry_inner.into_inner();
1299    let (r, c) = parse_sparse_coordinate(&mut inner)?;
1300    let real = inner.next().unwrap().as_str().parse::<f64>().unwrap();
1301    let imag = inner.next().unwrap().as_str().parse::<f64>().unwrap();
1302    let complex = Complex::<f64>::new(real, imag);
1303    Ok((r, c, T::from_c64(complex)?))
1304}
1305
1306/// Parse a pest structure to dense real entry, including a real number as data
1307fn parse_dense_real<T>(inner: &mut Pairs<'_, Rule>) -> Result<T, MatrixMarketError>
1308where
1309    T: MatrixMarketScalar,
1310{
1311    // unwrap() in this function are guaranteed by parsing the data
1312    let entry_inner = inner.next().unwrap();
1313    if entry_inner.as_rule() != Rule::DenseReal {
1314        return Err(MatrixMarketError::from_kind_and_message(
1315            MatrixMarketErrorKind::ParsingError,
1316            format!(
1317                "
1318        Dense real matrix requires 1 real number as data, but line {} was provided.  
1319        ",
1320                entry_inner.as_str()
1321            ),
1322        ));
1323    }
1324    let mut inner = entry_inner.into_inner();
1325    let d = inner.next().unwrap().as_str().parse::<f64>().unwrap();
1326    Ok(T::from_f64(d)?)
1327}
1328
1329/// Parse a pest structure to dense integer entry, including a integer number as data
1330fn parse_dense_int<T>(inner: &mut Pairs<'_, Rule>) -> Result<T, MatrixMarketError>
1331where
1332    T: MatrixMarketScalar,
1333{
1334    // unwrap() in this function are guaranteed by parsing the data
1335    let entry_inner = inner.next().unwrap();
1336    // Because integer numbers can also be parsed as float numbers, it will be checked again in `parse::<i128>()?`
1337    if entry_inner.as_rule() != Rule::DenseReal {
1338        return Err(MatrixMarketError::from_kind_and_message(
1339            MatrixMarketErrorKind::ParsingError,
1340            format!(
1341                "
1342        Dense real matrix requires 1 int number as data, but line {} was provided.  
1343        ",
1344                entry_inner.as_str()
1345            ),
1346        ));
1347    }
1348    let mut inner = entry_inner.into_inner();
1349    // Here to guarantee it is an integer number
1350    let d = inner.next().unwrap().as_str().parse::<i128>()?;
1351    Ok(T::from_i128(d)?)
1352}
1353
1354/// Parse a pest structure to dense complex entry, including 2 real number as complex data
1355fn parse_dense_complex<T>(inner: &mut Pairs<'_, Rule>) -> Result<T, MatrixMarketError>
1356where
1357    T: MatrixMarketScalar,
1358{
1359    // unwrap() in this function are guaranteed by parsing the data
1360    let entry_inner = inner.next().unwrap();
1361    // Note: theoretically, 2 positive integers could also become the complex number,
1362    // but it would be parsed as SparsePattern, because SparsePattern has higher priority.
1363    // But DenseComplex can't have higher priority,
1364    // because, it's more often to deal with "normal" SparsePattern, rather than "unnormal" DenseComplex
1365    if entry_inner.as_rule() != Rule::DenseComplex && entry_inner.as_rule() != Rule::SparsePattern {
1366        return Err(MatrixMarketError::from_kind_and_message(
1367            MatrixMarketErrorKind::ParsingError,
1368            format!(
1369                "
1370        Dense real matrix requires 2 real number as complex data, but line {} was provided.  
1371        ",
1372                entry_inner.as_str()
1373            ),
1374        ));
1375    }
1376    let mut inner = entry_inner.into_inner();
1377    let real = inner.next().unwrap().as_str().parse::<f64>().unwrap();
1378    let imag = inner.next().unwrap().as_str().parse::<f64>().unwrap();
1379    let complex = Complex::<f64>::new(real, imag);
1380    Ok(T::from_c64(complex)?)
1381}
1382
1383// Parse entry ends here-------------------------------------------------
1384
1385/// Parse the coordinates information used for sparse matrix
1386fn parse_sparse_coordinate(
1387    inner: &mut Pairs<'_, Rule>,
1388) -> Result<(usize, usize), MatrixMarketError> {
1389    // unwrap() in this function are guaranteed by parsing the data
1390    let r = inner.next().unwrap().as_str().parse::<usize>().unwrap();
1391    let c = inner.next().unwrap().as_str().parse::<usize>().unwrap();
1392    if r * c == 0 {
1393        return Err(MatrixMarketError::from_kind_and_message(
1394            MatrixMarketErrorKind::ZeroError,
1395            String::from("The data has to be one-indexed"),
1396        ));
1397    }
1398    // The coordinates in matrix market is one-based, but in CooMatrix is zero-based.
1399    Ok((r - 1, c - 1))
1400}
1401
1402/// Calculate the next coordinates used for dense matrix
1403fn next_dense_coordinate(
1404    current_dense_coordinate: &mut (usize, usize),
1405    shape: (usize, usize, usize),
1406    storagescheme: &StorageScheme,
1407) {
1408    // matrix market is column based format.
1409    // so it follows the order (0,0) -> (1,0) -> ... -> (row, 0) -> (0,1) -> ... ->(row,col)
1410    // current_dense_coordinate is (row, column)
1411    match storagescheme {
1412        StorageScheme::General => {
1413            if current_dense_coordinate.0 < shape.0 - 1 {
1414                current_dense_coordinate.0 += 1
1415            } else {
1416                // jump to next column, reset row to 1, column add 1
1417                current_dense_coordinate.0 = 0;
1418                current_dense_coordinate.1 += 1;
1419            }
1420        }
1421        StorageScheme::Symmetric | StorageScheme::Hermitian => {
1422            if current_dense_coordinate.0 < shape.0 - 1 {
1423                current_dense_coordinate.0 += 1
1424            } else {
1425                // jump to next column, column add 1, then set row equals to current column
1426                // for example   (0,0) -> (1,0) -> ... -> (row, 0) -> (1,1) -> ...
1427                current_dense_coordinate.1 += 1;
1428                current_dense_coordinate.0 = current_dense_coordinate.1;
1429            }
1430        }
1431        StorageScheme::Skew => {
1432            if current_dense_coordinate.0 < shape.0 - 1 {
1433                current_dense_coordinate.0 += 1;
1434            } else {
1435                // jump to next column, set row equals to current column, then column add 1
1436                // skew matrix doesn't have element on diagonal
1437                // for example  (1,0) -> (2,0) -> ... -> (row, 0) -> (2,1) -> ...
1438                current_dense_coordinate.1 += 1;
1439                current_dense_coordinate.0 = current_dense_coordinate.1 + 1;
1440            }
1441        }
1442    }
1443}
1444
1445/// Save a sparse matrix as a Matrix Market format string.
1446///
1447/// The exporter only writes the matrix into `coordinate` and `general` format.
1448///
1449///
1450/// Examples
1451/// --------
1452/// ```
1453/// # use nalgebra_sparse::CooMatrix;
1454/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1455/// use nalgebra_sparse::io::{save_to_matrix_market_str};
1456/// let expected_str = r#"%%matrixmarket matrix coordinate integer general
1457/// % matrixmarket file generated by nalgebra-sparse.
1458/// 5 4 2
1459/// 1 1 10
1460/// 2 3 5
1461/// "#;
1462/// let row_indices = vec![0,1];
1463/// let col_indices = vec![0,2];
1464/// let values = vec![10,5];
1465/// let matrix = CooMatrix::try_from_triplets(5,4,row_indices,col_indices,values)?;
1466/// let generated_matrixmarket_str = save_to_matrix_market_str(&matrix);
1467/// assert_eq!(expected_str,generated_matrixmarket_str);
1468/// # Ok(()) }
1469/// ```
1470pub fn save_to_matrix_market_str<T, S>(sparse_matrix: &S) -> String
1471where
1472    T: MatrixMarketScalar,
1473    S: MatrixMarketExport<T>,
1474{
1475    let mut bytes = Vec::<u8>::new();
1476    // This will call impl<A: Allocator> Write for Vec<u8, A>
1477    // The vector will grow as needed.
1478    // So, unwrap here won't cause any issue.
1479    save_to_matrix_market(&mut bytes, sparse_matrix).unwrap();
1480
1481    String::from_utf8(bytes)
1482        .expect("Unexpected non UTF-8 data was generated when export to matrix market string")
1483}
1484
1485/// Save a sparse matrix to a Matrix Market format file.
1486///
1487/// The exporter only saves the matrix with the `coordinate` and `general` matrix market formats.
1488///
1489/// Errors
1490/// --------
1491///
1492/// See [MatrixMarketErrorKind] for a list of possible error conditions.
1493///
1494/// Examples
1495/// --------
1496/// ```no_run
1497/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1498/// use nalgebra_sparse::io::{save_to_matrix_market_file,load_coo_from_matrix_market_str};
1499/// let str = r#"
1500/// %%matrixmarket matrix coordinate integer general
1501/// 5 4 2
1502/// 1 1 10
1503/// 2 3 5
1504/// "#;
1505/// let matrix = load_coo_from_matrix_market_str::<i32>(&str)?;
1506/// save_to_matrix_market_file(&matrix,"path/to/matrix.mtx")?;
1507/// # Ok(()) }
1508/// ```
1509pub fn save_to_matrix_market_file<T, S, P>(sparse_matrix: &S, path: P) -> Result<(), std::io::Error>
1510where
1511    T: MatrixMarketScalar,
1512    S: MatrixMarketExport<T>,
1513    P: AsRef<Path>,
1514{
1515    let file = File::create(path)?;
1516    let mut file = BufWriter::new(file);
1517    save_to_matrix_market(&mut file, sparse_matrix)?;
1518    // Quote from BufWriter doc.
1519    // > 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.
1520    file.flush()
1521        .expect("Unexpected error when flushing the buffer data to File");
1522    Ok(())
1523}
1524
1525/// Save a sparse matrix to an [std::io::Write] instance.
1526///
1527/// This is the most general save functionality. See [save_to_matrix_market_file] and
1528/// [save_to_matrix_market_str] for higher-level functionality.
1529pub fn save_to_matrix_market<T, S, W>(mut w: W, sparse_matrix: &S) -> Result<(), std::io::Error>
1530where
1531    T: MatrixMarketScalar,
1532    S: MatrixMarketExport<T>,
1533    W: Write,
1534{
1535    // write header
1536    writeln!(
1537        w,
1538        "%%matrixmarket matrix coordinate {} general",
1539        T::typename()
1540    )?;
1541
1542    //write comment
1543    writeln!(w, "% matrixmarket file generated by nalgebra-sparse.")?;
1544
1545    // write shape information
1546    writeln!(
1547        w,
1548        "{} {} {}",
1549        sparse_matrix.nrows(),
1550        sparse_matrix.ncols(),
1551        sparse_matrix.nnz()
1552    )?;
1553
1554    //write triplets
1555    let mut buffer = String::new();
1556    for (r, c, d) in sparse_matrix.triplet_iter() {
1557        buffer.clear();
1558        d.write_matrix_market(&mut buffer)
1559            .expect("Unexpected format error was generated when write to String");
1560        writeln!(w, "{} {} {}", r + 1, c + 1, buffer)?;
1561    }
1562    Ok(())
1563}