add support for matrix market export

This commit is contained in:
Hantao Hui 2022-01-24 23:17:30 +01:00
parent 6cc633474d
commit d511e372de
4 changed files with 190 additions and 10 deletions

View File

@ -37,4 +37,4 @@ nalgebra = { version="0.30", path = "../", features = ["compare"] }
[package.metadata.docs.rs]
# Enable certain features when building docs for docs.rs
features = [ "proptest-support", "compare" ]
features = [ "proptest-support", "compare" , "io"]

View File

@ -1,9 +1,10 @@
//! Implementation of matrix market io code.
//!
//! 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.
use crate::coo::CooMatrix;
use crate::CooMatrix;
use crate::SparseFormatError;
use crate::SparseFormatErrorKind;
use matrixcompare_core::SparseAccess;
use nalgebra::Complex;
use pest::iterators::Pairs;
use pest::Parser;
@ -12,7 +13,8 @@ use std::convert::Infallible;
use std::convert::TryFrom;
use std::fmt;
use std::fmt::Formatter;
use std::fs;
use std::fs::{self, File};
use std::io::Write;
use std::num::ParseIntError;
use std::num::TryFromIntError;
use std::path::Path;
@ -267,7 +269,7 @@ impl fmt::Display for MatrixMarketError {
write!(f, "InvalidHeader,")?;
}
MatrixMarketErrorKind::EntryMismatch => {
write!(f, "EntryNumUnmatched,")?;
write!(f, "EntryMismatch,")?;
}
MatrixMarketErrorKind::TypeMismatch => {
write!(f, "TypeMismatch,")?;
@ -288,7 +290,7 @@ impl fmt::Display for MatrixMarketError {
write!(f, "NotLowerTriangle,")?;
}
MatrixMarketErrorKind::NonSquare => {
write!(f, "NotSquareMatrix,")?;
write!(f, "NonSquare,")?;
}
}
write!(f, " message: {}", self.message)
@ -506,6 +508,10 @@ mod internal {
fn negative(self) -> Result<Self, MatrixMarketError>;
/// When matrix is a Hermitian matrix, it will convert itself to its conjugate.
fn conjugate(self) -> Result<Self, MatrixMarketError>;
/// Returns the name of SupportedMatrixMarketScalar, used when write the matrix
fn typename() -> &'static str;
/// Convert the data to string
fn to_matrixmarket_string(&self) -> String;
}
}
@ -557,6 +563,14 @@ macro_rules! mm_int_impl {
fn negative(self) -> Result<Self, MatrixMarketError> {
Ok(-self)
}
#[inline]
fn typename() -> &'static str {
"integer"
}
#[inline]
fn to_matrixmarket_string(&self) -> String {
self.to_string()
}
}
};
}
@ -602,6 +616,14 @@ macro_rules! mm_real_impl {
fn negative(self) -> Result<Self, MatrixMarketError> {
Ok(-self)
}
#[inline]
fn typename() -> &'static str {
"real"
}
#[inline]
fn to_matrixmarket_string(&self) -> String {
self.to_string()
}
}
};
}
@ -648,6 +670,14 @@ macro_rules! mm_complex_impl {
fn negative(self) -> Result<Self, MatrixMarketError> {
Ok(-self)
}
#[inline]
fn typename() -> &'static str {
"complex"
}
#[inline]
fn to_matrixmarket_string(&self) -> String {
self.re.to_string() + " " + &self.im.to_string()
}
}
};
}
@ -697,6 +727,15 @@ macro_rules! mm_pattern_impl {
format!("Pattern type has no negative"),
))
}
#[inline]
fn typename() -> &'static str {
"pattern"
}
#[inline]
fn to_matrixmarket_string(&self) -> String {
// pattern type will return an empty string
String::new()
}
}
};
}
@ -1329,3 +1368,89 @@ fn next_dense_coordinate(
}
}
}
/// Write a sparse matrix into Matrix Market format string.
///
/// Our exporter only writes matrix into `coordiante` and `general` format.
///
///
/// Examples
/// --------
/// ```
/// # use matrixcompare::assert_matrix_eq;
/// use nalgebra_sparse::io::{write_to_matrix_market_str,load_coo_from_matrix_market_str};
/// let str = r#"
/// %%matrixmarket matrix coordinate integer general
/// 5 4 2
/// 1 1 10
/// 2 3 5
/// "#;
/// let matrix = load_coo_from_matrix_market_str::<i32>(&str).unwrap();
/// let generated_matrixmarket_string = write_to_matrix_market_str(&matrix);
/// // 'generated_matrixmarket' should equal to the 'matrix'
/// let generated_matrixmarket = load_coo_from_matrix_market_str::<i32>(&generated_matrixmarket_string).unwrap();
/// assert_matrix_eq!(matrix,generated_matrixmarket);
/// ```
pub fn write_to_matrix_market_str<T: MatrixMarketScalar, S: SparseAccess<T>>(
sparse_matrix: &S,
) -> String {
let mut matrixmarket_string = String::new();
// write header
matrixmarket_string.push_str("%%matrixmarket matrix coordinate ");
matrixmarket_string.push_str(T::typename());
matrixmarket_string.push_str(" general\n% matrixmarket file generated by nalgebra-sparse.\n");
// write shape information
matrixmarket_string.push_str(&sparse_matrix.rows().to_string());
matrixmarket_string.push(' ');
matrixmarket_string.push_str(&sparse_matrix.cols().to_string());
matrixmarket_string.push(' ');
matrixmarket_string.push_str(&sparse_matrix.nnz().to_string());
matrixmarket_string.push('\n');
for (r, c, d) in sparse_matrix.fetch_triplets() {
matrixmarket_string.push_str(&(r + 1).to_string());
matrixmarket_string.push_str(" ");
matrixmarket_string.push_str(&(c + 1).to_string());
matrixmarket_string.push_str(" ");
matrixmarket_string.push_str(&d.to_matrixmarket_string());
matrixmarket_string.push_str("\n");
}
matrixmarket_string
}
/// Write a sparse matrix into Matrix Market format file.
///
/// Our exporter only writes matrix into `coordiante` and `general` format.
///
///
/// Errors
/// --------
///
/// See [MatrixMarketErrorKind] for a list of possible error conditions.
///
/// Examples
/// --------
/// ```
/// use nalgebra_sparse::io::{write_to_matrix_market_file,load_coo_from_matrix_market_str};
/// let str = r#"
/// %%matrixmarket matrix coordinate integer general
/// 5 4 2
/// 1 1 10
/// 2 3 5
/// "#;
/// let matrix = load_coo_from_matrix_market_str::<i32>(&str).unwrap();
/// let res = write_to_matrix_market_file(&matrix,"path/to/matrix.mtx");
/// if res.is_err(){
/// // do something
/// }
/// ```
pub fn write_to_matrix_market_file<T: MatrixMarketScalar, S: SparseAccess<T>, P: AsRef<Path>>(
matrix: &S,
path: P,
) -> Result<(), MatrixMarketError> {
let matrixmarket_string = write_to_matrix_market_str(matrix);
let mut file = File::create(path)?;
write!(file, "{}", matrixmarket_string)?;
Ok(())
}

View File

@ -6,7 +6,7 @@
//!
//! | Format | Import | Export |
//! | ------------------------------------------------|------------|------------|
//! | [Matrix market](#matrix-market-format) | Yes | No |
//! | [Matrix market](#matrix-market-format) | Yes | Yes |
//!
//! [Matrix market]: https://math.nist.gov/MatrixMarket/formats.html
//!
@ -32,7 +32,7 @@
//! > "*The Matrix Market Exchange Formats: Initial Design.*" (1996).
pub use self::matrix_market::{
load_coo_from_matrix_market_file, load_coo_from_matrix_market_str, MatrixMarketError,
MatrixMarketErrorKind, MatrixMarketScalar,
load_coo_from_matrix_market_file, load_coo_from_matrix_market_str, write_to_matrix_market_file,
write_to_matrix_market_str, MatrixMarketError, MatrixMarketErrorKind, MatrixMarketScalar,
};
mod matrix_market;

View File

@ -1,7 +1,7 @@
use matrixcompare::assert_matrix_eq;
use nalgebra::dmatrix;
use nalgebra::Complex;
use nalgebra_sparse::io::load_coo_from_matrix_market_str;
use nalgebra_sparse::io::{load_coo_from_matrix_market_str, write_to_matrix_market_str};
use nalgebra_sparse::CooMatrix;
#[test]
@ -19,6 +19,10 @@ fn test_matrixmarket_sparse_real_general_empty() {
assert_eq!(sparse_mat.nrows(), shape.0);
assert_eq!(sparse_mat.ncols(), shape.1);
assert_eq!(sparse_mat.nnz(), 0);
let generated_matrixmarket_str = write_to_matrix_market_str(&sparse_mat);
let generated_matrix = load_coo_from_matrix_market_str::<f32>(&generated_matrixmarket_str).unwrap();
assert_matrix_eq!(sparse_mat, generated_matrix);
}
}
@ -37,6 +41,10 @@ fn test_matrixmarket_dense_real_general_empty() {
assert_eq!(sparse_mat.nrows(), shape.0);
assert_eq!(sparse_mat.ncols(), shape.1);
assert_eq!(sparse_mat.nnz(), 0);
let generated_matrixmarket_str = write_to_matrix_market_str(&sparse_mat);
let generated_matrix = load_coo_from_matrix_market_str::<f32>(&generated_matrixmarket_str).unwrap();
assert_matrix_eq!(sparse_mat, generated_matrix);
}
}
@ -97,6 +105,10 @@ fn test_matrixmarket_sparse_real_general() {
0.0, 0.0, 0.0, 0.0, 12.0;
];
assert_matrix_eq!(sparse_mat, expected);
let generated_matrixmarket_str = write_to_matrix_market_str(&sparse_mat);
let generated_matrix = load_coo_from_matrix_market_str::<f32>(&generated_matrixmarket_str).unwrap();
assert_matrix_eq!(expected, generated_matrix);
}
#[test]
@ -125,6 +137,10 @@ fn test_matrixmarket_sparse_int_symmetric() {
-15, 0, 35, 0, 55;
];
assert_matrix_eq!(sparse_mat, expected);
let generated_matrixmarket_str = write_to_matrix_market_str(&sparse_mat);
let generated_matrix = load_coo_from_matrix_market_str::<i128>(&generated_matrixmarket_str).unwrap();
assert_matrix_eq!(expected, generated_matrix);
}
#[test]
@ -152,6 +168,10 @@ fn test_matrixmarket_sparse_complex_hermitian() {
Complex::<f64>{re:0.0,im:0.0}, Complex::<f64>{re:0.0,im:0.0}, Complex::<f64>{re:0.0,im:0.0}, Complex::<f64>{re:0.0,im:33.32},Complex::<f64>{re:12.0,im:0.0};
];
assert_matrix_eq!(sparse_mat, expected);
let generated_matrixmarket_str = write_to_matrix_market_str(&sparse_mat);
let generated_matrix = load_coo_from_matrix_market_str::<Complex<f64>>(&generated_matrixmarket_str).unwrap();
assert_matrix_eq!(expected, generated_matrix);
}
#[test]
@ -175,6 +195,10 @@ fn test_matrixmarket_sparse_real_skew() {
-15.0, 0.0, -35.0, 0.0, 0.0;
];
assert_matrix_eq!(sparse_mat, expected);
let generated_matrixmarket_str = write_to_matrix_market_str(&sparse_mat);
let generated_matrix = load_coo_from_matrix_market_str::<f64>(&generated_matrixmarket_str).unwrap();
assert_matrix_eq!(expected, generated_matrix);
}
#[test]
@ -198,7 +222,7 @@ fn test_matrixmarket_sparse_pattern_general() {
let pattern_matrix = load_coo_from_matrix_market_str::<()>(file_str).unwrap();
let nrows = pattern_matrix.nrows();
let ncols = pattern_matrix.ncols();
let (row_idx, col_idx, val) = pattern_matrix.disassemble();
let (row_idx, col_idx, val) = pattern_matrix.clone().disassemble();
let values = vec![1; val.len()];
let sparse_mat = CooMatrix::try_from_triplets(nrows, ncols, row_idx, col_idx, values).unwrap();
let expected = dmatrix![
@ -209,6 +233,17 @@ fn test_matrixmarket_sparse_pattern_general() {
0, 1, 0, 1, 1;
];
assert_matrix_eq!(sparse_mat, expected);
let generated_matrixmarket_str = write_to_matrix_market_str(&pattern_matrix);
let generated_matrix = load_coo_from_matrix_market_str::<()>(&generated_matrixmarket_str).unwrap();
let nrows = generated_matrix.nrows();
let ncols = generated_matrix.ncols();
let (row_idx, col_idx, val) = generated_matrix.clone().disassemble();
let values = vec![1; val.len()];
let generated_sparse_mat = CooMatrix::try_from_triplets(nrows, ncols, row_idx, col_idx, values).unwrap();
assert_matrix_eq!(expected, generated_sparse_mat);
}
#[test]
@ -240,6 +275,10 @@ fn test_matrixmarket_dense_real_general() {
4.0, 8.0, 12.0;
];
assert_matrix_eq!(sparse_mat, expected);
let generated_matrixmarket_str = write_to_matrix_market_str(&sparse_mat);
let generated_matrix = load_coo_from_matrix_market_str::<f32>(&generated_matrixmarket_str).unwrap();
assert_matrix_eq!(expected, generated_matrix);
}
#[test]
@ -269,6 +308,10 @@ fn test_matrixmarket_dense_real_symmetric() {
4.0, 7.0, 9.0, 10.0;
];
assert_matrix_eq!(sparse_mat, expected);
let generated_matrixmarket_str = write_to_matrix_market_str(&sparse_mat);
let generated_matrix = load_coo_from_matrix_market_str::<f32>(&generated_matrixmarket_str).unwrap();
assert_matrix_eq!(expected, generated_matrix);
}
#[test]
@ -298,6 +341,10 @@ fn test_matrixmarket_dense_complex_hermitian() {
Complex::<f64>{re:4.0,im:4.0}, Complex::<f64>{re:7.0,im:7.0} ,Complex::<f64>{re:9.0,im:9.0} ,Complex::<f64>{re:10.0,im:0.0};
];
assert_matrix_eq!(sparse_mat, expected);
let generated_matrixmarket_str = write_to_matrix_market_str(&sparse_mat);
let generated_matrix = load_coo_from_matrix_market_str::<Complex<f64>>(&generated_matrixmarket_str).unwrap();
assert_matrix_eq!(expected, generated_matrix);
}
#[test]
@ -322,6 +369,10 @@ fn test_matrixmarket_dense_int_skew() {
3, 5, 6, 0;
];
assert_matrix_eq!(sparse_mat, expected);
let generated_matrixmarket_str = write_to_matrix_market_str(&sparse_mat);
let generated_matrix = load_coo_from_matrix_market_str::<i32>(&generated_matrixmarket_str).unwrap();
assert_matrix_eq!(expected, generated_matrix);
}
#[test]
@ -342,4 +393,8 @@ fn test_matrixmarket_dense_complex_general() {
Complex::<f32>{re:1.0,im:0.0},Complex::<f32>{re:1.0,im:0.0};
];
assert_matrix_eq!(sparse_mat, expected);
let generated_matrixmarket_str = write_to_matrix_market_str(&sparse_mat);
let generated_matrix = load_coo_from_matrix_market_str::<Complex<f32>>(&generated_matrixmarket_str).unwrap();
assert_matrix_eq!(expected, generated_matrix);
}