1use std::error::Error;
4use std::fmt;
5use std::fs::File;
6use std::io;
7use std::io::{Seek, SeekFrom, Write};
8use std::ops::Neg;
9use std::path::Path;
10
11use crate::indexing::SpIndex;
12use crate::num_kinds::{NumKind, PrimitiveKind};
13use crate::num_matrixmarket::*;
14use crate::sparse::{SparseMat, TriMatI};
15
16#[derive(Debug)]
17pub enum IoError {
18 Io(io::Error),
19 BadMatrixMarketFile,
20 MismatchedMatrixMarketRead(NumKind, NumKind),
21 UnsupportedMatrixMarketFormat,
22}
23
24use self::IoError::*;
25
26impl fmt::Display for IoError {
27 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
28 match *self {
29 Self::Io(ref err) => err.fmt(f),
30 Self::BadMatrixMarketFile | Self::UnsupportedMatrixMarketFormat => {
31 write!(f, "Bad matrix market file.")
32 }
33 Self::MismatchedMatrixMarketRead(matrix_kind, file_kind) => {
34 write!(
35 f,
36 "Tried to load {file_kind} file into {matrix_kind} matrix."
37 )
38 }
39 }
40 }
41}
42
43impl Error for IoError {}
44
45impl From<io::Error> for IoError {
46 fn from(err: io::Error) -> Self {
47 Self::Io(err)
48 }
49}
50
51impl PartialEq for IoError {
52 fn eq(&self, rhs: &Self) -> bool {
53 match (self, rhs) {
54 (Self::BadMatrixMarketFile, Self::BadMatrixMarketFile)
55 | (
56 Self::UnsupportedMatrixMarketFormat,
57 Self::UnsupportedMatrixMarketFormat,
58 ) => true,
59 (
60 Self::MismatchedMatrixMarketRead(a1, a2),
61 Self::MismatchedMatrixMarketRead(b1, b2),
62 ) => a1 == a2 && b1 == b2,
63 _ => false,
64 }
65 }
66}
67
68#[derive(Debug, PartialEq, Eq)]
69enum DataType {
70 Integer,
71 Real,
72 Complex,
73 Pattern,
74}
75
76#[derive(Copy, Clone, Debug, PartialEq, Eq)]
77pub enum SymmetryMode {
78 General,
79 Hermitian,
80 Symmetric,
81 SkewSymmetric,
82}
83
84fn parse_header(header: &str) -> Result<(SymmetryMode, DataType), IoError> {
85 if !header.starts_with("%%matrixmarket matrix coordinate") {
86 return Err(BadMatrixMarketFile);
87 }
88 let data_type = if header.contains("real") {
89 DataType::Real
90 } else if header.contains("integer") {
91 DataType::Integer
92 } else if header.contains("complex") {
93 DataType::Complex
94 } else if header.contains("pattern") {
95 DataType::Pattern
96 } else {
97 return Err(BadMatrixMarketFile);
98 };
99 let sym_mode = if header.contains("general") {
100 SymmetryMode::General
101 } else if header.contains("skew-symmetric") {
102 SymmetryMode::SkewSymmetric
103 } else if header.contains("symmetric") {
104 SymmetryMode::Symmetric
105 } else if header.contains("hermitian") {
106 SymmetryMode::Hermitian
107 } else {
108 return Err(BadMatrixMarketFile);
109 };
110 Ok((sym_mode, data_type))
111}
112
113pub fn read_matrix_market<N, I, P>(mm_file: P) -> Result<TriMatI<N, I>, IoError>
119where
120 I: SpIndex,
121 N: PrimitiveKind
122 + Clone
123 + MatrixMarketRead
124 + MatrixMarketConjugate
125 + Neg<Output = N>,
126 P: AsRef<Path>,
127{
128 let mm_file = mm_file.as_ref();
129 let f = File::open(mm_file)?;
130 let mut reader = io::BufReader::new(f);
131 read_matrix_market_from_bufread(&mut reader)
132}
133
134pub fn read_matrix_market_from_bufread<N, I, R>(
140 reader: &mut R,
141) -> Result<TriMatI<N, I>, IoError>
142where
143 I: SpIndex,
144 N: PrimitiveKind
145 + Clone
146 + Neg<Output = N>
147 + MatrixMarketRead
148 + MatrixMarketConjugate,
149 R: io::BufRead + ?Sized,
150{
151 let mut line = String::with_capacity(1024);
153
154 reader.read_line(&mut line)?;
156 let header = line.to_lowercase();
157 let (sym_mode, data_type) = parse_header(&header)?;
158 let data_num_kind = match data_type {
159 DataType::Integer => NumKind::Integer,
160 DataType::Real => NumKind::Float,
161 DataType::Complex => NumKind::Complex,
162 DataType::Pattern => NumKind::Pattern,
163 };
164 if N::num_kind() != NumKind::Pattern && N::num_kind() != data_num_kind {
166 return Err(MismatchedMatrixMarketRead(N::num_kind(), data_num_kind));
167 }
168 let droping_data =
170 N::num_kind() == NumKind::Pattern && data_num_kind != NumKind::Pattern;
171
172 'header: loop {
174 line.clear();
175 let len = reader.read_line(&mut line)?;
176 if len == 0 || line.starts_with('%') {
177 continue 'header;
178 }
179 break;
180 }
181 let (rows, cols, entries) = {
186 let mut infos = line
187 .split_whitespace()
188 .filter_map(|s| s.parse::<usize>().ok());
189 let rows = infos.next().ok_or(BadMatrixMarketFile)?;
190 let cols = infos.next().ok_or(BadMatrixMarketFile)?;
191 let entries = infos.next().ok_or(BadMatrixMarketFile)?;
192 if infos.next().is_some() {
193 return Err(BadMatrixMarketFile);
194 }
195 (rows, cols, entries)
196 };
197 let nnz_max = if sym_mode == SymmetryMode::General {
198 entries
199 } else {
200 2 * entries
201 };
202 let mut row_inds = Vec::with_capacity(nnz_max);
203 let mut col_inds = Vec::with_capacity(nnz_max);
204 let mut data = Vec::with_capacity(nnz_max);
205 for _ in 0..entries {
207 'empty_lines: loop {
209 line.clear();
210 let len = reader.read_line(&mut line)?;
211 if len != 0 && line.split_whitespace().next().is_none() {
213 continue 'empty_lines;
214 }
215 break;
216 }
217 let mut entry = line.split_whitespace();
224 let row = entry
225 .next()
226 .ok_or(BadMatrixMarketFile)
227 .and_then(|s| s.parse::<usize>().or(Err(BadMatrixMarketFile)))?;
228 let col = entry
229 .next()
230 .ok_or(BadMatrixMarketFile)
231 .and_then(|s| s.parse::<usize>().or(Err(BadMatrixMarketFile)))?;
232 let row = row.checked_sub(1).ok_or(BadMatrixMarketFile)?;
234 let col = col.checked_sub(1).ok_or(BadMatrixMarketFile)?;
235 let val: N = MatrixMarketRead::mm_read(&mut entry)?;
236 row_inds.push(I::from_usize(row));
237 col_inds.push(I::from_usize(col));
238 data.push(val.clone());
239 if sym_mode != SymmetryMode::General && row != col {
240 if sym_mode == SymmetryMode::Hermitian {
241 row_inds.push(I::from_usize(col));
242 col_inds.push(I::from_usize(row));
243 let conj =
244 val.mm_conj().ok_or(UnsupportedMatrixMarketFormat)?;
245 data.push(conj);
246 } else if sym_mode == SymmetryMode::SkewSymmetric {
247 row_inds.push(I::from_usize(col));
248 col_inds.push(I::from_usize(row));
249 data.push(-val);
250 } else {
251 row_inds.push(I::from_usize(col));
252 col_inds.push(I::from_usize(row));
253 data.push(val);
254 }
255 }
256 if sym_mode == SymmetryMode::SkewSymmetric && row == col {
257 return Err(BadMatrixMarketFile);
258 }
259 if droping_data {
260 if entry.next().is_none() {
262 return Err(BadMatrixMarketFile);
263 }
264 } else {
265 if entry.next().is_some() {
267 return Err(BadMatrixMarketFile);
268 }
269 }
270 }
271
272 Ok(TriMatI::from_triplets(
273 (rows, cols),
274 row_inds,
275 col_inds,
276 data,
277 ))
278}
279
280pub fn write_matrix_market<'a, N, I, M, P>(
295 path: P,
296 mat: M,
297) -> Result<(), io::Error>
298where
299 I: 'a + SpIndex + fmt::Display,
300 N: 'a + PrimitiveKind + MatrixMarketDisplay,
301 for<'n> Displayable<&'n N>: std::fmt::Display,
302 M: IntoIterator<Item = (&'a N, (I, I))> + SparseMat,
303 P: AsRef<Path>,
304{
305 let f = File::create(path)?;
306 let mut writer = io::BufWriter::new(f);
307 write_matrix_market_to_bufwrite(&mut writer, mat)
308}
309pub fn write_matrix_market_to_bufwrite<'a, N, I, M, W>(
310 writer: &mut W,
311 mat: M,
312) -> Result<(), io::Error>
313where
314 I: 'a + SpIndex + fmt::Display,
315 N: 'a + PrimitiveKind + MatrixMarketDisplay,
316 for<'n> Displayable<&'n N>: std::fmt::Display,
317 M: IntoIterator<Item = (&'a N, (I, I))> + SparseMat,
318 W: io::Write + ?Sized,
319{
320 let (rows, cols, nnz) = (mat.rows(), mat.cols(), mat.nnz());
321
322 let data_type = match N::num_kind() {
324 NumKind::Integer => "integer",
325 NumKind::Float => "real",
326 NumKind::Complex => "complex",
327 NumKind::Pattern => "pattern",
328 };
329 writeln!(
330 writer,
331 "%%MatrixMarket matrix coordinate {data_type} general"
332 )?;
333 writeln!(writer, "% written by sprs")?;
334
335 writeln!(writer, "{rows} {cols} {nnz}")?;
337
338 for (val, (row, col)) in mat {
340 writeln!(
341 writer,
342 "{} {} {}",
343 row.index() + 1,
344 col.index() + 1,
345 val.mm_display()
346 )?;
347 }
348 Ok(())
349}
350
351pub fn write_matrix_market_sym<'a, N, I, M, P>(
363 path: P,
364 mat: M,
365 sym: SymmetryMode,
366) -> Result<(), io::Error>
367where
368 I: 'a + SpIndex + fmt::Display,
369 N: 'a + PrimitiveKind + MatrixMarketDisplay,
370 for<'n> Displayable<&'n N>: std::fmt::Display,
371 M: IntoIterator<Item = (&'a N, (I, I))> + SparseMat,
372 P: AsRef<Path>,
373{
374 let (rows, cols, nnz) = (mat.rows(), mat.cols(), mat.nnz());
375 let f = File::create(path)?;
376 let mut writer = io::BufWriter::new(f);
377
378 let data_type = match N::num_kind() {
380 NumKind::Integer => "integer",
381 NumKind::Float => "real",
382 NumKind::Complex => "complex",
383 NumKind::Pattern => "pattern",
384 };
385 let mode = match sym {
386 SymmetryMode::General => "general",
387 SymmetryMode::Symmetric => "symmetric",
388 SymmetryMode::SkewSymmetric => "skew-symmetric",
389 SymmetryMode::Hermitian => "hermitian",
390 };
391 writeln!(
392 writer,
393 "%%MatrixMarket matrix coordinate {data_type} {mode}"
394 )?;
395 writeln!(writer, "% written by sprs")?;
396
397 let dim_header_pos = writer.stream_position()?;
404 writeln!(writer, "{rows} {cols} {nnz}")?;
406
407 let mut entries = 0;
409 match sym {
410 SymmetryMode::General => {
411 for (val, (row, col)) in mat {
412 writeln!(
413 writer,
414 "{} {} {}",
415 row.index() + 1,
416 col.index() + 1,
417 val.mm_display()
418 )?;
419 entries += 1;
420 }
421 }
422 SymmetryMode::SkewSymmetric => {
423 for (val, (row, col)) in
424 mat.into_iter().filter(|&(_, (r, c))| r < c)
425 {
426 writeln!(
427 writer,
428 "{} {} {}",
429 row.index() + 1,
430 col.index() + 1,
431 val.mm_display()
432 )?;
433 entries += 1;
434 }
435 }
436 _ => {
437 for (val, (row, col)) in
438 mat.into_iter().filter(|&(_, (r, c))| r <= c)
439 {
440 writeln!(
441 writer,
442 "{} {} {}",
443 row.index() + 1,
444 col.index() + 1,
445 val.mm_display()
446 )?;
447 entries += 1;
448 }
449 }
450 };
451 assert!(entries <= nnz);
452 writer.seek(SeekFrom::Start(dim_header_pos))?;
453 write!(writer, "{rows} {cols} {entries}")?;
454 let dim_header_size = format!("{rows} {cols} {nnz}").len();
455 let new_size = format!("{rows} {cols} {entries}").len();
456 if new_size < dim_header_size {
457 let nb_spaces = dim_header_size - new_size;
458 for _ in 0..nb_spaces {
459 writer.write_all(b" ")?;
460 }
461 }
462 Ok(())
463}
464
465#[cfg(test)]
466mod test {
467 use super::{
468 read_matrix_market, read_matrix_market_from_bufread,
469 write_matrix_market, write_matrix_market_sym, IoError, SymmetryMode,
470 };
471 use crate::{num_kinds::Pattern, CsMat};
472 use ndarray::{arr2, Array2};
473 use num_complex::{Complex32, Complex64};
474 use tempfile::tempdir;
475 #[cfg_attr(miri, ignore)]
476 #[test]
477 fn simple_matrix_market_read() {
478 let path = "data/matrix_market/simple.mm";
479 let mat = read_matrix_market::<f64, usize, _>(path).unwrap();
480 assert_eq!(mat.rows(), 5);
481 assert_eq!(mat.cols(), 5);
482 assert_eq!(mat.nnz(), 8);
483 assert_eq!(mat.row_inds(), &[0, 1, 2, 0, 3, 3, 3, 4]);
484 assert_eq!(mat.col_inds(), &[0, 1, 2, 3, 1, 3, 4, 4]);
485 assert_eq!(
486 mat.data(),
487 &[1., 10.5, 1.5e-02, 6., 2.505e2, -2.8e2, 3.332e1, 1.2e+1]
488 );
489 }
490
491 #[cfg_attr(miri, ignore)]
492 #[test]
493 fn failing_matrix_market_reads() {
494 let complex_mm_path = "data/matrix_market/complex/simple.mtx";
495 let float_mm_path = "data/matrix_market/simple.mm";
496 let int_mm_path = "data/matrix_market/simple_int.mm";
497 let hermitian_int_mm_path =
498 "data/matrix_market/complex/hermitian-int.mtx";
499 assert!(read_matrix_market::<num_complex::Complex64, usize, _>(
500 complex_mm_path
501 )
502 .is_ok());
503 assert!(read_matrix_market::<i64, usize, _>(int_mm_path).is_ok());
504 assert!(read_matrix_market::<f64, usize, _>(float_mm_path).is_ok());
505
506 assert!(read_matrix_market::<f64, usize, _>(complex_mm_path).is_err());
507 assert!(read_matrix_market::<i64, usize, _>(complex_mm_path).is_err());
508
509 assert!(read_matrix_market::<num_complex::Complex64, usize, _>(
510 float_mm_path
511 )
512 .is_err());
513 assert!(read_matrix_market::<i64, usize, _>(float_mm_path).is_err());
514
515 assert!(read_matrix_market::<num_complex::Complex64, usize, _>(
516 int_mm_path
517 )
518 .is_err());
519 assert!(read_matrix_market::<f64, usize, _>(int_mm_path).is_err());
520
521 let err =
522 read_matrix_market::<f64, usize, _>(complex_mm_path).unwrap_err();
523 assert_eq!(
524 format!("{}", err),
525 "Tried to load complex file into real matrix."
526 );
527
528 assert_eq!(
529 read_matrix_market::<i64, usize, _>(hermitian_int_mm_path),
530 Err(crate::io::IoError::UnsupportedMatrixMarketFormat),
531 );
532 }
533
534 #[cfg_attr(miri, ignore)]
535 #[test]
536 fn simple_matrix_market_read_complex64() {
537 let path = "data/matrix_market/complex/simple.mtx";
538 let mat = read_matrix_market::<num_complex::Complex64, usize, _>(path)
539 .unwrap();
540 assert_eq!(mat.rows(), 2);
541 assert_eq!(mat.cols(), 2);
542 assert_eq!(mat.nnz(), 3);
543 assert_eq!(mat.row_inds(), &[0, 0, 1]);
544 assert_eq!(mat.col_inds(), &[0, 1, 0]);
545 assert_eq!(
546 mat.data(),
547 &[
548 Complex64::new(1.0, 2.0),
549 Complex64::new(3.0, 4.0),
550 Complex64::new(5.0, 6.0),
551 ]
552 );
553 }
554
555 #[cfg_attr(miri, ignore)]
556 #[test]
557 fn simple_matrix_market_read_complex64_hermitian() {
558 let path = "data/matrix_market/complex/hermitian.mtx";
559 let mat = read_matrix_market::<num_complex::Complex64, usize, _>(path)
560 .unwrap();
561 assert_eq!(mat.rows(), 2);
562 assert_eq!(mat.cols(), 2);
563 assert_eq!(mat.nnz(), 3);
564 assert_eq!(mat.row_inds(), &[0, 1, 0]);
565 assert_eq!(mat.col_inds(), &[0, 0, 1]);
566 assert_eq!(
567 mat.data(),
568 &[
569 Complex64::new(1.0, 2.0),
570 Complex64::new(5.0, 6.0),
571 Complex64::new(5.0, -6.0),
572 ]
573 );
574 let expected_dm: Array2<Complex64> = arr2(&[
575 [Complex64::new(1.0, 2.0), Complex64::new(5.0, -6.0)],
576 [Complex64::new(5.0, 6.0), Complex64::new(0.0, 0.0)],
577 ]);
578 let csr: CsMat<Complex64> = mat.to_csr();
579 let dm: Array2<Complex64> = csr.to_dense();
580 assert_eq!(dm, expected_dm);
581 println!("{}", dm);
582 }
583
584 #[cfg_attr(miri, ignore)]
585 #[test]
586 fn simple_matrix_market_read_complex32() {
587 let path = "data/matrix_market/complex/simple.mtx";
588 let mat = read_matrix_market::<num_complex::Complex32, usize, _>(path)
589 .unwrap();
590 assert_eq!(mat.rows(), 2);
591 assert_eq!(mat.cols(), 2);
592 assert_eq!(mat.nnz(), 3);
593 assert_eq!(mat.row_inds(), &[0, 0, 1]);
594 assert_eq!(mat.col_inds(), &[0, 1, 0]);
595 assert_eq!(
596 mat.data(),
597 &[
598 Complex32::new(1.0, 2.0),
599 Complex32::new(3.0, 4.0),
600 Complex32::new(5.0, 6.0),
601 ]
602 );
603 }
604
605 #[test]
606 #[cfg_attr(miri, ignore)]
607 fn simple_matrix_market_read_from_bufread() {
608 let path = "data/matrix_market/simple.mm";
609 let f = std::fs::File::open(path).unwrap();
610 let mut reader = std::io::BufReader::new(f);
611
612 let mat = read_matrix_market_from_bufread::<f64, usize, _>(&mut reader)
613 .unwrap();
614 assert_eq!(mat.rows(), 5);
615 assert_eq!(mat.cols(), 5);
616 assert_eq!(mat.nnz(), 8);
617 assert_eq!(mat.row_inds(), &[0, 1, 2, 0, 3, 3, 3, 4]);
618 assert_eq!(mat.col_inds(), &[0, 1, 2, 3, 1, 3, 4, 4]);
619 assert_eq!(
620 mat.data(),
621 &[1., 10.5, 1.5e-02, 6., 2.505e2, -2.8e2, 3.332e1, 1.2e+1]
622 );
623 }
624
625 #[test]
626 #[cfg_attr(miri, ignore)]
627 fn int_matrix_market_read() {
628 let path = "data/matrix_market/simple_int.mm";
629 let mat = read_matrix_market::<i32, usize, _>(path).unwrap();
630 assert_eq!(mat.rows(), 5);
631 assert_eq!(mat.cols(), 5);
632 assert_eq!(mat.nnz(), 8);
633 assert_eq!(mat.row_inds(), &[0, 1, 2, 0, 3, 3, 3, 4]);
634 assert_eq!(mat.col_inds(), &[0, 1, 2, 3, 1, 3, 4, 4]);
635 assert_eq!(mat.data(), &[1, 1, 1, 6, 2, -2, 3, 1]);
636 }
637
638 #[test]
639 #[cfg_attr(miri, ignore)]
640 fn matrix_market_read_fail_too_many_in_entry() {
641 let path = "data/matrix_market/bad_files/too_many_elems_in_entry.mm";
642 let res = read_matrix_market::<f64, i32, _>(path);
643 assert_eq!(res.unwrap_err(), IoError::BadMatrixMarketFile);
644 }
645
646 #[test]
647 #[cfg_attr(miri, ignore)]
648 fn matrix_market_read_fail_not_enough_entries() {
649 let path = "data/matrix_market/bad_files/not_enough_entries.mm";
650 let res = read_matrix_market::<f64, i32, _>(path);
651 assert_eq!(res.unwrap_err(), IoError::BadMatrixMarketFile);
652 }
653
654 #[test]
655 #[cfg_attr(miri, ignore)]
656 fn read_write_read_matrix_market() {
657 let path = "data/matrix_market/simple.mm";
658 let mat = read_matrix_market::<f64, usize, _>(path).unwrap();
659 let tmp_dir = tempdir().unwrap();
660 let save_path = tmp_dir.path().join("simple.mm");
661 write_matrix_market(&save_path, mat.view()).unwrap();
662 let mat2 = read_matrix_market::<f64, usize, _>(&save_path).unwrap();
663 assert_eq!(mat, mat2);
664 write_matrix_market(&save_path, &mat2).unwrap();
665 let mat3 = read_matrix_market::<f64, usize, _>(&save_path).unwrap();
666 assert_eq!(mat, mat3);
667 }
668
669 #[test]
670 #[cfg_attr(miri, ignore)]
671 fn read_write_read_matrix_market_via_csc() {
672 let path = "data/matrix_market/simple.mm";
673 let mat = read_matrix_market::<f64, usize, _>(path).unwrap();
674 let csc: CsMat<_> = mat.to_csc();
675 let tmp_dir = tempdir().unwrap();
676 let save_path = tmp_dir.path().join("simple_csc.mm");
677 write_matrix_market(&save_path, &csc).unwrap();
678 let mat2 = read_matrix_market::<f64, usize, _>(&save_path).unwrap();
679 assert_eq!(csc, mat2.to_csc());
680 }
681
682 #[test]
683 #[cfg_attr(miri, ignore)]
684 fn read_symmetric_matrix_market() {
685 let path = "data/matrix_market/symmetric.mm";
686 let mat = read_matrix_market::<f64, usize, _>(path).unwrap();
687 let csc = mat.to_csc();
688 let expected = CsMat::new_csc(
689 (5, 5),
690 vec![0, 1, 3, 4, 6, 8],
691 vec![0, 1, 3, 2, 1, 4, 3, 4],
692 vec![1., 10.5, 2.505e2, 1.5e-2, 2.505e2, 3.332e1, 3.332e1, 1.2e1],
693 );
694 assert_eq!(csc, expected);
695 let tmp_dir = tempdir().unwrap();
696 let save_path = tmp_dir.path().join("symmetric.mm");
697 write_matrix_market_sym(&save_path, &csc, SymmetryMode::Symmetric)
698 .unwrap();
699 let mat2 = read_matrix_market::<f64, usize, _>(&save_path).unwrap();
700 assert_eq!(csc, mat2.to_csc());
701 }
702
703 #[test]
704 #[cfg_attr(miri, ignore)]
705 fn read_symmetric_matrix_market_complex() {
706 let path = "data/matrix_market/complex/symmetric.mtx";
707 let mat = read_matrix_market::<Complex64, usize, _>(path).unwrap();
708 let csc = mat.to_csc();
709 let expected = CsMat::new_csc(
710 (2, 2),
711 vec![0, 2, 3],
712 vec![0, 1, 0],
713 vec![
714 Complex64::new(1.0, 2.0),
715 Complex64::new(3.0, 4.0),
716 Complex64::new(3.0, 4.0),
717 ],
718 );
719 assert_eq!(csc, expected);
720 let tmp_dir = tempdir().unwrap();
721 let save_path = tmp_dir.path().join("symmetric.mm");
722 write_matrix_market_sym(&save_path, &csc, SymmetryMode::Symmetric)
723 .unwrap();
724 let mat2 =
725 read_matrix_market::<Complex64, usize, _>(&save_path).unwrap();
726 assert_eq!(csc, mat2.to_csc());
727 }
728
729 #[test]
730 #[cfg_attr(miri, ignore)]
731 fn read_skew_symmetric_matrix_market_complex() {
732 let path = "data/matrix_market/complex/skew-symmetric.mtx";
733 let mat = read_matrix_market::<Complex64, usize, _>(path).unwrap();
734 println!("trimat: {:?}", mat);
735 let csc = mat.to_csc();
736 assert_eq!(csc.get(0, 0), None);
737 assert_eq!(csc.get(1, 1), None);
738 assert_eq!(csc.get(0, 1), Some(&Complex64::new(3.0, 4.0)));
739 let expected = CsMat::new_csc(
740 (2, 2),
741 vec![0, 1, 2],
742 vec![1, 0],
743 vec![Complex64::new(-3.0, -4.0), Complex64::new(3.0, 4.0)],
744 );
745 assert_eq!(expected.get(0, 0), None);
746 assert_eq!(expected.get(1, 1), None);
747 assert_eq!(expected.get(0, 1), Some(&Complex64::new(3.0, 4.0)));
748 assert_eq!(csc, expected);
749 let tmp_dir = tempdir().unwrap();
750 let save_path = tmp_dir.path().join("skew-symmetric.mm");
751 write_matrix_market_sym(&save_path, &csc, SymmetryMode::SkewSymmetric)
752 .unwrap();
753 let mat2 =
754 read_matrix_market::<Complex64, usize, _>(&save_path).unwrap();
755 assert_eq!(csc, mat2.to_csc());
756 }
757
758 #[test]
759 #[cfg_attr(miri, ignore)]
760 fn tricky_symmetric_matrix_market() {
763 let mat = CsMat::new(
772 (5, 5),
773 vec![0, 2, 4, 6, 8, 10],
774 vec![1, 4, 0, 2, 1, 3, 2, 4, 0, 3],
775 vec![2, 1, 2, 3, 3, 5, 5, 4, 1, 4],
776 );
777 let tmp_dir = tempdir().unwrap();
778 let save_path = tmp_dir.path().join("symmetric.mm");
779 write_matrix_market_sym(&save_path, &mat, SymmetryMode::Symmetric)
780 .unwrap();
781 let mat2 = read_matrix_market::<i32, usize, _>(&save_path).unwrap();
782 assert_eq!(mat, mat2.to_csr());
783 }
784
785 #[test]
786 #[cfg_attr(miri, ignore)]
787 fn skew_symmetric_matrix_market() {
788 let mat = CsMat::new(
789 (5, 5),
790 vec![0, 2, 4, 6, 8, 10],
791 vec![1, 4, 0, 2, 1, 3, 2, 4, 0, 3],
792 vec![2, 1, -2, 3, -3, 5, -5, 4, -1, -4],
793 );
794 let tmp_dir = tempdir().unwrap();
795 let save_path = tmp_dir.path().join("skew_symmetric.mm");
796 write_matrix_market_sym(&save_path, &mat, SymmetryMode::SkewSymmetric)
797 .unwrap();
798 let mat2 = read_matrix_market::<i32, usize, _>(&save_path).unwrap();
799 assert_eq!(mat, mat2.to_csr());
800 }
801
802 #[test]
803 #[cfg_attr(miri, ignore)]
804 fn general_matrix_via_symmetric_save() {
805 let mat = CsMat::new(
806 (5, 5),
807 vec![0, 2, 4, 6, 8, 10],
808 vec![0, 3, 0, 2, 1, 3, 2, 4, 0, 3],
809 vec![2, -1, 2, 3, 3, 5, 5, 4, 1, 4],
810 );
811 let tmp_dir = tempdir().unwrap();
812 let save_path = tmp_dir.path().join("general.mm");
813 write_matrix_market_sym(&save_path, &mat, SymmetryMode::General)
814 .unwrap();
815 let mat2 = read_matrix_market::<i32, usize, _>(&save_path).unwrap();
816 assert_eq!(mat, mat2.to_csr());
817 }
818
819 #[test]
820 #[cfg_attr(miri, ignore)]
821 fn read_pattern_matrix() {
822 let path = "data/matrix_market/pattern.mm";
823 let mat = read_matrix_market::<Pattern, usize, _>(path).unwrap();
824 println!("trimat: {:?}", mat);
825 let csc = mat.to_csc();
826 assert_eq!(csc.get(0, 0), Some(&Pattern {}));
827 assert_eq!(csc.get(1, 1), Some(&Pattern {}));
828 assert_eq!(csc.get(2, 2), Some(&Pattern {}));
829 assert_eq!(csc.get(3, 3), Some(&Pattern {}));
830 assert_eq!(csc.get(0, 1), None);
831 let expected = CsMat::new_csc(
832 (4, 4),
833 vec![0, 1, 2, 3, 4],
834 vec![0, 1, 2, 3],
835 vec![Pattern {}; 4],
836 );
837 assert_eq!(csc.get(0, 0), Some(&Pattern {}));
838 assert_eq!(csc.get(1, 1), Some(&Pattern {}));
839 assert_eq!(csc.get(2, 2), Some(&Pattern {}));
840 assert_eq!(csc.get(3, 3), Some(&Pattern {}));
841 assert_eq!(csc.get(0, 1), None);
842 assert_eq!(csc, expected);
843 let tmp_dir = tempdir().unwrap();
844 let save_path = tmp_dir.path().join("pattern_write.mm");
845 write_matrix_market(&save_path, &csc).unwrap();
846 let mat2 = read_matrix_market::<Pattern, usize, _>(&save_path).unwrap();
847 assert_eq!(csc, mat2.to_csc());
848 }
849
850 #[test]
851 #[cfg_attr(miri, ignore)]
852 fn read_non_pattern_mtx_to_pattern() {
853 let path = "data/matrix_market/simple.mm";
854 let mat = read_matrix_market::<Pattern, usize, _>(path).unwrap();
855 println!("trimat: {:?}", mat);
856 let csc = mat.to_csc();
857 assert_eq!(csc.get(1, 1), Some(&Pattern {}));
858 assert_eq!(csc.get(2, 2), Some(&Pattern {}));
859 assert_eq!(csc.get(3, 3), Some(&Pattern {}));
860 assert_eq!(csc.get(0, 1), None);
861 let expected = CsMat::new_csc(
862 (5, 5),
863 vec![0, 1, 3, 4, 6, 8],
864 vec![0, 1, 3, 2, 0, 3, 3, 4],
865 vec![Pattern {}; 8],
866 );
867 assert_eq!(csc.get(0, 0), Some(&Pattern {}));
868 assert_eq!(csc.get(1, 1), Some(&Pattern {}));
869 assert_eq!(csc.get(2, 2), Some(&Pattern {}));
870 assert_eq!(csc.get(3, 3), Some(&Pattern {}));
871 assert_eq!(csc.get(0, 1), None);
872 assert_eq!(csc, expected);
873 let tmp_dir = tempdir().unwrap();
874 let save_path = tmp_dir.path().join("non_pattern_write.mm");
875 write_matrix_market(&save_path, &csc).unwrap();
876 let mat2 = read_matrix_market::<Pattern, usize, _>(&save_path).unwrap();
877 assert_eq!(csc, mat2.to_csc());
878 }
879
880 #[test]
881 #[cfg_attr(miri, ignore)]
882 fn read_csc_to_csr_trans() {
883 let path = "data/matrix_market/simple.mm";
884 let mat = read_matrix_market::<Pattern, usize, _>(path).unwrap();
885 println!("trimat: {:?}", mat);
886 let csc: CsMat<Pattern> = mat.to_csc();
887 let csr = csc.to_csr();
888 let csc_copy = csr.to_csc();
889 let csr_copy = csc_copy.to_csr();
890
891 assert_eq!(csc, csc_copy);
892 assert_eq!(csr, csr_copy);
893 }
894}