1use 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#[derive(Debug)]
24pub struct MatrixMarketError {
25 error_kind: MatrixMarketErrorKind,
26 message: String,
27}
28
29#[non_exhaustive]
31#[derive(Copy, Clone, Debug, PartialEq)]
32pub enum MatrixMarketErrorKind {
33 ParsingError,
52
53 InvalidHeader,
71
72 EntryMismatch,
91
92 TypeMismatch,
112
113 ZeroError,
132
133 SparseFormatError(SparseFormatErrorKind),
153
154 DiagonalError,
186
187 IOError(std::io::ErrorKind),
199
200 NotLowerTriangle,
218
219 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 #[must_use]
249 pub fn kind(&self) -> MatrixMarketErrorKind {
250 self.error_kind
251 }
252
253 #[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
352impl 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 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 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 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
438fn 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 _ => Ok(()),
489 }
490}
491
492mod internal {
494 use crate::io::MatrixMarketError;
495 use na::{Complex, Scalar};
496
497 pub trait SupportedMatrixMarketScalar: Scalar {
498 fn from_i128(i: i128) -> Result<Self, MatrixMarketError>;
500 fn from_f64(f: f64) -> Result<Self, MatrixMarketError>;
502 fn from_c64(c: Complex<f64>) -> Result<Self, MatrixMarketError>;
504 fn from_pattern(p: ()) -> Result<Self, MatrixMarketError>;
506 fn negative(self) -> Result<Self, MatrixMarketError>;
508 fn conjugate(self) -> Result<Self, MatrixMarketError>;
510 fn typename() -> &'static str;
512 fn write_matrix_market<W: std::fmt::Write>(&self, w: W) -> Result<(), std::fmt::Error>;
514 }
515
516 pub trait SupportedMatrixMarketExport<T: SupportedMatrixMarketScalar> {
517 fn triplet_iter(&self) -> Box<dyn Iterator<Item = (usize, usize, &T)> + '_>;
519 fn nrows(&self) -> usize;
521 fn ncols(&self) -> usize;
523 fn nnz(&self) -> usize;
525 }
526}
527
528pub trait MatrixMarketScalar: internal::SupportedMatrixMarketScalar {}
533
534macro_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}
590macro_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
647macro_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}
703macro_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
778pub 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
822pub 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
855pub fn load_coo_from_matrix_market_str<T>(data: &str) -> Result<CooMatrix<T>, MatrixMarketError>
878where
879 T: MatrixMarketScalar,
880{
881 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 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 let mut current_dense_coordinate: (usize, usize) = (0, 0);
911 if header_type.storagescheme == StorageScheme::Skew {
912 current_dense_coordinate = (1, 0);
914 }
915 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 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 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 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 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]
1082fn 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]
1097fn parse_header(inner: &mut Pairs<'_, Rule>) -> Typecode {
1099 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
1125fn parse_sparse_shape(
1129 inner: &mut Pairs<'_, Rule>,
1130 storagescheme: &StorageScheme,
1131) -> Result<(usize, usize, usize), MatrixMarketError> {
1132 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 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
1154fn parse_dense_shape(
1156 inner: &mut Pairs<'_, Rule>,
1157 storagescheme: &StorageScheme,
1158) -> Result<(usize, usize, usize), MatrixMarketError> {
1159 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 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 match storagescheme {
1179 StorageScheme::General => {
1180 n = r * c;
1182 }
1183 StorageScheme::Symmetric | StorageScheme::Hermitian => {
1184 n = r * (r + 1) / 2;
1187 }
1188 StorageScheme::Skew => {
1189 n = r * (r - 1) / 2;
1192 }
1193 }
1194
1195 Ok((r, c, n))
1196}
1197
1198fn parse_sparse_real<T>(inner: &mut Pairs<'_, Rule>) -> Result<(usize, usize, T), MatrixMarketError>
1204where
1205 T: MatrixMarketScalar,
1206{
1207 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
1221fn parse_sparse_int<T>(inner: &mut Pairs<'_, Rule>) -> Result<(usize, usize, T), MatrixMarketError>
1223where
1224 T: MatrixMarketScalar,
1225{
1226 let entry_inner = inner.next().unwrap();
1228 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 let d = inner.next().unwrap().as_str().parse::<i128>()?;
1244 Ok((r, c, T::from_i128(d)?))
1245}
1246
1247fn parse_sparse_pattern<T>(
1249 inner: &mut Pairs<'_, Rule>,
1250) -> Result<(usize, usize, T), MatrixMarketError>
1251where
1252 T: MatrixMarketScalar,
1253{
1254 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
1272fn parse_sparse_complex<T>(
1274 inner: &mut Pairs<'_, Rule>,
1275) -> Result<(usize, usize, T), MatrixMarketError>
1276where
1277 T: MatrixMarketScalar,
1278{
1279 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
1294fn parse_dense_real<T>(inner: &mut Pairs<'_, Rule>) -> Result<T, MatrixMarketError>
1296where
1297 T: MatrixMarketScalar,
1298{
1299 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
1317fn parse_dense_int<T>(inner: &mut Pairs<'_, Rule>) -> Result<T, MatrixMarketError>
1319where
1320 T: MatrixMarketScalar,
1321{
1322 let entry_inner = inner.next().unwrap();
1324 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 let d = inner.next().unwrap().as_str().parse::<i128>()?;
1339 Ok(T::from_i128(d)?)
1340}
1341
1342fn parse_dense_complex<T>(inner: &mut Pairs<'_, Rule>) -> Result<T, MatrixMarketError>
1344where
1345 T: MatrixMarketScalar,
1346{
1347 let entry_inner = inner.next().unwrap();
1349 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
1371fn parse_sparse_coordinate(
1375 inner: &mut Pairs<'_, Rule>,
1376) -> Result<(usize, usize), MatrixMarketError> {
1377 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 Ok((r - 1, c - 1))
1388}
1389
1390fn next_dense_coordinate(
1392 current_dense_coordinate: &mut (usize, usize),
1393 shape: (usize, usize, usize),
1394 storagescheme: &StorageScheme,
1395) {
1396 match storagescheme {
1400 StorageScheme::General => {
1401 if current_dense_coordinate.0 < shape.0 - 1 {
1402 current_dense_coordinate.0 += 1
1403 } else {
1404 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 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 current_dense_coordinate.1 += 1;
1427 current_dense_coordinate.0 = current_dense_coordinate.1 + 1;
1428 }
1429 }
1430 }
1431}
1432
1433pub 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 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
1473pub 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 file.flush()
1509 .expect("Unexpected error when flushing the buffer data to File");
1510 Ok(())
1511}
1512
1513pub 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 writeln!(
1525 w,
1526 "%%matrixmarket matrix coordinate {} general",
1527 T::typename()
1528 )?;
1529
1530 writeln!(w, "% matrixmarket file generated by nalgebra-sparse.")?;
1532
1533 writeln!(
1535 w,
1536 "{} {} {}",
1537 sparse_matrix.nrows(),
1538 sparse_matrix.ncols(),
1539 sparse_matrix.nnz()
1540 )?;
1541
1542 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}