use crate::SparseFormatError;
use crate::SparseFormatErrorKind;
use crate::{CooMatrix, CscMatrix, CsrMatrix};
use nalgebra::Complex;
use pest::Parser;
use pest::iterators::Pairs;
use std::cmp::PartialEq;
use std::convert::Infallible;
use std::convert::TryFrom;
use std::fmt;
use std::fmt::Formatter;
use std::fs::{self, File};
use std::io::{BufWriter, Write};
use std::num::ParseIntError;
use std::num::TryFromIntError;
use std::path::Path;
use std::str::FromStr;
#[derive(Debug)]
pub struct MatrixMarketError {
error_kind: MatrixMarketErrorKind,
message: String,
}
#[non_exhaustive]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum MatrixMarketErrorKind {
ParsingError,
InvalidHeader,
EntryMismatch,
TypeMismatch,
ZeroError,
SparseFormatError(SparseFormatErrorKind),
DiagonalError,
IOError(std::io::ErrorKind),
NotLowerTriangle,
NonSquare,
}
impl MatrixMarketError {
fn from_kind_and_message(error_type: MatrixMarketErrorKind, message: String) -> Self {
Self {
error_kind: error_type,
message,
}
}
#[must_use]
pub fn kind(&self) -> MatrixMarketErrorKind {
self.error_kind
}
#[must_use]
pub fn message(&self) -> &str {
self.message.as_str()
}
}
impl fmt::Display for MatrixMarketError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "Matrix Market error: ")?;
match self.kind() {
MatrixMarketErrorKind::ParsingError => {
write!(f, "ParsingError,")?;
}
MatrixMarketErrorKind::InvalidHeader => {
write!(f, "InvalidHeader,")?;
}
MatrixMarketErrorKind::EntryMismatch => {
write!(f, "EntryMismatch,")?;
}
MatrixMarketErrorKind::TypeMismatch => {
write!(f, "TypeMismatch,")?;
}
MatrixMarketErrorKind::SparseFormatError(_) => {
write!(f, "SparseFormatError,")?;
}
MatrixMarketErrorKind::ZeroError => {
write!(f, "ZeroError,")?;
}
MatrixMarketErrorKind::IOError(_) => {
write!(f, "IOError,")?;
}
MatrixMarketErrorKind::DiagonalError => {
write!(f, "DiagonalError,")?;
}
MatrixMarketErrorKind::NotLowerTriangle => {
write!(f, "NotLowerTriangle,")?;
}
MatrixMarketErrorKind::NonSquare => {
write!(f, "NonSquare,")?;
}
}
write!(f, " message: {}", self.message)
}
}
impl std::error::Error for MatrixMarketError {}
impl MatrixMarketError {
fn from_pest_error<T>(error: pest::error::Error<T>) -> Self
where
T: fmt::Debug + std::hash::Hash + std::marker::Copy + Ord,
{
Self::from_kind_and_message(
MatrixMarketErrorKind::ParsingError,
format!("Can't parse the data.\n Error: {}", error),
)
}
}
impl From<ParseIntError> for MatrixMarketError {
fn from(err: ParseIntError) -> Self {
Self::from_kind_and_message(
MatrixMarketErrorKind::ParsingError,
format!("Can't parse data as i128.\n Error: {}", err),
)
}
}
impl From<SparseFormatError> for MatrixMarketError {
fn from(err: SparseFormatError) -> Self {
Self::from_kind_and_message(
MatrixMarketErrorKind::SparseFormatError(*err.kind()),
format!("{}", &err),
)
}
}
impl From<std::io::Error> for MatrixMarketError {
fn from(err: std::io::Error) -> Self {
Self::from_kind_and_message(
MatrixMarketErrorKind::IOError(err.kind()),
format!("{}", &err),
)
}
}
impl From<TryFromIntError> for MatrixMarketError {
fn from(err: TryFromIntError) -> Self {
Self::from_kind_and_message(
MatrixMarketErrorKind::TypeMismatch,
format!(
"Please consider using a larger integer type. Error message: {}",
&err
),
)
}
}
impl From<Infallible> for MatrixMarketError {
fn from(_err: Infallible) -> Self {
Self::from_kind_and_message(
MatrixMarketErrorKind::TypeMismatch,
format!("This won't happen"),
)
}
}
#[derive(Debug, PartialEq)]
enum Sparsity {
Sparse,
Dense,
}
#[derive(Debug, PartialEq)]
enum DataType {
Real,
Complex,
Pattern,
Integer,
}
#[derive(Debug, PartialEq)]
enum StorageScheme {
Symmetric,
General,
Skew,
Hermitian,
}
#[derive(Debug, PartialEq)]
struct Typecode {
sparsity: Sparsity,
datatype: DataType,
storagescheme: StorageScheme,
}
impl FromStr for Sparsity {
type Err = MatrixMarketError;
fn from_str(word: &str) -> Result<Self, Self::Err> {
match word {
"coordinate" => Ok(Sparsity::Sparse),
"array" => Ok(Sparsity::Dense),
_ => Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::ParsingError,
format!("keyword {} is unknown", word),
)),
}
}
}
impl FromStr for DataType {
type Err = MatrixMarketError;
fn from_str(word: &str) -> Result<Self, Self::Err> {
match word {
"real" => Ok(DataType::Real),
"complex" => Ok(DataType::Complex),
"integer" => Ok(DataType::Integer),
"pattern" => Ok(DataType::Pattern),
_ => Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::ParsingError,
format!("keyword {} is unknown", word),
)),
}
}
}
impl FromStr for StorageScheme {
type Err = MatrixMarketError;
fn from_str(word: &str) -> Result<Self, Self::Err> {
match word {
"skew-symmetric" => Ok(StorageScheme::Skew),
"general" => Ok(StorageScheme::General),
"symmetric" => Ok(StorageScheme::Symmetric),
"hermitian" => Ok(StorageScheme::Hermitian),
_ => Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::ParsingError,
format!("keyword {} is unknown", word),
)),
}
}
}
fn typecode_precheck(tc: &Typecode) -> Result<(), MatrixMarketError> {
match tc {
Typecode {
datatype: DataType::Real,
storagescheme: StorageScheme::Hermitian,
..
} => Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::InvalidHeader,
String::from("Real matrix can't be hermitian."),
)),
Typecode {
datatype: DataType::Integer,
storagescheme: StorageScheme::Hermitian,
..
} => Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::InvalidHeader,
String::from("Integer matrix can't be hermitian."),
)),
Typecode {
datatype: DataType::Pattern,
storagescheme: StorageScheme::Hermitian,
..
} => Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::InvalidHeader,
String::from("Pattern matrix can't be hermitian."),
)),
Typecode {
datatype: DataType::Pattern,
storagescheme: StorageScheme::Skew,
..
} => Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::InvalidHeader,
String::from("Pattern matrix can't be skew-symmetric."),
)),
Typecode {
datatype: DataType::Pattern,
sparsity: Sparsity::Dense,
..
} => Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::InvalidHeader,
String::from("Dense matrix can't be pattern matrix."),
)),
_ => Ok(()),
}
}
mod internal {
use crate::io::MatrixMarketError;
use na::{Complex, Scalar};
pub trait SupportedMatrixMarketScalar: Scalar {
fn from_i128(i: i128) -> Result<Self, MatrixMarketError>;
fn from_f64(f: f64) -> Result<Self, MatrixMarketError>;
fn from_c64(c: Complex<f64>) -> Result<Self, MatrixMarketError>;
fn from_pattern(p: ()) -> Result<Self, MatrixMarketError>;
fn negative(self) -> Result<Self, MatrixMarketError>;
fn conjugate(self) -> Result<Self, MatrixMarketError>;
fn typename() -> &'static str;
fn write_matrix_market<W: std::fmt::Write>(&self, w: W) -> Result<(), std::fmt::Error>;
}
pub trait SupportedMatrixMarketExport<T: SupportedMatrixMarketScalar> {
fn triplet_iter(&self) -> Box<dyn Iterator<Item = (usize, usize, &T)> + '_>;
fn nrows(&self) -> usize;
fn ncols(&self) -> usize;
fn nnz(&self) -> usize;
}
}
pub trait MatrixMarketScalar: internal::SupportedMatrixMarketScalar {}
macro_rules! mm_int_impl {
($T:ty) => {
impl MatrixMarketScalar for $T {}
impl internal::SupportedMatrixMarketScalar for $T {
#[inline]
fn from_i128(i: i128) -> Result<Self, MatrixMarketError> {
Ok(Self::try_from(i)?)
}
#[inline]
fn from_f64(_f: f64) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeMismatch,
format!("Int type can't be parsed from f64"),
))
}
#[inline]
fn from_c64(_c: Complex<f64>) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeMismatch,
format!("Int type can't be parsed from Complex<f64>"),
))
}
#[inline]
fn from_pattern(_p: ()) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeMismatch,
format!("Int type can't be parsed from ()"),
))
}
#[inline]
fn conjugate(self) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeMismatch,
format!("Int type has no conjugate"),
))
}
#[inline]
fn negative(self) -> Result<Self, MatrixMarketError> {
Ok(-self)
}
#[inline]
fn typename() -> &'static str {
"integer"
}
#[inline]
fn write_matrix_market<W: std::fmt::Write>(
&self,
mut w: W,
) -> Result<(), std::fmt::Error> {
write!(w, "{}", self)
}
}
};
}
macro_rules! mm_real_impl {
($T:ty) => {
impl MatrixMarketScalar for $T {}
impl internal::SupportedMatrixMarketScalar for $T {
#[inline]
fn from_i128(_i: i128) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeMismatch,
format!("real type can't be parsed from i128"),
))
}
#[inline]
fn from_f64(f: f64) -> Result<Self, MatrixMarketError> {
Ok(f as Self)
}
#[inline]
fn from_c64(_c: Complex<f64>) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeMismatch,
format!("real type can't be parsed from Complex<f64>"),
))
}
#[inline]
fn from_pattern(_p: ()) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeMismatch,
format!("real type can't be parsed from ()"),
))
}
#[inline]
fn conjugate(self) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeMismatch,
format!("real type has no conjugate"),
))
}
#[inline]
fn negative(self) -> Result<Self, MatrixMarketError> {
Ok(-self)
}
#[inline]
fn typename() -> &'static str {
"real"
}
#[inline]
fn write_matrix_market<W: std::fmt::Write>(
&self,
mut w: W,
) -> Result<(), std::fmt::Error> {
write!(w, "{}", self)
}
}
};
}
macro_rules! mm_complex_impl {
($T:ty) => {
impl MatrixMarketScalar for Complex<$T> {}
impl internal::SupportedMatrixMarketScalar for Complex<$T> {
#[inline]
fn from_i128(_i: i128) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeMismatch,
format!("Complex type can't be parsed from i128"),
))
}
#[inline]
fn from_f64(_f: f64) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeMismatch,
format!("Complex type can't be parsed from f64"),
))
}
#[inline]
fn from_c64(c: Complex<f64>) -> Result<Self, MatrixMarketError> {
Ok(Self {
re: c.re as $T,
im: c.im as $T,
})
}
#[inline]
fn from_pattern(_p: ()) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeMismatch,
format!("Complex type can't be parsed from ()"),
))
}
#[inline]
fn conjugate(self) -> Result<Self, MatrixMarketError> {
Ok(self.conj())
}
#[inline]
fn negative(self) -> Result<Self, MatrixMarketError> {
Ok(-self)
}
#[inline]
fn typename() -> &'static str {
"complex"
}
#[inline]
fn write_matrix_market<W: std::fmt::Write>(
&self,
mut w: W,
) -> Result<(), std::fmt::Error> {
write!(w, "{} {}", self.re, self.im)
}
}
};
}
macro_rules! mm_pattern_impl {
($T:ty) => {
impl MatrixMarketScalar for $T {}
impl internal::SupportedMatrixMarketScalar for $T {
#[inline]
fn from_i128(_i: i128) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeMismatch,
format!("Pattern type can't be parsed from i128"),
))
}
#[inline]
fn from_f64(_f: f64) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeMismatch,
format!("Pattern type can't be parsed from f64"),
))
}
#[inline]
fn from_c64(_c: Complex<f64>) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeMismatch,
format!("Pattern type can't be parsed from Complex<f64>"),
))
}
#[inline]
fn from_pattern(p: ()) -> Result<Self, MatrixMarketError> {
Ok(p)
}
#[inline]
fn conjugate(self) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeMismatch,
format!("Pattern type has no conjugate"),
))
}
#[inline]
fn negative(self) -> Result<Self, MatrixMarketError> {
Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::TypeMismatch,
format!("Pattern type has no negative"),
))
}
#[inline]
fn typename() -> &'static str {
"pattern"
}
#[inline]
fn write_matrix_market<W: std::fmt::Write>(
&self,
mut _w: W,
) -> Result<(), std::fmt::Error> {
Ok(())
}
}
};
}
mm_int_impl!(i8);
mm_int_impl!(i16);
mm_int_impl!(i32);
mm_int_impl!(i64);
mm_int_impl!(i128);
mm_real_impl!(f32);
mm_real_impl!(f64);
mm_complex_impl!(f32);
mm_complex_impl!(f64);
mm_pattern_impl!(());
pub trait MatrixMarketExport<T: MatrixMarketScalar>:
internal::SupportedMatrixMarketExport<T>
{
}
macro_rules! mm_matrix_impl {
($T_MATRIX:ty) => {
impl<T: MatrixMarketScalar> MatrixMarketExport<T> for $T_MATRIX {}
impl<T: internal::SupportedMatrixMarketScalar> internal::SupportedMatrixMarketExport<T>
for $T_MATRIX
{
#[inline]
fn triplet_iter(&self) -> Box<dyn Iterator<Item = (usize, usize, &T)> + '_> {
Box::new(self.triplet_iter())
}
#[inline]
fn nrows(&self) -> usize {
self.nrows()
}
#[inline]
fn ncols(&self) -> usize {
self.ncols()
}
#[inline]
fn nnz(&self) -> usize {
self.nnz()
}
}
};
}
mm_matrix_impl!(CooMatrix<T>);
mm_matrix_impl!(CsrMatrix<T>);
mm_matrix_impl!(CscMatrix<T>);
#[derive(Parser)]
#[grammar = "io/matrix_market.pest"]
struct MatrixMarketParser;
pub fn load_coo_from_matrix_market_file<T, P: AsRef<Path>>(
path: P,
) -> Result<CooMatrix<T>, MatrixMarketError>
where
T: MatrixMarketScalar,
{
let file = fs::read_to_string(path)?;
load_coo_from_matrix_market_str(&file)
}
pub fn load_coo_from_matrix_market_str<T>(data: &str) -> Result<CooMatrix<T>, MatrixMarketError>
where
T: MatrixMarketScalar,
{
let file = MatrixMarketParser::parse(Rule::Document, data)
.map_err(MatrixMarketError::from_pest_error)?
.next()
.unwrap();
let mut rows: Vec<usize> = Vec::new();
let mut cols: Vec<usize> = Vec::new();
let mut data: Vec<T> = Vec::new();
let mut lines = file.into_inner();
let header_line = lines.next().unwrap();
let header_type = parse_header(&mut header_line.into_inner());
typecode_precheck(&header_type)?;
let shape_line = lines.next().unwrap();
let shape: (usize, usize, usize);
match header_type.sparsity {
Sparsity::Sparse => {
shape = parse_sparse_shape(&mut shape_line.into_inner(), &header_type.storagescheme)?;
}
Sparsity::Dense => {
shape = parse_dense_shape(&mut shape_line.into_inner(), &header_type.storagescheme)?;
}
}
let mut current_dense_coordinate: (usize, usize) = (0, 0);
if header_type.storagescheme == StorageScheme::Skew {
current_dense_coordinate = (1, 0);
}
let count = lines.clone().count();
if count != shape.2 {
return Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::EntryMismatch,
format!(
"{} entries required for the matrix, but {} was provided",
shape.2, count,
),
));
}
for data_line in lines {
let entry: (usize, usize, T);
match header_type {
Typecode {
sparsity: Sparsity::Sparse,
datatype: DataType::Real,
..
} => {
entry = parse_sparse_real::<T>(&mut data_line.into_inner())?;
}
Typecode {
sparsity: Sparsity::Sparse,
datatype: DataType::Integer,
..
} => {
entry = parse_sparse_int::<T>(&mut data_line.into_inner())?;
}
Typecode {
sparsity: Sparsity::Sparse,
datatype: DataType::Pattern,
..
} => {
entry = parse_sparse_pattern::<T>(&mut data_line.into_inner())?;
}
Typecode {
sparsity: Sparsity::Sparse,
datatype: DataType::Complex,
..
} => {
entry = parse_sparse_complex::<T>(&mut data_line.into_inner())?;
}
Typecode {
sparsity: Sparsity::Dense,
datatype: DataType::Complex,
..
} => {
entry = (
current_dense_coordinate.0,
current_dense_coordinate.1,
parse_dense_complex::<T>(&mut data_line.into_inner())?,
);
next_dense_coordinate(
&mut current_dense_coordinate,
shape,
&header_type.storagescheme,
);
}
Typecode {
sparsity: Sparsity::Dense,
datatype: DataType::Real,
..
} => {
entry = (
current_dense_coordinate.0,
current_dense_coordinate.1,
parse_dense_real::<T>(&mut data_line.into_inner())?,
);
next_dense_coordinate(
&mut current_dense_coordinate,
shape,
&header_type.storagescheme,
);
}
Typecode {
sparsity: Sparsity::Dense,
datatype: DataType::Integer,
..
} => {
entry = (
current_dense_coordinate.0,
current_dense_coordinate.1,
parse_dense_int::<T>(&mut data_line.into_inner())?,
);
next_dense_coordinate(
&mut current_dense_coordinate,
shape,
&header_type.storagescheme,
);
}
_ => {
entry = (1, 1, T::from_i128(1)?)
}
}
let (r, c, d) = entry;
match header_type.storagescheme {
StorageScheme::General => {
rows.push(r);
cols.push(c);
data.push(d);
}
StorageScheme::Symmetric => {
check_lower_triangle(r, c)?;
rows.push(r);
cols.push(c);
data.push(d.clone());
if r != c {
rows.push(c);
cols.push(r);
data.push(d);
}
}
StorageScheme::Skew => {
check_lower_triangle(r, c)?;
rows.push(r);
cols.push(c);
data.push(d.clone());
if r == c {
return Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::DiagonalError,
format!(
"There is a diagonal element in skew matrix, in row(and column) {}",
r + 1
),
));
}
rows.push(c);
cols.push(r);
data.push(d.negative()?);
}
StorageScheme::Hermitian => {
check_lower_triangle(r, c)?;
rows.push(r);
cols.push(c);
data.push(d.clone());
if r == c && d != d.clone().conjugate()? {
return Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::DiagonalError,
format!(
"There is a diagonal element in hermitian matrix, which is not a real number, in row(and column) {}",
r + 1
),
));
}
if r != c {
rows.push(c);
cols.push(r);
data.push(d.conjugate()?);
}
}
}
}
Ok(CooMatrix::try_from_triplets(
shape.0, shape.1, rows, cols, data,
)?)
}
#[inline]
fn check_lower_triangle(r: usize, c: usize) -> Result<(), MatrixMarketError> {
if c > r {
return Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::NotLowerTriangle,
format!(
"Entry: row {} col {} should be put into lower triangle",
r, c
),
));
}
Ok(())
}
#[inline]
fn parse_header(inner: &mut Pairs<'_, Rule>) -> Typecode {
Typecode {
sparsity: inner
.next()
.unwrap()
.as_str()
.to_ascii_lowercase()
.parse::<Sparsity>()
.unwrap(),
datatype: inner
.next()
.unwrap()
.as_str()
.to_ascii_lowercase()
.parse::<DataType>()
.unwrap(),
storagescheme: inner
.next()
.unwrap()
.as_str()
.to_ascii_lowercase()
.parse::<StorageScheme>()
.unwrap(),
}
}
fn parse_sparse_shape(
inner: &mut Pairs<'_, Rule>,
storagescheme: &StorageScheme,
) -> Result<(usize, usize, usize), MatrixMarketError> {
let shape_inner = inner.next().unwrap();
if shape_inner.as_rule() != Rule::SparseShape {
return Err(MatrixMarketError::from_kind_and_message(MatrixMarketErrorKind::ParsingError,format!("
Shape shape line requires 3 int numbers as number of rows, columns and non-zeros, but line {} was provided here.
",shape_inner.as_str())));
}
let mut inner = shape_inner.into_inner();
let r = inner.next().unwrap().as_str().parse::<usize>().unwrap();
let c = inner.next().unwrap().as_str().parse::<usize>().unwrap();
let nnz = inner.next().unwrap().as_str().parse::<usize>().unwrap();
if *storagescheme != StorageScheme::General && r != c {
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
),
));
}
Ok((r, c, nnz))
}
fn parse_dense_shape(
inner: &mut Pairs<'_, Rule>,
storagescheme: &StorageScheme,
) -> Result<(usize, usize, usize), MatrixMarketError> {
let shape_inner = inner.next().unwrap();
if shape_inner.as_rule() != Rule::DenseShape {
return Err(MatrixMarketError::from_kind_and_message(MatrixMarketErrorKind::ParsingError,format!("
Shape shape line requires 2 int numbers as number of rows, columns, but line {} was provided here.
",shape_inner.as_str())));
}
let mut inner = shape_inner.into_inner();
let r = inner.next().unwrap().as_str().parse::<usize>().unwrap();
let c = inner.next().unwrap().as_str().parse::<usize>().unwrap();
if *storagescheme != StorageScheme::General && r != c {
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
),
));
}
let n: usize;
match storagescheme {
StorageScheme::General => {
n = r * c;
}
StorageScheme::Symmetric | StorageScheme::Hermitian => {
n = r * (r + 1) / 2;
}
StorageScheme::Skew => {
n = r * (r - 1) / 2;
}
}
Ok((r, c, n))
}
fn parse_sparse_real<T>(inner: &mut Pairs<'_, Rule>) -> Result<(usize, usize, T), MatrixMarketError>
where
T: MatrixMarketScalar,
{
let entry_inner = inner.next().unwrap();
if entry_inner.as_rule() != Rule::SparseReal {
return Err(MatrixMarketError::from_kind_and_message(MatrixMarketErrorKind::ParsingError,format!("
Spare real matrix requires 2 int number as coordinates and 1 real number as data, but line {} was provided.
",entry_inner.as_str() )));
}
let mut inner = entry_inner.into_inner();
let (r, c) = parse_sparse_coordinate(&mut inner)?;
let d = inner.next().unwrap().as_str().parse::<f64>().unwrap();
Ok((r, c, T::from_f64(d)?))
}
fn parse_sparse_int<T>(inner: &mut Pairs<'_, Rule>) -> Result<(usize, usize, T), MatrixMarketError>
where
T: MatrixMarketScalar,
{
let entry_inner = inner.next().unwrap();
if entry_inner.as_rule() != Rule::SparseReal {
return Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::ParsingError,
format!(
"
Spare real matrix requires 3 int number as coordinates and data, but line {} was provided.
",
entry_inner.as_str()
),
));
}
let mut inner = entry_inner.into_inner();
let (r, c) = parse_sparse_coordinate(&mut inner)?;
let d = inner.next().unwrap().as_str().parse::<i128>()?;
Ok((r, c, T::from_i128(d)?))
}
fn parse_sparse_pattern<T>(
inner: &mut Pairs<'_, Rule>,
) -> Result<(usize, usize, T), MatrixMarketError>
where
T: MatrixMarketScalar,
{
let entry_inner = inner.next().unwrap();
if entry_inner.as_rule() != Rule::SparsePattern {
return Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::ParsingError,
format!(
"
Spare real matrix requires 2 int number as coordinates, but line {} was provided.
",
entry_inner.as_str()
),
));
}
let mut inner = entry_inner.into_inner();
let (r, c) = parse_sparse_coordinate(&mut inner)?;
Ok((r, c, T::from_pattern(())?))
}
fn parse_sparse_complex<T>(
inner: &mut Pairs<'_, Rule>,
) -> Result<(usize, usize, T), MatrixMarketError>
where
T: MatrixMarketScalar,
{
let entry_inner = inner.next().unwrap();
if entry_inner.as_rule() != Rule::SparseComplex {
return Err(MatrixMarketError::from_kind_and_message(MatrixMarketErrorKind::ParsingError,format!("
Spare real matrix requires 2 int number as coordinates and 2 real number as complex data, but line {} was provided.
",entry_inner.as_str() )));
}
let mut inner = entry_inner.into_inner();
let (r, c) = parse_sparse_coordinate(&mut inner)?;
let real = inner.next().unwrap().as_str().parse::<f64>().unwrap();
let imag = inner.next().unwrap().as_str().parse::<f64>().unwrap();
let complex = Complex::<f64>::new(real, imag);
Ok((r, c, T::from_c64(complex)?))
}
fn parse_dense_real<T>(inner: &mut Pairs<'_, Rule>) -> Result<T, MatrixMarketError>
where
T: MatrixMarketScalar,
{
let entry_inner = inner.next().unwrap();
if entry_inner.as_rule() != Rule::DenseReal {
return Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::ParsingError,
format!(
"
Dense real matrix requires 1 real number as data, but line {} was provided.
",
entry_inner.as_str()
),
));
}
let mut inner = entry_inner.into_inner();
let d = inner.next().unwrap().as_str().parse::<f64>().unwrap();
Ok(T::from_f64(d)?)
}
fn parse_dense_int<T>(inner: &mut Pairs<'_, Rule>) -> Result<T, MatrixMarketError>
where
T: MatrixMarketScalar,
{
let entry_inner = inner.next().unwrap();
if entry_inner.as_rule() != Rule::DenseReal {
return Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::ParsingError,
format!(
"
Dense real matrix requires 1 int number as data, but line {} was provided.
",
entry_inner.as_str()
),
));
}
let mut inner = entry_inner.into_inner();
let d = inner.next().unwrap().as_str().parse::<i128>()?;
Ok(T::from_i128(d)?)
}
fn parse_dense_complex<T>(inner: &mut Pairs<'_, Rule>) -> Result<T, MatrixMarketError>
where
T: MatrixMarketScalar,
{
let entry_inner = inner.next().unwrap();
if entry_inner.as_rule() != Rule::DenseComplex && entry_inner.as_rule() != Rule::SparsePattern {
return Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::ParsingError,
format!(
"
Dense real matrix requires 2 real number as complex data, but line {} was provided.
",
entry_inner.as_str()
),
));
}
let mut inner = entry_inner.into_inner();
let real = inner.next().unwrap().as_str().parse::<f64>().unwrap();
let imag = inner.next().unwrap().as_str().parse::<f64>().unwrap();
let complex = Complex::<f64>::new(real, imag);
Ok(T::from_c64(complex)?)
}
fn parse_sparse_coordinate(
inner: &mut Pairs<'_, Rule>,
) -> Result<(usize, usize), MatrixMarketError> {
let r = inner.next().unwrap().as_str().parse::<usize>().unwrap();
let c = inner.next().unwrap().as_str().parse::<usize>().unwrap();
if r * c == 0 {
return Err(MatrixMarketError::from_kind_and_message(
MatrixMarketErrorKind::ZeroError,
String::from("The data has to be one-indexed"),
));
}
Ok((r - 1, c - 1))
}
fn next_dense_coordinate(
current_dense_coordinate: &mut (usize, usize),
shape: (usize, usize, usize),
storagescheme: &StorageScheme,
) {
match storagescheme {
StorageScheme::General => {
if current_dense_coordinate.0 < shape.0 - 1 {
current_dense_coordinate.0 += 1
} else {
current_dense_coordinate.0 = 0;
current_dense_coordinate.1 += 1;
}
}
StorageScheme::Symmetric | StorageScheme::Hermitian => {
if current_dense_coordinate.0 < shape.0 - 1 {
current_dense_coordinate.0 += 1
} else {
current_dense_coordinate.1 += 1;
current_dense_coordinate.0 = current_dense_coordinate.1;
}
}
StorageScheme::Skew => {
if current_dense_coordinate.0 < shape.0 - 1 {
current_dense_coordinate.0 += 1;
} else {
current_dense_coordinate.1 += 1;
current_dense_coordinate.0 = current_dense_coordinate.1 + 1;
}
}
}
}
pub fn save_to_matrix_market_str<T, S>(sparse_matrix: &S) -> String
where
T: MatrixMarketScalar,
S: MatrixMarketExport<T>,
{
let mut bytes = Vec::<u8>::new();
save_to_matrix_market(&mut bytes, sparse_matrix).unwrap();
String::from_utf8(bytes)
.expect("Unexpected non UTF-8 data was generated when export to matrix market string")
}
pub fn save_to_matrix_market_file<T, S, P>(sparse_matrix: &S, path: P) -> Result<(), std::io::Error>
where
T: MatrixMarketScalar,
S: MatrixMarketExport<T>,
P: AsRef<Path>,
{
let file = File::create(path)?;
let mut file = BufWriter::new(file);
save_to_matrix_market(&mut file, sparse_matrix)?;
file.flush()
.expect("Unexpected error when flushing the buffer data to File");
Ok(())
}
pub fn save_to_matrix_market<T, S, W>(mut w: W, sparse_matrix: &S) -> Result<(), std::io::Error>
where
T: MatrixMarketScalar,
S: MatrixMarketExport<T>,
W: Write,
{
writeln!(
w,
"%%matrixmarket matrix coordinate {} general",
T::typename()
)?;
writeln!(w, "% matrixmarket file generated by nalgebra-sparse.")?;
writeln!(
w,
"{} {} {}",
sparse_matrix.nrows(),
sparse_matrix.ncols(),
sparse_matrix.nnz()
)?;
let mut buffer = String::new();
for (r, c, d) in sparse_matrix.triplet_iter() {
buffer.clear();
d.write_matrix_market(&mut buffer)
.expect("Unexpected format error was generated when write to String");
writeln!(w, "{} {} {}", r + 1, c + 1, buffer)?;
}
Ok(())
}