From 38989ed5f0f2b2cd9ab24413d251fcd5bca652bd Mon Sep 17 00:00:00 2001 From: Fabian Loeschner Date: Tue, 4 Jan 2022 15:15:52 +0100 Subject: [PATCH] Move sparse matrix serialization to separate files --- nalgebra-sparse/src/coo.rs | 72 +------------------ nalgebra-sparse/src/coo/coo_serde.rs | 65 +++++++++++++++++ nalgebra-sparse/src/csc.rs | 72 +------------------ nalgebra-sparse/src/csc/csc_serde.rs | 65 +++++++++++++++++ nalgebra-sparse/src/csr.rs | 73 ++------------------ nalgebra-sparse/src/csr/csr_serde.rs | 65 +++++++++++++++++ nalgebra-sparse/src/pattern.rs | 64 ++--------------- nalgebra-sparse/src/pattern/pattern_serde.rs | 56 +++++++++++++++ 8 files changed, 265 insertions(+), 267 deletions(-) create mode 100644 nalgebra-sparse/src/coo/coo_serde.rs create mode 100644 nalgebra-sparse/src/csc/csc_serde.rs create mode 100644 nalgebra-sparse/src/csr/csr_serde.rs create mode 100644 nalgebra-sparse/src/pattern/pattern_serde.rs diff --git a/nalgebra-sparse/src/coo.rs b/nalgebra-sparse/src/coo.rs index 91ba207d..2b302e37 100644 --- a/nalgebra-sparse/src/coo.rs +++ b/nalgebra-sparse/src/coo.rs @@ -1,5 +1,8 @@ //! An implementation of the COO sparse matrix format. +#[cfg(feature = "serde-serialize")] +mod coo_serde; + use crate::SparseFormatError; /// A COO representation of a sparse matrix. @@ -273,72 +276,3 @@ impl CooMatrix { (self.row_indices, self.col_indices, self.values) } } - -#[cfg(feature = "serde-serialize")] -mod serde_serialize { - use super::CooMatrix; - use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; - - /// This is an intermediate type for (de)serializing `CooMatrix`. - /// - /// Deserialization requires using a `try_from_*` function for validation. We could have used - /// the `remote = "Self"` trick (https://github.com/serde-rs/serde/issues/1220) which allows - /// to directly serialize/deserialize the original fields and combine it with validation. - /// However, this would lead to nested serialization of the `CsMatrix` and `SparsityPattern` - /// types. Instead, we decided that we want a more human-readable serialization format using - /// field names like `row_indices` and `col_indices`. The easiest way to achieve this is to - /// introduce an intermediate type. It also allows the serialization format to stay constant - /// even if the internal layout in `nalgebra` changes. - /// - /// We want to avoid unnecessary copies when serializing (i.e. cloning slices into owned - /// storage). Therefore, we use generic arguments to allow using slices during serialization and - /// owned storage (i.e. `Vec`) during deserialization. Without a major update of serde, slices - /// and `Vec`s should always (de)serialize identically. - #[derive(Serialize, Deserialize)] - struct CooMatrixSerializationData { - nrows: usize, - ncols: usize, - row_indices: Indices, - col_indices: Indices, - values: Values, - } - - impl Serialize for CooMatrix - where - T: Serialize + Clone, - { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - CooMatrixSerializationData::<&[usize], &[T]> { - nrows: self.nrows(), - ncols: self.ncols(), - row_indices: self.row_indices(), - col_indices: self.col_indices(), - values: self.values(), - } - .serialize(serializer) - } - } - - impl<'de, T> Deserialize<'de> for CooMatrix - where - T: Deserialize<'de> + Clone, - { - fn deserialize(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - let de = CooMatrixSerializationData::, Vec>::deserialize(deserializer)?; - CooMatrix::try_from_triplets( - de.nrows, - de.ncols, - de.row_indices, - de.col_indices, - de.values, - ) - .map_err(|e| de::Error::custom(e)) - } - } -} diff --git a/nalgebra-sparse/src/coo/coo_serde.rs b/nalgebra-sparse/src/coo/coo_serde.rs new file mode 100644 index 00000000..7ffcdf4a --- /dev/null +++ b/nalgebra-sparse/src/coo/coo_serde.rs @@ -0,0 +1,65 @@ +use crate::coo::CooMatrix; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + +/// This is an intermediate type for (de)serializing `CooMatrix`. +/// +/// Deserialization requires using a `try_from_*` function for validation. We could have used +/// the `remote = "Self"` trick (https://github.com/serde-rs/serde/issues/1220) which allows +/// to directly serialize/deserialize the original fields and combine it with validation. +/// However, this would lead to nested serialization of the `CsMatrix` and `SparsityPattern` +/// types. Instead, we decided that we want a more human-readable serialization format using +/// field names like `row_indices` and `col_indices`. The easiest way to achieve this is to +/// introduce an intermediate type. It also allows the serialization format to stay constant +/// even if the internal layout in `nalgebra` changes. +/// +/// We want to avoid unnecessary copies when serializing (i.e. cloning slices into owned +/// storage). Therefore, we use generic arguments to allow using slices during serialization and +/// owned storage (i.e. `Vec`) during deserialization. Without a major update of serde, slices +/// and `Vec`s should always (de)serialize identically. +#[derive(Serialize, Deserialize)] +struct CooMatrixSerializationData { + nrows: usize, + ncols: usize, + row_indices: Indices, + col_indices: Indices, + values: Values, +} + +impl Serialize for CooMatrix +where + T: Serialize + Clone, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + CooMatrixSerializationData::<&[usize], &[T]> { + nrows: self.nrows(), + ncols: self.ncols(), + row_indices: self.row_indices(), + col_indices: self.col_indices(), + values: self.values(), + } + .serialize(serializer) + } +} + +impl<'de, T> Deserialize<'de> for CooMatrix +where + T: Deserialize<'de> + Clone, +{ + fn deserialize(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let de = CooMatrixSerializationData::, Vec>::deserialize(deserializer)?; + CooMatrix::try_from_triplets( + de.nrows, + de.ncols, + de.row_indices, + de.col_indices, + de.values, + ) + .map_err(|e| de::Error::custom(e)) + } +} diff --git a/nalgebra-sparse/src/csc.rs b/nalgebra-sparse/src/csc.rs index 7c9bb74a..85e4013c 100644 --- a/nalgebra-sparse/src/csc.rs +++ b/nalgebra-sparse/src/csc.rs @@ -3,6 +3,9 @@ //! This is the module-level documentation. See [`CscMatrix`] for the main documentation of the //! CSC implementation. +#[cfg(feature = "serde-serialize")] +mod csc_serde; + use crate::cs::{CsLane, CsLaneIter, CsLaneIterMut, CsLaneMut, CsMatrix}; use crate::csr::CsrMatrix; use crate::pattern::{SparsityPattern, SparsityPatternFormatError, SparsityPatternIter}; @@ -520,75 +523,6 @@ impl CscMatrix { } } -#[cfg(feature = "serde-serialize")] -mod serde_serialize { - use super::CscMatrix; - use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; - - /// This is an intermediate type for (de)serializing `CscMatrix`. - /// - /// Deserialization requires using a `try_from_*` function for validation. We could have used - /// the `remote = "Self"` trick (https://github.com/serde-rs/serde/issues/1220) which allows - /// to directly serialize/deserialize the original fields and combine it with validation. - /// However, this would lead to nested serialization of the `CsMatrix` and `SparsityPattern` - /// types. Instead, we decided that we want a more human-readable serialization format using - /// field names like `col_offsets` and `row_indices`. The easiest way to achieve this is to - /// introduce an intermediate type. It also allows the serialization format to stay constant - /// even if the internal layout in `nalgebra` changes. - /// - /// We want to avoid unnecessary copies when serializing (i.e. cloning slices into owned - /// storage). Therefore, we use generic arguments to allow using slices during serialization and - /// owned storage (i.e. `Vec`) during deserialization. Without a major update of serde, slices - /// and `Vec`s should always (de)serialize identically. - #[derive(Serialize, Deserialize)] - struct CscMatrixSerializationData { - nrows: usize, - ncols: usize, - col_offsets: Indices, - row_indices: Indices, - values: Values, - } - - impl Serialize for CscMatrix - where - T: Serialize + Clone, - { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - CscMatrixSerializationData::<&[usize], &[T]> { - nrows: self.nrows(), - ncols: self.ncols(), - col_offsets: self.col_offsets(), - row_indices: self.row_indices(), - values: self.values(), - } - .serialize(serializer) - } - } - - impl<'de, T> Deserialize<'de> for CscMatrix - where - T: Deserialize<'de> + Clone, - { - fn deserialize(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - let de = CscMatrixSerializationData::, Vec>::deserialize(deserializer)?; - CscMatrix::try_from_csc_data( - de.nrows, - de.ncols, - de.col_offsets, - de.row_indices, - de.values, - ) - .map_err(|e| de::Error::custom(e)) - } - } -} - /// Convert pattern format errors into more meaningful CSC-specific errors. /// /// This ensures that the terminology is consistent: we are talking about rows and columns, diff --git a/nalgebra-sparse/src/csc/csc_serde.rs b/nalgebra-sparse/src/csc/csc_serde.rs new file mode 100644 index 00000000..aab12d47 --- /dev/null +++ b/nalgebra-sparse/src/csc/csc_serde.rs @@ -0,0 +1,65 @@ +use crate::CscMatrix; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + +/// This is an intermediate type for (de)serializing `CscMatrix`. +/// +/// Deserialization requires using a `try_from_*` function for validation. We could have used +/// the `remote = "Self"` trick (https://github.com/serde-rs/serde/issues/1220) which allows +/// to directly serialize/deserialize the original fields and combine it with validation. +/// However, this would lead to nested serialization of the `CsMatrix` and `SparsityPattern` +/// types. Instead, we decided that we want a more human-readable serialization format using +/// field names like `col_offsets` and `row_indices`. The easiest way to achieve this is to +/// introduce an intermediate type. It also allows the serialization format to stay constant +/// even if the internal layout in `nalgebra` changes. +/// +/// We want to avoid unnecessary copies when serializing (i.e. cloning slices into owned +/// storage). Therefore, we use generic arguments to allow using slices during serialization and +/// owned storage (i.e. `Vec`) during deserialization. Without a major update of serde, slices +/// and `Vec`s should always (de)serialize identically. +#[derive(Serialize, Deserialize)] +struct CscMatrixSerializationData { + nrows: usize, + ncols: usize, + col_offsets: Indices, + row_indices: Indices, + values: Values, +} + +impl Serialize for CscMatrix +where + T: Serialize + Clone, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + CscMatrixSerializationData::<&[usize], &[T]> { + nrows: self.nrows(), + ncols: self.ncols(), + col_offsets: self.col_offsets(), + row_indices: self.row_indices(), + values: self.values(), + } + .serialize(serializer) + } +} + +impl<'de, T> Deserialize<'de> for CscMatrix +where + T: Deserialize<'de> + Clone, +{ + fn deserialize(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let de = CscMatrixSerializationData::, Vec>::deserialize(deserializer)?; + CscMatrix::try_from_csc_data( + de.nrows, + de.ncols, + de.col_offsets, + de.row_indices, + de.values, + ) + .map_err(|e| de::Error::custom(e)) + } +} diff --git a/nalgebra-sparse/src/csr.rs b/nalgebra-sparse/src/csr.rs index eb35b335..a87f923d 100644 --- a/nalgebra-sparse/src/csr.rs +++ b/nalgebra-sparse/src/csr.rs @@ -2,6 +2,10 @@ //! //! This is the module-level documentation. See [`CsrMatrix`] for the main documentation of the //! CSC implementation. + +#[cfg(feature = "serde-serialize")] +mod csr_serde; + use crate::cs::{CsLane, CsLaneIter, CsLaneIterMut, CsLaneMut, CsMatrix}; use crate::csc::CscMatrix; use crate::pattern::{SparsityPattern, SparsityPatternFormatError, SparsityPatternIter}; @@ -591,75 +595,6 @@ impl CsrMatrix { } } -#[cfg(feature = "serde-serialize")] -mod serde_serialize { - use super::CsrMatrix; - use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; - - /// This is an intermediate type for (de)serializing `CsrMatrix`. - /// - /// Deserialization requires using a `try_from_*` function for validation. We could have used - /// the `remote = "Self"` trick (https://github.com/serde-rs/serde/issues/1220) which allows - /// to directly serialize/deserialize the original fields and combine it with validation. - /// However, this would lead to nested serialization of the `CsMatrix` and `SparsityPattern` - /// types. Instead, we decided that we want a more human-readable serialization format using - /// field names like `row_offsets` and `cal_indices`. The easiest way to achieve this is to - /// introduce an intermediate type. It also allows the serialization format to stay constant - /// even if the internal layout in `nalgebra` changes. - /// - /// We want to avoid unnecessary copies when serializing (i.e. cloning slices into owned - /// storage). Therefore, we use generic arguments to allow using slices during serialization and - /// owned storage (i.e. `Vec`) during deserialization. Without a major update of serde, slices - /// and `Vec`s should always (de)serialize identically. - #[derive(Serialize, Deserialize)] - struct CsrMatrixSerializationData { - nrows: usize, - ncols: usize, - row_offsets: Indices, - col_indices: Indices, - values: Values, - } - - impl Serialize for CsrMatrix - where - T: Serialize + Clone, - { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - CsrMatrixSerializationData::<&[usize], &[T]> { - nrows: self.nrows(), - ncols: self.ncols(), - row_offsets: self.row_offsets(), - col_indices: self.col_indices(), - values: self.values(), - } - .serialize(serializer) - } - } - - impl<'de, T> Deserialize<'de> for CsrMatrix - where - T: Deserialize<'de> + Clone, - { - fn deserialize(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - let de = CsrMatrixSerializationData::, Vec>::deserialize(deserializer)?; - CsrMatrix::try_from_csr_data( - de.nrows, - de.ncols, - de.row_offsets, - de.col_indices, - de.values, - ) - .map_err(|e| de::Error::custom(e)) - } - } -} - /// Convert pattern format errors into more meaningful CSR-specific errors. /// /// This ensures that the terminology is consistent: we are talking about rows and columns, diff --git a/nalgebra-sparse/src/csr/csr_serde.rs b/nalgebra-sparse/src/csr/csr_serde.rs new file mode 100644 index 00000000..1b33fda0 --- /dev/null +++ b/nalgebra-sparse/src/csr/csr_serde.rs @@ -0,0 +1,65 @@ +use crate::CsrMatrix; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + +/// This is an intermediate type for (de)serializing `CsrMatrix`. +/// +/// Deserialization requires using a `try_from_*` function for validation. We could have used +/// the `remote = "Self"` trick (https://github.com/serde-rs/serde/issues/1220) which allows +/// to directly serialize/deserialize the original fields and combine it with validation. +/// However, this would lead to nested serialization of the `CsMatrix` and `SparsityPattern` +/// types. Instead, we decided that we want a more human-readable serialization format using +/// field names like `row_offsets` and `cal_indices`. The easiest way to achieve this is to +/// introduce an intermediate type. It also allows the serialization format to stay constant +/// even if the internal layout in `nalgebra` changes. +/// +/// We want to avoid unnecessary copies when serializing (i.e. cloning slices into owned +/// storage). Therefore, we use generic arguments to allow using slices during serialization and +/// owned storage (i.e. `Vec`) during deserialization. Without a major update of serde, slices +/// and `Vec`s should always (de)serialize identically. +#[derive(Serialize, Deserialize)] +struct CsrMatrixSerializationData { + nrows: usize, + ncols: usize, + row_offsets: Indices, + col_indices: Indices, + values: Values, +} + +impl Serialize for CsrMatrix +where + T: Serialize + Clone, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + CsrMatrixSerializationData::<&[usize], &[T]> { + nrows: self.nrows(), + ncols: self.ncols(), + row_offsets: self.row_offsets(), + col_indices: self.col_indices(), + values: self.values(), + } + .serialize(serializer) + } +} + +impl<'de, T> Deserialize<'de> for CsrMatrix +where + T: Deserialize<'de> + Clone, +{ + fn deserialize(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let de = CsrMatrixSerializationData::, Vec>::deserialize(deserializer)?; + CsrMatrix::try_from_csr_data( + de.nrows, + de.ncols, + de.row_offsets, + de.col_indices, + de.values, + ) + .map_err(|e| de::Error::custom(e)) + } +} diff --git a/nalgebra-sparse/src/pattern.rs b/nalgebra-sparse/src/pattern.rs index 2bc9e939..59fe8949 100644 --- a/nalgebra-sparse/src/pattern.rs +++ b/nalgebra-sparse/src/pattern.rs @@ -1,4 +1,8 @@ //! Sparsity patterns for CSR and CSC matrices. + +#[cfg(feature = "serde-serialize")] +mod pattern_serde; + use crate::cs::transpose_cs; use crate::SparseFormatError; use std::error::Error; @@ -289,66 +293,6 @@ pub enum SparsityPatternFormatError { NonmonotonicMinorIndices, } -#[cfg(feature = "serde-serialize")] -mod serde_serialize { - use super::SparsityPattern; - use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; - - /// This is an intermediate type for (de)serializing `SparsityPattern`. - /// - /// Deserialization requires using a `try_from_*` function for validation. We could have used - /// the `remote = "Self"` trick (https://github.com/serde-rs/serde/issues/1220) which allows - /// to directly serialize/deserialize the original fields and combine it with validation. - /// However, this would lead to nested serialization of the `CsMatrix` and `SparsityPattern` - /// types. Instead, we decided that we want a more human-readable serialization format using - /// field names like `major_offsets` and `minor_indices`. The easiest way to achieve this is to - /// introduce an intermediate type. It also allows the serialization format to stay constant - /// even when the internal layout in `nalgebra` changes. - /// - /// We want to avoid unnecessary copies when serializing (i.e. cloning slices into owned - /// storage). Therefore, we use generic arguments to allow using slices during serialization and - /// owned storage (i.e. `Vec`) during deserialization. Without a major update of serde, slices - /// and `Vec`s should always (de)serialize identically. - #[derive(Serialize, Deserialize)] - struct SparsityPatternSerializationData { - major_dim: usize, - minor_dim: usize, - major_offsets: Indices, - minor_indices: Indices, - } - - impl Serialize for SparsityPattern { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - SparsityPatternSerializationData::<&[usize]> { - major_dim: self.major_dim(), - minor_dim: self.minor_dim(), - major_offsets: self.major_offsets(), - minor_indices: self.minor_indices(), - } - .serialize(serializer) - } - } - - impl<'de> Deserialize<'de> for SparsityPattern { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let de = SparsityPatternSerializationData::>::deserialize(deserializer)?; - SparsityPattern::try_from_offsets_and_indices( - de.major_dim, - de.minor_dim, - de.major_offsets, - de.minor_indices, - ) - .map_err(|e| de::Error::custom(e)) - } - } -} - impl From for SparseFormatError { fn from(err: SparsityPatternFormatError) -> Self { use crate::SparseFormatErrorKind; diff --git a/nalgebra-sparse/src/pattern/pattern_serde.rs b/nalgebra-sparse/src/pattern/pattern_serde.rs new file mode 100644 index 00000000..e11a550a --- /dev/null +++ b/nalgebra-sparse/src/pattern/pattern_serde.rs @@ -0,0 +1,56 @@ +use crate::pattern::SparsityPattern; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + +/// This is an intermediate type for (de)serializing `SparsityPattern`. +/// +/// Deserialization requires using a `try_from_*` function for validation. We could have used +/// the `remote = "Self"` trick (https://github.com/serde-rs/serde/issues/1220) which allows +/// to directly serialize/deserialize the original fields and combine it with validation. +/// However, this would lead to nested serialization of the `CsMatrix` and `SparsityPattern` +/// types. Instead, we decided that we want a more human-readable serialization format using +/// field names like `major_offsets` and `minor_indices`. The easiest way to achieve this is to +/// introduce an intermediate type. It also allows the serialization format to stay constant +/// even when the internal layout in `nalgebra` changes. +/// +/// We want to avoid unnecessary copies when serializing (i.e. cloning slices into owned +/// storage). Therefore, we use generic arguments to allow using slices during serialization and +/// owned storage (i.e. `Vec`) during deserialization. Without a major update of serde, slices +/// and `Vec`s should always (de)serialize identically. +#[derive(Serialize, Deserialize)] +struct SparsityPatternSerializationData { + major_dim: usize, + minor_dim: usize, + major_offsets: Indices, + minor_indices: Indices, +} + +impl Serialize for SparsityPattern { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + SparsityPatternSerializationData::<&[usize]> { + major_dim: self.major_dim(), + minor_dim: self.minor_dim(), + major_offsets: self.major_offsets(), + minor_indices: self.minor_indices(), + } + .serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for SparsityPattern { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let de = SparsityPatternSerializationData::>::deserialize(deserializer)?; + SparsityPattern::try_from_offsets_and_indices( + de.major_dim, + de.minor_dim, + de.major_offsets, + de.minor_indices, + ) + .map_err(|e| de::Error::custom(e)) + } +}