Merge pull request #1019 from w1th0utnam3/sparse_serde
Implement Serde Serialize/Deserialize for nalgebra-sparse types
This commit is contained in:
commit
7ade44dc1d
|
@ -38,8 +38,12 @@ jobs:
|
|||
run: cargo build --features serde-serialize
|
||||
- name: Build nalgebra-lapack
|
||||
run: cd nalgebra-lapack; cargo build;
|
||||
- name: Build nalgebra-sparse
|
||||
- name: Build nalgebra-sparse --no-default-features
|
||||
run: cd nalgebra-sparse; cargo build --no-default-features;
|
||||
- name: Build nalgebra-sparse (default features)
|
||||
run: cd nalgebra-sparse; cargo build;
|
||||
- name: Build nalgebra-sparse --all-features
|
||||
run: cd nalgebra-sparse; cargo build --all-features;
|
||||
# Run this on it’s own job because it alone takes a lot of time.
|
||||
# So it’s best to let it run in parallel to the other jobs.
|
||||
build-nalgebra-all-features:
|
||||
|
@ -71,10 +75,10 @@ jobs:
|
|||
- name: test nalgebra-sparse
|
||||
# Manifest-path is necessary because cargo otherwise won't correctly forward features
|
||||
# We increase number of proptest cases to hopefully catch more potential bugs
|
||||
run: PROPTEST_CASES=10000 cargo test --manifest-path=nalgebra-sparse/Cargo.toml --features compare,proptest-support,io
|
||||
run: PROPTEST_CASES=10000 cargo test --manifest-path=nalgebra-sparse/Cargo.toml --features compare,proptest-support,io,serde-serialize
|
||||
- name: test nalgebra-sparse (slow tests)
|
||||
# Unfortunately, the "slow-tests" take so much time that we need to run them with --release
|
||||
run: PROPTEST_CASES=10000 cargo test --release --manifest-path=nalgebra-sparse/Cargo.toml --features compare,proptest-support,io,slow-tests slow
|
||||
run: PROPTEST_CASES=10000 cargo test --release --manifest-path=nalgebra-sparse/Cargo.toml --features compare,proptest-support,io,serde-serialize,slow-tests slow
|
||||
test-nalgebra-macros:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
|
|
@ -15,6 +15,7 @@ license = "Apache-2.0"
|
|||
[features]
|
||||
proptest-support = ["proptest", "nalgebra/proptest-support"]
|
||||
compare = [ "matrixcompare-core" ]
|
||||
serde-serialize = [ "serde/std" ]
|
||||
|
||||
# Enable matrix market I/O
|
||||
io = [ "pest", "pest_derive" ]
|
||||
|
@ -29,11 +30,13 @@ proptest = { version = "1.0", optional = true }
|
|||
matrixcompare-core = { version = "0.1.0", optional = true }
|
||||
pest = { version = "2", optional = true }
|
||||
pest_derive = { version = "2", optional = true }
|
||||
serde = { version = "1.0", default-features = false, features = [ "derive" ], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
itertools = "0.10"
|
||||
matrixcompare = { version = "0.3.0", features = [ "proptest-support" ] }
|
||||
nalgebra = { version="0.30", path = "../", features = ["compare"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
# Enable certain features when building docs for docs.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.
|
||||
|
|
|
@ -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<Indices, Values> {
|
||||
nrows: usize,
|
||||
ncols: usize,
|
||||
row_indices: Indices,
|
||||
col_indices: Indices,
|
||||
values: Values,
|
||||
}
|
||||
|
||||
impl<T> Serialize for CooMatrix<T>
|
||||
where
|
||||
T: Serialize + Clone,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
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<T>
|
||||
where
|
||||
T: Deserialize<'de> + Clone,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<CooMatrix<T>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let de = CooMatrixSerializationData::<Vec<usize>, Vec<T>>::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))
|
||||
}
|
||||
}
|
|
@ -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};
|
||||
|
|
|
@ -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<Indices, Values> {
|
||||
nrows: usize,
|
||||
ncols: usize,
|
||||
col_offsets: Indices,
|
||||
row_indices: Indices,
|
||||
values: Values,
|
||||
}
|
||||
|
||||
impl<T> Serialize for CscMatrix<T>
|
||||
where
|
||||
T: Serialize + Clone,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
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<T>
|
||||
where
|
||||
T: Deserialize<'de> + Clone,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<CscMatrix<T>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let de = CscMatrixSerializationData::<Vec<usize>, Vec<T>>::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))
|
||||
}
|
||||
}
|
|
@ -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};
|
||||
|
@ -9,7 +13,6 @@ use crate::{SparseEntry, SparseEntryMut, SparseFormatError, SparseFormatErrorKin
|
|||
|
||||
use nalgebra::Scalar;
|
||||
use num_traits::One;
|
||||
|
||||
use std::iter::FromIterator;
|
||||
use std::slice::{Iter, IterMut};
|
||||
|
||||
|
|
|
@ -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<Indices, Values> {
|
||||
nrows: usize,
|
||||
ncols: usize,
|
||||
row_offsets: Indices,
|
||||
col_indices: Indices,
|
||||
values: Values,
|
||||
}
|
||||
|
||||
impl<T> Serialize for CsrMatrix<T>
|
||||
where
|
||||
T: Serialize + Clone,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
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<T>
|
||||
where
|
||||
T: Deserialize<'de> + Clone,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<CsrMatrix<T>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let de = CsrMatrixSerializationData::<Vec<usize>, Vec<T>>::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))
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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<Indices> {
|
||||
major_dim: usize,
|
||||
minor_dim: usize,
|
||||
major_offsets: Indices,
|
||||
minor_indices: Indices,
|
||||
}
|
||||
|
||||
impl Serialize for SparsityPattern {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
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<D>(deserializer: D) -> Result<SparsityPattern, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let de = SparsityPatternSerializationData::<Vec<usize>>::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))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
#![cfg(feature = "serde-serialize")]
|
||||
//! Serialization tests
|
||||
#[cfg(any(not(feature = "proptest-support"), not(feature = "compare")))]
|
||||
compile_error!("Tests must be run with features `proptest-support` and `compare`");
|
||||
|
||||
#[macro_use]
|
||||
pub mod common;
|
||||
|
||||
use nalgebra_sparse::coo::CooMatrix;
|
||||
use nalgebra_sparse::csc::CscMatrix;
|
||||
use nalgebra_sparse::csr::CsrMatrix;
|
||||
use nalgebra_sparse::pattern::SparsityPattern;
|
||||
|
||||
use proptest::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::common::{csc_strategy, csr_strategy};
|
||||
|
||||
fn json_roundtrip<T: Serialize + for<'a> Deserialize<'a>>(csr: &T) -> T {
|
||||
let serialized = serde_json::to_string(csr).unwrap();
|
||||
let deserialized: T = serde_json::from_str(&serialized).unwrap();
|
||||
deserialized
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pattern_roundtrip() {
|
||||
{
|
||||
// A pattern with zero explicitly stored entries
|
||||
let pattern =
|
||||
SparsityPattern::try_from_offsets_and_indices(3, 2, vec![0, 0, 0, 0], Vec::new())
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(json_roundtrip(&pattern), pattern);
|
||||
}
|
||||
|
||||
{
|
||||
// Arbitrary pattern
|
||||
let offsets = vec![0, 2, 2, 5];
|
||||
let indices = vec![0, 5, 1, 2, 3];
|
||||
let pattern =
|
||||
SparsityPattern::try_from_offsets_and_indices(3, 6, offsets.clone(), indices.clone())
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(json_roundtrip(&pattern), pattern);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn pattern_deserialize_invalid() {
|
||||
assert!(serde_json::from_str::<SparsityPattern>(r#"{"major_dim":3,"minor_dim":6,"major_offsets":[0,2,2,5],"minor_indices":[0,5,1,2,3]}"#).is_ok());
|
||||
assert!(serde_json::from_str::<SparsityPattern>(r#"{"major_dim":0,"minor_dim":0,"major_offsets":[],"minor_indices":[]}"#).is_err());
|
||||
assert!(serde_json::from_str::<SparsityPattern>(r#"{"major_dim":3,"minor_dim":6,"major_offsets":[0, 3, 5],"minor_indices":[0, 1, 2, 3, 5]}"#).is_err());
|
||||
assert!(serde_json::from_str::<SparsityPattern>(r#"{"major_dim":3,"minor_dim":6,"major_offsets":[1, 2, 2, 5],"minor_indices":[0, 5, 1, 2, 3]}"#).is_err());
|
||||
assert!(serde_json::from_str::<SparsityPattern>(r#"{"major_dim":3,"minor_dim":6,"major_offsets":[0, 2, 2, 4],"minor_indices":[0, 5, 1, 2, 3]}"#).is_err());
|
||||
assert!(serde_json::from_str::<SparsityPattern>(r#"{"major_dim":3,"minor_dim":6,"major_offsets":[0, 2, 2],"minor_indices":[0, 5, 1, 2, 3]}"#).is_err());
|
||||
assert!(serde_json::from_str::<SparsityPattern>(r#"{"major_dim":3,"minor_dim":6,"major_offsets":[0, 3, 2, 5],"minor_indices":[0, 1, 2, 3, 4]}"#).is_err());
|
||||
assert!(serde_json::from_str::<SparsityPattern>(r#"{"major_dim":3,"minor_dim":6,"major_offsets":[0, 2, 2, 5],"minor_indices":[0, 2, 3, 1, 4]}"#).is_err());
|
||||
assert!(serde_json::from_str::<SparsityPattern>(r#"{"major_dim":3,"minor_dim":6,"major_offsets":[0, 2, 2, 5],"minor_indices":[0, 6, 1, 2, 3]}"#).is_err());
|
||||
assert!(serde_json::from_str::<SparsityPattern>(r#"{"major_dim":3,"minor_dim":6,"major_offsets":[0, 2, 2, 5],"minor_indices":[0, 5, 2, 2, 3]}"#).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coo_roundtrip() {
|
||||
{
|
||||
// A COO matrix without entries
|
||||
let matrix =
|
||||
CooMatrix::<i32>::try_from_triplets(3, 2, Vec::new(), Vec::new(), Vec::new()).unwrap();
|
||||
|
||||
assert_eq!(json_roundtrip(&matrix), matrix);
|
||||
}
|
||||
|
||||
{
|
||||
// Arbitrary COO matrix, no duplicates
|
||||
let i = vec![0, 1, 0, 0, 2];
|
||||
let j = vec![0, 2, 1, 3, 3];
|
||||
let v = vec![2, 3, 7, 3, 1];
|
||||
let matrix =
|
||||
CooMatrix::<i32>::try_from_triplets(3, 5, i.clone(), j.clone(), v.clone()).unwrap();
|
||||
|
||||
assert_eq!(json_roundtrip(&matrix), matrix);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coo_deserialize_invalid() {
|
||||
// Valid matrix: {"nrows":3,"ncols":5,"row_indices":[0,1,0,0,2],"col_indices":[0,2,1,3,3],"values":[2,3,7,3,1]}
|
||||
assert!(serde_json::from_str::<CooMatrix<i32>>(r#"{"nrows":3,"ncols":5,"row_indices":[0,1,0,0,2],"col_indices":[0,2,1,3,3],"values":[2,3,7,3,1]}"#).is_ok());
|
||||
assert!(serde_json::from_str::<CooMatrix<i32>>(r#"{"nrows":0,"ncols":0,"row_indices":[0,1,0,0,2],"col_indices":[0,2,1,3,3],"values":[2,3,7,3,4]}"#).is_err());
|
||||
assert!(serde_json::from_str::<CooMatrix<i32>>(r#"{"nrows":-3,"ncols":5,"row_indices":[0,1,0,0,2],"col_indices":[0,2,1,3,3],"values":[2,3,7,3,4]}"#).is_err());
|
||||
assert!(serde_json::from_str::<CooMatrix<i32>>(r#"{"nrows":3,"ncols":5,"row_indices":[0,1,0,0,2],"col_indices":[0,2,1,3,3],"values":[2,3,7,3]}"#).is_err());
|
||||
assert!(serde_json::from_str::<CooMatrix<i32>>(r#"{"nrows":3,"ncols":5,"row_indices":[0,1,0,0,2],"col_indices":[0,2,1,3,3],"values":[2,3,7,3,4,5]}"#).is_err());
|
||||
assert!(serde_json::from_str::<CooMatrix<i32>>(r#"{"nrows":3,"ncols":5,"row_indices":[0,1,0,0,2],"col_indices":[0,2,1,8,3],"values":[2,3,7,3,4]}"#).is_err());
|
||||
assert!(serde_json::from_str::<CooMatrix<i32>>(r#"{"nrows":3,"ncols":5,"row_indices":[0,1,0,0],"col_indices":[0,2,1,8,3],"values":[2,3,7,3,4]}"#).is_err());
|
||||
assert!(serde_json::from_str::<CooMatrix<i32>>(r#"{"nrows":3,"ncols":5,"row_indices":[0,10,0,0,2],"col_indices":[0,2,1,3,3],"values":[2,3,7,3,4]}"#).is_err());
|
||||
assert!(serde_json::from_str::<CooMatrix<i32>>(r#"{"nrows":3,"ncols":5,"row_indices":[0,1,0,0,2],"col_indices":[0,2,1,30,3],"values":[2,3,7,3,4]}"#).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coo_deserialize_duplicates() {
|
||||
assert_eq!(
|
||||
serde_json::from_str::<CooMatrix<i32>>(
|
||||
r#"{"nrows":3,"ncols":5,"row_indices":[0,1,0,0,2,0,1],"col_indices":[0,2,1,3,3,0,2],"values":[2,3,7,3,1,5,6]}"#
|
||||
).unwrap(),
|
||||
CooMatrix::<i32>::try_from_triplets(
|
||||
3,
|
||||
5,
|
||||
vec![0, 1, 0, 0, 2, 0, 1],
|
||||
vec![0, 2, 1, 3, 3, 0, 2],
|
||||
vec![2, 3, 7, 3, 1, 5, 6]
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn csc_roundtrip() {
|
||||
{
|
||||
// A CSC matrix with zero explicitly stored entries
|
||||
let offsets = vec![0, 0, 0, 0];
|
||||
let indices = vec![];
|
||||
let values = Vec::<i32>::new();
|
||||
let matrix = CscMatrix::try_from_csc_data(2, 3, offsets, indices, values).unwrap();
|
||||
|
||||
assert_eq!(json_roundtrip(&matrix), matrix);
|
||||
}
|
||||
|
||||
{
|
||||
// An arbitrary CSC matrix
|
||||
let offsets = vec![0, 2, 2, 5];
|
||||
let indices = vec![0, 5, 1, 2, 3];
|
||||
let values = vec![0, 1, 2, 3, 4];
|
||||
let matrix =
|
||||
CscMatrix::try_from_csc_data(6, 3, offsets.clone(), indices.clone(), values.clone())
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(json_roundtrip(&matrix), matrix);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn csc_deserialize_invalid() {
|
||||
// Valid matrix: {"nrows":6,"ncols":3,"col_offsets":[0,2,2,5],"row_indices":[0,5,1,2,3],"values":[0,1,2,3,4]}
|
||||
assert!(serde_json::from_str::<CscMatrix<i32>>(r#"{"nrows":6,"ncols":3,"col_offsets":[0,2,2,5],"row_indices":[0,5,1,2,3],"values":[0,1,2,3,4]}"#).is_ok());
|
||||
assert!(serde_json::from_str::<CscMatrix<i32>>(r#"{"nrows":0,"ncols":0,"col_offsets":[0,2,2,5],"row_indices":[0,5,1,2,3],"values":[0,1,2,3,4]}"#).is_err());
|
||||
assert!(serde_json::from_str::<CscMatrix<i32>>(r#"{"nrows":-6,"ncols":3,"col_offsets":[0,2,2,5],"row_indices":[0,5,1,2,3],"values":[0,1,2,3,4]}"#).is_err());
|
||||
assert!(serde_json::from_str::<CscMatrix<i32>>(r#"{"nrows":6,"ncols":3,"col_offsets":[0,2,2,5],"row_indices":[0,5,1,2,3],"values":[0,1,2,3]}"#).is_err());
|
||||
assert!(serde_json::from_str::<CscMatrix<i32>>(r#"{"nrows":6,"ncols":3,"col_offsets":[0,2,2,5],"row_indices":[0,5,1,2,3],"values":[0,1,2,3,4,5]}"#).is_err());
|
||||
assert!(serde_json::from_str::<CscMatrix<i32>>(r#"{"nrows":6,"ncols":3,"col_offsets":[0,2,2,5],"row_indices":[0,5,1,8,3],"values":[0,1,2,3,4]}"#).is_err());
|
||||
assert!(serde_json::from_str::<CscMatrix<i32>>(r#"{"nrows":6,"ncols":3,"col_offsets":[0,2,2,5],"row_indices":[0,5,1,2,3,1,1],"values":[0,1,2,3,4]}"#).is_err());
|
||||
// The following actually panics ('range end index 10 out of range for slice of length 5', nalgebra-sparse\src\pattern.rs:156:38)
|
||||
//assert!(serde_json::from_str::<CscMatrix<i32>>(r#"{"nrows":6,"ncols":3,"col_offsets":[0,10,2,5],"row_indices":[0,5,1,2,3],"values":[0,1,2,3,4]}"#).is_err());
|
||||
assert!(serde_json::from_str::<CscMatrix<i32>>(r#"{"nrows":3,"ncols":6,"row_offsets":[0,2,2,5],"col_indices":[0,5,1,2,3],"values":[0,1,2,3,4]}"#).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn csr_roundtrip() {
|
||||
{
|
||||
// A CSR matrix with zero explicitly stored entries
|
||||
let offsets = vec![0, 0, 0, 0];
|
||||
let indices = vec![];
|
||||
let values = Vec::<i32>::new();
|
||||
let matrix = CsrMatrix::try_from_csr_data(3, 2, offsets, indices, values).unwrap();
|
||||
|
||||
assert_eq!(json_roundtrip(&matrix), matrix);
|
||||
}
|
||||
|
||||
{
|
||||
// An arbitrary CSR matrix
|
||||
let offsets = vec![0, 2, 2, 5];
|
||||
let indices = vec![0, 5, 1, 2, 3];
|
||||
let values = vec![0, 1, 2, 3, 4];
|
||||
let matrix =
|
||||
CsrMatrix::try_from_csr_data(3, 6, offsets.clone(), indices.clone(), values.clone())
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(json_roundtrip(&matrix), matrix);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn csr_deserialize_invalid() {
|
||||
// Valid matrix: {"nrows":3,"ncols":6,"row_offsets":[0,2,2,5],"col_indices":[0,5,1,2,3],"values":[0,1,2,3,4]}
|
||||
assert!(serde_json::from_str::<CsrMatrix<i32>>(r#"{"nrows":3,"ncols":6,"row_offsets":[0,2,2,5],"col_indices":[0,5,1,2,3],"values":[0,1,2,3,4]}"#).is_ok());
|
||||
assert!(serde_json::from_str::<CsrMatrix<i32>>(r#"{"nrows":0,"ncols":0,"row_offsets":[0,2,2,5],"col_indices":[0,5,1,2,3],"values":[0,1,2,3,4]}"#).is_err());
|
||||
assert!(serde_json::from_str::<CsrMatrix<i32>>(r#"{"nrows":-3,"ncols":6,"row_offsets":[0,2,2,5],"col_indices":[0,5,1,2,3],"values":[0,1,2,3,4]}"#).is_err());
|
||||
assert!(serde_json::from_str::<CsrMatrix<i32>>(r#"{"nrows":3,"ncols":6,"row_offsets":[0,2,2,5],"col_indices":[0,5,1,2,3],"values":[0,1,2,3]}"#).is_err());
|
||||
assert!(serde_json::from_str::<CsrMatrix<i32>>(r#"{"nrows":3,"ncols":6,"row_offsets":[0,2,2,5],"col_indices":[0,5,1,2,3],"values":[0,1,2,3,4,5]}"#).is_err());
|
||||
assert!(serde_json::from_str::<CsrMatrix<i32>>(r#"{"nrows":3,"ncols":6,"row_offsets":[0,2,2,5],"col_indices":[0,5,1,8,3],"values":[0,1,2,3,4]}"#).is_err());
|
||||
assert!(serde_json::from_str::<CsrMatrix<i32>>(r#"{"nrows":3,"ncols":6,"row_offsets":[0,2,2,5],"col_indices":[0,5,1,2,3,1,1],"values":[0,1,2,3,4]}"#).is_err());
|
||||
// The following actually panics ('range end index 10 out of range for slice of length 5', nalgebra-sparse\src\pattern.rs:156:38)
|
||||
//assert!(serde_json::from_str::<CsrMatrix<i32>>(r#"{"nrows":3,"ncols":6,"row_offsets":[0,10,2,5],"col_indices":[0,5,1,2,3],"values":[0,1,2,3,4]}"#).is_err());
|
||||
assert!(serde_json::from_str::<CsrMatrix<i32>>(r#"{"nrows":6,"ncols":3,"col_offsets":[0,2,2,5],"row_indices":[0,5,1,2,3],"values":[0,1,2,3,4]}"#).is_err());
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn csc_roundtrip_proptest(csc in csc_strategy()) {
|
||||
prop_assert_eq!(json_roundtrip(&csc), csc);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn csr_roundtrip_proptest(csr in csr_strategy()) {
|
||||
prop_assert_eq!(json_roundtrip(&csr), csr);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue