diff --git a/.github/workflows/nalgebra-ci-build.yml b/.github/workflows/nalgebra-ci-build.yml index c00b6cbc..d1a6c923 100644 --- a/.github/workflows/nalgebra-ci-build.yml +++ b/.github/workflows/nalgebra-ci-build.yml @@ -61,7 +61,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: test - run: cargo test --features arbitrary,rand,serde-serialize,sparse,debug,io,compare,libm,proptest-support,slow-tests; + run: cargo test --features arbitrary,rand,serde-serialize,sparse,debug,io,compare,libm,proptest-support,slow-tests,rkyv-safe-deser; test-nalgebra-glm: runs-on: ubuntu-latest steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index 2950a1df..e035e084 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ documented here. This project adheres to [Semantic Versioning](https://semver.org/). +## [0.31.2] (09 Oct. 2022) + +### Modified +- Use `#[inline]` on the `Dim` implementation for `Const` to improve opt-level 1 performance. +- Make the `Point::new` constructions const-fn. + +### Added +- Add `UnitVector::cast` to change the underlying scalar type. + + ## [0.31.1] (31 July 2022) ### Modified diff --git a/Cargo.toml b/Cargo.toml index 5b9940df..4bd1e12f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nalgebra" -version = "0.31.1" +version = "0.31.2" authors = [ "Sébastien Crozet " ] description = "General-purpose linear algebra library with transformations and statically-sized or dynamically-sized matrices." @@ -66,6 +66,7 @@ rand = [ "rand-no-std", "rand-package/std", "rand-package/std_rng", "rand arbitrary = [ "quickcheck" ] proptest-support = [ "proptest" ] slow-tests = [] +rkyv-safe-deser = [ "rkyv-serialize", "rkyv/validation" ] [dependencies] nalgebra-macros = { version = "0.1", path = "nalgebra-macros", optional = true } diff --git a/nalgebra-sparse/src/coo.rs b/nalgebra-sparse/src/coo.rs index 25dc07fb..b71cb0b6 100644 --- a/nalgebra-sparse/src/coo.rs +++ b/nalgebra-sparse/src/coo.rs @@ -170,6 +170,16 @@ impl CooMatrix { .map(|((i, j), v)| (*i, *j, v)) } + /// A mutable iterator over triplets (i, j, v). + // TODO: Consider giving the iterator a concrete type instead of impl trait...? + pub fn triplet_iter_mut(&mut self) -> impl Iterator { + self.row_indices + .iter() + .zip(&self.col_indices) + .zip(self.values.iter_mut()) + .map(|((i, j), v)| (*i, *j, v)) + } + /// Reserves capacity for COO matrix by at least `additional` elements. /// /// This increase the capacities of triplet holding arrays by reserving more space to avoid diff --git a/nalgebra-sparse/src/csc.rs b/nalgebra-sparse/src/csc.rs index d926dafb..0cd89f5c 100644 --- a/nalgebra-sparse/src/csc.rs +++ b/nalgebra-sparse/src/csc.rs @@ -24,6 +24,7 @@ use std::slice::{Iter, IterMut}; /// # Usage /// /// ``` +/// use nalgebra_sparse::coo::CooMatrix; /// use nalgebra_sparse::csc::CscMatrix; /// use nalgebra::{DMatrix, Matrix3x4}; /// use matrixcompare::assert_matrix_eq; @@ -32,8 +33,9 @@ use std::slice::{Iter, IterMut}; /// // change the sparsity pattern of the matrix after it has been constructed. The easiest /// // way to construct a CSC matrix is to first incrementally construct a COO matrix, /// // and then convert it to CSC. -/// # use nalgebra_sparse::coo::CooMatrix; -/// # let coo = CooMatrix::::new(3, 3); +/// +/// let mut coo = CooMatrix::::new(3, 3); +/// coo.push(2, 0, 1.0); /// let csc = CscMatrix::from(&coo); /// /// // Alternatively, a CSC matrix can be constructed directly from raw CSC data. diff --git a/nalgebra-sparse/src/csr.rs b/nalgebra-sparse/src/csr.rs index 90be35f1..255e8404 100644 --- a/nalgebra-sparse/src/csr.rs +++ b/nalgebra-sparse/src/csr.rs @@ -25,6 +25,7 @@ use std::slice::{Iter, IterMut}; /// # Usage /// /// ``` +/// use nalgebra_sparse::coo::CooMatrix; /// use nalgebra_sparse::csr::CsrMatrix; /// use nalgebra::{DMatrix, Matrix3x4}; /// use matrixcompare::assert_matrix_eq; @@ -33,8 +34,9 @@ use std::slice::{Iter, IterMut}; /// // change the sparsity pattern of the matrix after it has been constructed. The easiest /// // way to construct a CSR matrix is to first incrementally construct a COO matrix, /// // and then convert it to CSR. -/// # use nalgebra_sparse::coo::CooMatrix; -/// # let coo = CooMatrix::::new(3, 3); +/// +/// let mut coo = CooMatrix::::new(3, 3); +/// coo.push(2, 0, 1.0); /// let csr = CsrMatrix::from(&coo); /// /// // Alternatively, a CSR matrix can be constructed directly from raw CSR data. diff --git a/nalgebra-sparse/tests/unit_tests/coo.rs b/nalgebra-sparse/tests/unit_tests/coo.rs index d232b041..8e46651f 100644 --- a/nalgebra-sparse/tests/unit_tests/coo.rs +++ b/nalgebra-sparse/tests/unit_tests/coo.rs @@ -87,6 +87,40 @@ fn coo_construction_for_valid_data() { } } +#[test] +fn coo_triplets_iter_mut() { + // Arbitrary matrix, with duplicates + let i = vec![0, 1, 0, 0, 0, 0, 2, 1]; + let j = vec![0, 2, 0, 1, 0, 3, 3, 2]; + let v = vec![2, 3, 4, 7, 1, 3, 1, 5]; + let mut coo = + CooMatrix::::try_from_triplets(3, 5, i.clone(), j.clone(), v.clone()).unwrap(); + + let actual_triplets: Vec<_> = coo.triplet_iter_mut().map(|(i, j, v)| (i, j, *v)).collect(); + + let expected_triplets: Vec<_> = i + .iter() + .zip(&j) + .zip(&v) + .map(|((i, j), v)| (*i, *j, *v)) + .collect(); + assert_eq!(expected_triplets, actual_triplets); + + for (_i, _j, v) in coo.triplet_iter_mut() { + *v += *v; + } + + let actual_triplets: Vec<_> = coo.triplet_iter_mut().map(|(i, j, v)| (i, j, *v)).collect(); + let v = vec![4, 6, 8, 14, 2, 6, 2, 10]; + let expected_triplets: Vec<_> = i + .iter() + .zip(&j) + .zip(&v) + .map(|((i, j), v)| (*i, *j, *v)) + .collect(); + assert_eq!(expected_triplets, actual_triplets); +} + #[test] fn coo_try_from_triplets_reports_out_of_bounds_indices() { { diff --git a/src/base/array_storage.rs b/src/base/array_storage.rs index 3bc71e1a..d155cba7 100644 --- a/src/base/array_storage.rs +++ b/src/base/array_storage.rs @@ -27,11 +27,14 @@ use std::mem; /// A array-based statically sized matrix data storage. #[repr(transparent)] #[derive(Copy, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize) )] +#[cfg_attr( + feature = "rkyv-serialize", + archive_attr(derive(bytecheck::CheckBytes)) +)] #[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))] pub struct ArrayStorage(pub [[T; R]; C]); diff --git a/src/base/dimension.rs b/src/base/dimension.rs index b1b700d7..06697618 100644 --- a/src/base/dimension.rs +++ b/src/base/dimension.rs @@ -13,11 +13,14 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; /// Dim of dynamically-sized algebraic entities. #[derive(Clone, Copy, Eq, PartialEq, Debug)] -#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize) )] +#[cfg_attr( + feature = "rkyv-serialize", + archive_attr(derive(bytecheck::CheckBytes)) +)] #[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))] pub struct Dynamic { value: usize, @@ -207,7 +210,10 @@ dim_ops!( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize) )] -#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] +#[cfg_attr( + feature = "rkyv-serialize", + archive_attr(derive(bytecheck::CheckBytes)) +)] #[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))] pub struct Const; diff --git a/src/base/matrix.rs b/src/base/matrix.rs index d9294c9e..e9cc5258 100644 --- a/src/base/matrix.rs +++ b/src/base/matrix.rs @@ -150,11 +150,14 @@ pub type MatrixCross = /// some concrete types for `T` and a compatible data storage type `S`). #[repr(C)] #[derive(Clone, Copy)] -#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize) )] +#[cfg_attr( + feature = "rkyv-serialize", + archive_attr(derive(bytecheck::CheckBytes)) +)] #[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))] pub struct Matrix { /// The data storage that contains all the matrix components. Disappointed? diff --git a/src/base/unit.rs b/src/base/unit.rs index 6fc00092..1596b71a 100644 --- a/src/base/unit.rs +++ b/src/base/unit.rs @@ -21,11 +21,14 @@ use crate::{Dim, Matrix, OMatrix, RealField, Scalar, SimdComplexField, SimdRealF /// in their documentation, read their dedicated pages directly. #[repr(transparent)] #[derive(Clone, Hash, Copy)] -#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize) )] +#[cfg_attr( + feature = "rkyv-serialize", + archive_attr(derive(bytecheck::CheckBytes)) +)] // #[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))] pub struct Unit { pub(crate) value: T, diff --git a/src/geometry/dual_quaternion.rs b/src/geometry/dual_quaternion.rs index 6f1b7053..3b283629 100644 --- a/src/geometry/dual_quaternion.rs +++ b/src/geometry/dual_quaternion.rs @@ -40,11 +40,14 @@ use simba::scalar::{ClosedNeg, RealField}; /// See #[repr(C)] #[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize) )] +#[cfg_attr( + feature = "rkyv-serialize", + archive_attr(derive(bytecheck::CheckBytes)) +)] #[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))] pub struct DualQuaternion { /// The real component of the quaternion diff --git a/src/geometry/isometry.rs b/src/geometry/isometry.rs index 92169742..d245c99b 100755 --- a/src/geometry/isometry.rs +++ b/src/geometry/isometry.rs @@ -70,7 +70,10 @@ use crate::geometry::{AbstractRotation, Point, Translation}; feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize) )] -#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] +#[cfg_attr( + feature = "rkyv-serialize", + archive_attr(derive(bytecheck::CheckBytes)) +)] pub struct Isometry { /// The pure rotational part of this isometry. pub rotation: R, diff --git a/src/geometry/orthographic.rs b/src/geometry/orthographic.rs index 7348f676..ede7f587 100644 --- a/src/geometry/orthographic.rs +++ b/src/geometry/orthographic.rs @@ -19,11 +19,14 @@ use crate::geometry::{Point3, Projective3}; /// A 3D orthographic projection stored as a homogeneous 4x4 matrix. #[repr(C)] -#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize) )] +#[cfg_attr( + feature = "rkyv-serialize", + archive_attr(derive(bytecheck::CheckBytes)) +)] #[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))] #[derive(Copy, Clone)] pub struct Orthographic3 { diff --git a/src/geometry/perspective.rs b/src/geometry/perspective.rs index 351960bb..de29747f 100644 --- a/src/geometry/perspective.rs +++ b/src/geometry/perspective.rs @@ -20,11 +20,14 @@ use crate::geometry::{Point3, Projective3}; /// A 3D perspective projection stored as a homogeneous 4x4 matrix. #[repr(C)] -#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize) )] +#[cfg_attr( + feature = "rkyv-serialize", + archive_attr(derive(bytecheck::CheckBytes)) +)] #[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))] #[derive(Copy, Clone)] pub struct Perspective3 { diff --git a/src/geometry/point.rs b/src/geometry/point.rs index 306c18e5..e9a21439 100644 --- a/src/geometry/point.rs +++ b/src/geometry/point.rs @@ -40,7 +40,10 @@ use std::mem::MaybeUninit; feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize) )] -#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] +#[cfg_attr( + feature = "rkyv-serialize", + archive_attr(derive(bytecheck::CheckBytes)) +)] pub struct OPoint where DefaultAllocator: Allocator, diff --git a/src/geometry/point_construction.rs b/src/geometry/point_construction.rs index ac54b349..598cf4ed 100644 --- a/src/geometry/point_construction.rs +++ b/src/geometry/point_construction.rs @@ -202,11 +202,29 @@ impl Point1 { /// assert_eq!(p.x, 1.0); /// ``` #[inline] + #[cfg(not(feature = "cuda"))] pub const fn new(x: T) -> Self { Point { coords: Vector1::new(x), } } + + /// Initializes this point from its components. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::Point1; + /// let p = Point1::new(1.0); + /// assert_eq!(p.x, 1.0); + /// ``` + #[inline] + #[cfg(feature = "cuda")] + pub fn new(x: T) -> Self { + Point { + coords: Vector1::new(x), + } + } } macro_rules! componentwise_constructors_impl( ($($doc: expr; $Point: ident, $Vector: ident, $($args: ident:$irow: expr),*);* $(;)*) => {$( @@ -216,9 +234,22 @@ macro_rules! componentwise_constructors_impl( #[doc = $doc] #[doc = "```"] #[inline] + #[cfg(not(feature = "cuda"))] pub const fn new($($args: T),*) -> Self { Point { coords: $Vector::new($($args),*) } } + + // TODO: always let new be const once CUDA updates its supported + // nightly version to something more recent. + #[doc = "Initializes this point from its components."] + #[doc = "# Example\n```"] + #[doc = $doc] + #[doc = "```"] + #[inline] + #[cfg(feature = "cuda")] + pub fn new($($args: T),*) -> Self { + Point { coords: $Vector::new($($args),*) } + } } )*} ); diff --git a/src/geometry/quaternion.rs b/src/geometry/quaternion.rs index f38dca6f..53da24e3 100755 --- a/src/geometry/quaternion.rs +++ b/src/geometry/quaternion.rs @@ -23,11 +23,14 @@ use crate::geometry::{Point3, Rotation}; /// that may be used as a rotation. #[repr(C)] #[derive(Copy, Clone)] -#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize) )] +#[cfg_attr( + feature = "rkyv-serialize", + archive_attr(derive(bytecheck::CheckBytes)) +)] #[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))] pub struct Quaternion { /// This quaternion as a 4D vector of coordinates in the `[ x, y, z, w ]` storage order. diff --git a/src/geometry/rotation.rs b/src/geometry/rotation.rs index 2a8bf427..4a71da0f 100755 --- a/src/geometry/rotation.rs +++ b/src/geometry/rotation.rs @@ -49,11 +49,14 @@ use crate::geometry::Point; /// * [Conversion to a matrix `matrix`, `to_homogeneous`…](#conversion-to-a-matrix) /// #[repr(C)] -#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize) )] +#[cfg_attr( + feature = "rkyv-serialize", + archive_attr(derive(bytecheck::CheckBytes)) +)] #[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))] #[derive(Copy, Clone)] pub struct Rotation { diff --git a/src/geometry/scale.rs b/src/geometry/scale.rs index 37da1ef0..e6c46e47 100755 --- a/src/geometry/scale.rs +++ b/src/geometry/scale.rs @@ -17,11 +17,14 @@ use crate::geometry::Point; /// A scale which supports non-uniform scaling. #[repr(C)] -#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize) )] +#[cfg_attr( + feature = "rkyv-serialize", + archive_attr(derive(bytecheck::CheckBytes)) +)] #[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))] #[derive(Copy, Clone)] pub struct Scale { diff --git a/src/geometry/similarity.rs b/src/geometry/similarity.rs index 8c38ff1e..a827246e 100755 --- a/src/geometry/similarity.rs +++ b/src/geometry/similarity.rs @@ -38,7 +38,10 @@ use crate::geometry::{AbstractRotation, Isometry, Point, Translation}; feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize) )] -#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] +#[cfg_attr( + feature = "rkyv-serialize", + archive_attr(derive(bytecheck::CheckBytes)) +)] pub struct Similarity { /// The part of this similarity that does not include the scaling factor. pub isometry: Isometry, diff --git a/src/geometry/translation.rs b/src/geometry/translation.rs index bef66f68..7e10ac4c 100755 --- a/src/geometry/translation.rs +++ b/src/geometry/translation.rs @@ -17,11 +17,14 @@ use crate::geometry::Point; /// A translation. #[repr(C)] -#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize) )] +#[cfg_attr( + feature = "rkyv-serialize", + archive_attr(derive(bytecheck::CheckBytes)) +)] #[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))] #[derive(Copy, Clone)] pub struct Translation { diff --git a/tests/core/mod.rs b/tests/core/mod.rs index 5fb8c82b..acdcd236 100644 --- a/tests/core/mod.rs +++ b/tests/core/mod.rs @@ -7,6 +7,8 @@ mod matrix; mod matrix_slice; #[cfg(feature = "mint")] mod mint; +#[cfg(feature = "rkyv-serialize-no-std")] +mod rkyv; mod serde; #[cfg(feature = "compare")] diff --git a/tests/core/rkyv.rs b/tests/core/rkyv.rs new file mode 100644 index 00000000..b89f5211 --- /dev/null +++ b/tests/core/rkyv.rs @@ -0,0 +1,44 @@ +use na::{ + Isometry3, IsometryMatrix2, IsometryMatrix3, Matrix3x4, Point2, Point3, Quaternion, Rotation2, + Rotation3, Similarity3, SimilarityMatrix2, SimilarityMatrix3, Translation2, Translation3, +}; +use rkyv::ser::{serializers::AllocSerializer, Serializer}; + +macro_rules! test_rkyv( + ($($test: ident, $ty: ident);* $(;)*) => {$( + #[test] + fn $test() { + let v: $ty = rand::random(); + // serialize + let mut serializer = AllocSerializer::<0>::default(); + serializer.serialize_value(&v).unwrap(); + let serialized = serializer.into_serializer().into_inner(); + + let deserialized: $ty = unsafe { rkyv::from_bytes_unchecked(&serialized).unwrap() }; + assert_eq!(v, deserialized); + + #[cfg(feature = "rkyv-safe-deser")] + { + let deserialized: $ty = rkyv::from_bytes(&serialized).unwrap(); + assert_eq!(v, deserialized); + } + } + )*} +); + +test_rkyv!( + rkyv_matrix3x4, Matrix3x4; + rkyv_point3, Point3; + rkyv_translation3, Translation3; + rkyv_rotation3, Rotation3; + rkyv_isometry3, Isometry3; + rkyv_isometry_matrix3, IsometryMatrix3; + rkyv_similarity3, Similarity3; + rkyv_similarity_matrix3, SimilarityMatrix3; + rkyv_quaternion, Quaternion; + rkyv_point2, Point2; + rkyv_translation2, Translation2; + rkyv_rotation2, Rotation2; + rkyv_isometry_matrix2, IsometryMatrix2; + rkyv_similarity_matrix2, SimilarityMatrix2; +);