1use 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#[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(
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
1160fn parse_dense_shape(
1162 inner: &mut Pairs<'_, Rule>,
1163 storagescheme: &StorageScheme,
1164) -> Result<(usize, usize, usize), MatrixMarketError> {
1165 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 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 match storagescheme {
1191 StorageScheme::General => {
1192 n = r * c;
1194 }
1195 StorageScheme::Symmetric | StorageScheme::Hermitian => {
1196 n = r * (r + 1) / 2;
1199 }
1200 StorageScheme::Skew => {
1201 n = r * (r - 1) / 2;
1204 }
1205 }
1206
1207 Ok((r, c, n))
1208}
1209
1210fn parse_sparse_real<T>(inner: &mut Pairs<'_, Rule>) -> Result<(usize, usize, T), MatrixMarketError>
1216where
1217 T: MatrixMarketScalar,
1218{
1219 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
1233fn parse_sparse_int<T>(inner: &mut Pairs<'_, Rule>) -> Result<(usize, usize, T), MatrixMarketError>
1235where
1236 T: MatrixMarketScalar,
1237{
1238 let entry_inner = inner.next().unwrap();
1240 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 let d = inner.next().unwrap().as_str().parse::<i128>()?;
1256 Ok((r, c, T::from_i128(d)?))
1257}
1258
1259fn parse_sparse_pattern<T>(
1261 inner: &mut Pairs<'_, Rule>,
1262) -> Result<(usize, usize, T), MatrixMarketError>
1263where
1264 T: MatrixMarketScalar,
1265{
1266 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
1284fn parse_sparse_complex<T>(
1286 inner: &mut Pairs<'_, Rule>,
1287) -> Result<(usize, usize, T), MatrixMarketError>
1288where
1289 T: MatrixMarketScalar,
1290{
1291 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
1306fn parse_dense_real<T>(inner: &mut Pairs<'_, Rule>) -> Result<T, MatrixMarketError>
1308where
1309 T: MatrixMarketScalar,
1310{
1311 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
1329fn parse_dense_int<T>(inner: &mut Pairs<'_, Rule>) -> Result<T, MatrixMarketError>
1331where
1332 T: MatrixMarketScalar,
1333{
1334 let entry_inner = inner.next().unwrap();
1336 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 let d = inner.next().unwrap().as_str().parse::<i128>()?;
1351 Ok(T::from_i128(d)?)
1352}
1353
1354fn parse_dense_complex<T>(inner: &mut Pairs<'_, Rule>) -> Result<T, MatrixMarketError>
1356where
1357 T: MatrixMarketScalar,
1358{
1359 let entry_inner = inner.next().unwrap();
1361 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
1383fn parse_sparse_coordinate(
1387 inner: &mut Pairs<'_, Rule>,
1388) -> Result<(usize, usize), MatrixMarketError> {
1389 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 Ok((r - 1, c - 1))
1400}
1401
1402fn next_dense_coordinate(
1404 current_dense_coordinate: &mut (usize, usize),
1405 shape: (usize, usize, usize),
1406 storagescheme: &StorageScheme,
1407) {
1408 match storagescheme {
1412 StorageScheme::General => {
1413 if current_dense_coordinate.0 < shape.0 - 1 {
1414 current_dense_coordinate.0 += 1
1415 } else {
1416 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 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 current_dense_coordinate.1 += 1;
1439 current_dense_coordinate.0 = current_dense_coordinate.1 + 1;
1440 }
1441 }
1442 }
1443}
1444
1445pub 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 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
1485pub 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 file.flush()
1521 .expect("Unexpected error when flushing the buffer data to File");
1522 Ok(())
1523}
1524
1525pub 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 writeln!(
1537 w,
1538 "%%matrixmarket matrix coordinate {} general",
1539 T::typename()
1540 )?;
1541
1542 writeln!(w, "% matrixmarket file generated by nalgebra-sparse.")?;
1544
1545 writeln!(
1547 w,
1548 "{} {} {}",
1549 sparse_matrix.nrows(),
1550 sparse_matrix.ncols(),
1551 sparse_matrix.nnz()
1552 )?;
1553
1554 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}