diff --git a/CHANGELOG.md b/CHANGELOG.md index fcfbfd5a..22aa582b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,16 @@ documented here. This project adheres to [Semantic Versioning](http://semver.org/). -## [0.17.0] - WIP +## [0.18.0] - WIP + +### Added + * Add `.renormalize` to `Unit<...>` and `Rotation3` to correct potential drift due to repeated operations. + Those drifts can cause them not to be pure rotations anymore. + * Add the `::from_matrix` constructor too all rotation types to extract a rotation from a raw matrix. + * Add the `::from_matrix_eps` constructor too all rotation types to extract a rotation from a raw matrix. This takes + more argument than `::from_matrix` to control the convergence of the underlying optimization algorithm. + +## [0.17.0] ### Added * Add swizzling up to dimension 3 for vectors. For example, you can do `v.zxy()` as an equivalent to `Vector3::new(v.z, v.x, v.y)`. @@ -35,7 +44,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). * Implement `Extend>` for matrices with dynamic storage. This will concatenate the columns of both matrices. * Implement `Into` for the `MatrixVec` storage. * Implement `Hash` for all matrices. - + * Add a `.len()` method to retrieve the size of a `MatrixVec`. + ### Modified * The orthographic projection no longer require that `bottom < top`, that `left < right`, and that `znear < zfar`. The only restriction now ith that they must not be equal (in which case the projection would be singular). @@ -46,11 +56,14 @@ This project adheres to [Semantic Versioning](http://semver.org/). * Renamed `.unwrap()` to `.into_inner()` for geometric types that wrap another type. This is for the case of `Unit`, `Transform`, `Orthographic3`, `Perspective3`, `Rotation`. * Deprecate several functions at the root of the crate (replaced by methods). - + +### Removed + * Remove the `Deref` impl for `MatrixVec` as it could cause hard-to-understand compilation errors. + ### nalgebra-glm * Add several alternative projection computations, e.g., `ortho_lh`, `ortho_lh_no`, `perspective_lh`, etc. * Add features matching those of nalgebra, in particular: `serde-serialize`, `abmonation-serialize`, std` (enabled by default). - + ## [0.16.0] All dependencies have been updated to their latest versions. diff --git a/Cargo.toml b/Cargo.toml index 3dbb4731..43323c94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nalgebra" -version = "0.16.13" +version = "0.17.2" authors = [ "Sébastien Crozet " ] description = "Linear algebra library with transformations and statically-sized or dynamically-sized matrices." @@ -12,6 +12,8 @@ categories = [ "science" ] keywords = [ "linear", "algebra", "matrix", "vector", "math" ] license = "BSD-3-Clause" +exclude = ["/ci/*", "/.travis.yml", "/Makefile"] + [lib] name = "nalgebra" path = "src/lib.rs" @@ -23,8 +25,10 @@ stdweb = [ "rand/stdweb" ] arbitrary = [ "quickcheck" ] serde-serialize = [ "serde", "serde_derive", "num-complex/serde" ] abomonation-serialize = [ "abomonation" ] +sparse = [ ] debug = [ ] alloc = [ ] +io = [ "pest", "pest_derive" ] [dependencies] typenum = "1.10" @@ -33,13 +37,15 @@ rand = { version = "0.6", default-features = false } num-traits = { version = "0.2", default-features = false } num-complex = { version = "0.2", default-features = false } approx = { version = "0.3", default-features = false } -alga = { version = "0.7", default-features = false } +alga = { version = "0.8", default-features = false } matrixmultiply = { version = "0.2", optional = true } serde = { version = "1.0", optional = true } serde_derive = { version = "1.0", optional = true } abomonation = { version = "0.7", optional = true } mint = { version = "0.5", optional = true } -quickcheck = { version = "0.7", optional = true } +quickcheck = { version = "0.8", optional = true } +pest = { version = "2.0", optional = true } +pest_derive = { version = "2.0", optional = true } [dev-dependencies] serde_json = "1.0" diff --git a/ci/build.sh b/ci/build.sh index 4c5c4d1b..550c9a69 100755 --- a/ci/build.sh +++ b/ci/build.sh @@ -11,7 +11,7 @@ if [ -z "$NO_STD" ]; then cargo build --verbose -p nalgebra --features "serde-serialize"; cargo build --verbose -p nalgebra --features "abomonation-serialize"; cargo build --verbose -p nalgebra --features "debug"; - cargo build --verbose -p nalgebra --features "debug arbitrary mint serde-serialize abomonation-serialize"; + cargo build --verbose -p nalgebra --all-features else cargo build -p nalgebra-lapack; fi diff --git a/examples/matrix_construction.rs b/examples/matrix_construction.rs index ddcb25c3..bb78458f 100644 --- a/examples/matrix_construction.rs +++ b/examples/matrix_construction.rs @@ -51,8 +51,8 @@ fn main() { // Components listed column-by-column. 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, ] - .iter() - .cloned(), + .iter() + .cloned(), ); assert_eq!(dm, dm1); diff --git a/nalgebra-glm/Cargo.toml b/nalgebra-glm/Cargo.toml index 0ba77cd6..1da1333d 100644 --- a/nalgebra-glm/Cargo.toml +++ b/nalgebra-glm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nalgebra-glm" -version = "0.2.1" +version = "0.3.0" authors = ["sebcrozet "] description = "A computer-graphics oriented API for nalgebra, inspired by the C++ GLM library." @@ -23,5 +23,5 @@ abomonation-serialize = [ "nalgebra/abomonation-serialize" ] [dependencies] num-traits = { version = "0.2", default-features = false } approx = { version = "0.3", default-features = false } -alga = { version = "0.7", default-features = false } -nalgebra = { path = "..", version = "^0.16.13", default-features = false } +alga = { version = "0.8", default-features = false } +nalgebra = { path = "..", version = "0.17", default-features = false } diff --git a/nalgebra-glm/src/ext/matrix_transform.rs b/nalgebra-glm/src/ext/matrix_transform.rs index c901a1d0..82926249 100644 --- a/nalgebra-glm/src/ext/matrix_transform.rs +++ b/nalgebra-glm/src/ext/matrix_transform.rs @@ -9,7 +9,7 @@ where DefaultAllocator: Alloc { TMat::::identity() } -/// Build a right hand look at view matrix +/// Build a look at view matrix based on the right handedness. /// /// # Parameters: /// diff --git a/nalgebra-glm/src/gtc/integer.rs b/nalgebra-glm/src/gtc/integer.rs index a5f1cc8f..df65843f 100644 --- a/nalgebra-glm/src/gtc/integer.rs +++ b/nalgebra-glm/src/gtc/integer.rs @@ -15,4 +15,4 @@ //pub fn uround(x: &TVec) -> TVec // where DefaultAllocator: Alloc { // unimplemented!() -//} \ No newline at end of file +//} diff --git a/nalgebra-glm/src/gtc/packing.rs b/nalgebra-glm/src/gtc/packing.rs index 414afe72..b0261239 100644 --- a/nalgebra-glm/src/gtc/packing.rs +++ b/nalgebra-glm/src/gtc/packing.rs @@ -288,4 +288,4 @@ pub fn unpackUnorm4x16(p: u64) -> Vec4 { pub fn unpackUnorm4x4(p: u16) -> Vec4 { unimplemented!() -} \ No newline at end of file +} diff --git a/nalgebra-glm/src/lib.rs b/nalgebra-glm/src/lib.rs index 9fd33445..53830f2b 100644 --- a/nalgebra-glm/src/lib.rs +++ b/nalgebra-glm/src/lib.rs @@ -13,7 +13,7 @@ ```toml [dependencies] - nalgebra-glm = "0.1" + nalgebra-glm = "0.3" ``` Then, you should add an `extern crate` statement to your `lib.rs` or `main.rs` file. It is **strongly @@ -110,6 +110,7 @@ and keep in mind it is possible to convert, e.g., an `Isometry3` to a `Mat4` and vice-versa (see the [conversions section](#conversions)). */ +#![doc(html_favicon_url = "http://nalgebra.org/img/favicon.ico")] #![cfg_attr(not(feature = "std"), no_std)] extern crate num_traits as num; diff --git a/nalgebra-glm/tests/lib.rs b/nalgebra-glm/tests/lib.rs index 0e76300c..ce95c64f 100644 --- a/nalgebra-glm/tests/lib.rs +++ b/nalgebra-glm/tests/lib.rs @@ -52,4 +52,4 @@ pub fn perspective_glm_nalgebra_project_same() assert_eq!(na_mat, gl_mat); assert_eq!(na_pt, gl_pt); -} \ No newline at end of file +} diff --git a/nalgebra-lapack/Cargo.toml b/nalgebra-lapack/Cargo.toml index c59211ab..ac54589c 100644 --- a/nalgebra-lapack/Cargo.toml +++ b/nalgebra-lapack/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nalgebra-lapack" -version = "0.8.0" +version = "0.9.0" authors = [ "Sébastien Crozet ", "Andrew Straw " ] description = "Linear algebra library with transformations and satically-sized or dynamically-sized matrices." @@ -22,10 +22,10 @@ accelerate = ["lapack-src/accelerate"] intel-mkl = ["lapack-src/intel-mkl"] [dependencies] -nalgebra = { version = "0.16", path = ".." } +nalgebra = { version = "0.17", path = ".." } num-traits = "0.2" num-complex = { version = "0.2", default-features = false } -alga = { version = "0.7", default-features = false } +alga = { version = "0.8", default-features = false } serde = { version = "1.0", optional = true } serde_derive = { version = "1.0", optional = true } lapack = { version = "0.16", default-features = false } @@ -33,7 +33,7 @@ lapack-src = { version = "0.2", default-features = false } # clippy = "*" [dev-dependencies] -nalgebra = { version = "0.16", path = "..", features = [ "arbitrary" ] } -quickcheck = "0.7" +nalgebra = { version = "0.17", path = "..", features = [ "arbitrary" ] } +quickcheck = "0.8" approx = "0.3" rand = "0.6" diff --git a/nalgebra-lapack/src/cholesky.rs b/nalgebra-lapack/src/cholesky.rs index 3976373d..02552915 100644 --- a/nalgebra-lapack/src/cholesky.rs +++ b/nalgebra-lapack/src/cholesky.rs @@ -62,7 +62,7 @@ where DefaultAllocator: Allocator N::xpotrf(uplo, dim, m.as_mut_slice(), dim, &mut info); lapack_check!(info); - Some(Cholesky { l: m }) + Some(Self { l: m }) } /// Retrieves the lower-triangular factor of the cholesky decomposition. diff --git a/nalgebra-lapack/src/eigen.rs b/nalgebra-lapack/src/eigen.rs index d95219cb..fbc49319 100644 --- a/nalgebra-lapack/src/eigen.rs +++ b/nalgebra-lapack/src/eigen.rs @@ -127,7 +127,7 @@ where DefaultAllocator: Allocator + Allocator lapack_check!(info); if wi.iter().all(|e| e.is_zero()) { - return Some(Eigen { + return Some(Self { eigenvalues: wr, left_eigenvectors: Some(vl), eigenvectors: Some(vr), @@ -156,7 +156,7 @@ where DefaultAllocator: Allocator + Allocator lapack_check!(info); if wi.iter().all(|e| e.is_zero()) { - return Some(Eigen { + return Some(Self { eigenvalues: wr, left_eigenvectors: Some(vl), eigenvectors: None, @@ -185,7 +185,7 @@ where DefaultAllocator: Allocator + Allocator lapack_check!(info); if wi.iter().all(|e| e.is_zero()) { - return Some(Eigen { + return Some(Self { eigenvalues: wr, left_eigenvectors: None, eigenvectors: Some(vr), @@ -212,7 +212,7 @@ where DefaultAllocator: Allocator + Allocator lapack_check!(info); if wi.iter().all(|e| e.is_zero()) { - return Some(Eigen { + return Some(Self { eigenvalues: wr, left_eigenvectors: None, eigenvectors: None, diff --git a/nalgebra-lapack/src/hessenberg.rs b/nalgebra-lapack/src/hessenberg.rs index 6bfa673b..65a27d1e 100644 --- a/nalgebra-lapack/src/hessenberg.rs +++ b/nalgebra-lapack/src/hessenberg.rs @@ -48,7 +48,7 @@ impl> Hessenberg where DefaultAllocator: Allocator + Allocator> { /// Computes the hessenberg decomposition of the matrix `m`. - pub fn new(mut m: MatrixN) -> Hessenberg { + pub fn new(mut m: MatrixN) -> Self { let nrows = m.data.shape().0; let n = nrows.value() as i32; @@ -83,7 +83,7 @@ where DefaultAllocator: Allocator + Allocator> ); lapack_panic!(info); - Hessenberg { h: m, tau: tau } + Self { h: m, tau: tau } } /// Computes the hessenberg matrix of this decomposition. diff --git a/nalgebra-lapack/src/lib.rs b/nalgebra-lapack/src/lib.rs index a001dcc3..c343ba83 100644 --- a/nalgebra-lapack/src/lib.rs +++ b/nalgebra-lapack/src/lib.rs @@ -68,8 +68,10 @@ #![deny(unused_qualifications)] #![deny(unused_results)] #![deny(missing_docs)] -#![doc(html_favicon_url = "http://nalgebra.org/img/favicon.ico", - html_root_url = "http://nalgebra.org/rustdoc")] +#![doc( + html_favicon_url = "http://nalgebra.org/img/favicon.ico", + html_root_url = "http://nalgebra.org/rustdoc" +)] extern crate alga; extern crate lapack; diff --git a/nalgebra-lapack/src/lu.rs b/nalgebra-lapack/src/lu.rs index c9b1167c..ad76e245 100644 --- a/nalgebra-lapack/src/lu.rs +++ b/nalgebra-lapack/src/lu.rs @@ -82,7 +82,7 @@ where ); lapack_panic!(info); - LU { lu: m, p: ipiv } + Self { lu: m, p: ipiv } } /// Gets the lower-triangular matrix part of the decomposition. diff --git a/nalgebra-lapack/src/qr.rs b/nalgebra-lapack/src/qr.rs index 66220e49..1fa9b066 100644 --- a/nalgebra-lapack/src/qr.rs +++ b/nalgebra-lapack/src/qr.rs @@ -54,14 +54,14 @@ where DefaultAllocator: Allocator + Allocator> { /// Computes the QR decomposition of the matrix `m`. - pub fn new(mut m: MatrixMN) -> QR { + pub fn new(mut m: MatrixMN) -> Self { let (nrows, ncols) = m.data.shape(); let mut info = 0; let mut tau = unsafe { Matrix::new_uninitialized_generic(nrows.min(ncols), U1) }; if nrows.value() == 0 || ncols.value() == 0 { - return QR { qr: m, tau: tau }; + return Self { qr: m, tau: tau }; } let lwork = N::xgeqrf_work_size( @@ -86,7 +86,7 @@ where DefaultAllocator: Allocator &mut info, ); - QR { qr: m, tau: tau } + Self { qr: m, tau: tau } } /// Retrieves the upper trapezoidal submatrix `R` of this decomposition. diff --git a/nalgebra-lapack/src/symmetric_eigen.rs b/nalgebra-lapack/src/symmetric_eigen.rs index e7d5bd0f..48e444ba 100644 --- a/nalgebra-lapack/src/symmetric_eigen.rs +++ b/nalgebra-lapack/src/symmetric_eigen.rs @@ -62,7 +62,7 @@ where DefaultAllocator: Allocator + Allocator pub fn new(m: MatrixN) -> Self { let (vals, vecs) = Self::do_decompose(m, true).expect("SymmetricEigen: convergence failure."); - SymmetricEigen { + Self { eigenvalues: vals, eigenvectors: vecs.unwrap(), } diff --git a/rustfmt.toml b/rustfmt.toml index dff7375b..8dc8e61a 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,3 +1,3 @@ unstable_features = true indent_style = "Block" -where_single_line = true +where_single_line = true \ No newline at end of file diff --git a/src/base/alias_slice.rs b/src/base/alias_slice.rs index 1b368c8e..790e4e59 100644 --- a/src/base/alias_slice.rs +++ b/src/base/alias_slice.rs @@ -175,24 +175,24 @@ pub type MatrixSliceXx6<'a, N, RStride = U1, CStride = Dynamic> = MatrixSliceMN<'a, N, Dynamic, U6, RStride, CStride>; /// A column vector slice with `D` rows. -pub type VectorSliceN<'a, N, D, Stride = U1> = - Matrix>; +pub type VectorSliceN<'a, N, D, RStride = U1, CStride = D> = + Matrix>; /// A column vector slice dynamic numbers of rows and columns. -pub type DVectorSlice<'a, N, Stride = U1> = VectorSliceN<'a, N, Dynamic, Stride>; +pub type DVectorSlice<'a, N, RStride = U1, CStride = Dynamic> = VectorSliceN<'a, N, Dynamic, RStride, CStride>; /// A 1D column vector slice. -pub type VectorSlice1<'a, N, Stride = U1> = VectorSliceN<'a, N, U1, Stride>; +pub type VectorSlice1<'a, N, RStride = U1, CStride = U1> = VectorSliceN<'a, N, U1, RStride, CStride>; /// A 2D column vector slice. -pub type VectorSlice2<'a, N, Stride = U1> = VectorSliceN<'a, N, U2, Stride>; +pub type VectorSlice2<'a, N, RStride = U1, CStride = U2> = VectorSliceN<'a, N, U2, RStride, CStride>; /// A 3D column vector slice. -pub type VectorSlice3<'a, N, Stride = U1> = VectorSliceN<'a, N, U3, Stride>; +pub type VectorSlice3<'a, N, RStride = U1, CStride = U3> = VectorSliceN<'a, N, U3, RStride, CStride>; /// A 4D column vector slice. -pub type VectorSlice4<'a, N, Stride = U1> = VectorSliceN<'a, N, U4, Stride>; +pub type VectorSlice4<'a, N, RStride = U1, CStride = U4> = VectorSliceN<'a, N, U4, RStride, CStride>; /// A 5D column vector slice. -pub type VectorSlice5<'a, N, Stride = U1> = VectorSliceN<'a, N, U5, Stride>; +pub type VectorSlice5<'a, N, RStride = U1, CStride = U5> = VectorSliceN<'a, N, U5, RStride, CStride>; /// A 6D column vector slice. -pub type VectorSlice6<'a, N, Stride = U1> = VectorSliceN<'a, N, U6, Stride>; +pub type VectorSlice6<'a, N, RStride = U1, CStride = U6> = VectorSliceN<'a, N, U6, RStride, CStride>; /* * @@ -367,21 +367,21 @@ pub type MatrixSliceMutXx6<'a, N, RStride = U1, CStride = Dynamic> = MatrixSliceMutMN<'a, N, Dynamic, U6, RStride, CStride>; /// A mutable column vector slice with `D` rows. -pub type VectorSliceMutN<'a, N, D, Stride = U1> = - Matrix>; +pub type VectorSliceMutN<'a, N, D, RStride = U1, CStride = D> = + Matrix>; /// A mutable column vector slice dynamic numbers of rows and columns. -pub type DVectorSliceMut<'a, N, Stride = U1> = VectorSliceMutN<'a, N, Dynamic, Stride>; +pub type DVectorSliceMut<'a, N, RStride = U1, CStride = Dynamic> = VectorSliceMutN<'a, N, Dynamic, RStride, CStride>; /// A 1D mutable column vector slice. -pub type VectorSliceMut1<'a, N, Stride = U1> = VectorSliceMutN<'a, N, U1, Stride>; +pub type VectorSliceMut1<'a, N, RStride = U1, CStride = U1> = VectorSliceMutN<'a, N, U1, RStride, CStride>; /// A 2D mutable column vector slice. -pub type VectorSliceMut2<'a, N, Stride = U1> = VectorSliceMutN<'a, N, U2, Stride>; +pub type VectorSliceMut2<'a, N, RStride = U1, CStride = U2> = VectorSliceMutN<'a, N, U2, RStride, CStride>; /// A 3D mutable column vector slice. -pub type VectorSliceMut3<'a, N, Stride = U1> = VectorSliceMutN<'a, N, U3, Stride>; +pub type VectorSliceMut3<'a, N, RStride = U1, CStride = U3> = VectorSliceMutN<'a, N, U3, RStride, CStride>; /// A 4D mutable column vector slice. -pub type VectorSliceMut4<'a, N, Stride = U1> = VectorSliceMutN<'a, N, U4, Stride>; +pub type VectorSliceMut4<'a, N, RStride = U1, CStride = U4> = VectorSliceMutN<'a, N, U4, RStride, CStride>; /// A 5D mutable column vector slice. -pub type VectorSliceMut5<'a, N, Stride = U1> = VectorSliceMutN<'a, N, U5, Stride>; +pub type VectorSliceMut5<'a, N, RStride = U1, CStride = U5> = VectorSliceMutN<'a, N, U5, RStride, CStride>; /// A 6D mutable column vector slice. -pub type VectorSliceMut6<'a, N, Stride = U1> = VectorSliceMutN<'a, N, U6, Stride>; +pub type VectorSliceMut6<'a, N, RStride = U1, CStride = U6> = VectorSliceMutN<'a, N, U6, RStride, CStride>; diff --git a/src/base/array_storage.rs b/src/base/array_storage.rs index f1a0ea13..3beab9e1 100644 --- a/src/base/array_storage.rs +++ b/src/base/array_storage.rs @@ -331,7 +331,7 @@ where let mut curr = 0; while let Some(value) = try!(visitor.next_element()) { - out[curr] = value; + *out.get_mut(curr).ok_or_else(|| V::Error::invalid_length(curr, &self))? = value; curr += 1; } diff --git a/src/base/blas.rs b/src/base/blas.rs index 1379db19..c16496d4 100644 --- a/src/base/blas.rs +++ b/src/base/blas.rs @@ -13,18 +13,18 @@ use base::dimension::{Dim, Dynamic, U1, U2, U3, U4}; use base::storage::{Storage, StorageMut}; use base::{DefaultAllocator, Matrix, Scalar, SquareMatrix, Vector}; -impl> Vector { - /// Computes the index of the vector component with the largest value. +impl> Vector { + /// Computes the index and value of the vector component with the largest value. /// /// # Examples: /// /// ``` /// # use nalgebra::Vector3; /// let vec = Vector3::new(11, -15, 13); - /// assert_eq!(vec.imax(), 2); + /// assert_eq!(vec.argmax(), (2, 13)); /// ``` #[inline] - pub fn imax(&self) -> usize { + pub fn argmax(&self) -> (usize, N) { assert!(!self.is_empty(), "The input vector must not be empty."); let mut the_max = unsafe { self.vget_unchecked(0) }; @@ -39,7 +39,21 @@ impl> Vector } } - the_i + (the_i, *the_max) + } + + /// Computes the index of the vector component with the largest value. + /// + /// # Examples: + /// + /// ``` + /// # use nalgebra::Vector3; + /// let vec = Vector3::new(11, -15, 13); + /// assert_eq!(vec.imax(), 2); + /// ``` + #[inline] + pub fn imax(&self) -> usize { + self.argmax().0 } /// Computes the index of the vector component with the largest absolute value. @@ -52,7 +66,8 @@ impl> Vector /// assert_eq!(vec.iamax(), 1); /// ``` #[inline] - pub fn iamax(&self) -> usize { + pub fn iamax(&self) -> usize + where N: Signed { assert!(!self.is_empty(), "The input vector must not be empty."); let mut the_max = unsafe { self.vget_unchecked(0).abs() }; @@ -70,6 +85,34 @@ impl> Vector the_i } + /// Computes the index and value of the vector component with the smallest value. + /// + /// # Examples: + /// + /// ``` + /// # use nalgebra::Vector3; + /// let vec = Vector3::new(11, -15, 13); + /// assert_eq!(vec.argmin(), (1, -15)); + /// ``` + #[inline] + pub fn argmin(&self) -> (usize, N) { + assert!(!self.is_empty(), "The input vector must not be empty."); + + let mut the_min = unsafe { self.vget_unchecked(0) }; + let mut the_i = 0; + + for i in 1..self.nrows() { + let val = unsafe { self.vget_unchecked(i) }; + + if val < the_min { + the_min = val; + the_i = i; + } + } + + (the_i, *the_min) + } + /// Computes the index of the vector component with the smallest value. /// /// # Examples: @@ -81,21 +124,7 @@ impl> Vector /// ``` #[inline] pub fn imin(&self) -> usize { - assert!(!self.is_empty(), "The input vector must not be empty."); - - let mut the_max = unsafe { self.vget_unchecked(0) }; - let mut the_i = 0; - - for i in 1..self.nrows() { - let val = unsafe { self.vget_unchecked(i) }; - - if val < the_max { - the_max = val; - the_i = i; - } - } - - the_i + self.argmin().0 } /// Computes the index of the vector component with the smallest absolute value. @@ -108,17 +137,18 @@ impl> Vector /// assert_eq!(vec.iamin(), 0); /// ``` #[inline] - pub fn iamin(&self) -> usize { + pub fn iamin(&self) -> usize + where N: Signed { assert!(!self.is_empty(), "The input vector must not be empty."); - let mut the_max = unsafe { self.vget_unchecked(0).abs() }; + let mut the_min = unsafe { self.vget_unchecked(0).abs() }; let mut the_i = 0; for i in 1..self.nrows() { let val = unsafe { self.vget_unchecked(i).abs() }; - if val < the_max { - the_max = val; + if val < the_min { + the_min = val; the_i = i; } } @@ -627,7 +657,6 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Matrix2x3, Matrix3x4, Matrix2x4}; /// let mut mat1 = Matrix2x4::identity(); /// let mat2 = Matrix2x3::new(1.0, 2.0, 3.0, @@ -760,7 +789,6 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Matrix3x2, Matrix3x4, Matrix2x4}; /// let mut mat1 = Matrix2x4::identity(); /// let mat2 = Matrix3x2::new(1.0, 4.0, @@ -879,7 +907,6 @@ where N: Scalar + Zero + One + ClosedAdd + ClosedMul /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{DMatrix, DVector}; /// // Note that all those would also work with statically-sized matrices. /// // We use DMatrix/DVector since that's the only case where pre-allocating the @@ -934,7 +961,6 @@ where N: Scalar + Zero + One + ClosedAdd + ClosedMul /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Matrix2, Matrix3, Matrix2x3, Vector2}; /// let mut mat = Matrix2::identity(); /// let lhs = Matrix2x3::new(1.0, 2.0, 3.0, @@ -971,7 +997,6 @@ where N: Scalar + Zero + One + ClosedAdd + ClosedMul /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{DMatrix, DVector}; /// // Note that all those would also work with statically-sized matrices. /// // We use DMatrix/DVector since that's the only case where pre-allocating the @@ -1026,7 +1051,6 @@ where N: Scalar + Zero + One + ClosedAdd + ClosedMul /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Matrix2, Matrix3x2, Matrix3}; /// let mut mat = Matrix2::identity(); /// let rhs = Matrix3x2::new(1.0, 2.0, diff --git a/src/base/cg.rs b/src/base/cg.rs index 357e21bf..5883e710 100644 --- a/src/base/cg.rs +++ b/src/base/cg.rs @@ -130,8 +130,14 @@ impl Matrix4 { /// It maps the view direction `target - eye` to the positive `z` axis and the origin to the /// `eye`. #[inline] + pub fn face_towards(eye: &Point3, target: &Point3, up: &Vector3) -> Self { + IsometryMatrix3::face_towards(eye, target, up).to_homogeneous() + } + + /// Deprecated: Use [Matrix4::face_towards] instead. + #[deprecated(note="renamed to `face_towards`")] pub fn new_observer_frame(eye: &Point3, target: &Point3, up: &Vector3) -> Self { - IsometryMatrix3::new_observer_frame(eye, target, up).to_homogeneous() + Matrix4::face_towards(eye, target, up) } /// Builds a right-handed look-at view matrix. diff --git a/src/base/componentwise.rs b/src/base/componentwise.rs index 195bc29a..9081cf36 100644 --- a/src/base/componentwise.rs +++ b/src/base/componentwise.rs @@ -1,4 +1,4 @@ -// Non-conventional componentwise operators. +// Non-conventional component-wise operators. use num::{Signed, Zero}; use std::ops::{Add, Mul}; diff --git a/src/base/construction.rs b/src/base/construction.rs index 8adc90c1..aeee6121 100644 --- a/src/base/construction.rs +++ b/src/base/construction.rs @@ -270,7 +270,7 @@ where DefaultAllocator: Allocator /// let vec_ptr = vec.as_ptr(); /// /// let matrix = Matrix::from_vec_generic(Dynamic::new(vec.len()), U1, vec); - /// let matrix_storage_ptr = matrix.data.as_ptr(); + /// let matrix_storage_ptr = matrix.data.as_vec().as_ptr(); /// /// // `matrix` is backed by exactly the same `Vec` as it was constructed from. /// assert_eq!(matrix_storage_ptr, vec_ptr); @@ -296,7 +296,7 @@ where /// /// let m = Matrix3::from_diagonal(&Vector3::new(1.0, 2.0, 3.0)); /// // The two additional arguments represent the matrix dimensions. - /// let dm = DMatrix::from_diagonal(&DVector::from_row_slice(3, &[1.0, 2.0, 3.0])); + /// let dm = DMatrix::from_diagonal(&DVector::from_row_slice(&[1.0, 2.0, 3.0])); /// /// assert!(m.m11 == 1.0 && m.m12 == 0.0 && m.m13 == 0.0 && /// m.m21 == 0.0 && m.m22 == 2.0 && m.m23 == 0.0 && @@ -444,63 +444,6 @@ macro_rules! impl_constructors( Self::from_iterator_generic($($gargs, )* iter) } - /// Creates a matrix with its elements filled with the components provided by a slice - /// in row-major order. - /// - /// The order of elements in the slice must follow the usual mathematic writing, i.e., - /// row-by-row. - /// - /// # Example - /// ``` - /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; - /// # use std::iter; - /// - /// let v = Vector3::from_row_slice(&[0, 1, 2]); - /// // The additional argument represents the vector dimension. - /// let dv = DVector::from_row_slice(3, &[0, 1, 2]); - /// let m = Matrix2x3::from_row_slice(&[0, 1, 2, 3, 4, 5]); - /// // The two additional arguments represent the matrix dimensions. - /// let dm = DMatrix::from_row_slice(2, 3, &[0, 1, 2, 3, 4, 5]); - /// - /// assert!(v.x == 0 && v.y == 1 && v.z == 2); - /// assert!(dv[0] == 0 && dv[1] == 1 && dv[2] == 2); - /// assert!(m.m11 == 0 && m.m12 == 1 && m.m13 == 2 && - /// m.m21 == 3 && m.m22 == 4 && m.m23 == 5); - /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 1 && dm[(0, 2)] == 2 && - /// dm[(1, 0)] == 3 && dm[(1, 1)] == 4 && dm[(1, 2)] == 5); - /// ``` - #[inline] - pub fn from_row_slice($($args: usize,)* slice: &[N]) -> Self { - Self::from_row_slice_generic($($gargs, )* slice) - } - - /// Creates a matrix with its elements filled with the components provided by a slice - /// in column-major order. - /// - /// # Example - /// ``` - /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; - /// # use std::iter; - /// - /// let v = Vector3::from_column_slice(&[0, 1, 2]); - /// // The additional argument represents the vector dimension. - /// let dv = DVector::from_column_slice(3, &[0, 1, 2]); - /// let m = Matrix2x3::from_column_slice(&[0, 1, 2, 3, 4, 5]); - /// // The two additional arguments represent the matrix dimensions. - /// let dm = DMatrix::from_column_slice(2, 3, &[0, 1, 2, 3, 4, 5]); - /// - /// assert!(v.x == 0 && v.y == 1 && v.z == 2); - /// assert!(dv[0] == 0 && dv[1] == 1 && dv[2] == 2); - /// assert!(m.m11 == 0 && m.m12 == 2 && m.m13 == 4 && - /// m.m21 == 1 && m.m22 == 3 && m.m23 == 5); - /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 2 && dm[(0, 2)] == 4 && - /// dm[(1, 0)] == 1 && dm[(1, 1)] == 3 && dm[(1, 2)] == 5); - /// ``` - #[inline] - pub fn from_column_slice($($args: usize,)* slice: &[N]) -> Self { - Self::from_column_slice_generic($($gargs, )* slice) - } - /// Creates a matrix or vector filled with the results of a function applied to each of its /// component coordinates. /// @@ -612,32 +555,6 @@ macro_rules! impl_constructors( ) -> Self { Self::from_distribution_generic($($gargs, )* distribution, rng) } - - /// Creates a matrix backed by a given `Vec`. - /// - /// The output matrix is filled column-by-column. - /// - /// # Example - /// ``` - /// # use nalgebra::{DMatrix, Matrix2x3}; - /// - /// let m = Matrix2x3::from_vec(vec![0, 1, 2, 3, 4, 5]); - /// - /// assert!(m.m11 == 0 && m.m12 == 2 && m.m13 == 4 && - /// m.m21 == 1 && m.m22 == 3 && m.m23 == 5); - /// - /// - /// // The two additional arguments represent the matrix dimensions. - /// let dm = DMatrix::from_vec(2, 3, vec![0, 1, 2, 3, 4, 5]); - /// - /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 2 && dm[(0, 2)] == 4 && - /// dm[(1, 0)] == 1 && dm[(1, 1)] == 3 && dm[(1, 2)] == 5); - /// ``` - #[inline] - #[cfg(feature = "std")] - pub fn from_vec($($args: usize,)* data: Vec) -> Self { - Self::from_vec_generic($($gargs, )* data) - } } impl MatrixMN @@ -676,6 +593,125 @@ impl_constructors!(Dynamic, Dynamic; Dynamic::new(nrows), Dynamic::new(ncols); nrows, ncols); +/* + * + * Constructors that don't necessarily require all dimensions + * to be specified whon one dimension is already known. + * + */ +macro_rules! impl_constructors_from_data( + ($data: ident; $($Dims: ty),*; $(=> $DimIdent: ident: $DimBound: ident),*; $($gargs: expr),*; $($args: ident),*) => { + impl MatrixMN + where DefaultAllocator: Allocator { + /// Creates a matrix with its elements filled with the components provided by a slice + /// in row-major order. + /// + /// The order of elements in the slice must follow the usual mathematic writing, i.e., + /// row-by-row. + /// + /// # Example + /// ``` + /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; + /// # use std::iter; + /// + /// let v = Vector3::from_row_slice(&[0, 1, 2]); + /// // The additional argument represents the vector dimension. + /// let dv = DVector::from_row_slice(&[0, 1, 2]); + /// let m = Matrix2x3::from_row_slice(&[0, 1, 2, 3, 4, 5]); + /// // The two additional arguments represent the matrix dimensions. + /// let dm = DMatrix::from_row_slice(2, 3, &[0, 1, 2, 3, 4, 5]); + /// + /// assert!(v.x == 0 && v.y == 1 && v.z == 2); + /// assert!(dv[0] == 0 && dv[1] == 1 && dv[2] == 2); + /// assert!(m.m11 == 0 && m.m12 == 1 && m.m13 == 2 && + /// m.m21 == 3 && m.m22 == 4 && m.m23 == 5); + /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 1 && dm[(0, 2)] == 2 && + /// dm[(1, 0)] == 3 && dm[(1, 1)] == 4 && dm[(1, 2)] == 5); + /// ``` + #[inline] + pub fn from_row_slice($($args: usize,)* $data: &[N]) -> Self { + Self::from_row_slice_generic($($gargs, )* $data) + } + + /// Creates a matrix with its elements filled with the components provided by a slice + /// in column-major order. + /// + /// # Example + /// ``` + /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; + /// # use std::iter; + /// + /// let v = Vector3::from_column_slice(&[0, 1, 2]); + /// // The additional argument represents the vector dimension. + /// let dv = DVector::from_column_slice(&[0, 1, 2]); + /// let m = Matrix2x3::from_column_slice(&[0, 1, 2, 3, 4, 5]); + /// // The two additional arguments represent the matrix dimensions. + /// let dm = DMatrix::from_column_slice(2, 3, &[0, 1, 2, 3, 4, 5]); + /// + /// assert!(v.x == 0 && v.y == 1 && v.z == 2); + /// assert!(dv[0] == 0 && dv[1] == 1 && dv[2] == 2); + /// assert!(m.m11 == 0 && m.m12 == 2 && m.m13 == 4 && + /// m.m21 == 1 && m.m22 == 3 && m.m23 == 5); + /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 2 && dm[(0, 2)] == 4 && + /// dm[(1, 0)] == 1 && dm[(1, 1)] == 3 && dm[(1, 2)] == 5); + /// ``` + #[inline] + pub fn from_column_slice($($args: usize,)* $data: &[N]) -> Self { + Self::from_column_slice_generic($($gargs, )* $data) + } + + /// Creates a matrix backed by a given `Vec`. + /// + /// The output matrix is filled column-by-column. + /// + /// # Example + /// ``` + /// # use nalgebra::{DMatrix, Matrix2x3}; + /// + /// let m = Matrix2x3::from_vec(vec![0, 1, 2, 3, 4, 5]); + /// + /// assert!(m.m11 == 0 && m.m12 == 2 && m.m13 == 4 && + /// m.m21 == 1 && m.m22 == 3 && m.m23 == 5); + /// + /// + /// // The two additional arguments represent the matrix dimensions. + /// let dm = DMatrix::from_vec(2, 3, vec![0, 1, 2, 3, 4, 5]); + /// + /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 2 && dm[(0, 2)] == 4 && + /// dm[(1, 0)] == 1 && dm[(1, 1)] == 3 && dm[(1, 2)] == 5); + /// ``` + #[inline] + #[cfg(feature = "std")] + pub fn from_vec($($args: usize,)* $data: Vec) -> Self { + Self::from_vec_generic($($gargs, )* $data) + } + } + } +); + +// FIXME: this is not very pretty. We could find a better call syntax. +impl_constructors_from_data!(data; R, C; // Arguments for Matrix + => R: DimName, => C: DimName; // Type parameters for impl + R::name(), C::name(); // Arguments for `_generic` constructors. + ); // Arguments for non-generic constructors. + +impl_constructors_from_data!(data; R, Dynamic; + => R: DimName; + R::name(), Dynamic::new(data.len() / R::dim()); + ); + +impl_constructors_from_data!(data; Dynamic, C; + => C: DimName; + Dynamic::new(data.len() / C::dim()), C::name(); + ); + +impl_constructors_from_data!(data; Dynamic, Dynamic; + ; + Dynamic::new(nrows), Dynamic::new(ncols); + nrows, ncols); + + + /* * * Zero, One, Rand traits. diff --git a/src/base/conversion.rs b/src/base/conversion.rs index 2e5ca9df..fcb6907d 100644 --- a/src/base/conversion.rs +++ b/src/base/conversion.rs @@ -12,8 +12,10 @@ use typenum::Prod; use base::allocator::{Allocator, SameShapeAllocator}; use base::constraint::{SameNumberOfColumns, SameNumberOfRows, ShapeConstraint}; use base::dimension::{ - Dim, DimName, Dynamic, U1, U10, U11, U12, U13, U14, U15, U16, U2, U3, U4, U5, U6, U7, U8, U9, + Dim, DimName, U1, U10, U11, U12, U13, U14, U15, U16, U2, U3, U4, U5, U6, U7, U8, U9, }; +#[cfg(any(feature = "std", feature = "alloc"))] +use base::dimension::Dynamic; use base::iter::{MatrixIter, MatrixIterMut}; use base::storage::{ContiguousStorage, ContiguousStorageMut, Storage, StorageMut}; #[cfg(any(feature = "std", feature = "alloc"))] diff --git a/src/base/dimension.rs b/src/base/dimension.rs index 528c8ace..5d1d1bd9 100644 --- a/src/base/dimension.rs +++ b/src/base/dimension.rs @@ -22,8 +22,8 @@ pub struct Dynamic { impl Dynamic { /// A dynamic size equal to `value`. #[inline] - pub fn new(value: usize) -> Dynamic { - Dynamic { value: value } + pub fn new(value: usize) -> Self { + Self { value: value } } } @@ -80,7 +80,7 @@ impl Dim for Dynamic { #[inline] fn from_usize(dim: usize) -> Self { - Dynamic::new(dim) + Self::new(dim) } #[inline] @@ -93,8 +93,8 @@ impl Add for Dynamic { type Output = Dynamic; #[inline] - fn add(self, rhs: usize) -> Dynamic { - Dynamic::new(self.value + rhs) + fn add(self, rhs: usize) -> Self { + Self::new(self.value + rhs) } } @@ -102,8 +102,8 @@ impl Sub for Dynamic { type Output = Dynamic; #[inline] - fn sub(self, rhs: usize) -> Dynamic { - Dynamic::new(self.value - rhs) + fn sub(self, rhs: usize) -> Self { + Self::new(self.value - rhs) } } @@ -364,7 +364,8 @@ impl< G: Bit + Any + Debug + Copy + PartialEq + Send + Sync, > IsNotStaticOne for UInt, A>, B>, C>, D>, E>, F>, G> -{} +{ +} impl NamedDim for UInt @@ -405,4 +406,5 @@ impl IsNotStaticOne for UInt -{} +{ +} diff --git a/src/base/edition.rs b/src/base/edition.rs index 8b6581e8..32e2c1aa 100644 --- a/src/base/edition.rs +++ b/src/base/edition.rs @@ -1,12 +1,18 @@ use num::{One, Zero}; use std::cmp; use std::ptr; +#[cfg(any(feature = "std", feature = "alloc"))] +use std::iter::ExactSizeIterator; +#[cfg(any(feature = "std", feature = "alloc"))] +use std::mem; use base::allocator::{Allocator, Reallocator}; use base::constraint::{DimEq, SameNumberOfColumns, SameNumberOfRows, ShapeConstraint}; use base::dimension::{ - Dim, DimAdd, DimDiff, DimMin, DimMinimum, DimName, DimSub, DimSum, Dynamic, U1, + Dim, DimAdd, DimDiff, DimMin, DimMinimum, DimName, DimSub, DimSum, U1, }; +#[cfg(any(feature = "std", feature = "alloc"))] +use base::dimension::Dynamic; use base::storage::{Storage, StorageMut}; #[cfg(any(feature = "std", feature = "alloc"))] use base::DMatrix; @@ -23,7 +29,7 @@ impl> Matrix { res } - /// Extracts the upper triangular part of this matrix (including the diagonal). + /// Extracts the lower triangular part of this matrix (including the diagonal). #[inline] pub fn lower_triangle(&self) -> MatrixMN where DefaultAllocator: Allocator { @@ -32,6 +38,54 @@ impl> Matrix { res } + + /// Creates a new matrix by extracting the given set of rows from `self`. + #[cfg(any(feature = "std", feature = "alloc"))] + pub fn select_rows<'a, I>(&self, irows: I) -> MatrixMN + where I: IntoIterator, + I::IntoIter: ExactSizeIterator + Clone, + DefaultAllocator: Allocator { + let irows = irows.into_iter(); + let ncols = self.data.shape().1; + let mut res = unsafe { MatrixMN::new_uninitialized_generic(Dynamic::new(irows.len()), ncols) }; + + // First, check that all the indices from irows are valid. + // This will allow us to use unchecked access in the inner loop. + for i in irows.clone() { + assert!(*i < self.nrows(), "Row index out of bounds.") + } + + for j in 0..ncols.value() { + // FIXME: use unchecked column indexing + let mut res = res.column_mut(j); + let mut src = self.column(j); + + for (destination, source) in irows.clone().enumerate() { + unsafe { + *res.vget_unchecked_mut(destination) = *src.vget_unchecked(*source) + } + } + } + + res + } + + /// Creates a new matrix by extracting the given set of columns from `self`. + #[cfg(any(feature = "std", feature = "alloc"))] + pub fn select_columns<'a, I>(&self, icols: I) -> MatrixMN + where I: IntoIterator, + I::IntoIter: ExactSizeIterator, + DefaultAllocator: Allocator { + let icols = icols.into_iter(); + let nrows = self.data.shape().0; + let mut res = unsafe { MatrixMN::new_uninitialized_generic(nrows, Dynamic::new(icols.len())) }; + + for (destination, source) in icols.enumerate() { + res.column_mut(destination).copy_from(&self.column(*source)) + } + + res + } } impl> Matrix { @@ -248,6 +302,7 @@ impl> Matrix { /// Removes `n` consecutive columns from this matrix, starting with the `i`-th (included). #[inline] + #[cfg(any(feature = "std", feature = "alloc"))] pub fn remove_columns(self, i: usize, n: usize) -> MatrixMN where C: DimSub, @@ -330,6 +385,7 @@ impl> Matrix { /// Removes `n` consecutive rows from this matrix, starting with the `i`-th (included). #[inline] + #[cfg(any(feature = "std", feature = "alloc"))] pub fn remove_rows(self, i: usize, n: usize) -> MatrixMN where R: DimSub, @@ -407,6 +463,7 @@ impl> Matrix { /// Inserts `n` columns filled with `val` starting at the `i-th` position. #[inline] + #[cfg(any(feature = "std", feature = "alloc"))] pub fn insert_columns(self, i: usize, n: usize, val: N) -> MatrixMN where C: DimAdd, @@ -484,6 +541,7 @@ impl> Matrix { /// Inserts `n` rows filled with `val` starting at the `i-th` position. #[inline] + #[cfg(any(feature = "std", feature = "alloc"))] pub fn insert_rows(self, i: usize, n: usize, val: N) -> MatrixMN where R: DimAdd, @@ -549,6 +607,29 @@ impl> Matrix { self.resize_generic(Dynamic::new(new_nrows), Dynamic::new(new_ncols), val) } + /// Resizes this matrix vertically, i.e., so that it contains `new_nrows` rows while keeping the same number of columns. + /// + /// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more + /// rows than `self`, then the extra rows are filled with `val`. + #[cfg(any(feature = "std", feature = "alloc"))] + pub fn resize_vertically(self, new_nrows: usize, val: N) -> MatrixMN + where DefaultAllocator: Reallocator { + let ncols = self.data.shape().1; + self.resize_generic(Dynamic::new(new_nrows), ncols, val) + } + + /// Resizes this matrix horizontally, i.e., so that it contains `new_ncolumns` columns while keeping the same number of columns. + /// + /// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more + /// columns than `self`, then the extra columns are filled with `val`. + #[cfg(any(feature = "std", feature = "alloc"))] + pub fn resize_horizontally(self, new_ncols: usize, val: N) -> MatrixMN + where DefaultAllocator: Reallocator { + let nrows = self.data.shape().0; + self.resize_generic(nrows, Dynamic::new(new_ncols), val) + } + + /// Resizes this matrix so that it contains `R2::value()` rows and `C2::value()` columns. /// /// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more @@ -626,6 +707,61 @@ impl> Matrix { } } +#[cfg(any(feature = "std", feature = "alloc"))] +impl DMatrix { + /// Resizes this matrix in-place. + /// + /// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more + /// rows and/or columns than `self`, then the extra rows or columns are filled with `val`. + /// + /// Defined only for owned fully-dynamic matrices, i.e., `DMatrix`. + pub fn resize_mut(&mut self, new_nrows: usize, new_ncols: usize, val: N) + where DefaultAllocator: Reallocator { + let placeholder = unsafe { Self::new_uninitialized(0, 0) }; + let old = mem::replace(self, placeholder); + let new = old.resize(new_nrows, new_ncols, val); + let _ = mem::replace(self, new); + } +} + +#[cfg(any(feature = "std", feature = "alloc"))] +impl MatrixMN + where DefaultAllocator: Allocator { + /// Changes the number of rows of this matrix in-place. + /// + /// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more + /// rows than `self`, then the extra rows are filled with `val`. + /// + /// Defined only for owned matrices with a dynamic number of rows (for example, `DVector`). + #[cfg(any(feature = "std", feature = "alloc"))] + pub fn resize_vertically_mut(&mut self, new_nrows: usize, val: N) + where DefaultAllocator: Reallocator { + let placeholder = unsafe { Self::new_uninitialized_generic(Dynamic::new(0), self.data.shape().1) }; + let old = mem::replace(self, placeholder); + let new = old.resize_vertically(new_nrows, val); + let _ = mem::replace(self, new); + } +} + +#[cfg(any(feature = "std", feature = "alloc"))] +impl MatrixMN + where DefaultAllocator: Allocator { + /// Changes the number of column of this matrix in-place. + /// + /// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more + /// columns than `self`, then the extra columns are filled with `val`. + /// + /// Defined only for owned matrices with a dynamic number of columns (for example, `DVector`). + #[cfg(any(feature = "std", feature = "alloc"))] + pub fn resize_horizontally_mut(&mut self, new_ncols: usize, val: N) + where DefaultAllocator: Reallocator { + let placeholder = unsafe { Self::new_uninitialized_generic(self.data.shape().0, Dynamic::new(0)) }; + let old = mem::replace(self, placeholder); + let new = old.resize_horizontally(new_ncols, val); + let _ = mem::replace(self, new); + } +} + unsafe fn compress_rows( data: &mut [N], nrows: usize, @@ -706,6 +842,7 @@ unsafe fn extend_rows( /// Extend the number of columns of the `Matrix` with elements from /// a given iterator. +#[cfg(any(feature = "std", feature = "alloc"))] impl Extend for Matrix where N: Scalar, @@ -753,6 +890,7 @@ where /// Extend the number of rows of the `Vector` with elements from /// a given iterator. +#[cfg(any(feature = "std", feature = "alloc"))] impl Extend for Matrix where N: Scalar, @@ -764,15 +902,16 @@ where /// # Example /// ``` /// # use nalgebra::DVector; - /// let mut vector = DVector::from_vec(3, vec![0, 1, 2]); + /// let mut vector = DVector::from_vec(vec![0, 1, 2]); /// vector.extend(vec![3, 4, 5]); - /// assert!(vector.eq(&DVector::from_vec(6, vec![0, 1, 2, 3, 4, 5]))); + /// assert!(vector.eq(&DVector::from_vec(vec![0, 1, 2, 3, 4, 5]))); /// ``` fn extend>(&mut self, iter: I) { self.data.extend(iter); } } +#[cfg(any(feature = "std", feature = "alloc"))] impl Extend> for Matrix where N: Scalar, diff --git a/src/base/indexing.rs b/src/base/indexing.rs index 304242c4..976eef87 100644 --- a/src/base/indexing.rs +++ b/src/base/indexing.rs @@ -711,4 +711,4 @@ impl_index_pairs!{ => DimDiff where C: DimSub], } -} \ No newline at end of file +} diff --git a/src/base/iter.rs b/src/base/iter.rs index fe8fd4f6..8a5f10ab 100644 --- a/src/base/iter.rs +++ b/src/base/iter.rs @@ -3,9 +3,9 @@ use std::marker::PhantomData; use std::mem; -use base::dimension::Dim; +use base::dimension::{Dim, U1}; use base::storage::{Storage, StorageMut}; -use base::Scalar; +use base::{Scalar, Matrix, MatrixSlice, MatrixSliceMut}; macro_rules! iterator { (struct $Name:ident for $Storage:ident.$ptr: ident -> $Ptr:ty, $Ref:ty, $SRef: ty) => { @@ -96,3 +96,226 @@ macro_rules! iterator { iterator!(struct MatrixIter for Storage.ptr -> *const N, &'a N, &'a S); iterator!(struct MatrixIterMut for StorageMut.ptr_mut -> *mut N, &'a mut N, &'a mut S); + + +/* + * + * Row iterators. + * + */ +#[derive(Clone)] +/// An iterator through the rows of a matrix. +pub struct RowIter<'a, N: Scalar, R: Dim, C: Dim, S: Storage> { + mat: &'a Matrix, + curr: usize +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + Storage> RowIter<'a, N, R, C, S> { + pub(crate) fn new(mat: &'a Matrix) -> Self { + RowIter { + mat, curr: 0 + } + } +} + + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + Storage> Iterator for RowIter<'a, N, R, C, S> { + type Item = MatrixSlice<'a, N, U1, C, S::RStride, S::CStride>; + + #[inline] + fn next(&mut self) -> Option { + if self.curr < self.mat.nrows() { + let res = self.mat.row(self.curr); + self.curr += 1; + Some(res) + } else { + None + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.mat.nrows() - self.curr, Some(self.mat.nrows() - self.curr)) + } + + #[inline] + fn count(self) -> usize { + self.mat.nrows() - self.curr + } +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + Storage> ExactSizeIterator for RowIter<'a, N, R, C, S> { + #[inline] + fn len(&self) -> usize { + self.mat.nrows() - self.curr + } +} + + +/// An iterator through the mutable rows of a matrix. +pub struct RowIterMut<'a, N: Scalar, R: Dim, C: Dim, S: StorageMut> { + mat: *mut Matrix, + curr: usize, + phantom: PhantomData<&'a mut Matrix> +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + StorageMut> RowIterMut<'a, N, R, C, S> { + pub(crate) fn new(mat: &'a mut Matrix) -> Self { + RowIterMut { + mat, + curr: 0, + phantom: PhantomData + } + } + + fn nrows(&self) -> usize { + unsafe { + (*self.mat).nrows() + } + } +} + + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + StorageMut> Iterator for RowIterMut<'a, N, R, C, S> { + type Item = MatrixSliceMut<'a, N, U1, C, S::RStride, S::CStride>; + + #[inline] + fn next(&mut self) -> Option { + if self.curr < self.nrows() { + let res = unsafe { (*self.mat).row_mut(self.curr) }; + self.curr += 1; + Some(res) + } else { + None + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.nrows() - self.curr, Some(self.nrows() - self.curr)) + } + + #[inline] + fn count(self) -> usize { + self.nrows() - self.curr + } +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + StorageMut> ExactSizeIterator for RowIterMut<'a, N, R, C, S> { + #[inline] + fn len(&self) -> usize { + self.nrows() - self.curr + } +} + + +/* + * + * Column iterators. + * + */ +#[derive(Clone)] +/// An iterator through the columns of a matrix. +pub struct ColumnIter<'a, N: Scalar, R: Dim, C: Dim, S: Storage> { + mat: &'a Matrix, + curr: usize +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + Storage> ColumnIter<'a, N, R, C, S> { + pub(crate) fn new(mat: &'a Matrix) -> Self { + ColumnIter { + mat, curr: 0 + } + } +} + + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + Storage> Iterator for ColumnIter<'a, N, R, C, S> { + type Item = MatrixSlice<'a, N, R, U1, S::RStride, S::CStride>; + + #[inline] + fn next(&mut self) -> Option { + if self.curr < self.mat.ncols() { + let res = self.mat.column(self.curr); + self.curr += 1; + Some(res) + } else { + None + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.mat.ncols() - self.curr, Some(self.mat.ncols() - self.curr)) + } + + #[inline] + fn count(self) -> usize { + self.mat.ncols() - self.curr + } +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + Storage> ExactSizeIterator for ColumnIter<'a, N, R, C, S> { + #[inline] + fn len(&self) -> usize { + self.mat.ncols() - self.curr + } +} + + +/// An iterator through the mutable columns of a matrix. +pub struct ColumnIterMut<'a, N: Scalar, R: Dim, C: Dim, S: StorageMut> { + mat: *mut Matrix, + curr: usize, + phantom: PhantomData<&'a mut Matrix> +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + StorageMut> ColumnIterMut<'a, N, R, C, S> { + pub(crate) fn new(mat: &'a mut Matrix) -> Self { + ColumnIterMut { + mat, + curr: 0, + phantom: PhantomData + } + } + + fn ncols(&self) -> usize { + unsafe { + (*self.mat).ncols() + } + } +} + + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + StorageMut> Iterator for ColumnIterMut<'a, N, R, C, S> { + type Item = MatrixSliceMut<'a, N, R, U1, S::RStride, S::CStride>; + + #[inline] + fn next(&mut self) -> Option { + if self.curr < self.ncols() { + let res = unsafe { (*self.mat).column_mut(self.curr) }; + self.curr += 1; + Some(res) + } else { + None + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.ncols() - self.curr, Some(self.ncols() - self.curr)) + } + + #[inline] + fn count(self) -> usize { + self.ncols() - self.curr + } +} + +impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + StorageMut> ExactSizeIterator for ColumnIterMut<'a, N, R, C, S> { + #[inline] + fn len(&self) -> usize { + self.ncols() - self.curr + } +} + diff --git a/src/base/matrix.rs b/src/base/matrix.rs index 7a29fe2a..fcd53703 100644 --- a/src/base/matrix.rs +++ b/src/base/matrix.rs @@ -22,7 +22,7 @@ use alga::general::{ClosedAdd, ClosedMul, ClosedSub, Real, Ring}; use base::allocator::{Allocator, SameShapeAllocator, SameShapeC, SameShapeR}; use base::constraint::{DimEq, SameNumberOfColumns, SameNumberOfRows, ShapeConstraint}; use base::dimension::{Dim, DimAdd, DimSum, IsNotStaticOne, U1, U2, U3}; -use base::iter::{MatrixIter, MatrixIterMut}; +use base::iter::{MatrixIter, MatrixIterMut, RowIter, RowIterMut, ColumnIter, ColumnIterMut}; use base::storage::{ ContiguousStorage, ContiguousStorageMut, Owned, SameShapeStorage, Storage, StorageMut, }; @@ -152,7 +152,7 @@ impl Matrix { impl> Matrix { /// Creates a new matrix with the given data. #[inline] - pub fn from_data(data: S) -> Matrix { + pub fn from_data(data: S) -> Self { unsafe { Self::from_data_statically_unchecked(data) } } @@ -247,6 +247,37 @@ impl> Matrix { MatrixIter::new(&self.data) } + /// Iterate through the rows of this matrix. + /// + /// # Example + /// ``` + /// # use nalgebra::Matrix2x3; + /// let mut a = Matrix2x3::new(1, 2, 3, + /// 4, 5, 6); + /// for (i, row) in a.row_iter().enumerate() { + /// assert_eq!(row, a.row(i)) + /// } + /// ``` + #[inline] + pub fn row_iter(&self) -> RowIter { + RowIter::new(self) + } + + /// Iterate through the columns of this matrix. + /// # Example + /// ``` + /// # use nalgebra::Matrix2x3; + /// let mut a = Matrix2x3::new(1, 2, 3, + /// 4, 5, 6); + /// for (i, column) in a.column_iter().enumerate() { + /// assert_eq!(column, a.column(i)) + /// } + /// ``` + #[inline] + pub fn column_iter(&self) -> ColumnIter { + ColumnIter::new(self) + } + /// Computes the row and column coordinates of the i-th element of this matrix seen as a /// vector. #[inline] @@ -264,6 +295,15 @@ impl> Matrix { } } + /// Returns a pointer to the start of the matrix. + /// + /// If the matrix is not empty, this pointer is guaranteed to be aligned + /// and non-null. + #[inline] + pub fn as_ptr(&self) -> *const N { + self.data.ptr() + } + /// Tests whether `self` and `rhs` are equal up to a given epsilon. /// /// See `relative_eq` from the `RelativeEq` trait for more details. @@ -393,7 +433,7 @@ impl> Matrix { } /// Returns a matrix containing the result of `f` applied to each of its entries. Unlike `map`, - /// `f` also gets passed the row and column index, i.e. `f(value, row, col)`. + /// `f` also gets passed the row and column index, i.e. `f(row, col, value)`. #[inline] pub fn map_with_location N2>( &self, @@ -493,6 +533,57 @@ impl> Matrix { res } + /// Folds a function `f` on each entry of `self`. + #[inline] + pub fn fold(&self, init: Acc, mut f: impl FnMut(Acc, N) -> Acc) -> Acc { + let (nrows, ncols) = self.data.shape(); + + let mut res = init; + + for j in 0..ncols.value() { + for i in 0..nrows.value() { + unsafe { + let a = *self.data.get_unchecked(i, j); + res = f(res, a) + } + } + } + + res + } + + /// Folds a function `f` on each pairs of entries from `self` and `rhs`. + #[inline] + pub fn zip_fold(&self, rhs: &Matrix, init: Acc, mut f: impl FnMut(Acc, N, N2) -> Acc) -> Acc + where + N2: Scalar, + R2: Dim, + C2: Dim, + S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns + { + let (nrows, ncols) = self.data.shape(); + + let mut res = init; + + assert!( + (nrows.value(), ncols.value()) == rhs.shape(), + "Matrix simultaneous traversal error: dimension mismatch." + ); + + for j in 0..ncols.value() { + for i in 0..nrows.value() { + unsafe { + let a = *self.data.get_unchecked(i, j); + let b = *rhs.data.get_unchecked(i, j); + res = f(res, a, b) + } + } + } + + res + } + /// Transposes `self` and store the result into `out`. #[inline] pub fn transpose_to(&self, out: &mut Matrix) @@ -540,6 +631,55 @@ impl> Matrix { MatrixIterMut::new(&mut self.data) } + /// Returns a mutable pointer to the start of the matrix. + /// + /// If the matrix is not empty, this pointer is guaranteed to be aligned + /// and non-null. + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut N { + self.data.ptr_mut() + } + + /// Mutably iterates through this matrix rows. + /// + /// # Example + /// ``` + /// # use nalgebra::Matrix2x3; + /// let mut a = Matrix2x3::new(1, 2, 3, + /// 4, 5, 6); + /// for (i, mut row) in a.row_iter_mut().enumerate() { + /// row *= (i + 1) * 10; + /// } + /// + /// let expected = Matrix2x3::new(10, 20, 30, + /// 80, 100, 120); + /// assert_eq!(a, expected); + /// ``` + #[inline] + pub fn row_iter_mut(&mut self) -> RowIterMut { + RowIterMut::new(self) + } + + /// Mutably iterates through this matrix columns. + /// + /// # Example + /// ``` + /// # use nalgebra::Matrix2x3; + /// let mut a = Matrix2x3::new(1, 2, 3, + /// 4, 5, 6); + /// for (i, mut col) in a.column_iter_mut().enumerate() { + /// col *= (i + 1) * 10; + /// } + /// + /// let expected = Matrix2x3::new(10, 40, 90, + /// 40, 100, 180); + /// assert_eq!(a, expected); + /// ``` + #[inline] + pub fn column_iter_mut(&mut self) -> ColumnIterMut { + ColumnIterMut::new(self) + } + /// Swaps two entries without bound-checking. #[inline] pub unsafe fn swap_unchecked(&mut self, row_cols1: (usize, usize), row_cols2: (usize, usize)) { @@ -633,8 +773,7 @@ impl> Matrix { /// Replaces each component of `self` by the result of a closure `f` applied on it. #[inline] - pub fn apply N>(&mut self, mut f: F) - where DefaultAllocator: Allocator { + pub fn apply N>(&mut self, mut f: F) { let (nrows, ncols) = self.shape(); for j in 0..ncols { @@ -646,6 +785,71 @@ impl> Matrix { } } } + + /// Replaces each component of `self` by the result of a closure `f` applied on its components + /// joined with the components from `rhs`. + #[inline] + pub fn zip_apply(&mut self, rhs: &Matrix, mut f: impl FnMut(N, N2) -> N) + where N2: Scalar, + R2: Dim, + C2: Dim, + S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { + let (nrows, ncols) = self.shape(); + + assert!( + (nrows, ncols) == rhs.shape(), + "Matrix simultaneous traversal error: dimension mismatch." + ); + + for j in 0..ncols { + for i in 0..nrows { + unsafe { + let e = self.data.get_unchecked_mut(i, j); + let rhs = rhs.get_unchecked((i, j)); + *e = f(*e, *rhs) + } + } + } + } + + + /// Replaces each component of `self` by the result of a closure `f` applied on its components + /// joined with the components from `b` and `c`. + #[inline] + pub fn zip_zip_apply(&mut self, b: &Matrix, c: &Matrix, mut f: impl FnMut(N, N2, N3) -> N) + where N2: Scalar, + R2: Dim, + C2: Dim, + S2: Storage, + N3: Scalar, + R3: Dim, + C3: Dim, + S3: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { + let (nrows, ncols) = self.shape(); + + assert!( + (nrows, ncols) == b.shape(), + "Matrix simultaneous traversal error: dimension mismatch." + ); + assert!( + (nrows, ncols) == c.shape(), + "Matrix simultaneous traversal error: dimension mismatch." + ); + + for j in 0..ncols { + for i in 0..nrows { + unsafe { + let e = self.data.get_unchecked_mut(i, j); + let b = b.get_unchecked((i, j)); + let c = c.get_unchecked((i, j)); + *e = f(*e, *b, *c) + } + } + } + } } impl> Vector { @@ -832,14 +1036,7 @@ impl, S: Storage> Vector { #[inline] pub fn to_homogeneous(&self) -> VectorN> where DefaultAllocator: Allocator> { - let len = self.len(); - let hnrows = DimSum::::from_usize(len + 1); - let mut res = unsafe { VectorN::::new_uninitialized_generic(hnrows, U1) }; - res.generic_slice_mut((0, 0), self.data.shape()) - .copy_from(self); - res[(len, 0)] = N::zero(); - - res + self.push(N::zero()) } /// Constructs a vector from coordinates in projective space, i.e., removes a `0` at the end of @@ -859,6 +1056,22 @@ impl, S: Storage> Vector { } } +impl, S: Storage> Vector { + /// Constructs a new vector of higher dimension by appending `element` to the end of `self`. + #[inline] + pub fn push(&self, element: N) -> VectorN> + where DefaultAllocator: Allocator> { + let len = self.len(); + let hnrows = DimSum::::from_usize(len + 1); + let mut res = unsafe { VectorN::::new_uninitialized_generic(hnrows, U1) }; + res.generic_slice_mut((0, 0), self.data.shape()) + .copy_from(self); + res[(len, 0)] = element; + + res + } +} + impl AbsDiffEq for Matrix where N: Scalar + AbsDiffEq, @@ -1242,67 +1455,6 @@ impl> Matrix { } } -impl> Matrix { - /// The squared L2 norm of this vector. - #[inline] - pub fn norm_squared(&self) -> N { - let mut res = N::zero(); - - for i in 0..self.ncols() { - let col = self.column(i); - res += col.dot(&col) - } - - res - } - - /// The L2 norm of this matrix. - #[inline] - pub fn norm(&self) -> N { - self.norm_squared().sqrt() - } - - /// A synonym for the norm of this matrix. - /// - /// Aka the length. - /// - /// This function is simply implemented as a call to `norm()` - #[inline] - pub fn magnitude(&self) -> N { - self.norm() - } - - /// A synonym for the squared norm of this matrix. - /// - /// Aka the squared length. - /// - /// This function is simply implemented as a call to `norm_squared()` - #[inline] - pub fn magnitude_squared(&self) -> N { - self.norm_squared() - } - - /// Returns a normalized version of this matrix. - #[inline] - pub fn normalize(&self) -> MatrixMN - where DefaultAllocator: Allocator { - self / self.norm() - } - - /// Returns a normalized version of this matrix unless its norm as smaller or equal to `eps`. - #[inline] - pub fn try_normalize(&self, min_norm: N) -> Option> - where DefaultAllocator: Allocator { - let n = self.norm(); - - if n <= min_norm { - None - } else { - Some(self / n) - } - } -} - impl> Vector { @@ -1377,32 +1529,6 @@ impl> Unit> { } } -impl> Matrix { - /// Normalizes this matrix in-place and returns its norm. - #[inline] - pub fn normalize_mut(&mut self) -> N { - let n = self.norm(); - *self /= n; - - n - } - - /// Normalizes this matrix in-place or does nothing if its norm is smaller or equal to `eps`. - /// - /// If the normalization succeeded, returns the old normal of this matrix. - #[inline] - pub fn try_normalize_mut(&mut self, min_norm: N) -> Option { - let n = self.norm(); - - if n <= min_norm { - None - } else { - *self /= n; - Some(n) - } - } -} - impl AbsDiffEq for Unit> where N: Scalar + AbsDiffEq, diff --git a/src/base/matrix_alga.rs b/src/base/matrix_alga.rs index 7c454986..6ce60809 100644 --- a/src/base/matrix_alga.rs +++ b/src/base/matrix_alga.rs @@ -6,7 +6,7 @@ use num::{One, Zero}; use alga::general::{ AbstractGroup, AbstractGroupAbelian, AbstractLoop, AbstractMagma, AbstractModule, AbstractMonoid, AbstractQuasigroup, AbstractSemigroup, Additive, ClosedAdd, ClosedMul, - ClosedNeg, Field, Identity, Inverse, JoinSemilattice, Lattice, MeetSemilattice, Module, + ClosedNeg, Field, Identity, TwoSidedInverse, JoinSemilattice, Lattice, MeetSemilattice, Module, Multiplicative, Real, RingCommutative, }; use alga::linear::{ @@ -45,18 +45,18 @@ where } } -impl Inverse for MatrixMN +impl TwoSidedInverse for MatrixMN where N: Scalar + ClosedNeg, DefaultAllocator: Allocator, { #[inline] - fn inverse(&self) -> MatrixMN { + fn two_sided_inverse(&self) -> Self { -self } #[inline] - fn inverse_mut(&mut self) { + fn two_sided_inverse_mut(&mut self) { *self = -self.clone() } } @@ -203,7 +203,7 @@ impl FiniteDimInnerSpace for MatrixMN where DefaultAllocator: Allocator { #[inline] - fn orthonormalize(vs: &mut [MatrixMN]) -> usize { + fn orthonormalize(vs: &mut [Self]) -> usize { let mut nbasis_elements = 0; for i in 0..vs.len() { diff --git a/src/base/matrix_slice.rs b/src/base/matrix_slice.rs index a217332e..a4ce1533 100644 --- a/src/base/matrix_slice.rs +++ b/src/base/matrix_slice.rs @@ -4,9 +4,9 @@ use std::slice; use base::allocator::Allocator; use base::default_allocator::DefaultAllocator; -use base::dimension::{Dim, DimName, Dynamic, U1}; +use base::dimension::{Dim, DimName, Dynamic, U1, IsNotStaticOne}; use base::iter::MatrixIter; -use base::storage::{Owned, Storage, StorageMut}; +use base::storage::{Owned, Storage, StorageMut, ContiguousStorage, ContiguousStorageMut}; use base::{Matrix, Scalar}; macro_rules! slice_storage_impl( @@ -91,14 +91,15 @@ slice_storage_impl!("A mutable matrix data storage for mutable matrix slice. Onl impl<'a, N: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> Copy for SliceStorage<'a, N, R, C, RStride, CStride> -{} +{ +} impl<'a, N: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> Clone for SliceStorage<'a, N, R, C, RStride, CStride> { #[inline] fn clone(&self) -> Self { - SliceStorage { + Self { ptr: self.ptr, shape: self.shape, strides: self.strides, @@ -146,8 +147,6 @@ macro_rules! storage_impl( } } - - #[inline] fn into_owned(self) -> Owned where DefaultAllocator: Allocator { @@ -199,6 +198,14 @@ unsafe impl<'a, N: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> StorageMu } } +unsafe impl<'a, N: Scalar, R: Dim, CStride: Dim> ContiguousStorage for SliceStorage<'a, N, R, U1, U1, CStride> { } +unsafe impl<'a, N: Scalar, R: Dim, CStride: Dim> ContiguousStorage for SliceStorageMut<'a, N, R, U1, U1, CStride> { } +unsafe impl<'a, N: Scalar, R: Dim, CStride: Dim> ContiguousStorageMut for SliceStorageMut<'a, N, R, U1, U1, CStride> { } + +unsafe impl<'a, N: Scalar, R: DimName, C: Dim + IsNotStaticOne> ContiguousStorage for SliceStorage<'a, N, R, C, U1, R> { } +unsafe impl<'a, N: Scalar, R: DimName, C: Dim + IsNotStaticOne> ContiguousStorage for SliceStorageMut<'a, N, R, C, U1, R> { } +unsafe impl<'a, N: Scalar, R: DimName, C: Dim + IsNotStaticOne> ContiguousStorageMut for SliceStorageMut<'a, N, R, C, U1, R> { } + impl> Matrix { #[inline] fn assert_slice_index( @@ -859,3 +866,25 @@ impl> Matrix { self.slice_range_mut(.., cols) } } + + +impl<'a, N, R, C, RStride, CStride> From> +for MatrixSlice<'a, N, R, C, RStride, CStride> + where + N: Scalar, + R: Dim, + C: Dim, + RStride: Dim, + CStride: Dim, +{ + fn from(slice_mut: MatrixSliceMut<'a, N, R, C, RStride, CStride>) -> Self { + let data = SliceStorage { + ptr: slice_mut.data.ptr, + shape: slice_mut.data.shape, + strides: slice_mut.data.strides, + _phantoms: PhantomData, + }; + + unsafe { Matrix::from_data_statically_unchecked(data) } + } +} \ No newline at end of file diff --git a/src/base/mod.rs b/src/base/mod.rs index e7240fb3..0ec6311c 100644 --- a/src/base/mod.rs +++ b/src/base/mod.rs @@ -29,6 +29,8 @@ mod properties; mod scalar; mod swizzle; mod unit; +mod statistics; +mod norm; #[doc(hidden)] pub mod helper; @@ -36,6 +38,7 @@ pub mod helper; pub use self::matrix::*; pub use self::scalar::*; pub use self::unit::*; +pub use self::norm::*; pub use self::default_allocator::*; pub use self::dimension::*; diff --git a/src/base/norm.rs b/src/base/norm.rs new file mode 100644 index 00000000..68fb88a1 --- /dev/null +++ b/src/base/norm.rs @@ -0,0 +1,238 @@ +use num::Signed; +use std::cmp::PartialOrd; + +use allocator::Allocator; +use ::{Real, Scalar}; +use storage::{Storage, StorageMut}; +use base::{DefaultAllocator, Matrix, Dim, MatrixMN}; +use constraint::{SameNumberOfRows, SameNumberOfColumns, ShapeConstraint}; + + +// FIXME: this should be be a trait on alga? +/// A trait for abstract matrix norms. +/// +/// This may be moved to the alga crate in the future. +pub trait Norm { + /// Apply this norm to the given matrix. + fn norm(&self, m: &Matrix) -> N + where R: Dim, C: Dim, S: Storage; + /// Use the metric induced by this norm to compute the metric distance between the two given matrices. + fn metric_distance(&self, m1: &Matrix, m2: &Matrix) -> N + where R1: Dim, C1: Dim, S1: Storage, + R2: Dim, C2: Dim, S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns; +} + +/// Euclidean norm. +pub struct EuclideanNorm; +/// Lp norm. +pub struct LpNorm(pub i32); +/// L-infinite norm aka. Chebytchev norm aka. uniform norm aka. suppremum norm. +pub struct UniformNorm; + +impl Norm for EuclideanNorm { + #[inline] + fn norm(&self, m: &Matrix) -> N + where R: Dim, C: Dim, S: Storage { + m.norm_squared().sqrt() + } + + #[inline] + fn metric_distance(&self, m1: &Matrix, m2: &Matrix) -> N + where R1: Dim, C1: Dim, S1: Storage, + R2: Dim, C2: Dim, S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { + m1.zip_fold(m2, N::zero(), |acc, a, b| { + let diff = a - b; + acc + diff * diff + }).sqrt() + } +} + +impl Norm for LpNorm { + #[inline] + fn norm(&self, m: &Matrix) -> N + where R: Dim, C: Dim, S: Storage { + m.fold(N::zero(), |a, b| { + a + b.abs().powi(self.0) + }).powf(::convert(1.0 / (self.0 as f64))) + } + + #[inline] + fn metric_distance(&self, m1: &Matrix, m2: &Matrix) -> N + where R1: Dim, C1: Dim, S1: Storage, + R2: Dim, C2: Dim, S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { + m1.zip_fold(m2, N::zero(), |acc, a, b| { + let diff = a - b; + acc + diff.abs().powi(self.0) + }).powf(::convert(1.0 / (self.0 as f64))) + } +} + +impl Norm for UniformNorm { + #[inline] + fn norm(&self, m: &Matrix) -> N + where R: Dim, C: Dim, S: Storage { + m.amax() + } + + #[inline] + fn metric_distance(&self, m1: &Matrix, m2: &Matrix) -> N + where R1: Dim, C1: Dim, S1: Storage, + R2: Dim, C2: Dim, S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { + m1.zip_fold(m2, N::zero(), |acc, a, b| { + let val = (a - b).abs(); + if val > acc { + val + } else { + acc + } + }) + } +} + + +impl> Matrix { + /// The squared L2 norm of this vector. + #[inline] + pub fn norm_squared(&self) -> N { + let mut res = N::zero(); + + for i in 0..self.ncols() { + let col = self.column(i); + res += col.dot(&col) + } + + res + } + + /// The L2 norm of this matrix. + /// + /// Use `.apply_norm` to apply a custom norm. + #[inline] + pub fn norm(&self) -> N { + self.norm_squared().sqrt() + } + + /// Compute the distance between `self` and `rhs` using the metric induced by the euclidean norm. + /// + /// Use `.apply_metric_distance` to apply a custom norm. + #[inline] + pub fn metric_distance(&self, rhs: &Matrix) -> N + where R2: Dim, C2: Dim, S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { + self.apply_metric_distance(rhs, &EuclideanNorm) + } + + /// Uses the given `norm` to compute the norm of `self`. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Vector3, UniformNorm, LpNorm, EuclideanNorm}; + /// + /// let v = Vector3::new(1.0, 2.0, 3.0); + /// assert_eq!(v.apply_norm(&UniformNorm), 3.0); + /// assert_eq!(v.apply_norm(&LpNorm(1)), 6.0); + /// assert_eq!(v.apply_norm(&EuclideanNorm), v.norm()); + /// ``` + #[inline] + pub fn apply_norm(&self, norm: &impl Norm) -> N { + norm.norm(self) + } + + /// Uses the metric induced by the given `norm` to compute the metric distance between `self` and `rhs`. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Vector3, UniformNorm, LpNorm, EuclideanNorm}; + /// + /// let v1 = Vector3::new(1.0, 2.0, 3.0); + /// let v2 = Vector3::new(10.0, 20.0, 30.0); + /// + /// assert_eq!(v1.apply_metric_distance(&v2, &UniformNorm), 27.0); + /// assert_eq!(v1.apply_metric_distance(&v2, &LpNorm(1)), 27.0 + 18.0 + 9.0); + /// assert_eq!(v1.apply_metric_distance(&v2, &EuclideanNorm), (v1 - v2).norm()); + /// ``` + #[inline] + pub fn apply_metric_distance(&self, rhs: &Matrix, norm: &impl Norm) -> N + where R2: Dim, C2: Dim, S2: Storage, + ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { + norm.metric_distance(self,rhs) + } + + /// The Lp norm of this matrix. + #[inline] + pub fn lp_norm(&self, p: i32) -> N { + self.apply_norm(&LpNorm(p)) + } + + /// A synonym for the norm of this matrix. + /// + /// Aka the length. + /// + /// This function is simply implemented as a call to `norm()` + #[inline] + pub fn magnitude(&self) -> N { + self.norm() + } + + /// A synonym for the squared norm of this matrix. + /// + /// Aka the squared length. + /// + /// This function is simply implemented as a call to `norm_squared()` + #[inline] + pub fn magnitude_squared(&self) -> N { + self.norm_squared() + } + + /// Returns a normalized version of this matrix. + #[inline] + pub fn normalize(&self) -> MatrixMN + where DefaultAllocator: Allocator { + self / self.norm() + } + + /// Returns a normalized version of this matrix unless its norm as smaller or equal to `eps`. + #[inline] + pub fn try_normalize(&self, min_norm: N) -> Option> + where DefaultAllocator: Allocator { + let n = self.norm(); + + if n <= min_norm { + None + } else { + Some(self / n) + } + } +} + +impl> Matrix { + /// Normalizes this matrix in-place and returns its norm. + #[inline] + pub fn normalize_mut(&mut self) -> N { + let n = self.norm(); + *self /= n; + + n + } + + /// Normalizes this matrix in-place or does nothing if its norm is smaller or equal to `eps`. + /// + /// If the normalization succeeded, returns the old normal of this matrix. + #[inline] + pub fn try_normalize_mut(&mut self, min_norm: N) -> Option { + let n = self.norm(); + + if n <= min_norm { + None + } else { + *self /= n; + Some(n) + } + } +} diff --git a/src/base/ops.rs b/src/base/ops.rs index da81789f..301232f2 100644 --- a/src/base/ops.rs +++ b/src/base/ops.rs @@ -11,7 +11,7 @@ use base::allocator::{Allocator, SameShapeAllocator, SameShapeC, SameShapeR}; use base::constraint::{ AreMultipliable, DimEq, SameNumberOfColumns, SameNumberOfRows, ShapeConstraint, }; -use base::dimension::{Dim, DimMul, DimName, DimProd}; +use base::dimension::{Dim, DimMul, DimName, DimProd, Dynamic}; use base::storage::{ContiguousStorageMut, Storage, StorageMut}; use base::{DefaultAllocator, Matrix, MatrixMN, MatrixN, MatrixSum, Scalar}; @@ -24,7 +24,7 @@ impl> Index for Matrix &N { + fn index(&self, i: usize) -> &Self::Output { let ij = self.vector_to_matrix_index(i); &self[ij] } @@ -38,7 +38,7 @@ where type Output = N; #[inline] - fn index(&self, ij: (usize, usize)) -> &N { + fn index(&self, ij: (usize, usize)) -> &Self::Output { let shape = self.shape(); assert!( ij.0 < shape.0 && ij.1 < shape.1, @@ -384,6 +384,36 @@ where } } +impl iter::Sum for MatrixMN +where + N: Scalar + ClosedAdd + Zero, + DefaultAllocator: Allocator, +{ + /// # Example + /// ``` + /// # use nalgebra::DVector; + /// assert_eq!(vec![DVector::repeat(3, 1.0f64), + /// DVector::repeat(3, 1.0f64), + /// DVector::repeat(3, 1.0f64)].into_iter().sum::>(), + /// DVector::repeat(3, 1.0f64) + DVector::repeat(3, 1.0f64) + DVector::repeat(3, 1.0f64)); + /// ``` + /// + /// # Panics + /// Panics if the iterator is empty: + /// ```should_panic + /// # use std::iter; + /// # use nalgebra::DMatrix; + /// iter::empty::>().sum::>(); // panics! + /// ``` + fn sum>>(mut iter: I) -> MatrixMN { + if let Some(first) = iter.next() { + iter.fold(first, |acc, x| acc + x) + } else { + panic!("Cannot compute `sum` of empty iterator.") + } + } +} + impl<'a, N, R: DimName, C: DimName> iter::Sum<&'a MatrixMN> for MatrixMN where N: Scalar + ClosedAdd + Zero, @@ -394,6 +424,36 @@ where } } +impl<'a, N, C: Dim> iter::Sum<&'a MatrixMN> for MatrixMN +where + N: Scalar + ClosedAdd + Zero, + DefaultAllocator: Allocator, +{ + /// # Example + /// ``` + /// # use nalgebra::DVector; + /// let v = &DVector::repeat(3, 1.0f64); + /// + /// assert_eq!(vec![v, v, v].into_iter().sum::>(), + /// v + v + v); + /// ``` + /// + /// # Panics + /// Panics if the iterator is empty: + /// ```should_panic + /// # use std::iter; + /// # use nalgebra::DMatrix; + /// iter::empty::<&DMatrix>().sum::>(); // panics! + /// ``` + fn sum>>(mut iter: I) -> MatrixMN { + if let Some(first) = iter.next() { + iter.fold(first.clone(), |acc, x| acc + x) + } else { + panic!("Cannot compute `sum` of empty iterator.") + } + } +} + /* * * Multiplication @@ -761,7 +821,7 @@ where } impl> Matrix { - /// Returns the absolute value of the coefficient with the largest absolute value. + /// Returns the absolute value of the component with the largest absolute value. #[inline] pub fn amax(&self) -> N { let mut max = N::zero(); @@ -777,7 +837,7 @@ impl> Matri max } - /// Returns the absolute value of the coefficient with the smallest absolute value. + /// Returns the absolute value of the component with the smallest absolute value. #[inline] pub fn amin(&self) -> N { let mut it = self.iter(); @@ -796,4 +856,42 @@ impl> Matri min } + + /// Returns the component with the largest value. + #[inline] + pub fn max(&self) -> N { + let mut it = self.iter(); + let mut max = it + .next() + .expect("max: empty matrices not supported."); + + for e in it { + let ae = e; + + if ae > max { + max = ae; + } + } + + *max + } + + /// Returns the component with the smallest value. + #[inline] + pub fn min(&self) -> N { + let mut it = self.iter(); + let mut min = it + .next() + .expect("min: empty matrices not supported."); + + for e in it { + let ae = e; + + if ae < min { + min = ae; + } + } + + *min + } } diff --git a/src/base/statistics.rs b/src/base/statistics.rs new file mode 100644 index 00000000..11cc9e1c --- /dev/null +++ b/src/base/statistics.rs @@ -0,0 +1,309 @@ +use ::{Real, Dim, Matrix, VectorN, RowVectorN, DefaultAllocator, U1, VectorSliceN}; +use storage::Storage; +use allocator::Allocator; + +impl> Matrix { + /// Returns a row vector where each element is the result of the application of `f` on the + /// corresponding column of the original matrix. + #[inline] + pub fn compress_rows(&self, f: impl Fn(VectorSliceN) -> N) -> RowVectorN + where DefaultAllocator: Allocator { + + let ncols = self.data.shape().1; + let mut res = unsafe { RowVectorN::new_uninitialized_generic(U1, ncols) }; + + for i in 0..ncols.value() { + // FIXME: avoid bound checking of column. + unsafe { *res.get_unchecked_mut((0, i)) = f(self.column(i)); } + } + + res + } + + /// Returns a column vector where each element is the result of the application of `f` on the + /// corresponding column of the original matrix. + /// + /// This is the same as `self.compress_rows(f).transpose()`. + #[inline] + pub fn compress_rows_tr(&self, f: impl Fn(VectorSliceN) -> N) -> VectorN + where DefaultAllocator: Allocator { + + let ncols = self.data.shape().1; + let mut res = unsafe { VectorN::new_uninitialized_generic(ncols, U1) }; + + for i in 0..ncols.value() { + // FIXME: avoid bound checking of column. + unsafe { *res.vget_unchecked_mut(i) = f(self.column(i)); } + } + + res + } + + /// Returns a column vector resulting from the folding of `f` on each column of this matrix. + #[inline] + pub fn compress_columns(&self, init: VectorN, f: impl Fn(&mut VectorN, VectorSliceN)) -> VectorN + where DefaultAllocator: Allocator { + let mut res = init; + + for i in 0..self.ncols() { + f(&mut res, self.column(i)) + } + + res + } +} + +impl> Matrix { + /* + * + * Sum computation. + * + */ + /// The sum of all the elements of this matrix. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::Matrix2x3; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.sum(), 21.0); + /// ``` + #[inline] + pub fn sum(&self) -> N { + self.iter().cloned().fold(N::zero(), |a, b| a + b) + } + + /// The sum of all the rows of this matrix. + /// + /// Use `.row_variance_tr` if you need the result in a column vector instead. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, RowVector3}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.row_sum(), RowVector3::new(5.0, 7.0, 9.0)); + /// ``` + #[inline] + pub fn row_sum(&self) -> RowVectorN + where DefaultAllocator: Allocator { + self.compress_rows(|col| col.sum()) + } + + /// The sum of all the rows of this matrix. The result is transposed and returned as a column vector. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, Vector3}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.row_sum_tr(), Vector3::new(5.0, 7.0, 9.0)); + /// ``` + #[inline] + pub fn row_sum_tr(&self) -> VectorN + where DefaultAllocator: Allocator { + self.compress_rows_tr(|col| col.sum()) + } + + /// The sum of all the columns of this matrix. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, Vector2}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.column_sum(), Vector2::new(6.0, 15.0)); + /// ``` + #[inline] + pub fn column_sum(&self) -> VectorN + where DefaultAllocator: Allocator { + let nrows = self.data.shape().0; + self.compress_columns(VectorN::zeros_generic(nrows, U1), |out, col| { + out.axpy(N::one(), &col, N::one()) + }) + } + + /* + * + * Variance computation. + * + */ + /// The variance of all the elements of this matrix. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::Matrix2x3; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_relative_eq!(m.variance(), 35.0 / 12.0, epsilon = 1.0e-8); + /// ``` + #[inline] + pub fn variance(&self) -> N { + if self.len() == 0 { + N::zero() + } else { + let val = self.iter().cloned().fold((N::zero(), N::zero()), |a, b| (a.0 + b * b, a.1 + b)); + let denom = N::one() / ::convert::<_, N>(self.len() as f64); + val.0 * denom - (val.1 * denom) * (val.1 * denom) + } + } + + /// The variance of all the rows of this matrix. + /// + /// Use `.row_variance_tr` if you need the result in a column vector instead. + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, RowVector3}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.row_variance(), RowVector3::new(2.25, 2.25, 2.25)); + /// ``` + #[inline] + pub fn row_variance(&self) -> RowVectorN + where DefaultAllocator: Allocator { + self.compress_rows(|col| col.variance()) + } + + /// The variance of all the rows of this matrix. The result is transposed and returned as a column vector. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, Vector3}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.row_variance_tr(), Vector3::new(2.25, 2.25, 2.25)); + /// ``` + #[inline] + pub fn row_variance_tr(&self) -> VectorN + where DefaultAllocator: Allocator { + self.compress_rows_tr(|col| col.variance()) + } + + /// The variance of all the columns of this matrix. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Matrix2x3, Vector2}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_relative_eq!(m.column_variance(), Vector2::new(2.0 / 3.0, 2.0 / 3.0), epsilon = 1.0e-8); + /// ``` + #[inline] + pub fn column_variance(&self) -> VectorN + where DefaultAllocator: Allocator { + let (nrows, ncols) = self.data.shape(); + + let mut mean = self.column_mean(); + mean.apply(|e| -(e * e)); + + let denom = N::one() / ::convert::<_, N>(ncols.value() as f64); + self.compress_columns(mean, |out, col| { + for i in 0..nrows.value() { + unsafe { + let val = col.vget_unchecked(i); + *out.vget_unchecked_mut(i) += denom * *val * *val + } + } + }) + } + + /* + * + * Mean computation. + * + */ + /// The mean of all the elements of this matrix. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::Matrix2x3; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.mean(), 3.5); + /// ``` + #[inline] + pub fn mean(&self) -> N { + if self.len() == 0 { + N::zero() + } else { + self.sum() / ::convert(self.len() as f64) + } + } + + /// The mean of all the rows of this matrix. + /// + /// Use `.row_mean_tr` if you need the result in a column vector instead. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, RowVector3}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.row_mean(), RowVector3::new(2.5, 3.5, 4.5)); + /// ``` + #[inline] + pub fn row_mean(&self) -> RowVectorN + where DefaultAllocator: Allocator { + self.compress_rows(|col| col.mean()) + } + + /// The mean of all the rows of this matrix. The result is transposed and returned as a column vector. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, Vector3}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.row_mean_tr(), Vector3::new(2.5, 3.5, 4.5)); + /// ``` + #[inline] + pub fn row_mean_tr(&self) -> VectorN + where DefaultAllocator: Allocator { + self.compress_rows_tr(|col| col.mean()) + } + + /// The mean of all the columns of this matrix. + /// + /// # Example + /// + /// ``` + /// # use nalgebra::{Matrix2x3, Vector2}; + /// + /// let m = Matrix2x3::new(1.0, 2.0, 3.0, + /// 4.0, 5.0, 6.0); + /// assert_eq!(m.column_mean(), Vector2::new(2.0, 5.0)); + /// ``` + #[inline] + pub fn column_mean(&self) -> VectorN + where DefaultAllocator: Allocator { + let (nrows, ncols) = self.data.shape(); + let denom = N::one() / ::convert::<_, N>(ncols.value() as f64); + self.compress_columns(VectorN::zeros_generic(nrows, U1), |out, col| { + out.axpy(denom, &col, N::one()) + }) + } +} diff --git a/src/base/unit.rs b/src/base/unit.rs index af400389..3d0a9c03 100644 --- a/src/base/unit.rs +++ b/src/base/unit.rs @@ -13,6 +13,8 @@ use abomonation::Abomonation; use alga::general::SubsetOf; use alga::linear::NormedSpace; +use ::Real; + /// A wrapper that ensures the underlying algebraic entity has a unit norm. /// /// Use `.as_ref()` or `.into_inner()` to obtain the underlying value by-reference or by-move. @@ -91,11 +93,24 @@ impl Unit { /// Normalizes this value again. This is useful when repeated computations /// might cause a drift in the norm because of float inaccuracies. /// - /// Returns the norm before re-normalization (should be close to `1.0`). + /// Returns the norm before re-normalization. See `.renormalize_fast` for a faster alternative + /// that may be slightly less accurate if `self` drifted significantly from having a unit length. #[inline] pub fn renormalize(&mut self) -> T::Field { self.value.normalize_mut() } + + /// Normalizes this value again using a first-order Taylor approximation. + /// This is useful when repeated computations might cause a drift in the norm + /// because of float inaccuracies. + #[inline] + pub fn renormalize_fast(&mut self) + where T::Field: Real { + let sq_norm = self.value.norm_squared(); + let _3: T::Field = ::convert(3.0); + let _0_5: T::Field = ::convert(0.5); + self.value *= _0_5 * (_3 - sq_norm); + } } impl Unit { diff --git a/src/base/vec_storage.rs b/src/base/vec_storage.rs index b5005c0c..afde3677 100644 --- a/src/base/vec_storage.rs +++ b/src/base/vec_storage.rs @@ -1,6 +1,5 @@ #[cfg(feature = "abomonation-serialize")] use std::io::{Result as IOResult, Write}; -use std::ops::Deref; #[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::vec::Vec; @@ -37,12 +36,12 @@ pub type MatrixVec = VecStorage; impl VecStorage { /// Creates a new dynamic matrix data storage from the given vector and shape. #[inline] - pub fn new(nrows: R, ncols: C, data: Vec) -> VecStorage { + pub fn new(nrows: R, ncols: C, data: Vec) -> Self { assert!( nrows.value() * ncols.value() == data.len(), "Data storage buffer dimension mismatch." ); - VecStorage { + Self { data: data, nrows: nrows, ncols: ncols, @@ -51,15 +50,16 @@ impl VecStorage { /// The underlying data storage. #[inline] - pub fn data(&self) -> &Vec { + pub fn as_vec(&self) -> &Vec { &self.data } /// The underlying mutable data storage. /// - /// This is unsafe because this may cause UB if the vector is modified by the user. + /// This is unsafe because this may cause UB if the size of the vector is changed + /// by the user. #[inline] - pub unsafe fn data_mut(&mut self) -> &mut Vec { + pub unsafe fn as_vec_mut(&mut self) -> &mut Vec { &mut self.data } @@ -81,14 +81,11 @@ impl VecStorage { self.data } -} - -impl Deref for VecStorage { - type Target = Vec; + /// The number of elements on the underlying vector. #[inline] - fn deref(&self) -> &Self::Target { - &self.data + pub fn len(&self) -> usize { + self.data.len() } } @@ -145,7 +142,7 @@ where DefaultAllocator: Allocator #[inline] fn as_slice(&self) -> &[N] { - &self[..] + &self.data } } @@ -189,7 +186,7 @@ where DefaultAllocator: Allocator #[inline] fn as_slice(&self) -> &[N] { - &self[..] + &self.data } } diff --git a/src/geometry/isometry.rs b/src/geometry/isometry.rs index 7ad3d4d9..0d3c6619 100644 --- a/src/geometry/isometry.rs +++ b/src/geometry/isometry.rs @@ -100,7 +100,7 @@ where DefaultAllocator: Allocator { #[inline] fn clone(&self) -> Self { - Isometry::from_parts(self.translation.clone(), self.rotation.clone()) + Self::from_parts(self.translation.clone(), self.rotation.clone()) } } @@ -113,7 +113,6 @@ where DefaultAllocator: Allocator /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Isometry3, Translation3, UnitQuaternion, Vector3, Point3}; /// let tra = Translation3::new(0.0, 0.0, 3.0); @@ -123,8 +122,8 @@ where DefaultAllocator: Allocator /// assert_relative_eq!(iso * Point3::new(1.0, 2.0, 3.0), Point3::new(-1.0, 2.0, 0.0), epsilon = 1.0e-6); /// ``` #[inline] - pub fn from_parts(translation: Translation, rotation: R) -> Isometry { - Isometry { + pub fn from_parts(translation: Translation, rotation: R) -> Self { + Self { rotation: rotation, translation: translation, _noconstruct: PhantomData, @@ -145,7 +144,7 @@ where DefaultAllocator: Allocator /// assert_eq!(inv * (iso * pt), pt); /// ``` #[inline] - pub fn inverse(&self) -> Isometry { + pub fn inverse(&self) -> Self { let mut res = self.clone(); res.inverse_mut(); res @@ -167,7 +166,7 @@ where DefaultAllocator: Allocator /// ``` #[inline] pub fn inverse_mut(&mut self) { - self.rotation.inverse_mut(); + self.rotation.two_sided_inverse_mut(); self.translation.inverse_mut(); self.translation.vector = self.rotation.transform_vector(&self.translation.vector); } @@ -197,7 +196,6 @@ where DefaultAllocator: Allocator /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Isometry2, Translation2, UnitComplex, Vector2}; /// let mut iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::PI / 6.0); @@ -220,7 +218,6 @@ where DefaultAllocator: Allocator /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Isometry2, Translation2, UnitComplex, Vector2, Point2}; /// let mut iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2); @@ -272,7 +269,6 @@ where DefaultAllocator: Allocator /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Isometry2, Vector2, Matrix3}; /// let iso = Isometry2::new(Vector2::new(10.0, 20.0), f32::consts::FRAC_PI_6); @@ -310,7 +306,7 @@ where DefaultAllocator: Allocator, { #[inline] - fn eq(&self, right: &Isometry) -> bool { + fn eq(&self, right: &Self) -> bool { self.translation == right.translation && self.rotation == right.rotation } } diff --git a/src/geometry/isometry_alga.rs b/src/geometry/isometry_alga.rs index 7decea8e..246947ca 100644 --- a/src/geometry/isometry_alga.rs +++ b/src/geometry/isometry_alga.rs @@ -1,6 +1,6 @@ use alga::general::{ AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, - AbstractSemigroup, Id, Identity, Inverse, Multiplicative, Real, + AbstractSemigroup, Id, Identity, TwoSidedInverse, Multiplicative, Real, }; use alga::linear::Isometry as AlgaIsometry; use alga::linear::{ @@ -30,18 +30,18 @@ where } } -impl Inverse for Isometry +impl TwoSidedInverse for Isometry where R: Rotation>, DefaultAllocator: Allocator, { #[inline] - fn inverse(&self) -> Self { + fn two_sided_inverse(&self) -> Self { self.inverse() } #[inline] - fn inverse_mut(&mut self) { + fn two_sided_inverse_mut(&mut self) { self.inverse_mut() } } @@ -121,7 +121,7 @@ where type Translation = Translation; #[inline] - fn decompose(&self) -> (Translation, R, Id, R) { + fn decompose(&self) -> (Self::Translation, R, Id, R) { ( self.translation.clone(), self.rotation.clone(), diff --git a/src/geometry/isometry_construction.rs b/src/geometry/isometry_construction.rs index 4d0702f0..5a402c0a 100644 --- a/src/geometry/isometry_construction.rs +++ b/src/geometry/isometry_construction.rs @@ -16,7 +16,7 @@ use base::{DefaultAllocator, Vector2, Vector3}; use geometry::{ Isometry, Point, Point3, Rotation, Rotation2, Rotation3, Translation, UnitComplex, - UnitQuaternion, + UnitQuaternion, Translation2, Translation3 }; impl>> Isometry @@ -49,7 +49,6 @@ where DefaultAllocator: Allocator /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Isometry2, Point2, UnitComplex}; /// let rot = UnitComplex::new(f32::consts::PI); @@ -130,6 +129,18 @@ impl Isometry> { Rotation::::new(angle), ) } + + /// Creates a new isometry from the given translation coordinates. + #[inline] + pub fn translation(x: N, y: N) -> Self { + Self::new(Vector2::new(x, y), N::zero()) + } + + /// Creates a new isometry from the given rotation angle. + #[inline] + pub fn rotation(angle: N) -> Self { + Self::new(Vector2::zeros(), angle) + } } impl Isometry> { @@ -153,6 +164,18 @@ impl Isometry> { UnitComplex::from_angle(angle), ) } + + /// Creates a new isometry from the given translation coordinates. + #[inline] + pub fn translation(x: N, y: N) -> Self { + Self::from_parts(Translation2::new(x, y), UnitComplex::identity()) + } + + /// Creates a new isometry from the given rotation angle. + #[inline] + pub fn rotation(angle: N) -> Self { + Self::new(Vector2::zeros(), angle) + } } // 3D rotation. @@ -165,7 +188,6 @@ macro_rules! isometry_construction_impl( /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Isometry3, IsometryMatrix3, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; @@ -191,6 +213,18 @@ macro_rules! isometry_construction_impl( $RotId::<$($RotParams),*>::from_scaled_axis(axisangle)) } + /// Creates a new isometry from the given translation coordinates. + #[inline] + pub fn translation(x: N, y: N, z: N) -> Self { + Self::from_parts(Translation3::new(x, y, z), $RotId::identity()) + } + + /// Creates a new isometry from the given rotation angle. + #[inline] + pub fn rotation(axisangle: Vector3) -> Self { + Self::new(Vector3::zeros(), axisangle) + } + /// Creates an isometry that corresponds to the local frame of an observer standing at the /// point `eye` and looking toward `target`. /// @@ -206,7 +240,6 @@ macro_rules! isometry_construction_impl( /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Isometry3, IsometryMatrix3, Point3, Vector3}; /// let eye = Point3::new(1.0, 2.0, 3.0); @@ -214,23 +247,32 @@ macro_rules! isometry_construction_impl( /// let up = Vector3::y(); /// /// // Isometry with its rotation part represented as a UnitQuaternion - /// let iso = Isometry3::new_observer_frame(&eye, &target, &up); + /// let iso = Isometry3::face_towards(&eye, &target, &up); /// assert_eq!(iso * Point3::origin(), eye); /// assert_relative_eq!(iso * Vector3::z(), Vector3::x()); /// /// // Isometry with its rotation part represented as Rotation3 (a 3x3 rotation matrix). - /// let iso = IsometryMatrix3::new_observer_frame(&eye, &target, &up); + /// let iso = IsometryMatrix3::face_towards(&eye, &target, &up); /// assert_eq!(iso * Point3::origin(), eye); /// assert_relative_eq!(iso * Vector3::z(), Vector3::x()); /// ``` #[inline] - pub fn new_observer_frame(eye: &Point3, + pub fn face_towards(eye: &Point3, target: &Point3, up: &Vector3) -> Self { Self::from_parts( Translation::from(eye.coords.clone()), - $RotId::new_observer_frame(&(target - eye), up)) + $RotId::face_towards(&(target - eye), up)) + } + + /// Deprecated: Use [Isometry::face_towards] instead. + #[deprecated(note="renamed to `face_towards`")] + pub fn new_observer_frame(eye: &Point3, + target: &Point3, + up: &Vector3) + -> Self { + Self::face_towards(eye, target, up) } /// Builds a right-handed look-at view matrix. @@ -249,7 +291,6 @@ macro_rules! isometry_construction_impl( /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Isometry3, IsometryMatrix3, Point3, Vector3}; /// let eye = Point3::new(1.0, 2.0, 3.0); @@ -293,7 +334,6 @@ macro_rules! isometry_construction_impl( /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Isometry3, IsometryMatrix3, Point3, Vector3}; /// let eye = Point3::new(1.0, 2.0, 3.0); diff --git a/src/geometry/isometry_ops.rs b/src/geometry/isometry_ops.rs index a3608a63..6a4b921e 100644 --- a/src/geometry/isometry_ops.rs +++ b/src/geometry/isometry_ops.rs @@ -200,8 +200,8 @@ isometry_binop_assign_impl_all!( DivAssign, div_assign; self: Isometry, rhs: R; // FIXME: don't invert explicitly? - [val] => *self *= rhs.inverse(); - [ref] => *self *= rhs.inverse(); + [val] => *self *= rhs.two_sided_inverse(); + [ref] => *self *= rhs.two_sided_inverse(); ); // Isometry × R diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs index 3100eb9c..9e25dc29 100644 --- a/src/geometry/mod.rs +++ b/src/geometry/mod.rs @@ -37,6 +37,7 @@ mod translation_alga; mod translation_alias; mod translation_construction; mod translation_conversion; +mod translation_coordinates; mod translation_ops; mod isometry; diff --git a/src/geometry/orthographic.rs b/src/geometry/orthographic.rs index 4a04c8a8..3117c1b6 100644 --- a/src/geometry/orthographic.rs +++ b/src/geometry/orthographic.rs @@ -26,7 +26,7 @@ impl Copy for Orthographic3 {} impl Clone for Orthographic3 { #[inline] fn clone(&self) -> Self { - Orthographic3::from_matrix_unchecked(self.matrix.clone()) + Self::from_matrix_unchecked(self.matrix.clone()) } } @@ -57,7 +57,7 @@ impl<'a, N: Real + Deserialize<'a>> Deserialize<'a> for Orthographic3 { where Des: Deserializer<'a> { let matrix = Matrix4::::deserialize(deserializer)?; - Ok(Orthographic3::from_matrix_unchecked(matrix)) + Ok(Self::from_matrix_unchecked(matrix)) } } @@ -69,7 +69,6 @@ impl Orthographic3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Orthographic3, Point3}; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// // Check this projection actually transforms the view cuboid into the double-unit cube. @@ -136,7 +135,7 @@ impl Orthographic3 { /// ``` #[inline] pub fn from_matrix_unchecked(matrix: Matrix4) -> Self { - Orthographic3 { matrix: matrix } + Self { matrix: matrix } } /// Creates a new orthographic projection matrix from an aspect ratio and the vertical field of view. @@ -170,7 +169,6 @@ impl Orthographic3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Orthographic3, Point3, Matrix4}; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// let inv = proj.inverse(); @@ -271,7 +269,6 @@ impl Orthographic3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Orthographic3, Point3, Matrix4}; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// let expected = Matrix4::new( @@ -299,7 +296,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// assert_relative_eq!(proj.left(), 1.0, epsilon = 1.0e-6); @@ -316,7 +312,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// assert_relative_eq!(proj.right(), 10.0, epsilon = 1.0e-6); @@ -333,7 +328,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// assert_relative_eq!(proj.bottom(), 2.0, epsilon = 1.0e-6); @@ -350,7 +344,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// assert_relative_eq!(proj.top(), 20.0, epsilon = 1.0e-6); @@ -367,7 +360,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// assert_relative_eq!(proj.znear(), 0.1, epsilon = 1.0e-6); @@ -384,7 +376,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// assert_relative_eq!(proj.zfar(), 1000.0, epsilon = 1.0e-6); @@ -403,7 +394,6 @@ impl Orthographic3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Orthographic3, Point3}; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// @@ -439,7 +429,6 @@ impl Orthographic3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Orthographic3, Point3}; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// @@ -478,7 +467,6 @@ impl Orthographic3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Orthographic3, Vector3}; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// @@ -504,7 +492,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_left(2.0); @@ -524,7 +511,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_right(15.0); @@ -544,7 +530,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_bottom(8.0); @@ -564,7 +549,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_top(15.0); @@ -584,7 +568,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_znear(8.0); @@ -604,7 +587,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_zfar(15.0); @@ -624,7 +606,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_left_and_right(7.0, 70.0); @@ -650,7 +631,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_bottom_and_top(7.0, 70.0); @@ -676,7 +656,6 @@ impl Orthographic3 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_znear_and_zfar(50.0, 5000.0); diff --git a/src/geometry/perspective.rs b/src/geometry/perspective.rs index b7e0d8ea..550469be 100644 --- a/src/geometry/perspective.rs +++ b/src/geometry/perspective.rs @@ -27,7 +27,7 @@ impl Copy for Perspective3 {} impl Clone for Perspective3 { #[inline] fn clone(&self) -> Self { - Perspective3::from_matrix_unchecked(self.matrix.clone()) + Self::from_matrix_unchecked(self.matrix.clone()) } } @@ -58,7 +58,7 @@ impl<'a, N: Real + Deserialize<'a>> Deserialize<'a> for Perspective3 { where Des: Deserializer<'a> { let matrix = Matrix4::::deserialize(deserializer)?; - Ok(Perspective3::from_matrix_unchecked(matrix)) + Ok(Self::from_matrix_unchecked(matrix)) } } @@ -75,7 +75,7 @@ impl Perspective3 { ); let matrix = Matrix4::identity(); - let mut res = Perspective3::from_matrix_unchecked(matrix); + let mut res = Self::from_matrix_unchecked(matrix); res.set_fovy(fovy); res.set_aspect(aspect); @@ -93,7 +93,7 @@ impl Perspective3 { /// projection. #[inline] pub fn from_matrix_unchecked(matrix: Matrix4) -> Self { - Perspective3 { matrix: matrix } + Self { matrix: matrix } } /// Retrieves the inverse of the underlying homogeneous matrix. diff --git a/src/geometry/point.rs b/src/geometry/point.rs index 0d24ab1f..14ce7dc2 100644 --- a/src/geometry/point.rs +++ b/src/geometry/point.rs @@ -66,7 +66,7 @@ where where Des: Deserializer<'a> { let coords = VectorN::::deserialize(deserializer)?; - Ok(Point::from(coords)) + Ok(Self::from(coords)) } } @@ -126,8 +126,8 @@ where DefaultAllocator: Allocator /// Creates a new point with the given coordinates. #[deprecated(note = "Use Point::from(vector) instead.")] #[inline] - pub fn from_coordinates(coords: VectorN) -> Point { - Point { coords: coords } + pub fn from_coordinates(coords: VectorN) -> Self { + Self { coords: coords } } /// The dimension of this point. diff --git a/src/geometry/point_alga.rs b/src/geometry/point_alga.rs index 56940dd1..2f81ceff 100644 --- a/src/geometry/point_alga.rs +++ b/src/geometry/point_alga.rs @@ -54,7 +54,7 @@ where { #[inline] fn meet(&self, other: &Self) -> Self { - Point::from(self.coords.meet(&other.coords)) + Self::from(self.coords.meet(&other.coords)) } } @@ -65,7 +65,7 @@ where { #[inline] fn join(&self, other: &Self) -> Self { - Point::from(self.coords.join(&other.coords)) + Self::from(self.coords.join(&other.coords)) } } @@ -78,6 +78,6 @@ where fn meet_join(&self, other: &Self) -> (Self, Self) { let (meet, join) = self.coords.meet_join(&other.coords); - (Point::from(meet), Point::from(join)) + (Self::from(meet), Self::from(join)) } } diff --git a/src/geometry/point_construction.rs b/src/geometry/point_construction.rs index 2c030f61..b6c46779 100644 --- a/src/geometry/point_construction.rs +++ b/src/geometry/point_construction.rs @@ -145,7 +145,7 @@ where { #[inline] fn arbitrary(g: &mut G) -> Self { - Point::from(VectorN::arbitrary(g)) + Self::from(VectorN::arbitrary(g)) } } @@ -163,7 +163,7 @@ macro_rules! componentwise_constructors_impl( #[doc = $doc] #[doc = "```"] #[inline] - pub fn new($($args: N),*) -> Point { + pub fn new($($args: N),*) -> Self { unsafe { let mut res = Self::new_uninitialized(); $( *res.get_unchecked_mut($irow) = $args; )* @@ -194,7 +194,7 @@ macro_rules! from_array_impl( ($($D: ty, $len: expr);*) => {$( impl From<[N; $len]> for Point { fn from (coords: [N; $len]) -> Self { - Point { + Self { coords: coords.into() } } diff --git a/src/geometry/point_conversion.rs b/src/geometry/point_conversion.rs index 84efab6d..50a3a4d2 100644 --- a/src/geometry/point_conversion.rs +++ b/src/geometry/point_conversion.rs @@ -45,7 +45,7 @@ where #[inline] unsafe fn from_superset_unchecked(m: &Point) -> Self { - Point::from(Matrix::from_superset_unchecked(&m.coords)) + Self::from(Matrix::from_superset_unchecked(&m.coords)) } } diff --git a/src/geometry/point_ops.rs b/src/geometry/point_ops.rs index a923fbf9..0a20bc90 100644 --- a/src/geometry/point_ops.rs +++ b/src/geometry/point_ops.rs @@ -46,11 +46,11 @@ where DefaultAllocator: Allocator impl Neg for Point where DefaultAllocator: Allocator { - type Output = Point; + type Output = Self; #[inline] fn neg(self) -> Self::Output { - Point::from(-self.coords) + Self::Output::from(-self.coords) } } @@ -61,7 +61,7 @@ where DefaultAllocator: Allocator #[inline] fn neg(self) -> Self::Output { - Point::from(-&self.coords) + Self::Output::from(-&self.coords) } } diff --git a/src/geometry/quaternion.rs b/src/geometry/quaternion.rs index 4b0ce6b3..b58d885c 100644 --- a/src/geometry/quaternion.rs +++ b/src/geometry/quaternion.rs @@ -68,7 +68,7 @@ impl Copy for Quaternion {} impl Clone for Quaternion { #[inline] fn clone(&self) -> Self { - Quaternion::from(self.coords.clone()) + Self::from(self.coords.clone()) } } @@ -90,7 +90,7 @@ where Owned: Deserialize<'a> where Des: Deserializer<'a> { let coords = Vector4::::deserialize(deserializer)?; - Ok(Quaternion::from(coords)) + Ok(Self::from(coords)) } } @@ -98,15 +98,15 @@ impl Quaternion { /// Moves this unit quaternion into one that owns its data. #[inline] #[deprecated(note = "This method is a no-op and will be removed in a future release.")] - pub fn into_owned(self) -> Quaternion { + pub fn into_owned(self) -> Self { self } /// Clones this unit quaternion into one that owns its data. #[inline] #[deprecated(note = "This method is a no-op and will be removed in a future release.")] - pub fn clone_owned(&self) -> Quaternion { - Quaternion::from(self.coords.clone_owned()) + pub fn clone_owned(&self) -> Self { + Self::from(self.coords.clone_owned()) } /// Normalizes this quaternion. @@ -114,15 +114,14 @@ impl Quaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let q_normalized = q.normalize(); /// relative_eq!(q_normalized.norm(), 1.0); /// ``` #[inline] - pub fn normalize(&self) -> Quaternion { - Quaternion::from(self.coords.normalize()) + pub fn normalize(&self) -> Self { + Self::from(self.coords.normalize()) } /// The conjugate of this quaternion. @@ -135,14 +134,14 @@ impl Quaternion { /// assert!(conj.i == -2.0 && conj.j == -3.0 && conj.k == -4.0 && conj.w == 1.0); /// ``` #[inline] - pub fn conjugate(&self) -> Quaternion { + pub fn conjugate(&self) -> Self { let v = Vector4::new( -self.coords[0], -self.coords[1], -self.coords[2], self.coords[3], ); - Quaternion::from(v) + Self::from(v) } /// Inverts this quaternion if it is not zero. @@ -150,7 +149,6 @@ impl Quaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let inv_q = q.try_inverse(); @@ -165,8 +163,8 @@ impl Quaternion { /// assert!(inv_q.is_none()); /// ``` #[inline] - pub fn try_inverse(&self) -> Option> { - let mut res = Quaternion::from(self.coords.clone_owned()); + pub fn try_inverse(&self) -> Option { + let mut res = Self::from(self.coords.clone_owned()); if res.try_inverse_mut() { Some(res) @@ -188,7 +186,7 @@ impl Quaternion { /// assert_eq!(q1.lerp(&q2, 0.1), Quaternion::new(1.9, 3.8, 5.7, 7.6)); /// ``` #[inline] - pub fn lerp(&self, other: &Quaternion, t: N) -> Quaternion { + pub fn lerp(&self, other: &Self, t: N) -> Self { self * (N::one() - t) + other * t } @@ -240,7 +238,6 @@ impl Quaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// assert_relative_eq!(q.norm(), 5.47722557, epsilon = 1.0e-6); @@ -258,7 +255,6 @@ impl Quaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// assert_relative_eq!(q.magnitude(), 5.47722557, epsilon = 1.0e-6); @@ -345,18 +341,17 @@ impl Quaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(2.0, 5.0, 0.0, 0.0); /// assert_relative_eq!(q.ln(), Quaternion::new(1.683647, 1.190289, 0.0, 0.0), epsilon = 1.0e-6) /// ``` #[inline] - pub fn ln(&self) -> Quaternion { + pub fn ln(&self) -> Self { let n = self.norm(); let v = self.vector(); let s = self.scalar(); - Quaternion::from_parts(n.ln(), v.normalize() * (s / n).acos()) + Self::from_parts(n.ln(), v.normalize() * (s / n).acos()) } /// Compute the exponential of a quaternion. @@ -364,13 +359,12 @@ impl Quaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.683647, 1.190289, 0.0, 0.0); /// assert_relative_eq!(q.exp(), Quaternion::new(2.0, 5.0, 0.0, 0.0), epsilon = 1.0e-5) /// ``` #[inline] - pub fn exp(&self) -> Quaternion { + pub fn exp(&self) -> Self { self.exp_eps(N::default_epsilon()) } @@ -380,7 +374,6 @@ impl Quaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.683647, 1.190289, 0.0, 0.0); /// assert_relative_eq!(q.exp_eps(1.0e-6), Quaternion::new(2.0, 5.0, 0.0, 0.0), epsilon = 1.0e-5); @@ -390,18 +383,18 @@ impl Quaternion { /// assert_eq!(q.exp_eps(1.0e-6), Quaternion::identity()); /// ``` #[inline] - pub fn exp_eps(&self, eps: N) -> Quaternion { + pub fn exp_eps(&self, eps: N) -> Self { let v = self.vector(); let nn = v.norm_squared(); if nn <= eps * eps { - Quaternion::identity() + Self::identity() } else { let w_exp = self.scalar().exp(); let n = nn.sqrt(); let nv = v * (w_exp * n.sin() / n); - Quaternion::from_parts(w_exp * n.cos(), nv) + Self::from_parts(w_exp * n.cos(), nv) } } @@ -410,13 +403,12 @@ impl Quaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// assert_relative_eq!(q.powf(1.5), Quaternion::new( -6.2576659, 4.1549037, 6.2323556, 8.3098075), epsilon = 1.0e-6); /// ``` #[inline] - pub fn powf(&self, n: N) -> Quaternion { + pub fn powf(&self, n: N) -> Self { (self.ln() * n).exp() } @@ -476,7 +468,6 @@ impl Quaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Quaternion; /// let mut q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// @@ -506,7 +497,6 @@ impl Quaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Quaternion; /// let mut q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// q.normalize_mut(); @@ -530,7 +520,7 @@ impl> AbsDiffEq for Quaternion { fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { self.as_vector().abs_diff_eq(other.as_vector(), epsilon) || // Account for the double-covering of S², i.e. q = -q - self.as_vector().iter().zip(other.as_vector().iter()).all(|(a, b)| a.abs_diff_eq(&-*b, epsilon)) + self.as_vector().iter().zip(other.as_vector().iter()).all(|(a, b)| a.abs_diff_eq(&-*b, epsilon)) } } @@ -550,7 +540,7 @@ impl> RelativeEq for Quaternion { { self.as_vector().relative_eq(other.as_vector(), epsilon, max_relative) || // Account for the double-covering of S², i.e. q = -q - self.as_vector().iter().zip(other.as_vector().iter()).all(|(a, b)| a.relative_eq(&-*b, epsilon, max_relative)) + self.as_vector().iter().zip(other.as_vector().iter()).all(|(a, b)| a.relative_eq(&-*b, epsilon, max_relative)) } } @@ -564,7 +554,7 @@ impl> UlpsEq for Quaternion { fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { self.as_vector().ulps_eq(other.as_vector(), epsilon, max_ulps) || // Account for the double-covering of S², i.e. q = -q. - self.as_vector().iter().zip(other.as_vector().iter()).all(|(a, b)| a.ulps_eq(&-*b, epsilon, max_ulps)) + self.as_vector().iter().zip(other.as_vector().iter()).all(|(a, b)| a.ulps_eq(&-*b, epsilon, max_ulps)) } } @@ -587,7 +577,7 @@ impl UnitQuaternion { #[deprecated( note = "This method is unnecessary and will be removed in a future release. Use `.clone()` instead." )] - pub fn into_owned(self) -> UnitQuaternion { + pub fn into_owned(self) -> Self { self } @@ -596,7 +586,7 @@ impl UnitQuaternion { #[deprecated( note = "This method is unnecessary and will be removed in a future release. Use `.clone()` instead." )] - pub fn clone_owned(&self) -> UnitQuaternion { + pub fn clone_owned(&self) -> Self { *self } @@ -647,8 +637,8 @@ impl UnitQuaternion { /// assert_eq!(conj, UnitQuaternion::from_axis_angle(&-axis, 1.78)); /// ``` #[inline] - pub fn conjugate(&self) -> UnitQuaternion { - UnitQuaternion::new_unchecked(self.as_ref().conjugate()) + pub fn conjugate(&self) -> Self { + Self::new_unchecked(self.as_ref().conjugate()) } /// Inverts this quaternion if it is not zero. @@ -663,7 +653,7 @@ impl UnitQuaternion { /// assert_eq!(inv * rot, UnitQuaternion::identity()); /// ``` #[inline] - pub fn inverse(&self) -> UnitQuaternion { + pub fn inverse(&self) -> Self { self.conjugate() } @@ -672,14 +662,13 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{UnitQuaternion, Vector3}; /// let rot1 = UnitQuaternion::from_axis_angle(&Vector3::y_axis(), 1.0); /// let rot2 = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), 0.1); /// assert_relative_eq!(rot1.angle_to(&rot2), 1.0045657, epsilon = 1.0e-6); /// ``` #[inline] - pub fn angle_to(&self, other: &UnitQuaternion) -> N { + pub fn angle_to(&self, other: &Self) -> N { let delta = self.rotation_to(other); delta.angle() } @@ -691,7 +680,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{UnitQuaternion, Vector3}; /// let rot1 = UnitQuaternion::from_axis_angle(&Vector3::y_axis(), 1.0); /// let rot2 = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), 0.1); @@ -699,7 +687,7 @@ impl UnitQuaternion { /// assert_relative_eq!(rot_to * rot1, rot2, epsilon = 1.0e-6); /// ``` #[inline] - pub fn rotation_to(&self, other: &UnitQuaternion) -> UnitQuaternion { + pub fn rotation_to(&self, other: &Self) -> Self{ other / self } @@ -715,7 +703,7 @@ impl UnitQuaternion { /// assert_eq!(q1.lerp(&q2, 0.1), Quaternion::new(0.9, 0.1, 0.0, 0.0)); /// ``` #[inline] - pub fn lerp(&self, other: &UnitQuaternion, t: N) -> Quaternion { + pub fn lerp(&self, other: &Self, t: N) -> Quaternion { self.as_ref().lerp(other.as_ref(), t) } @@ -731,11 +719,11 @@ impl UnitQuaternion { /// assert_eq!(q1.nlerp(&q2, 0.1), UnitQuaternion::new_normalize(Quaternion::new(0.9, 0.1, 0.0, 0.0))); /// ``` #[inline] - pub fn nlerp(&self, other: &UnitQuaternion, t: N) -> UnitQuaternion { + pub fn nlerp(&self, other: &Self, t: N) -> Self { let mut res = self.lerp(other, t); let _ = res.normalize_mut(); - UnitQuaternion::new_unchecked(res) + Self::new_unchecked(res) } /// Spherical linear interpolation between two unit quaternions. @@ -743,7 +731,7 @@ impl UnitQuaternion { /// Panics if the angle between both quaternion is 180 degrees (in which case the interpolation /// is not well-defined). Use `.try_slerp` instead to avoid the panic. #[inline] - pub fn slerp(&self, other: &UnitQuaternion, t: N) -> UnitQuaternion { + pub fn slerp(&self, other: &Self, t: N) -> Self { Unit::new_unchecked(Quaternion::from( Unit::new_unchecked(self.coords) .slerp(&Unit::new_unchecked(other.coords), t) @@ -764,10 +752,10 @@ impl UnitQuaternion { #[inline] pub fn try_slerp( &self, - other: &UnitQuaternion, + other: &Self, t: N, epsilon: N, - ) -> Option> + ) -> Option { Unit::new_unchecked(self.coords) .try_slerp(&Unit::new_unchecked(other.coords), t, epsilon) @@ -785,7 +773,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{UnitQuaternion, Vector3, Unit}; /// let axisangle = Vector3::new(0.1, 0.2, 0.3); /// let mut rot = UnitQuaternion::new(axisangle); @@ -828,7 +815,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{UnitQuaternion, Vector3, Unit}; /// let axisangle = Vector3::new(0.1, 0.2, 0.3); /// let rot = UnitQuaternion::new(axisangle); @@ -885,7 +871,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Vector3, UnitQuaternion}; /// let axisangle = Vector3::new(0.1, 0.2, 0.3); /// let q = UnitQuaternion::new(axisangle); @@ -908,7 +893,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{UnitQuaternion, Vector3, Unit}; /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let angle = 1.2; @@ -918,11 +902,11 @@ impl UnitQuaternion { /// assert_eq!(pow.angle(), 2.4); /// ``` #[inline] - pub fn powf(&self, n: N) -> UnitQuaternion { + pub fn powf(&self, n: N) -> Self { if let Some(v) = self.axis() { - UnitQuaternion::from_axis_angle(&v, self.angle() * n) + Self::from_axis_angle(&v, self.angle() * n) } else { - UnitQuaternion::identity() + Self::identity() } } @@ -932,7 +916,6 @@ impl UnitQuaternion { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Vector3, Matrix3}; /// let q = UnitQuaternion::from_axis_angle(&Vector3::z_axis(), f32::consts::FRAC_PI_6); @@ -990,7 +973,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::UnitQuaternion; /// let rot = UnitQuaternion::from_euler_angles(0.1, 0.2, 0.3); /// let euler = rot.euler_angles(); @@ -1009,7 +991,6 @@ impl UnitQuaternion { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Vector3, Matrix4}; /// let rot = UnitQuaternion::from_axis_angle(&Vector3::z_axis(), f32::consts::FRAC_PI_6); diff --git a/src/geometry/quaternion_alga.rs b/src/geometry/quaternion_alga.rs index fe1a33b8..529d27f4 100644 --- a/src/geometry/quaternion_alga.rs +++ b/src/geometry/quaternion_alga.rs @@ -2,7 +2,7 @@ use num::Zero; use alga::general::{ AbstractGroup, AbstractGroupAbelian, AbstractLoop, AbstractMagma, AbstractModule, - AbstractMonoid, AbstractQuasigroup, AbstractSemigroup, Additive, Id, Identity, Inverse, Module, + AbstractMonoid, AbstractQuasigroup, AbstractSemigroup, Additive, Id, Identity, TwoSidedInverse, Module, Multiplicative, Real, }; use alga::linear::{ @@ -42,9 +42,9 @@ impl AbstractMagma for Quaternion { } } -impl Inverse for Quaternion { +impl TwoSidedInverse for Quaternion { #[inline] - fn inverse(&self) -> Self { + fn two_sided_inverse(&self) -> Self { -self } } @@ -173,14 +173,14 @@ impl AbstractMagma for UnitQuaternion { } } -impl Inverse for UnitQuaternion { +impl TwoSidedInverse for UnitQuaternion { #[inline] - fn inverse(&self) -> Self { + fn two_sided_inverse(&self) -> Self { self.inverse() } #[inline] - fn inverse_mut(&mut self) { + fn two_sided_inverse_mut(&mut self) { self.inverse_mut() } } diff --git a/src/geometry/quaternion_construction.rs b/src/geometry/quaternion_construction.rs index 122eb883..0a30fabe 100644 --- a/src/geometry/quaternion_construction.rs +++ b/src/geometry/quaternion_construction.rs @@ -15,9 +15,9 @@ use base::dimension::U3; use base::storage::Storage; #[cfg(feature = "arbitrary")] use base::Vector3; -use base::{Unit, Vector, Vector4}; +use base::{Unit, Vector, Vector4, Matrix3}; -use geometry::{Quaternion, Rotation, UnitQuaternion}; +use geometry::{Quaternion, Rotation3, UnitQuaternion}; impl Quaternion { /// Creates a quaternion from a 4D vector. The quaternion scalar part corresponds to the `w` @@ -25,7 +25,7 @@ impl Quaternion { #[inline] #[deprecated(note = "Use `::from` instead.")] pub fn from_vector(vector: Vector4) -> Self { - Quaternion { coords: vector } + Self { coords: vector } } /// Creates a new quaternion from its individual components. Note that the arguments order does @@ -130,7 +130,7 @@ where Owned: Send { #[inline] fn arbitrary(g: &mut G) -> Self { - Quaternion::new( + Self::new( N::arbitrary(g), N::arbitrary(g), N::arbitrary(g), @@ -166,7 +166,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Point3, Vector3}; /// let axis = Vector3::y_axis(); @@ -208,7 +207,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::UnitQuaternion; /// let rot = UnitQuaternion::from_euler_angles(0.1, 0.2, 0.3); /// let euler = rot.euler_angles(); @@ -237,7 +235,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Rotation3, UnitQuaternion, Vector3}; /// let axis = Vector3::y_axis(); /// let angle = 0.1; @@ -248,7 +245,7 @@ impl UnitQuaternion { /// assert_relative_eq!(q.angle(), rot.angle(), epsilon = 1.0e-6); /// ``` #[inline] - pub fn from_rotation_matrix(rotmat: &Rotation) -> Self { + pub fn from_rotation_matrix(rotmat: &Rotation3) -> Self { // Robust matrix to quaternion transformation. // See http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion let tr = rotmat[(0, 0)] + rotmat[(1, 1)] + rotmat[(2, 2)]; @@ -296,13 +293,38 @@ impl UnitQuaternion { Self::new_unchecked(res) } + /// Builds an unit quaternion by extracting the rotation part of the given transformation `m`. + /// + /// This is an iterative method. See `.from_matrix_eps` to provide mover + /// convergence parameters and starting solution. + /// This implements "A Robust Method to Extract the Rotational Part of Deformations" by Müller et al. + pub fn from_matrix(m: &Matrix3) -> Self { + Rotation3::from_matrix(m).into() + } + + /// Builds an unit quaternion by extracting the rotation part of the given transformation `m`. + /// + /// This implements "A Robust Method to Extract the Rotational Part of Deformations" by Müller et al. + /// + /// # Parameters + /// + /// * `m`: the matrix from which the rotational part is to be extracted. + /// * `eps`: the angular errors tolerated between the current rotation and the optimal one. + /// * `max_iter`: the maximum number of iterations. Loops indefinitely until convergence if set to `0`. + /// * `guess`: an estimate of the solution. Convergence will be significantly faster if an initial solution close + /// to the actual solution is provided. Can be set to `UnitQuaternion::identity()` if no other + /// guesses come to mind. + pub fn from_matrix_eps(m: &Matrix3, eps: N, max_iter: usize, guess: Self) -> Self { + let guess = Rotation3::from(guess); + Rotation3::from_matrix_eps(m, eps, max_iter, guess).into() + } + /// The unit quaternion needed to make `a` and `b` be collinear and point toward the same /// direction. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Vector3, UnitQuaternion}; /// let a = Vector3::new(1.0, 2.0, 3.0); /// let b = Vector3::new(3.0, 1.0, 2.0); @@ -325,7 +347,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Vector3, UnitQuaternion}; /// let a = Vector3::new(1.0, 2.0, 3.0); /// let b = Vector3::new(3.0, 1.0, 2.0); @@ -361,7 +382,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Unit, Vector3, UnitQuaternion}; /// let a = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let b = Unit::new_normalize(Vector3::new(3.0, 1.0, 2.0)); @@ -387,7 +407,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Unit, Vector3, UnitQuaternion}; /// let a = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let b = Unit::new_normalize(Vector3::new(3.0, 1.0, 2.0)); @@ -446,22 +465,31 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Vector3}; /// let dir = Vector3::new(1.0, 2.0, 3.0); /// let up = Vector3::y(); /// - /// let q = UnitQuaternion::new_observer_frame(&dir, &up); + /// let q = UnitQuaternion::face_towards(&dir, &up); /// assert_relative_eq!(q * Vector3::z(), dir.normalize()); /// ``` #[inline] - pub fn new_observer_frame(dir: &Vector, up: &Vector) -> Self + pub fn face_towards(dir: &Vector, up: &Vector) -> Self where SB: Storage, SC: Storage, { - Self::from_rotation_matrix(&Rotation::::new_observer_frame(dir, up)) + Self::from_rotation_matrix(&Rotation3::face_towards(dir, up)) + } + + /// Deprecated: Use [UnitQuaternion::face_towards] instead. + #[deprecated(note="renamed to `face_towards`")] + pub fn new_observer_frames(dir: &Vector, up: &Vector) -> Self + where + SB: Storage, + SC: Storage, + { + Self::face_towards(dir, up) } /// Builds a right-handed look-at view matrix without translation. @@ -478,7 +506,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Vector3}; /// let dir = Vector3::new(1.0, 2.0, 3.0); @@ -493,7 +520,7 @@ impl UnitQuaternion { SB: Storage, SC: Storage, { - Self::new_observer_frame(&-dir, up).inverse() + Self::face_towards(&-dir, up).inverse() } /// Builds a left-handed look-at view matrix without translation. @@ -510,7 +537,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Vector3}; /// let dir = Vector3::new(1.0, 2.0, 3.0); @@ -525,7 +551,7 @@ impl UnitQuaternion { SB: Storage, SC: Storage, { - Self::new_observer_frame(dir, up).inverse() + Self::face_towards(dir, up).inverse() } /// Creates a new unit quaternion rotation from a rotation axis scaled by the rotation angle. @@ -535,7 +561,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; @@ -565,7 +590,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; @@ -596,7 +620,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; @@ -625,7 +648,6 @@ impl UnitQuaternion { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; @@ -687,7 +709,7 @@ where #[inline] fn arbitrary(g: &mut G) -> Self { let axisangle = Vector3::arbitrary(g); - UnitQuaternion::from_scaled_axis(axisangle) + Self::from_scaled_axis(axisangle) } } diff --git a/src/geometry/quaternion_conversion.rs b/src/geometry/quaternion_conversion.rs index c2d70726..a0b44390 100644 --- a/src/geometry/quaternion_conversion.rs +++ b/src/geometry/quaternion_conversion.rs @@ -103,7 +103,7 @@ impl SubsetOf> for UnitQuaternion where N1: Real, N2: Real + SupersetOf, - R: AlgaRotation> + SupersetOf>, + R: AlgaRotation> + SupersetOf, { #[inline] fn to_superset(&self) -> Isometry { @@ -125,7 +125,7 @@ impl SubsetOf> for UnitQuaternion where N1: Real, N2: Real + SupersetOf, - R: AlgaRotation> + SupersetOf>, + R: AlgaRotation> + SupersetOf, { #[inline] fn to_superset(&self) -> Similarity { @@ -186,7 +186,7 @@ impl> SubsetOf> for UnitQuaterni #[cfg(feature = "mint")] impl From> for Quaternion { fn from(q: mint::Quaternion) -> Self { - Quaternion::new(q.s, q.v.x, q.v.y, q.v.z) + Self::new(q.s, q.v.x, q.v.y, q.v.z) } } @@ -220,14 +220,28 @@ impl Into> for UnitQuaternion { impl From> for Matrix4 { #[inline] - fn from(q: UnitQuaternion) -> Matrix4 { + fn from(q: UnitQuaternion) -> Self { q.to_homogeneous() } } +impl From> for Rotation3 { + #[inline] + fn from(q: UnitQuaternion) -> Self { + q.to_rotation_matrix() + } +} + +impl From> for UnitQuaternion { + #[inline] + fn from(q: Rotation3) -> Self { + Self::from_rotation_matrix(&q) + } +} + impl From> for Matrix3 { #[inline] - fn from(q: UnitQuaternion) -> Matrix3 { + fn from(q: UnitQuaternion) -> Self { q.to_rotation_matrix().into_inner() } } @@ -235,6 +249,6 @@ impl From> for Matrix3 { impl From> for Quaternion { #[inline] fn from(coords: Vector4) -> Self { - Quaternion { coords } + Self { coords } } } diff --git a/src/geometry/quaternion_ops.rs b/src/geometry/quaternion_ops.rs index 842b4edd..2ed72453 100644 --- a/src/geometry/quaternion_ops.rs +++ b/src/geometry/quaternion_ops.rs @@ -67,7 +67,7 @@ impl Index for Quaternion { type Output = N; #[inline] - fn index(&self, i: usize) -> &N { + fn index(&self, i: usize) -> &Self::Output { &self.coords[i] } } diff --git a/src/geometry/reflection.rs b/src/geometry/reflection.rs index fa08dcdd..6b668c6f 100644 --- a/src/geometry/reflection.rs +++ b/src/geometry/reflection.rs @@ -18,8 +18,8 @@ impl> Reflection { /// /// The bias is the position of the plane on the axis. In particular, a bias equal to zero /// represents a plane that passes through the origin. - pub fn new(axis: Unit>, bias: N) -> Reflection { - Reflection { + pub fn new(axis: Unit>, bias: N) -> Self { + Self { axis: axis.into_inner(), bias: bias, } @@ -30,7 +30,7 @@ impl> Reflection { pub fn new_containing_point( axis: Unit>, pt: &Point, - ) -> Reflection + ) -> Self where D: DimName, DefaultAllocator: Allocator, diff --git a/src/geometry/rotation.rs b/src/geometry/rotation.rs index 299ddb1e..ca9d888f 100644 --- a/src/geometry/rotation.rs +++ b/src/geometry/rotation.rs @@ -53,7 +53,7 @@ where { #[inline] fn clone(&self) -> Self { - Rotation::from_matrix_unchecked(self.matrix.clone()) + Self::from_matrix_unchecked(self.matrix.clone()) } } @@ -100,7 +100,7 @@ where where Des: Deserializer<'a> { let matrix = MatrixN::::deserialize(deserializer)?; - Ok(Rotation::from_matrix_unchecked(matrix)) + Ok(Self::from_matrix_unchecked(matrix)) } } @@ -241,13 +241,13 @@ where DefaultAllocator: Allocator /// assert_eq!(*rot.matrix(), mat); /// ``` #[inline] - pub fn from_matrix_unchecked(matrix: MatrixN) -> Rotation { + pub fn from_matrix_unchecked(matrix: MatrixN) -> Self { assert!( matrix.is_square(), "Unable to create a rotation from a non-square matrix." ); - Rotation { matrix: matrix } + Self { matrix: matrix } } /// Transposes `self`. @@ -257,7 +257,6 @@ where DefaultAllocator: Allocator /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Rotation2, Rotation3, Vector3}; /// let rot = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); /// let tr_rot = rot.transpose(); @@ -270,8 +269,8 @@ where DefaultAllocator: Allocator /// assert_relative_eq!(tr_rot * rot, Rotation2::identity(), epsilon = 1.0e-6); /// ``` #[inline] - pub fn transpose(&self) -> Rotation { - Rotation::from_matrix_unchecked(self.matrix.transpose()) + pub fn transpose(&self) -> Self { + Self::from_matrix_unchecked(self.matrix.transpose()) } /// Inverts `self`. @@ -281,7 +280,6 @@ where DefaultAllocator: Allocator /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Rotation2, Rotation3, Vector3}; /// let rot = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); /// let inv = rot.inverse(); @@ -294,7 +292,7 @@ where DefaultAllocator: Allocator /// assert_relative_eq!(inv * rot, Rotation2::identity(), epsilon = 1.0e-6); /// ``` #[inline] - pub fn inverse(&self) -> Rotation { + pub fn inverse(&self) -> Self { self.transpose() } @@ -305,7 +303,6 @@ where DefaultAllocator: Allocator /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Rotation2, Rotation3, Vector3}; /// let rot = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); /// let mut tr_rot = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); @@ -333,7 +330,6 @@ where DefaultAllocator: Allocator /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Rotation2, Rotation3, Vector3}; /// let rot = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); /// let mut inv = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); @@ -361,7 +357,7 @@ impl PartialEq for Rotation where DefaultAllocator: Allocator { #[inline] - fn eq(&self, right: &Rotation) -> bool { + fn eq(&self, right: &Self) -> bool { self.matrix == right.matrix } } diff --git a/src/geometry/rotation_alga.rs b/src/geometry/rotation_alga.rs index b3bf7477..18c47b41 100644 --- a/src/geometry/rotation_alga.rs +++ b/src/geometry/rotation_alga.rs @@ -1,6 +1,6 @@ use alga::general::{ AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, - AbstractSemigroup, Id, Identity, Inverse, Multiplicative, Real, + AbstractSemigroup, Id, Identity, TwoSidedInverse, Multiplicative, Real, }; use alga::linear::{ self, AffineTransformation, DirectIsometry, Isometry, OrthogonalTransformation, @@ -27,16 +27,16 @@ where DefaultAllocator: Allocator } } -impl Inverse for Rotation +impl TwoSidedInverse for Rotation where DefaultAllocator: Allocator { #[inline] - fn inverse(&self) -> Self { + fn two_sided_inverse(&self) -> Self { self.transpose() } #[inline] - fn inverse_mut(&mut self) { + fn two_sided_inverse_mut(&mut self) { self.transpose_mut() } } diff --git a/src/geometry/rotation_conversion.rs b/src/geometry/rotation_conversion.rs index 3b574041..044d85af 100644 --- a/src/geometry/rotation_conversion.rs +++ b/src/geometry/rotation_conversion.rs @@ -102,7 +102,7 @@ impl SubsetOf> for Rotation where N1: Real, N2: Real + SupersetOf, - R: AlgaRotation> + SupersetOf>, + R: AlgaRotation> + SupersetOf, DefaultAllocator: Allocator + Allocator, { #[inline] @@ -125,7 +125,7 @@ impl SubsetOf> for Rotation where N1: Real, N2: Real + SupersetOf, - R: AlgaRotation> + SupersetOf>, + R: AlgaRotation> + SupersetOf, DefaultAllocator: Allocator + Allocator, { #[inline] @@ -219,28 +219,28 @@ impl From> for Rotation3 { impl From> for Matrix3 { #[inline] - fn from(q: Rotation2) -> Matrix3 { + fn from(q: Rotation2) ->Self { q.to_homogeneous() } } impl From> for Matrix2 { #[inline] - fn from(q: Rotation2) -> Matrix2 { + fn from(q: Rotation2) -> Self { q.into_inner() } } impl From> for Matrix4 { #[inline] - fn from(q: Rotation3) -> Matrix4 { + fn from(q: Rotation3) -> Self { q.to_homogeneous() } } impl From> for Matrix3 { #[inline] - fn from(q: Rotation3) -> Matrix3 { + fn from(q: Rotation3) -> Self { q.into_inner() } } diff --git a/src/geometry/rotation_specialization.rs b/src/geometry/rotation_specialization.rs index 57d097ce..0aeb3041 100644 --- a/src/geometry/rotation_specialization.rs +++ b/src/geometry/rotation_specialization.rs @@ -11,9 +11,9 @@ use std::ops::Neg; use base::dimension::{U1, U2, U3}; use base::storage::Storage; -use base::{MatrixN, Unit, Vector, Vector1, Vector3, VectorN}; +use base::{Matrix2, Matrix3, MatrixN, Unit, Vector, Vector1, Vector3, VectorN}; -use geometry::{Rotation2, Rotation3, UnitComplex}; +use geometry::{Rotation2, Rotation3, UnitComplex, UnitQuaternion}; /* * @@ -27,7 +27,6 @@ impl Rotation2 { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Rotation2, Point2}; /// let rot = Rotation2::new(f32::consts::FRAC_PI_2); @@ -36,7 +35,7 @@ impl Rotation2 { /// ``` pub fn new(angle: N) -> Self { let (sia, coa) = angle.sin_cos(); - Self::from_matrix_unchecked(MatrixN::::new(coa, -sia, sia, coa)) + Self::from_matrix_unchecked(Matrix2::new(coa, -sia, sia, coa)) } /// Builds a 2 dimensional rotation matrix from an angle in radian wrapped in a 1-dimensional vector. @@ -49,6 +48,51 @@ impl Rotation2 { Self::new(axisangle[0]) } + /// Builds a rotation matrix by extracting the rotation part of the given transformation `m`. + /// + /// This is an iterative method. See `.from_matrix_eps` to provide mover + /// convergence parameters and starting solution. + /// This implements "A Robust Method to Extract the Rotational Part of Deformations" by Müller et al. + pub fn from_matrix(m: &Matrix2) -> Self { + Self::from_matrix_eps(m, N::default_epsilon(), 0, Self::identity()) + } + + /// Builds a rotation matrix by extracting the rotation part of the given transformation `m`. + /// + /// This implements "A Robust Method to Extract the Rotational Part of Deformations" by Müller et al. + /// + /// # Parameters + /// + /// * `m`: the matrix from which the rotational part is to be extracted. + /// * `eps`: the angular errors tolerated between the current rotation and the optimal one. + /// * `max_iter`: the maximum number of iterations. Loops indefinitely until convergence if set to `0`. + /// * `guess`: an estimate of the solution. Convergence will be significantly faster if an initial solution close + /// to the actual solution is provided. Can be set to `Rotation2::identity()` if no other + /// guesses come to mind. + pub fn from_matrix_eps(m: &Matrix2, eps: N, mut max_iter: usize, guess: Self) -> Self { + if max_iter == 0 { + max_iter = usize::max_value(); + } + + let mut rot = guess.into_inner(); + + for _ in 0..max_iter { + let axis = rot.column(0).perp(&m.column(0)) + + rot.column(1).perp(&m.column(1)); + let denom = rot.column(0).dot(&m.column(0)) + + rot.column(1).dot(&m.column(1)); + + let angle = axis / (denom.abs() + N::default_epsilon()); + if angle.abs() > eps { + rot = Self::new(angle) * rot; + } else { + break; + } + } + + Self::from_matrix_unchecked(rot) + } + /// The rotation matrix required to align `a` and `b` but with its angle. /// /// This is the rotation `R` such that `(R * a).angle(b) == 0 && (R * a).dot(b).is_positive()`. @@ -56,7 +100,6 @@ impl Rotation2 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Vector2, Rotation2}; /// let a = Vector2::new(1.0, 2.0); /// let b = Vector2::new(2.0, 1.0); @@ -79,7 +122,6 @@ impl Rotation2 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Vector2, Rotation2}; /// let a = Vector2::new(1.0, 2.0); /// let b = Vector2::new(2.0, 1.0); @@ -100,15 +142,12 @@ impl Rotation2 { { ::convert(UnitComplex::scaled_rotation_between(a, b, s).to_rotation_matrix()) } -} -impl Rotation2 { /// The rotation angle. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Rotation2; /// let rot = Rotation2::new(1.78); /// assert_relative_eq!(rot.angle(), 1.78); @@ -123,14 +162,13 @@ impl Rotation2 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Rotation2; /// let rot1 = Rotation2::new(0.1); /// let rot2 = Rotation2::new(1.7); /// assert_relative_eq!(rot1.angle_to(&rot2), 1.6); /// ``` #[inline] - pub fn angle_to(&self, other: &Rotation2) -> N { + pub fn angle_to(&self, other: &Self) -> N { self.rotation_to(other).angle() } @@ -141,7 +179,6 @@ impl Rotation2 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Rotation2; /// let rot1 = Rotation2::new(0.1); /// let rot2 = Rotation2::new(1.7); @@ -151,24 +188,37 @@ impl Rotation2 { /// assert_relative_eq!(rot_to.inverse() * rot2, rot1); /// ``` #[inline] - pub fn rotation_to(&self, other: &Rotation2) -> Rotation2 { + pub fn rotation_to(&self, other: &Self) -> Self { other * self.inverse() } + + /* FIXME: requires alga v0.9 to be released so that Complex implements VectorSpace. + /// Ensure this rotation is an orthonormal rotation matrix. This is useful when repeated + /// computations might cause the matrix from progressively not being orthonormal anymore. + #[inline] + pub fn renormalize(&mut self) { + let mut c = UnitComplex::from(*self); + let _ = c.renormalize(); + + *self = Self::from_matrix_eps(self.matrix(), N::default_epsilon(), 0, c.into()) + } + */ + + /// Raise the quaternion to a given floating power, i.e., returns the rotation with the angle /// of `self` multiplied by `n`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Rotation2; /// let rot = Rotation2::new(0.78); /// let pow = rot.powf(2.0); /// assert_relative_eq!(pow.angle(), 2.0 * 0.78); /// ``` #[inline] - pub fn powf(&self, n: N) -> Rotation2 { + pub fn powf(&self, n: N) -> Self { Self::new(self.angle() * n) } @@ -217,7 +267,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Rotation3, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; @@ -238,6 +287,54 @@ impl Rotation3 { Self::from_axis_angle(&axis, angle) } + /// Builds a rotation matrix by extracting the rotation part of the given transformation `m`. + /// + /// This is an iterative method. See `.from_matrix_eps` to provide mover + /// convergence parameters and starting solution. + /// This implements "A Robust Method to Extract the Rotational Part of Deformations" by Müller et al. + pub fn from_matrix(m: &Matrix3) -> Self { + Self::from_matrix_eps(m, N::default_epsilon(), 0, Self::identity()) + } + + /// Builds a rotation matrix by extracting the rotation part of the given transformation `m`. + /// + /// This implements "A Robust Method to Extract the Rotational Part of Deformations" by Müller et al. + /// + /// # Parameters + /// + /// * `m`: the matrix from which the rotational part is to be extracted. + /// * `eps`: the angular errors tolerated between the current rotation and the optimal one. + /// * `max_iter`: the maximum number of iterations. Loops indefinitely until convergence if set to `0`. + /// * `guess`: a guess of the solution. Convergence will be significantly faster if an initial solution close + /// to the actual solution is provided. Can be set to `Rotation3::identity()` if no other + /// guesses come to mind. + pub fn from_matrix_eps(m: &Matrix3, eps: N, mut max_iter: usize, guess: Self) -> Self { + if max_iter == 0 { + max_iter = usize::max_value(); + } + + let mut rot = guess.into_inner(); + + for _ in 0..max_iter { + let axis = rot.column(0).cross(&m.column(0)) + + rot.column(1).cross(&m.column(1)) + + rot.column(2).cross(&m.column(2)); + let denom = rot.column(0).dot(&m.column(0)) + + rot.column(1).dot(&m.column(1)) + + rot.column(2).dot(&m.column(2)); + + let axisangle = axis / (denom.abs() + N::default_epsilon()); + + if let Some((axis, angle)) = Unit::try_new_and_get(axisangle, eps) { + rot = Rotation3::from_axis_angle(&axis, angle) * rot; + } else { + break; + } + } + + Self::from_matrix_unchecked(rot) + } + /// Builds a 3D rotation matrix from an axis scaled by the rotation angle. /// /// This is the same as `Self::new(axisangle)`. @@ -245,7 +342,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Rotation3, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; @@ -269,7 +365,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Rotation3, Point3, Vector3}; /// let axis = Vector3::y_axis(); @@ -322,7 +417,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Rotation3; /// let rot = Rotation3::from_euler_angles(0.1, 0.2, 0.3); /// let euler = rot.euler_angles(); @@ -363,7 +457,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::Rotation3; /// let rot = Rotation3::from_euler_angles(0.1, 0.2, 0.3); /// let euler = rot.euler_angles(); @@ -390,6 +483,16 @@ impl Rotation3 { } } + /// Ensure this rotation is an orthonormal rotation matrix. This is useful when repeated + /// computations might cause the matrix from progressively not being orthonormal anymore. + #[inline] + pub fn renormalize(&mut self) { + let mut c = UnitQuaternion::from(*self); + let _ = c.renormalize(); + + *self = Self::from_matrix_eps(self.matrix(), N::default_epsilon(), 0, c.into()) + } + /// Creates a rotation that corresponds to the local frame of an observer standing at the /// origin and looking toward `dir`. /// @@ -403,17 +506,16 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Rotation3, Vector3}; /// let dir = Vector3::new(1.0, 2.0, 3.0); /// let up = Vector3::y(); /// - /// let rot = Rotation3::new_observer_frame(&dir, &up); + /// let rot = Rotation3::face_towards(&dir, &up); /// assert_relative_eq!(rot * Vector3::z(), dir.normalize()); /// ``` #[inline] - pub fn new_observer_frame(dir: &Vector, up: &Vector) -> Self + pub fn face_towards(dir: &Vector, up: &Vector) -> Self where SB: Storage, SC: Storage, @@ -427,6 +529,16 @@ impl Rotation3 { )) } + /// Deprecated: Use [Rotation3::face_towards] instead. + #[deprecated(note="renamed to `face_towards`")] + pub fn new_observer_frames(dir: &Vector, up: &Vector) -> Self + where + SB: Storage, + SC: Storage, + { + Self::face_towards(dir, up) + } + /// Builds a right-handed look-at view matrix without translation. /// /// It maps the view direction `dir` to the **negative** `z` axis. @@ -441,7 +553,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Rotation3, Vector3}; /// let dir = Vector3::new(1.0, 2.0, 3.0); @@ -456,7 +567,7 @@ impl Rotation3 { SB: Storage, SC: Storage, { - Self::new_observer_frame(&dir.neg(), up).inverse() + Self::face_towards(&dir.neg(), up).inverse() } /// Builds a left-handed look-at view matrix without translation. @@ -473,7 +584,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Rotation3, Vector3}; /// let dir = Vector3::new(1.0, 2.0, 3.0); @@ -488,7 +598,7 @@ impl Rotation3 { SB: Storage, SC: Storage, { - Self::new_observer_frame(dir, up).inverse() + Self::face_towards(dir, up).inverse() } /// The rotation matrix required to align `a` and `b` but with its angle. @@ -498,7 +608,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Vector3, Rotation3}; /// let a = Vector3::new(1.0, 2.0, 3.0); /// let b = Vector3::new(3.0, 1.0, 2.0); @@ -521,7 +630,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Vector3, Rotation3}; /// let a = Vector3::new(1.0, 2.0, 3.0); /// let b = Vector3::new(3.0, 1.0, 2.0); @@ -566,7 +674,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Unit, Rotation3, Vector3}; /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let rot = Rotation3::from_axis_angle(&axis, 1.78); @@ -584,7 +691,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Rotation3, Vector3, Unit}; /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let angle = 1.2; @@ -611,7 +717,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Rotation3, Vector3, Unit}; /// let axisangle = Vector3::new(0.1, 0.2, 0.3); /// let rot = Rotation3::new(axisangle); @@ -633,7 +738,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Rotation3, Vector3, Unit}; /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let angle = 1.2; @@ -660,14 +764,13 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Rotation3, Vector3}; /// let rot1 = Rotation3::from_axis_angle(&Vector3::y_axis(), 1.0); /// let rot2 = Rotation3::from_axis_angle(&Vector3::x_axis(), 0.1); /// assert_relative_eq!(rot1.angle_to(&rot2), 1.0045657, epsilon = 1.0e-6); /// ``` #[inline] - pub fn angle_to(&self, other: &Rotation3) -> N { + pub fn angle_to(&self, other: &Self) -> N { self.rotation_to(other).angle() } @@ -678,7 +781,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Rotation3, Vector3}; /// let rot1 = Rotation3::from_axis_angle(&Vector3::y_axis(), 1.0); /// let rot2 = Rotation3::from_axis_angle(&Vector3::x_axis(), 0.1); @@ -686,7 +788,7 @@ impl Rotation3 { /// assert_relative_eq!(rot_to * rot1, rot2, epsilon = 1.0e-6); /// ``` #[inline] - pub fn rotation_to(&self, other: &Rotation3) -> Rotation3 { + pub fn rotation_to(&self, other: &Self) -> Self { other * self.inverse() } @@ -696,7 +798,6 @@ impl Rotation3 { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Rotation3, Vector3, Unit}; /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let angle = 1.2; @@ -706,7 +807,7 @@ impl Rotation3 { /// assert_eq!(pow.angle(), 2.4); /// ``` #[inline] - pub fn powf(&self, n: N) -> Rotation3 { + pub fn powf(&self, n: N) -> Self { if let Some(axis) = self.axis() { Self::from_axis_angle(&axis, self.angle() * n) } else if self.matrix()[(0, 0)] < N::zero() { diff --git a/src/geometry/similarity.rs b/src/geometry/similarity.rs index f321d575..1d2f50b4 100644 --- a/src/geometry/similarity.rs +++ b/src/geometry/similarity.rs @@ -106,20 +106,20 @@ where translation: Translation, rotation: R, scaling: N, - ) -> Similarity + ) -> Self { - Similarity::from_isometry(Isometry::from_parts(translation, rotation), scaling) + Self::from_isometry(Isometry::from_parts(translation, rotation), scaling) } /// Creates a new similarity from its rotational and translational parts. #[inline] - pub fn from_isometry(isometry: Isometry, scaling: N) -> Similarity { + pub fn from_isometry(isometry: Isometry, scaling: N) -> Self { assert!( !relative_eq!(scaling, N::zero()), "The scaling factor must not be zero." ); - Similarity { + Self { isometry: isometry, scaling: scaling, } @@ -127,13 +127,13 @@ where /// Creates a new similarity that applies only a scaling factor. #[inline] - pub fn from_scaling(scaling: N) -> Similarity { + pub fn from_scaling(scaling: N) -> Self { Self::from_isometry(Isometry::identity(), scaling) } /// Inverts `self`. #[inline] - pub fn inverse(&self) -> Similarity { + pub fn inverse(&self) -> Self { let mut res = self.clone(); res.inverse_mut(); res @@ -277,7 +277,7 @@ where DefaultAllocator: Allocator, { #[inline] - fn eq(&self, right: &Similarity) -> bool { + fn eq(&self, right: &Self) -> bool { self.isometry == right.isometry && self.scaling == right.scaling } } diff --git a/src/geometry/similarity_alga.rs b/src/geometry/similarity_alga.rs index c416cad8..e8a6b154 100644 --- a/src/geometry/similarity_alga.rs +++ b/src/geometry/similarity_alga.rs @@ -1,6 +1,6 @@ use alga::general::{ AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, - AbstractSemigroup, Identity, Inverse, Multiplicative, Real, + AbstractSemigroup, Identity, TwoSidedInverse, Multiplicative, Real, }; use alga::linear::Similarity as AlgaSimilarity; use alga::linear::{AffineTransformation, ProjectiveTransformation, Rotation, Transformation}; @@ -27,18 +27,18 @@ where } } -impl Inverse for Similarity +impl TwoSidedInverse for Similarity where R: Rotation>, DefaultAllocator: Allocator, { #[inline] - fn inverse(&self) -> Self { + fn two_sided_inverse(&self) -> Self { self.inverse() } #[inline] - fn inverse_mut(&mut self) { + fn two_sided_inverse_mut(&mut self) { self.inverse_mut() } } diff --git a/src/geometry/similarity_construction.rs b/src/geometry/similarity_construction.rs index ccb39866..47bb46c7 100644 --- a/src/geometry/similarity_construction.rs +++ b/src/geometry/similarity_construction.rs @@ -86,7 +86,6 @@ where /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Similarity2, Point2, UnitComplex}; /// let rot = UnitComplex::new(f32::consts::FRAC_PI_2); @@ -135,7 +134,6 @@ impl Similarity> { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{SimilarityMatrix2, Vector2, Point2}; /// let sim = SimilarityMatrix2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2, 3.0); @@ -159,7 +157,6 @@ impl Similarity> { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Similarity2, Vector2, Point2}; /// let sim = Similarity2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2, 3.0); @@ -187,7 +184,6 @@ macro_rules! similarity_construction_impl( /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Similarity3, SimilarityMatrix3, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; @@ -227,7 +223,6 @@ macro_rules! similarity_construction_impl( /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Similarity3, SimilarityMatrix3, Point3, Vector3}; /// let eye = Point3::new(1.0, 2.0, 3.0); @@ -235,22 +230,32 @@ macro_rules! similarity_construction_impl( /// let up = Vector3::y(); /// /// // Similarity with its rotation part represented as a UnitQuaternion - /// let sim = Similarity3::new_observer_frame(&eye, &target, &up, 3.0); + /// let sim = Similarity3::face_towards(&eye, &target, &up, 3.0); /// assert_eq!(sim * Point3::origin(), eye); /// assert_relative_eq!(sim * Vector3::z(), Vector3::x() * 3.0, epsilon = 1.0e-6); /// /// // Similarity with its rotation part represented as Rotation3 (a 3x3 rotation matrix). - /// let sim = SimilarityMatrix3::new_observer_frame(&eye, &target, &up, 3.0); + /// let sim = SimilarityMatrix3::face_towards(&eye, &target, &up, 3.0); /// assert_eq!(sim * Point3::origin(), eye); /// assert_relative_eq!(sim * Vector3::z(), Vector3::x() * 3.0, epsilon = 1.0e-6); /// ``` #[inline] - pub fn new_observer_frame(eye: &Point3, + pub fn face_towards(eye: &Point3, target: &Point3, up: &Vector3, scaling: N) -> Self { - Self::from_isometry(Isometry::<_, U3, $Rot>::new_observer_frame(eye, target, up), scaling) + Self::from_isometry(Isometry::<_, U3, $Rot>::face_towards(eye, target, up), scaling) + } + + /// Deprecated: Use [SimilarityMatrix3::face_towards] instead. + #[deprecated(note="renamed to `face_towards`")] + pub fn new_observer_frames(eye: &Point3, + target: &Point3, + up: &Vector3, + scaling: N) + -> Self { + Self::face_towards(eye, target, up, scaling) } /// Builds a right-handed look-at view matrix including scaling factor. @@ -268,7 +273,6 @@ macro_rules! similarity_construction_impl( /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Similarity3, SimilarityMatrix3, Point3, Vector3}; /// let eye = Point3::new(1.0, 2.0, 3.0); @@ -307,7 +311,6 @@ macro_rules! similarity_construction_impl( /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{Similarity3, SimilarityMatrix3, Point3, Vector3}; /// let eye = Point3::new(1.0, 2.0, 3.0); diff --git a/src/geometry/similarity_ops.rs b/src/geometry/similarity_ops.rs index 57fdc05f..081e5133 100644 --- a/src/geometry/similarity_ops.rs +++ b/src/geometry/similarity_ops.rs @@ -222,8 +222,8 @@ similarity_binop_assign_impl_all!( DivAssign, div_assign; self: Similarity, rhs: R; // FIXME: don't invert explicitly? - [val] => *self *= rhs.inverse(); - [ref] => *self *= rhs.inverse(); + [val] => *self *= rhs.two_sided_inverse(); + [ref] => *self *= rhs.two_sided_inverse(); ); // Similarity × R diff --git a/src/geometry/transform.rs b/src/geometry/transform.rs index 08248a46..0d5d9c4a 100644 --- a/src/geometry/transform.rs +++ b/src/geometry/transform.rs @@ -350,7 +350,6 @@ where DefaultAllocator: Allocator, DimNameSum> /// # Examples /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Matrix3, Transform2}; /// /// let m = Matrix3::new(2.0, 2.0, -0.3, @@ -383,7 +382,6 @@ where DefaultAllocator: Allocator, DimNameSum> /// # Examples /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Matrix3, Projective2}; /// /// let m = Matrix3::new(2.0, 2.0, -0.3, @@ -407,7 +405,6 @@ where DefaultAllocator: Allocator, DimNameSum> /// # Examples /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Matrix3, Transform2}; /// /// let m = Matrix3::new(2.0, 2.0, -0.3, @@ -437,7 +434,6 @@ where DefaultAllocator: Allocator, DimNameSum> /// # Examples /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Matrix3, Projective2}; /// /// let m = Matrix3::new(2.0, 2.0, -0.3, diff --git a/src/geometry/transform_alga.rs b/src/geometry/transform_alga.rs index 652da373..c5ba675b 100644 --- a/src/geometry/transform_alga.rs +++ b/src/geometry/transform_alga.rs @@ -1,6 +1,6 @@ use alga::general::{ AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, - AbstractSemigroup, Identity, Inverse, Multiplicative, Real, + AbstractSemigroup, Identity, TwoSidedInverse, Multiplicative, Real, }; use alga::linear::{ProjectiveTransformation, Transformation}; @@ -26,18 +26,18 @@ where } } -impl, C> Inverse for Transform +impl, C> TwoSidedInverse for Transform where C: SubTCategoryOf, DefaultAllocator: Allocator, DimNameSum>, { #[inline] - fn inverse(&self) -> Self { + fn two_sided_inverse(&self) -> Self { self.clone().inverse() } #[inline] - fn inverse_mut(&mut self) { + fn two_sided_inverse_mut(&mut self) { self.inverse_mut() } } @@ -116,12 +116,12 @@ where { #[inline] fn inverse_transform_point(&self, pt: &Point) -> Point { - self.inverse() * pt + self.two_sided_inverse() * pt } #[inline] fn inverse_transform_vector(&self, v: &VectorN) -> VectorN { - self.inverse() * v + self.two_sided_inverse() * v } } diff --git a/src/geometry/transform_conversion.rs b/src/geometry/transform_conversion.rs index 0ab96221..ab5ad5df 100644 --- a/src/geometry/transform_conversion.rs +++ b/src/geometry/transform_conversion.rs @@ -57,7 +57,7 @@ where #[inline] unsafe fn from_superset_unchecked(m: &MatrixN>) -> Self { - Transform::from_matrix_unchecked(::convert_ref_unchecked(m)) + Self::from_matrix_unchecked(::convert_ref_unchecked(m)) } } diff --git a/src/geometry/translation_alga.rs b/src/geometry/translation_alga.rs index 24aa28d2..fdd24014 100644 --- a/src/geometry/translation_alga.rs +++ b/src/geometry/translation_alga.rs @@ -1,6 +1,6 @@ use alga::general::{ AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, - AbstractSemigroup, Id, Identity, Inverse, Multiplicative, Real, + AbstractSemigroup, Id, Identity, TwoSidedInverse, Multiplicative, Real, }; use alga::linear::Translation as AlgaTranslation; use alga::linear::{ @@ -28,16 +28,16 @@ where DefaultAllocator: Allocator } } -impl Inverse for Translation +impl TwoSidedInverse for Translation where DefaultAllocator: Allocator { #[inline] - fn inverse(&self) -> Self { + fn two_sided_inverse(&self) -> Self { self.inverse() } #[inline] - fn inverse_mut(&mut self) { + fn two_sided_inverse_mut(&mut self) { self.inverse_mut() } } diff --git a/src/geometry/translation_coordinates.rs b/src/geometry/translation_coordinates.rs new file mode 100644 index 00000000..01207a32 --- /dev/null +++ b/src/geometry/translation_coordinates.rs @@ -0,0 +1,44 @@ +use std::mem; +use std::ops::{Deref, DerefMut}; + +use base::allocator::Allocator; +use base::coordinates::{X, XY, XYZ, XYZW, XYZWA, XYZWAB}; +use base::dimension::{U1, U2, U3, U4, U5, U6}; +use base::{DefaultAllocator, Scalar}; + +use geometry::Translation; + +/* + * + * Give coordinates to Translation{1 .. 6} + * + */ + +macro_rules! deref_impl( + ($D: ty, $Target: ident $(, $comps: ident)*) => { + impl Deref for Translation + where DefaultAllocator: Allocator { + type Target = $Target; + + #[inline] + fn deref(&self) -> &Self::Target { + unsafe { mem::transmute(self) } + } + } + + impl DerefMut for Translation + where DefaultAllocator: Allocator { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { mem::transmute(self) } + } + } + } +); + +deref_impl!(U1, X, x); +deref_impl!(U2, XY, x, y); +deref_impl!(U3, XYZ, x, y, z); +deref_impl!(U4, XYZW, x, y, z, w); +deref_impl!(U5, XYZWA, x, y, z, w, a); +deref_impl!(U6, XYZWAB, x, y, z, w, a, b); diff --git a/src/geometry/unit_complex.rs b/src/geometry/unit_complex.rs index 62280056..fe1058af 100644 --- a/src/geometry/unit_complex.rs +++ b/src/geometry/unit_complex.rs @@ -85,7 +85,6 @@ impl UnitComplex { /// # Example /// ``` /// # extern crate num_complex; - /// # extern crate nalgebra; /// # use num_complex::Complex; /// # use nalgebra::UnitComplex; /// let angle = 1.78f32; @@ -109,7 +108,7 @@ impl UnitComplex { /// ``` #[inline] pub fn conjugate(&self) -> Self { - UnitComplex::new_unchecked(self.conj()) + Self::new_unchecked(self.conj()) } /// Inverts this complex number if it is not zero. @@ -117,7 +116,6 @@ impl UnitComplex { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::UnitComplex; /// let rot = UnitComplex::new(1.2); /// let inv = rot.inverse(); @@ -134,7 +132,6 @@ impl UnitComplex { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::UnitComplex; /// let rot1 = UnitComplex::new(0.1); /// let rot2 = UnitComplex::new(1.7); @@ -153,7 +150,6 @@ impl UnitComplex { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::UnitComplex; /// let rot1 = UnitComplex::new(0.1); /// let rot2 = UnitComplex::new(1.7); @@ -172,7 +168,6 @@ impl UnitComplex { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::UnitComplex; /// let angle = 1.7; /// let rot = UnitComplex::new(angle); @@ -192,7 +187,6 @@ impl UnitComplex { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::UnitComplex; /// let angle = 1.7; /// let mut rot = UnitComplex::new(angle); @@ -213,7 +207,6 @@ impl UnitComplex { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::UnitComplex; /// let rot = UnitComplex::new(0.78); /// let pow = rot.powf(2.0); @@ -310,18 +303,4 @@ impl UlpsEq for UnitComplex { self.re.ulps_eq(&other.re, epsilon, max_ulps) && self.im.ulps_eq(&other.im, epsilon, max_ulps) } -} - -impl From> for Matrix3 { - #[inline] - fn from(q: UnitComplex) -> Matrix3 { - q.to_homogeneous() - } -} - -impl From> for Matrix2 { - #[inline] - fn from(q: UnitComplex) -> Matrix2 { - q.to_rotation_matrix().into_inner() - } -} +} \ No newline at end of file diff --git a/src/geometry/unit_complex_alga.rs b/src/geometry/unit_complex_alga.rs index 59b11903..21b956d9 100644 --- a/src/geometry/unit_complex_alga.rs +++ b/src/geometry/unit_complex_alga.rs @@ -1,6 +1,6 @@ use alga::general::{ AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, - AbstractSemigroup, Id, Identity, Inverse, Multiplicative, Real, + AbstractSemigroup, Id, Identity, TwoSidedInverse, Multiplicative, Real, }; use alga::linear::{ AffineTransformation, DirectIsometry, Isometry, OrthogonalTransformation, @@ -31,14 +31,14 @@ impl AbstractMagma for UnitComplex { } } -impl Inverse for UnitComplex { +impl TwoSidedInverse for UnitComplex { #[inline] - fn inverse(&self) -> Self { + fn two_sided_inverse(&self) -> Self { self.inverse() } #[inline] - fn inverse_mut(&mut self) { + fn two_sided_inverse_mut(&mut self) { self.inverse_mut() } } diff --git a/src/geometry/unit_complex_construction.rs b/src/geometry/unit_complex_construction.rs index fa4a0451..ba1d6694 100644 --- a/src/geometry/unit_complex_construction.rs +++ b/src/geometry/unit_complex_construction.rs @@ -9,7 +9,7 @@ use rand::Rng; use alga::general::Real; use base::dimension::{U1, U2}; use base::storage::Storage; -use base::{Unit, Vector}; +use base::{Unit, Vector, Matrix2}; use geometry::{Rotation2, UnitComplex}; impl UnitComplex { @@ -35,7 +35,6 @@ impl UnitComplex { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitComplex, Point2}; /// let rot = UnitComplex::new(f32::consts::FRAC_PI_2); @@ -56,7 +55,6 @@ impl UnitComplex { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitComplex, Point2}; /// let rot = UnitComplex::from_angle(f32::consts::FRAC_PI_2); @@ -78,7 +76,6 @@ impl UnitComplex { /// /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use std::f32; /// # use nalgebra::{UnitComplex, Vector2, Point2}; /// let angle = f32::consts::FRAC_PI_2; @@ -88,7 +85,7 @@ impl UnitComplex { /// ``` #[inline] pub fn from_cos_sin_unchecked(cos: N, sin: N) -> Self { - UnitComplex::new_unchecked(Complex::new(cos, sin)) + Self::new_unchecked(Complex::new(cos, sin)) } /// Builds a unit complex rotation from an angle in radian wrapped in a 1-dimensional vector. @@ -132,13 +129,38 @@ impl UnitComplex { Self::new_unchecked(Complex::new(rotmat[(0, 0)], rotmat[(1, 0)])) } + /// Builds an unit complex by extracting the rotation part of the given transformation `m`. + /// + /// This is an iterative method. See `.from_matrix_eps` to provide mover + /// convergence parameters and starting solution. + /// This implements "A Robust Method to Extract the Rotational Part of Deformations" by Müller et al. + pub fn from_matrix(m: &Matrix2) -> Self { + Rotation2::from_matrix(m).into() + } + + /// Builds an unit complex by extracting the rotation part of the given transformation `m`. + /// + /// This implements "A Robust Method to Extract the Rotational Part of Deformations" by Müller et al. + /// + /// # Parameters + /// + /// * `m`: the matrix from which the rotational part is to be extracted. + /// * `eps`: the angular errors tolerated between the current rotation and the optimal one. + /// * `max_iter`: the maximum number of iterations. Loops indefinitely until convergence if set to `0`. + /// * `guess`: an estimate of the solution. Convergence will be significantly faster if an initial solution close + /// to the actual solution is provided. Can be set to `UnitQuaternion::identity()` if no other + /// guesses come to mind. + pub fn from_matrix_eps(m: &Matrix2, eps: N, max_iter: usize, guess: Self) -> Self { + let guess = Rotation2::from(guess); + Rotation2::from_matrix_eps(m, eps, max_iter, guess).into() + } + /// The unit complex needed to make `a` and `b` be collinear and point toward the same /// direction. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Vector2, UnitComplex}; /// let a = Vector2::new(1.0, 2.0); /// let b = Vector2::new(2.0, 1.0); @@ -161,7 +183,6 @@ impl UnitComplex { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Vector2, UnitComplex}; /// let a = Vector2::new(1.0, 2.0); /// let b = Vector2::new(2.0, 1.0); @@ -197,7 +218,6 @@ impl UnitComplex { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Unit, Vector2, UnitComplex}; /// let a = Unit::new_normalize(Vector2::new(1.0, 2.0)); /// let b = Unit::new_normalize(Vector2::new(2.0, 1.0)); @@ -223,7 +243,6 @@ impl UnitComplex { /// # Example /// ``` /// # #[macro_use] extern crate approx; - /// # extern crate nalgebra; /// # use nalgebra::{Unit, Vector2, UnitComplex}; /// let a = Unit::new_normalize(Vector2::new(1.0, 2.0)); /// let b = Unit::new_normalize(Vector2::new(2.0, 1.0)); diff --git a/src/geometry/unit_complex_conversion.rs b/src/geometry/unit_complex_conversion.rs index ee0ed5d8..1751a67c 100644 --- a/src/geometry/unit_complex_conversion.rs +++ b/src/geometry/unit_complex_conversion.rs @@ -5,10 +5,10 @@ use alga::general::{Real, SubsetOf, SupersetOf}; use alga::linear::Rotation as AlgaRotation; use base::dimension::U2; -use base::Matrix3; +use base::{Matrix2, Matrix3}; use geometry::{ Isometry, Point2, Rotation2, Similarity, SuperTCategoryOf, TAffine, Transform, Translation, - UnitComplex, + UnitComplex }; /* @@ -74,7 +74,7 @@ impl SubsetOf> for UnitComplex where N1: Real, N2: Real + SupersetOf, - R: AlgaRotation> + SupersetOf>, + R: AlgaRotation> + SupersetOf, { #[inline] fn to_superset(&self) -> Isometry { @@ -96,7 +96,7 @@ impl SubsetOf> for UnitComplex where N1: Real, N2: Real + SupersetOf, - R: AlgaRotation> + SupersetOf>, + R: AlgaRotation> + SupersetOf, { #[inline] fn to_superset(&self) -> Similarity { @@ -153,3 +153,32 @@ impl> SubsetOf> for UnitComplex< Self::from_rotation_matrix(&rot) } } + + +impl From> for Rotation2 { + #[inline] + fn from(q: UnitComplex) -> Self { + q.to_rotation_matrix() + } +} + +impl From> for UnitComplex { + #[inline] + fn from(q: Rotation2) -> Self { + Self::from_rotation_matrix(&q) + } +} + +impl From> for Matrix3 { + #[inline] + fn from(q: UnitComplex) -> Matrix3 { + q.to_homogeneous() + } +} + +impl From> for Matrix2 { + #[inline] + fn from(q: UnitComplex) -> Self { + q.to_rotation_matrix().into_inner() + } +} diff --git a/src/geometry/unit_complex_ops.rs b/src/geometry/unit_complex_ops.rs index c9d31ad4..75b45eda 100644 --- a/src/geometry/unit_complex_ops.rs +++ b/src/geometry/unit_complex_ops.rs @@ -45,11 +45,11 @@ use geometry::{Isometry, Point2, Rotation, Similarity, Translation, UnitComplex} */ // UnitComplex × UnitComplex -impl Mul> for UnitComplex { - type Output = UnitComplex; +impl Mul for UnitComplex { + type Output = Self; #[inline] - fn mul(self, rhs: UnitComplex) -> UnitComplex { + fn mul(self, rhs: Self) -> Self { Unit::new_unchecked(self.into_inner() * rhs.into_inner()) } } @@ -58,16 +58,16 @@ impl<'a, N: Real> Mul> for &'a UnitComplex { type Output = UnitComplex; #[inline] - fn mul(self, rhs: UnitComplex) -> UnitComplex { + fn mul(self, rhs: UnitComplex) -> Self::Output { Unit::new_unchecked(self.complex() * rhs.into_inner()) } } impl<'b, N: Real> Mul<&'b UnitComplex> for UnitComplex { - type Output = UnitComplex; + type Output = Self; #[inline] - fn mul(self, rhs: &'b UnitComplex) -> UnitComplex { + fn mul(self, rhs: &'b UnitComplex) -> Self::Output { Unit::new_unchecked(self.into_inner() * rhs.complex()) } } @@ -76,17 +76,17 @@ impl<'a, 'b, N: Real> Mul<&'b UnitComplex> for &'a UnitComplex { type Output = UnitComplex; #[inline] - fn mul(self, rhs: &'b UnitComplex) -> UnitComplex { + fn mul(self, rhs: &'b UnitComplex) -> Self::Output { Unit::new_unchecked(self.complex() * rhs.complex()) } } // UnitComplex ÷ UnitComplex -impl Div> for UnitComplex { - type Output = UnitComplex; +impl Div for UnitComplex { + type Output = Self; #[inline] - fn div(self, rhs: UnitComplex) -> UnitComplex { + fn div(self, rhs: Self) -> Self::Output { Unit::new_unchecked(self.into_inner() * rhs.conjugate().into_inner()) } } @@ -95,16 +95,16 @@ impl<'a, N: Real> Div> for &'a UnitComplex { type Output = UnitComplex; #[inline] - fn div(self, rhs: UnitComplex) -> UnitComplex { + fn div(self, rhs: UnitComplex) -> Self::Output { Unit::new_unchecked(self.complex() * rhs.conjugate().into_inner()) } } impl<'b, N: Real> Div<&'b UnitComplex> for UnitComplex { - type Output = UnitComplex; + type Output = Self; #[inline] - fn div(self, rhs: &'b UnitComplex) -> UnitComplex { + fn div(self, rhs: &'b UnitComplex) -> Self::Output { Unit::new_unchecked(self.into_inner() * rhs.conjugate().into_inner()) } } @@ -113,7 +113,7 @@ impl<'a, 'b, N: Real> Div<&'b UnitComplex> for &'a UnitComplex { type Output = UnitComplex; #[inline] - fn div(self, rhs: &'b UnitComplex) -> UnitComplex { + fn div(self, rhs: &'b UnitComplex) -> Self::Output { Unit::new_unchecked(self.complex() * rhs.conjugate().into_inner()) } } diff --git a/src/io/matrix_market.pest b/src/io/matrix_market.pest new file mode 100644 index 00000000..e024ec57 --- /dev/null +++ b/src/io/matrix_market.pest @@ -0,0 +1,16 @@ +WHITESPACE = _{ " " } + +Comments = _{ "%" ~ (!NEWLINE ~ ANY)* } +Header = { "%%" ~ (!NEWLINE ~ ANY)* } +Shape = { Dimension ~ Dimension ~ Dimension } +Document = { + SOI ~ + NEWLINE* ~ + Header ~ + (NEWLINE ~ Comments)* ~ + (NEWLINE ~ Shape) ~ + (NEWLINE ~ Entry?)* +} +Dimension = @{ ASCII_DIGIT+ } +Value = @{ ("+" | "-")? ~ NUMBER+ ~ ("." ~ NUMBER+)? ~ ("e" ~ ("+" | "-")? ~ NUMBER+)? } +Entry = { Dimension ~ Dimension ~ Value } \ No newline at end of file diff --git a/src/io/matrix_market.rs b/src/io/matrix_market.rs new file mode 100644 index 00000000..f4919cd4 --- /dev/null +++ b/src/io/matrix_market.rs @@ -0,0 +1,53 @@ +use std::fs; +use std::path::Path; + +use pest::Parser; +use sparse::CsMatrix; +use Real; + +#[derive(Parser)] +#[grammar = "io/matrix_market.pest"] +struct MatrixMarketParser; + +// FIXME: return an Error instead of an Option. +/// Parses a Matrix Market file at the given path, and returns the corresponding sparse matrix. +pub fn cs_matrix_from_matrix_market>(path: P) -> Option> { + let file = fs::read_to_string(path).ok()?; + cs_matrix_from_matrix_market_str(&file) +} + +// FIXME: return an Error instead of an Option. +/// Parses a Matrix Market file described by the given string, and returns the corresponding sparse matrix. +pub fn cs_matrix_from_matrix_market_str(data: &str) -> Option> { + let file = MatrixMarketParser::parse(Rule::Document, data) + .unwrap() + .next()?; + let mut shape = (0, 0, 0); + let mut rows: Vec = Vec::new(); + let mut cols: Vec = Vec::new(); + let mut data: Vec = Vec::new(); + + for line in file.into_inner() { + match line.as_rule() { + Rule::Header => {} + Rule::Shape => { + let mut inner = line.into_inner(); + shape.0 = inner.next()?.as_str().parse::().ok()?; + shape.1 = inner.next()?.as_str().parse::().ok()?; + shape.2 = inner.next()?.as_str().parse::().ok()?; + } + Rule::Entry => { + let mut inner = line.into_inner(); + // NOTE: indices are 1-based. + rows.push(inner.next()?.as_str().parse::().ok()? - 1); + cols.push(inner.next()?.as_str().parse::().ok()? - 1); + data.push(::convert(inner.next()?.as_str().parse::().ok()?)); + } + _ => return None, // FIXME: return an Err instead. + } + } + + Some(CsMatrix::from_triplet( + shape.0, shape.1, &rows, &cols, &data, + )) +} diff --git a/src/io/mod.rs b/src/io/mod.rs new file mode 100644 index 00000000..1b172b20 --- /dev/null +++ b/src/io/mod.rs @@ -0,0 +1,5 @@ +//! Parsers for various matrix formats. + +pub use self::matrix_market::{cs_matrix_from_matrix_market, cs_matrix_from_matrix_market_str}; + +mod matrix_market; diff --git a/src/lib.rs b/src/lib.rs index 1edaa167..80149c7c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,7 +15,7 @@ Simply add the following to your `Cargo.toml` file: ```.ignore [dependencies] -nalgebra = "0.16" +nalgebra = "0.17" ``` @@ -83,8 +83,10 @@ an optimized set of tools for computer graphics and physics. Those features incl #![deny(unused_results)] #![deny(missing_docs)] #![warn(incoherent_fundamental_impls)] -#![doc(html_favicon_url = "http://nalgebra.org/img/favicon.ico", - html_root_url = "http://nalgebra.org/rustdoc")] +#![doc( + html_favicon_url = "http://nalgebra.org/img/favicon.ico", + html_root_url = "http://nalgebra.org/rustdoc" +)] #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(all(feature = "alloc", not(feature = "std")), feature(alloc))] @@ -121,11 +123,21 @@ extern crate alloc; #[cfg(not(feature = "std"))] extern crate core as std; +#[cfg(feature = "io")] +extern crate pest; +#[macro_use] +#[cfg(feature = "io")] +extern crate pest_derive; + pub mod base; #[cfg(feature = "debug")] pub mod debug; pub mod geometry; +#[cfg(feature = "io")] +pub mod io; pub mod linalg; +#[cfg(feature = "sparse")] +pub mod sparse; #[cfg(feature = "std")] #[deprecated( @@ -135,11 +147,13 @@ pub use base as core; pub use base::*; pub use geometry::*; pub use linalg::*; +#[cfg(feature = "sparse")] +pub use sparse::*; use std::cmp::{self, Ordering, PartialOrd}; use alga::general::{ - Additive, AdditiveGroup, Identity, Inverse, JoinSemilattice, Lattice, MeetSemilattice, + Additive, AdditiveGroup, Identity, TwoSidedInverse, JoinSemilattice, Lattice, MeetSemilattice, Multiplicative, SupersetOf, }; use alga::linear::SquareMatrix as AlgaSquareMatrix; @@ -413,8 +427,8 @@ pub fn try_inverse(m: &M) -> Option { /// /// * [`try_inverse`](fn.try_inverse.html) #[inline] -pub fn inverse>(m: &M) -> M { - m.inverse() +pub fn inverse>(m: &M) -> M { + m.two_sided_inverse() } /* diff --git a/src/linalg/full_piv_lu.rs b/src/linalg/full_piv_lu.rs index 022152f4..962d4d8b 100644 --- a/src/linalg/full_piv_lu.rs +++ b/src/linalg/full_piv_lu.rs @@ -61,7 +61,7 @@ where DefaultAllocator: Allocator + Allocator<(usize, usize), DimMinimu let mut q = PermutationSequence::identity_generic(min_nrows_ncols); if min_nrows_ncols.value() == 0 { - return FullPivLU { + return Self { lu: matrix, p: p, q: q, @@ -91,7 +91,7 @@ where DefaultAllocator: Allocator + Allocator<(usize, usize), DimMinimu } } - FullPivLU { + Self { lu: matrix, p: p, q: q, diff --git a/src/linalg/permutation_sequence.rs b/src/linalg/permutation_sequence.rs index ac58d857..3aef1be6 100644 --- a/src/linalg/permutation_sequence.rs +++ b/src/linalg/permutation_sequence.rs @@ -69,7 +69,7 @@ where DefaultAllocator: Allocator<(usize, usize), D> #[inline] pub fn identity_generic(dim: D) -> Self { unsafe { - PermutationSequence { + Self { len: 0, ipiv: VectorN::new_uninitialized_generic(dim, U1), } diff --git a/src/linalg/schur.rs b/src/linalg/schur.rs index 66299c07..0d425087 100644 --- a/src/linalg/schur.rs +++ b/src/linalg/schur.rs @@ -55,7 +55,7 @@ where + Allocator, { /// Computes the Schur decomposition of a square matrix. - pub fn new(m: MatrixN) -> RealSchur { + pub fn new(m: MatrixN) -> Self { Self::try_new(m, N::default_epsilon(), 0).unwrap() } @@ -70,7 +70,7 @@ where /// * `max_niter` − maximum total number of iterations performed by the algorithm. If this /// number of iteration is exceeded, `None` is returned. If `niter == 0`, then the algorithm /// continues indefinitely until convergence. - pub fn try_new(m: MatrixN, eps: N, max_niter: usize) -> Option> { + pub fn try_new(m: MatrixN, eps: N, max_niter: usize) -> Option { let mut work = unsafe { VectorN::new_uninitialized_generic(m.data.shape().0, U1) }; Self::do_decompose(m, &mut work, eps, max_niter, true).map(|(q, t)| RealSchur { diff --git a/src/linalg/svd.rs b/src/linalg/svd.rs index 5c1d9a0f..67b49604 100644 --- a/src/linalg/svd.rs +++ b/src/linalg/svd.rs @@ -271,7 +271,7 @@ where } } - Some(SVD { + Some(Self { u: u, v_t: v_t, singular_values: b.diagonal, diff --git a/src/linalg/symmetric_tridiagonal.rs b/src/linalg/symmetric_tridiagonal.rs index 5ac75b47..2e4108ae 100644 --- a/src/linalg/symmetric_tridiagonal.rs +++ b/src/linalg/symmetric_tridiagonal.rs @@ -83,7 +83,7 @@ where DefaultAllocator: Allocator + Allocator> } } - SymmetricTridiagonal { + Self { tri: m, off_diagonal: off_diagonal, } diff --git a/src/sparse/cs_matrix.rs b/src/sparse/cs_matrix.rs new file mode 100644 index 00000000..4c2983eb --- /dev/null +++ b/src/sparse/cs_matrix.rs @@ -0,0 +1,517 @@ +use alga::general::ClosedAdd; +use num::Zero; +use std::iter; +use std::marker::PhantomData; +use std::ops::Range; +use std::slice; + +use allocator::Allocator; +use sparse::cs_utils; +use { + DefaultAllocator, Dim, Dynamic, Scalar, Vector, VectorN, U1 +}; + +pub struct ColumnEntries<'a, N> { + curr: usize, + i: &'a [usize], + v: &'a [N], +} + +impl<'a, N> ColumnEntries<'a, N> { + #[inline] + pub fn new(i: &'a [usize], v: &'a [N]) -> Self { + assert_eq!(i.len(), v.len()); + Self { curr: 0, i, v } + } +} + +impl<'a, N: Copy> Iterator for ColumnEntries<'a, N> { + type Item = (usize, N); + + #[inline] + fn next(&mut self) -> Option { + if self.curr >= self.i.len() { + None + } else { + let res = Some((unsafe { *self.i.get_unchecked(self.curr) }, unsafe { + *self.v.get_unchecked(self.curr) + })); + self.curr += 1; + res + } + } +} + +// FIXME: this structure exists for now only because impl trait +// cannot be used for trait method return types. +/// Trait for iterable compressed-column matrix storage. +pub trait CsStorageIter<'a, N, R, C = U1> { + /// Iterator through all the rows of a specific columns. + /// + /// The elements are given as a tuple (row_index, value). + type ColumnEntries: Iterator; + /// Iterator through the row indices of a specific column. + type ColumnRowIndices: Iterator; + + /// Iterates through all the row indices of the j-th column. + fn column_row_indices(&'a self, j: usize) -> Self::ColumnRowIndices; + #[inline(always)] + /// Iterates through all the entries of the j-th column. + fn column_entries(&'a self, j: usize) -> Self::ColumnEntries; +} + +/// Trait for mutably iterable compressed-column sparse matrix storage. +pub trait CsStorageIterMut<'a, N: 'a, R, C = U1> { + /// Mutable iterator through all the values of the sparse matrix. + type ValuesMut: Iterator; + /// Mutable iterator through all the rows of a specific columns. + /// + /// The elements are given as a tuple (row_index, value). + type ColumnEntriesMut: Iterator; + + /// A mutable iterator through the values buffer of the sparse matrix. + fn values_mut(&'a mut self) -> Self::ValuesMut; + /// Iterates mutably through all the entries of the j-th column. + fn column_entries_mut(&'a mut self, j: usize) -> Self::ColumnEntriesMut; +} + +/// Trait for compressed column sparse matrix storage. +pub trait CsStorage: for<'a> CsStorageIter<'a, N, R, C> { + /// The shape of the stored matrix. + fn shape(&self) -> (R, C); + /// Retrieve the i-th row index of the underlying row index buffer. + /// + /// No bound-checking is performed. + unsafe fn row_index_unchecked(&self, i: usize) -> usize; + /// The i-th value on the contiguous value buffer of this storage. + /// + /// No bound-checking is performed. + unsafe fn get_value_unchecked(&self, i: usize) -> &N; + /// The i-th value on the contiguous value buffer of this storage. + fn get_value(&self, i: usize) -> &N; + /// Retrieve the i-th row index of the underlying row index buffer. + fn row_index(&self, i: usize) -> usize; + /// The value indices for the `i`-th column. + fn column_range(&self, i: usize) -> Range; + /// The size of the value buffer (i.e. the entries known as possibly being non-zero). + fn len(&self) -> usize; +} + +/// Trait for compressed column sparse matrix mutable storage. +pub trait CsStorageMut: + CsStorage + for<'a> CsStorageIterMut<'a, N, R, C> +{ +} + +/// A storage of column-compressed sparse matrix based on a Vec. +#[derive(Clone, Debug, PartialEq)] +pub struct CsVecStorage +where DefaultAllocator: Allocator +{ + pub(crate) shape: (R, C), + pub(crate) p: VectorN, + pub(crate) i: Vec, + pub(crate) vals: Vec, +} + +impl CsVecStorage +where DefaultAllocator: Allocator +{ + /// The value buffer of this storage. + pub fn values(&self) -> &[N] { + &self.vals + } + + /// The column shifts buffer. + pub fn p(&self) -> &[usize] { + self.p.as_slice() + } + + /// The row index buffers. + pub fn i(&self) -> &[usize] { + &self.i + } +} + +impl CsVecStorage where DefaultAllocator: Allocator {} + +impl<'a, N: Scalar, R: Dim, C: Dim> CsStorageIter<'a, N, R, C> for CsVecStorage +where DefaultAllocator: Allocator +{ + type ColumnEntries = ColumnEntries<'a, N>; + type ColumnRowIndices = iter::Cloned>; + + #[inline] + fn column_entries(&'a self, j: usize) -> Self::ColumnEntries { + let rng = self.column_range(j); + ColumnEntries::new(&self.i[rng.clone()], &self.vals[rng]) + } + + #[inline] + fn column_row_indices(&'a self, j: usize) -> Self::ColumnRowIndices { + let rng = self.column_range(j); + self.i[rng.clone()].iter().cloned() + } +} + +impl CsStorage for CsVecStorage +where DefaultAllocator: Allocator +{ + #[inline] + fn shape(&self) -> (R, C) { + self.shape + } + + #[inline] + fn len(&self) -> usize { + self.vals.len() + } + + #[inline] + fn row_index(&self, i: usize) -> usize { + self.i[i] + } + + #[inline] + unsafe fn row_index_unchecked(&self, i: usize) -> usize { + *self.i.get_unchecked(i) + } + + #[inline] + unsafe fn get_value_unchecked(&self, i: usize) -> &N { + self.vals.get_unchecked(i) + } + + #[inline] + fn get_value(&self, i: usize) -> &N { + &self.vals[i] + } + + #[inline] + fn column_range(&self, j: usize) -> Range { + let end = if j + 1 == self.p.len() { + self.len() + } else { + self.p[j + 1] + }; + + self.p[j]..end + } +} + +impl<'a, N: Scalar, R: Dim, C: Dim> CsStorageIterMut<'a, N, R, C> for CsVecStorage +where DefaultAllocator: Allocator +{ + type ValuesMut = slice::IterMut<'a, N>; + type ColumnEntriesMut = iter::Zip>, slice::IterMut<'a, N>>; + + #[inline] + fn values_mut(&'a mut self) -> Self::ValuesMut { + self.vals.iter_mut() + } + + #[inline] + fn column_entries_mut(&'a mut self, j: usize) -> Self::ColumnEntriesMut { + let rng = self.column_range(j); + self.i[rng.clone()] + .iter() + .cloned() + .zip(self.vals[rng].iter_mut()) + } +} + +impl CsStorageMut for CsVecStorage where DefaultAllocator: Allocator +{} + +/* +pub struct CsSliceStorage<'a, N: Scalar, R: Dim, C: DimAdd> { + shape: (R, C), + p: VectorSlice>, + i: VectorSlice, + vals: VectorSlice, +}*/ + +/// A compressed sparse column matrix. +#[derive(Clone, Debug, PartialEq)] +pub struct CsMatrix< + N: Scalar, + R: Dim = Dynamic, + C: Dim = Dynamic, + S: CsStorage = CsVecStorage, +> { + pub(crate) data: S, + _phantoms: PhantomData<(N, R, C)>, +} + +/// A column compressed sparse vector. +pub type CsVector> = CsMatrix; + +impl CsMatrix +where DefaultAllocator: Allocator +{ + /// Creates a new compressed sparse column matrix with the specified dimension and + /// `nvals` possible non-zero values. + pub fn new_uninitialized_generic(nrows: R, ncols: C, nvals: usize) -> Self { + let mut i = Vec::with_capacity(nvals); + unsafe { + i.set_len(nvals); + } + i.shrink_to_fit(); + + let mut vals = Vec::with_capacity(nvals); + unsafe { + vals.set_len(nvals); + } + vals.shrink_to_fit(); + + CsMatrix { + data: CsVecStorage { + shape: (nrows, ncols), + p: VectorN::zeros_generic(ncols, U1), + i, + vals, + }, + _phantoms: PhantomData, + } + } + + /* + pub(crate) fn from_parts_generic( + nrows: R, + ncols: C, + p: VectorN, + i: Vec, + vals: Vec, + ) -> Self + where + N: Zero + ClosedAdd, + DefaultAllocator: Allocator, + { + assert_eq!(ncols.value(), p.len(), "Invalid inptr size."); + assert_eq!(i.len(), vals.len(), "Invalid value size."); + + // Check p. + for ptr in &p { + assert!(*ptr < i.len(), "Invalid inptr value."); + } + + for ptr in p.as_slice().windows(2) { + assert!(ptr[0] <= ptr[1], "Invalid inptr ordering."); + } + + // Check i. + for i in &i { + assert!(*i < nrows.value(), "Invalid row ptr value.") + } + + let mut res = CsMatrix { + data: CsVecStorage { + shape: (nrows, ncols), + p, + i, + vals, + }, + _phantoms: PhantomData, + }; + + // Sort and remove duplicates. + res.sort(); + res.dedup(); + + res + }*/ +} + +/* +impl CsMatrix { + pub(crate) fn from_parts( + nrows: usize, + ncols: usize, + p: Vec, + i: Vec, + vals: Vec, + ) -> Self + { + let nrows = Dynamic::new(nrows); + let ncols = Dynamic::new(ncols); + let p = DVector::from_data(VecStorage::new(ncols, U1, p)); + Self::from_parts_generic(nrows, ncols, p, i, vals) + } +} +*/ + +impl> CsMatrix { + pub(crate) fn from_data(data: S) -> Self { + CsMatrix { + data, + _phantoms: PhantomData, + } + } + + /// The size of the data buffer. + pub fn len(&self) -> usize { + self.data.len() + } + + /// The number of rows of this matrix. + pub fn nrows(&self) -> usize { + self.data.shape().0.value() + } + + /// The number of rows of this matrix. + pub fn ncols(&self) -> usize { + self.data.shape().1.value() + } + + /// The shape of this matrix. + pub fn shape(&self) -> (usize, usize) { + let (nrows, ncols) = self.data.shape(); + (nrows.value(), ncols.value()) + } + + /// Whether this matrix is square or not. + pub fn is_square(&self) -> bool { + let (nrows, ncols) = self.data.shape(); + nrows.value() == ncols.value() + } + + /// Should always return `true`. + /// + /// This method is generally used for debugging and should typically not be called in user code. + /// This checks that the row inner indices of this matrix are sorted. It takes `O(n)` time, + /// where n` is `self.len()`. + /// All operations of CSC matrices on nalgebra assume, and will return, sorted indices. + /// If at any time this `is_sorted` method returns `false`, then, something went wrong + /// and an issue should be open on the nalgebra repository with details on how to reproduce + /// this. + pub fn is_sorted(&self) -> bool { + for j in 0..self.ncols() { + let mut curr = None; + for idx in self.data.column_row_indices(j) { + if let Some(curr) = curr { + if idx <= curr { + return false; + } + } + + curr = Some(idx); + } + } + + true + } + + /// Computes the transpose of this sparse matrix. + pub fn transpose(&self) -> CsMatrix + where DefaultAllocator: Allocator { + let (nrows, ncols) = self.data.shape(); + + let nvals = self.len(); + let mut res = CsMatrix::new_uninitialized_generic(ncols, nrows, nvals); + let mut workspace = Vector::zeros_generic(nrows, U1); + + // Compute p. + for i in 0..nvals { + let row_id = self.data.row_index(i); + workspace[row_id] += 1; + } + + let _ = cs_utils::cumsum(&mut workspace, &mut res.data.p); + + // Fill the result. + for j in 0..ncols.value() { + for (row_id, value) in self.data.column_entries(j) { + let shift = workspace[row_id]; + + res.data.vals[shift] = value; + res.data.i[shift] = j; + workspace[row_id] += 1; + } + } + + res + } +} + +impl> CsMatrix { + /// Iterator through all the mutable values of this sparse matrix. + #[inline] + pub fn values_mut(&mut self) -> impl Iterator { + self.data.values_mut() + } +} + +impl CsMatrix +where DefaultAllocator: Allocator +{ + pub(crate) fn sort(&mut self) + where DefaultAllocator: Allocator { + // Size = R + let nrows = self.data.shape().0; + let mut workspace = unsafe { VectorN::new_uninitialized_generic(nrows, U1) }; + self.sort_with_workspace(workspace.as_mut_slice()); + } + + pub(crate) fn sort_with_workspace(&mut self, workspace: &mut [N]) { + assert!( + workspace.len() >= self.nrows(), + "Workspace must be able to hold at least self.nrows() elements." + ); + + for j in 0..self.ncols() { + // Scatter the row in the workspace. + for (irow, val) in self.data.column_entries(j) { + workspace[irow] = val; + } + + // Sort the index vector. + let range = self.data.column_range(j); + self.data.i[range.clone()].sort(); + + // Permute the values too. + for (i, irow) in range.clone().zip(self.data.i[range].iter().cloned()) { + self.data.vals[i] = workspace[irow]; + } + } + } + + // Remove dupliate entries on a sorted CsMatrix. + pub(crate) fn dedup(&mut self) + where N: Zero + ClosedAdd { + let mut curr_i = 0; + + for j in 0..self.ncols() { + let range = self.data.column_range(j); + self.data.p[j] = curr_i; + + if range.start != range.end { + let mut value = N::zero(); + let mut irow = self.data.i[range.start]; + + for idx in range { + let curr_irow = self.data.i[idx]; + + if curr_irow == irow { + value += self.data.vals[idx]; + } else { + self.data.i[curr_i] = irow; + self.data.vals[curr_i] = value; + value = self.data.vals[idx]; + irow = curr_irow; + curr_i += 1; + } + } + + // Handle the last entry. + self.data.i[curr_i] = irow; + self.data.vals[curr_i] = value; + curr_i += 1; + } + } + + self.data.i.truncate(curr_i); + self.data.i.shrink_to_fit(); + self.data.vals.truncate(curr_i); + self.data.vals.shrink_to_fit(); + } +} diff --git a/src/sparse/cs_matrix_cholesky.rs b/src/sparse/cs_matrix_cholesky.rs new file mode 100644 index 00000000..5d834ef2 --- /dev/null +++ b/src/sparse/cs_matrix_cholesky.rs @@ -0,0 +1,408 @@ +use std::iter; +use std::mem; + +use allocator::Allocator; +use sparse::{CsMatrix, CsStorage, CsStorageIter, CsStorageIterMut, CsVecStorage}; +use {DefaultAllocator, Dim, Real, VectorN, U1}; + +/// The cholesky decomposition of a column compressed sparse matrix. +pub struct CsCholesky +where DefaultAllocator: Allocator + Allocator +{ + // Non-zero pattern of the original matrix upper-triangular part. + // Unlike the original matrix, the `original_p` array does contain the last sentinel value + // equal to `original_i.len()` at the end. + original_p: Vec, + original_i: Vec, + // Decomposition result. + l: CsMatrix, + // Used only for the pattern. + // FIXME: store only the nonzero pattern instead. + u: CsMatrix, + ok: bool, + // Workspaces. + work_x: VectorN, + work_c: VectorN, +} + +impl CsCholesky +where DefaultAllocator: Allocator + Allocator +{ + /// Computes the cholesky decomposition of the sparse matrix `m`. + pub fn new(m: &CsMatrix) -> Self { + let mut me = Self::new_symbolic(m); + let _ = me.decompose_left_looking(&m.data.vals); + me + } + /// Perform symbolic analysis for the given matrix. + /// + /// This does not access the numerical values of `m`. + pub fn new_symbolic(m: &CsMatrix) -> Self { + assert!( + m.is_square(), + "The matrix `m` must be square to compute its elimination tree." + ); + + let (l, u) = Self::nonzero_pattern(m); + + // Workspaces. + let work_x = unsafe { VectorN::new_uninitialized_generic(m.data.shape().0, U1) }; + let work_c = unsafe { VectorN::new_uninitialized_generic(m.data.shape().1, U1) }; + let mut original_p = m.data.p.as_slice().to_vec(); + original_p.push(m.data.i.len()); + + CsCholesky { + original_p, + original_i: m.data.i.clone(), + l, + u, + ok: false, + work_x, + work_c, + } + } + + /// The lower-triangular matrix of the cholesky decomposition. + pub fn l(&self) -> Option<&CsMatrix> { + if self.ok { + Some(&self.l) + } else { + None + } + } + + /// Extracts the lower-triangular matrix of the cholesky decomposition. + pub fn unwrap_l(self) -> Option> { + if self.ok { + Some(self.l) + } else { + None + } + } + + /// Perform a numerical left-looking cholesky decomposition of a matrix with the same structure as the + /// one used to initialize `self`, but with different non-zero values provided by `values`. + pub fn decompose_left_looking(&mut self, values: &[N]) -> bool { + assert!( + values.len() >= self.original_i.len(), + "The set of values is too small." + ); + + let n = self.l.nrows(); + + // Reset `work_c` to the column pointers of `l`. + self.work_c.copy_from(&self.l.data.p); + + unsafe { + for k in 0..n { + // Scatter the k-th column of the original matrix with the values provided. + let range_k = + *self.original_p.get_unchecked(k)..*self.original_p.get_unchecked(k + 1); + + *self.work_x.vget_unchecked_mut(k) = N::zero(); + for p in range_k.clone() { + let irow = *self.original_i.get_unchecked(p); + + if irow >= k { + *self.work_x.vget_unchecked_mut(irow) = *values.get_unchecked(p); + } + } + + for j in self.u.data.column_row_indices(k) { + let factor = -*self + .l + .data + .vals + .get_unchecked(*self.work_c.vget_unchecked(j)); + *self.work_c.vget_unchecked_mut(j) += 1; + + if j < k { + for (z, val) in self.l.data.column_entries(j) { + if z >= k { + *self.work_x.vget_unchecked_mut(z) += val * factor; + } + } + } + } + + let diag = *self.work_x.vget_unchecked(k); + + if diag > N::zero() { + let denom = diag.sqrt(); + *self + .l + .data + .vals + .get_unchecked_mut(*self.l.data.p.vget_unchecked(k)) = denom; + + for (p, val) in self.l.data.column_entries_mut(k) { + *val = *self.work_x.vget_unchecked(p) / denom; + *self.work_x.vget_unchecked_mut(p) = N::zero(); + } + } else { + self.ok = false; + return false; + } + } + } + + self.ok = true; + true + } + + /// Perform a numerical up-looking cholesky decomposition of a matrix with the same structure as the + /// one used to initialize `self`, but with different non-zero values provided by `values`. + pub fn decompose_up_looking(&mut self, values: &[N]) -> bool { + assert!( + values.len() >= self.original_i.len(), + "The set of values is too small." + ); + + // Reset `work_c` to the column pointers of `l`. + self.work_c.copy_from(&self.l.data.p); + + // Perform the decomposition. + for k in 0..self.l.nrows() { + unsafe { + // Scatter the k-th column of the original matrix with the values provided. + let column_range = + *self.original_p.get_unchecked(k)..*self.original_p.get_unchecked(k + 1); + + *self.work_x.vget_unchecked_mut(k) = N::zero(); + for p in column_range.clone() { + let irow = *self.original_i.get_unchecked(p); + + if irow <= k { + *self.work_x.vget_unchecked_mut(irow) = *values.get_unchecked(p); + } + } + + let mut diag = *self.work_x.vget_unchecked(k); + *self.work_x.vget_unchecked_mut(k) = N::zero(); + + // Triangular solve. + for irow in self.u.data.column_row_indices(k) { + if irow >= k { + continue; + } + + let lki = *self.work_x.vget_unchecked(irow) + / *self + .l + .data + .vals + .get_unchecked(*self.l.data.p.vget_unchecked(irow)); + *self.work_x.vget_unchecked_mut(irow) = N::zero(); + + for p in + *self.l.data.p.vget_unchecked(irow) + 1..*self.work_c.vget_unchecked(irow) + { + *self + .work_x + .vget_unchecked_mut(*self.l.data.i.get_unchecked(p)) -= + *self.l.data.vals.get_unchecked(p) * lki; + } + + diag -= lki * lki; + let p = *self.work_c.vget_unchecked(irow); + *self.work_c.vget_unchecked_mut(irow) += 1; + *self.l.data.i.get_unchecked_mut(p) = k; + *self.l.data.vals.get_unchecked_mut(p) = lki; + } + + if diag <= N::zero() { + self.ok = false; + return false; + } + + // Deal with the diagonal element. + let p = *self.work_c.vget_unchecked(k); + *self.work_c.vget_unchecked_mut(k) += 1; + *self.l.data.i.get_unchecked_mut(p) = k; + *self.l.data.vals.get_unchecked_mut(p) = diag.sqrt(); + } + } + + self.ok = true; + true + } + + fn elimination_tree>(m: &CsMatrix) -> Vec { + let nrows = m.nrows(); + let mut forest: Vec<_> = iter::repeat(usize::max_value()).take(nrows).collect(); + let mut ancestor: Vec<_> = iter::repeat(usize::max_value()).take(nrows).collect(); + + for k in 0..nrows { + for irow in m.data.column_row_indices(k) { + let mut i = irow; + + while i < k { + let i_ancestor = ancestor[i]; + ancestor[i] = k; + + if i_ancestor == usize::max_value() { + forest[i] = k; + break; + } + + i = i_ancestor; + } + } + } + + forest + } + + fn reach>( + m: &CsMatrix, + j: usize, + max_j: usize, + tree: &[usize], + marks: &mut Vec, + out: &mut Vec, + ) + { + marks.clear(); + marks.resize(tree.len(), false); + + // FIXME: avoid all those allocations. + let mut tmp = Vec::new(); + let mut res = Vec::new(); + + for irow in m.data.column_row_indices(j) { + let mut curr = irow; + while curr != usize::max_value() && curr <= max_j && !marks[curr] { + marks[curr] = true; + tmp.push(curr); + curr = tree[curr]; + } + + tmp.append(&mut res); + mem::swap(&mut tmp, &mut res); + } + + out.append(&mut res); + } + + fn nonzero_pattern>( + m: &CsMatrix, + ) -> (CsMatrix, CsMatrix) { + let etree = Self::elimination_tree(m); + let (nrows, ncols) = m.data.shape(); + let mut rows = Vec::with_capacity(m.len()); + let mut cols = unsafe { VectorN::new_uninitialized_generic(m.data.shape().0, U1) }; + let mut marks = Vec::new(); + + // NOTE: the following will actually compute the non-zero pattern of + // the transpose of l. + for i in 0..nrows.value() { + cols[i] = rows.len(); + Self::reach(m, i, i, &etree, &mut marks, &mut rows); + } + + let mut vals = Vec::with_capacity(rows.len()); + unsafe { + vals.set_len(rows.len()); + } + vals.shrink_to_fit(); + + let data = CsVecStorage { + shape: (nrows, ncols), + p: cols, + i: rows, + vals, + }; + + let u = CsMatrix::from_data(data); + // XXX: avoid this transpose. + let l = u.transpose(); + + (l, u) + } + + /* + * + * NOTE: All the following methods are untested and currently unused. + * + * + fn column_counts>( + m: &CsMatrix, + tree: &[usize], + ) -> Vec { + let len = m.data.shape().0.value(); + let mut counts: Vec<_> = iter::repeat(0).take(len).collect(); + let mut reach = Vec::new(); + let mut marks = Vec::new(); + + for i in 0..len { + Self::reach(m, i, i, tree, &mut marks, &mut reach); + + for j in reach.drain(..) { + counts[j] += 1; + } + } + + counts + } + + fn tree_postorder(tree: &[usize]) -> Vec { + // FIXME: avoid all those allocations? + let mut first_child: Vec<_> = iter::repeat(usize::max_value()).take(tree.len()).collect(); + let mut other_children: Vec<_> = + iter::repeat(usize::max_value()).take(tree.len()).collect(); + + // Build the children list from the parent list. + // The set of children of the node `i` is given by the linked list + // starting at `first_child[i]`. The nodes of this list are then: + // { first_child[i], other_children[first_child[i]], other_children[other_children[first_child[i]], ... } + for (i, parent) in tree.iter().enumerate() { + if *parent != usize::max_value() { + let brother = first_child[*parent]; + first_child[*parent] = i; + other_children[i] = brother; + } + } + + let mut stack = Vec::with_capacity(tree.len()); + let mut postorder = Vec::with_capacity(tree.len()); + + for (i, node) in tree.iter().enumerate() { + if *node == usize::max_value() { + Self::dfs( + i, + &mut first_child, + &other_children, + &mut stack, + &mut postorder, + ) + } + } + + postorder + } + + fn dfs( + i: usize, + first_child: &mut [usize], + other_children: &[usize], + stack: &mut Vec, + result: &mut Vec, + ) { + stack.clear(); + stack.push(i); + + while let Some(n) = stack.pop() { + let child = first_child[n]; + + if child == usize::max_value() { + // No children left. + result.push(n); + } else { + stack.push(n); + stack.push(child); + first_child[n] = other_children[child]; + } + } + } + */ +} diff --git a/src/sparse/cs_matrix_conversion.rs b/src/sparse/cs_matrix_conversion.rs new file mode 100644 index 00000000..0017340f --- /dev/null +++ b/src/sparse/cs_matrix_conversion.rs @@ -0,0 +1,114 @@ +use alga::general::ClosedAdd; +use num::Zero; + +use allocator::Allocator; +use sparse::cs_utils; +use sparse::{CsMatrix, CsStorage}; +use storage::Storage; +use {DefaultAllocator, Dim, Dynamic, Matrix, MatrixMN, Scalar}; + +impl<'a, N: Scalar + Zero + ClosedAdd> CsMatrix { + /// Creates a column-compressed sparse matrix from a sparse matrix in triplet form. + pub fn from_triplet( + nrows: usize, + ncols: usize, + irows: &[usize], + icols: &[usize], + vals: &[N], + ) -> Self + { + Self::from_triplet_generic(Dynamic::new(nrows), Dynamic::new(ncols), irows, icols, vals) + } +} + +impl<'a, N: Scalar + Zero + ClosedAdd, R: Dim, C: Dim> CsMatrix +where DefaultAllocator: Allocator + Allocator +{ + /// Creates a column-compressed sparse matrix from a sparse matrix in triplet form. + pub fn from_triplet_generic( + nrows: R, + ncols: C, + irows: &[usize], + icols: &[usize], + vals: &[N], + ) -> Self + { + assert!(vals.len() == irows.len()); + assert!(vals.len() == icols.len()); + + let mut res = CsMatrix::new_uninitialized_generic(nrows, ncols, vals.len()); + let mut workspace = res.data.p.clone(); + + // Column count. + for j in icols.iter().cloned() { + workspace[j] += 1; + } + + let _ = cs_utils::cumsum(&mut workspace, &mut res.data.p); + + // Fill i and vals. + for ((i, j), val) in irows + .iter() + .cloned() + .zip(icols.iter().cloned()) + .zip(vals.iter().cloned()) + { + let offset = workspace[j]; + res.data.i[offset] = i; + res.data.vals[offset] = val; + workspace[j] = offset + 1; + } + + // Sort the result. + res.sort(); + res.dedup(); + res + } +} + +impl<'a, N: Scalar + Zero, R: Dim, C: Dim, S> From> for MatrixMN +where + S: CsStorage, + DefaultAllocator: Allocator, +{ + fn from(m: CsMatrix) -> Self { + let (nrows, ncols) = m.data.shape(); + let mut res = MatrixMN::zeros_generic(nrows, ncols); + + for j in 0..ncols.value() { + for (i, val) in m.data.column_entries(j) { + res[(i, j)] = val; + } + } + + res + } +} + +impl<'a, N: Scalar + Zero, R: Dim, C: Dim, S> From> for CsMatrix +where + S: Storage, + DefaultAllocator: Allocator + Allocator, +{ + fn from(m: Matrix) -> Self { + let (nrows, ncols) = m.data.shape(); + let len = m.iter().filter(|e| !e.is_zero()).count(); + let mut res = CsMatrix::new_uninitialized_generic(nrows, ncols, len); + let mut nz = 0; + + for j in 0..ncols.value() { + let column = m.column(j); + res.data.p[j] = nz; + + for i in 0..nrows.value() { + if !column[i].is_zero() { + res.data.i[nz] = i; + res.data.vals[nz] = column[i]; + nz += 1; + } + } + } + + res + } +} diff --git a/src/sparse/cs_matrix_ops.rs b/src/sparse/cs_matrix_ops.rs new file mode 100644 index 00000000..b944c4e2 --- /dev/null +++ b/src/sparse/cs_matrix_ops.rs @@ -0,0 +1,304 @@ +use alga::general::{ClosedAdd, ClosedMul}; +use num::{One, Zero}; +use std::ops::{Add, Mul}; + +use allocator::Allocator; +use constraint::{AreMultipliable, DimEq, ShapeConstraint}; +use sparse::{CsMatrix, CsStorage, CsStorageMut, CsVector}; +use storage::StorageMut; +use {DefaultAllocator, Dim, Scalar, Vector, VectorN, U1}; + +impl> CsMatrix { + fn scatter( + &self, + j: usize, + beta: N, + timestamps: &mut [usize], + timestamp: usize, + workspace: &mut [N], + mut nz: usize, + res: &mut CsMatrix, + ) -> usize + where + N: ClosedAdd + ClosedMul, + DefaultAllocator: Allocator, + { + for (i, val) in self.data.column_entries(j) { + if timestamps[i] < timestamp { + timestamps[i] = timestamp; + res.data.i[nz] = i; + nz += 1; + workspace[i] = val * beta; + } else { + workspace[i] += val * beta; + } + } + + nz + } +} + +/* +impl CsVector { + pub fn axpy(&mut self, alpha: N, x: CsVector, beta: N) { + // First, compute the number of non-zero entries. + let mut nnzero = 0; + + // Allocate a size large enough. + self.data.set_column_len(0, nnzero); + + // Fill with the axpy. + let mut i = self.len(); + let mut j = x.len(); + let mut k = nnzero - 1; + let mut rid1 = self.data.row_index(0, i - 1); + let mut rid2 = x.data.row_index(0, j - 1); + + while k > 0 { + if rid1 == rid2 { + self.data.set_row_index(0, k, rid1); + self[k] = alpha * x[j] + beta * self[k]; + i -= 1; + j -= 1; + } else if rid1 < rid2 { + self.data.set_row_index(0, k, rid1); + self[k] = beta * self[i]; + i -= 1; + } else { + self.data.set_row_index(0, k, rid2); + self[k] = alpha * x[j]; + j -= 1; + } + + k -= 1; + } + } +} +*/ + +impl> Vector { + /// Perform a sparse axpy operation: `self = alpha * x + beta * self` operation. + pub fn axpy_cs(&mut self, alpha: N, x: &CsVector, beta: N) + where + S2: CsStorage, + ShapeConstraint: DimEq, + { + if beta.is_zero() { + for i in 0..x.len() { + unsafe { + let k = x.data.row_index_unchecked(i); + let y = self.vget_unchecked_mut(k); + *y = alpha * *x.data.get_value_unchecked(i); + } + } + } else { + // Needed to be sure even components not present on `x` are multiplied. + *self *= beta; + + for i in 0..x.len() { + unsafe { + let k = x.data.row_index_unchecked(i); + let y = self.vget_unchecked_mut(k); + *y += alpha * *x.data.get_value_unchecked(i); + } + } + } + } + + /* + pub fn gemv_sparse(&mut self, alpha: N, a: &CsMatrix, x: &DVector, beta: N) + where + S2: CsStorage { + let col2 = a.column(0); + let val = unsafe { *x.vget_unchecked(0) }; + self.axpy_sparse(alpha * val, &col2, beta); + + for j in 1..ncols2 { + let col2 = a.column(j); + let val = unsafe { *x.vget_unchecked(j) }; + + self.axpy_sparse(alpha * val, &col2, N::one()); + } + } + */ +} + +impl<'a, 'b, N, R1, R2, C1, C2, S1, S2> Mul<&'b CsMatrix> + for &'a CsMatrix +where + N: Scalar + ClosedAdd + ClosedMul + Zero, + R1: Dim, + C1: Dim, + R2: Dim, + C2: Dim, + S1: CsStorage, + S2: CsStorage, + ShapeConstraint: AreMultipliable, + DefaultAllocator: Allocator + Allocator + Allocator, +{ + type Output = CsMatrix; + + fn mul(self, rhs: &'b CsMatrix) -> Self::Output { + let (nrows1, ncols1) = self.data.shape(); + let (nrows2, ncols2) = rhs.data.shape(); + assert_eq!( + ncols1.value(), + nrows2.value(), + "Mismatched dimensions for matrix multiplication." + ); + + let mut res = CsMatrix::new_uninitialized_generic(nrows1, ncols2, self.len() + rhs.len()); + let mut workspace = VectorN::::zeros_generic(nrows1, U1); + let mut nz = 0; + + for j in 0..ncols2.value() { + res.data.p[j] = nz; + let new_size_bound = nz + nrows1.value(); + res.data.i.resize(new_size_bound, 0); + res.data.vals.resize(new_size_bound, N::zero()); + + for (i, beta) in rhs.data.column_entries(j) { + for (k, val) in self.data.column_entries(i) { + workspace[k] += val * beta; + } + } + + for (i, val) in workspace.as_mut_slice().iter_mut().enumerate() { + if !val.is_zero() { + res.data.i[nz] = i; + res.data.vals[nz] = *val; + *val = N::zero(); + nz += 1; + } + } + } + + // NOTE: the following has a lower complexity, but is slower in many cases, likely because + // of branching inside of the inner loop. + // + // let mut res = CsMatrix::new_uninitialized_generic(nrows1, ncols2, self.len() + rhs.len()); + // let mut timestamps = VectorN::zeros_generic(nrows1, U1); + // let mut workspace = unsafe { VectorN::new_uninitialized_generic(nrows1, U1) }; + // let mut nz = 0; + // + // for j in 0..ncols2.value() { + // res.data.p[j] = nz; + // let new_size_bound = nz + nrows1.value(); + // res.data.i.resize(new_size_bound, 0); + // res.data.vals.resize(new_size_bound, N::zero()); + // + // for (i, val) in rhs.data.column_entries(j) { + // nz = self.scatter( + // i, + // val, + // timestamps.as_mut_slice(), + // j + 1, + // workspace.as_mut_slice(), + // nz, + // &mut res, + // ); + // } + // + // // Keep the output sorted. + // let range = res.data.p[j]..nz; + // res.data.i[range.clone()].sort(); + // + // for p in range { + // res.data.vals[p] = workspace[res.data.i[p]] + // } + // } + + res.data.i.truncate(nz); + res.data.i.shrink_to_fit(); + res.data.vals.truncate(nz); + res.data.vals.shrink_to_fit(); + res + } +} + +impl<'a, 'b, N, R1, R2, C1, C2, S1, S2> Add<&'b CsMatrix> + for &'a CsMatrix +where + N: Scalar + ClosedAdd + ClosedMul + One, + R1: Dim, + C1: Dim, + R2: Dim, + C2: Dim, + S1: CsStorage, + S2: CsStorage, + ShapeConstraint: DimEq + DimEq, + DefaultAllocator: Allocator + Allocator + Allocator, +{ + type Output = CsMatrix; + + fn add(self, rhs: &'b CsMatrix) -> Self::Output { + let (nrows1, ncols1) = self.data.shape(); + let (nrows2, ncols2) = rhs.data.shape(); + assert_eq!( + (nrows1.value(), ncols1.value()), + (nrows2.value(), ncols2.value()), + "Mismatched dimensions for matrix sum." + ); + + let mut res = CsMatrix::new_uninitialized_generic(nrows1, ncols2, self.len() + rhs.len()); + let mut timestamps = VectorN::zeros_generic(nrows1, U1); + let mut workspace = unsafe { VectorN::new_uninitialized_generic(nrows1, U1) }; + let mut nz = 0; + + for j in 0..ncols2.value() { + res.data.p[j] = nz; + + nz = self.scatter( + j, + N::one(), + timestamps.as_mut_slice(), + j + 1, + workspace.as_mut_slice(), + nz, + &mut res, + ); + + nz = rhs.scatter( + j, + N::one(), + timestamps.as_mut_slice(), + j + 1, + workspace.as_mut_slice(), + nz, + &mut res, + ); + + // Keep the output sorted. + let range = res.data.p[j]..nz; + res.data.i[range.clone()].sort(); + + for p in range { + res.data.vals[p] = workspace[res.data.i[p]] + } + } + + res.data.i.truncate(nz); + res.data.i.shrink_to_fit(); + res.data.vals.truncate(nz); + res.data.vals.shrink_to_fit(); + res + } +} + +impl<'a, 'b, N, R, C, S> Mul for CsMatrix +where + N: Scalar + ClosedAdd + ClosedMul + Zero, + R: Dim, + C: Dim, + S: CsStorageMut, +{ + type Output = Self; + + fn mul(mut self, rhs: N) -> Self::Output { + for e in self.values_mut() { + *e *= rhs + } + + self + } +} diff --git a/src/sparse/cs_matrix_solve.rs b/src/sparse/cs_matrix_solve.rs new file mode 100644 index 00000000..2a13188e --- /dev/null +++ b/src/sparse/cs_matrix_solve.rs @@ -0,0 +1,283 @@ +use allocator::Allocator; +use constraint::{SameNumberOfRows, ShapeConstraint}; +use sparse::{CsMatrix, CsStorage, CsVector}; +use storage::{Storage, StorageMut}; +use {DefaultAllocator, Dim, Matrix, MatrixMN, Real, VectorN, U1}; + +impl> CsMatrix { + /// Solve a lower-triangular system with a dense right-hand-side. + pub fn solve_lower_triangular( + &self, + b: &Matrix, + ) -> Option> + where + S2: Storage, + DefaultAllocator: Allocator, + ShapeConstraint: SameNumberOfRows, + { + let mut b = b.clone_owned(); + if self.solve_lower_triangular_mut(&mut b) { + Some(b) + } else { + None + } + } + + /// Solve a lower-triangular system with `self` transposed and a dense right-hand-side. + pub fn tr_solve_lower_triangular( + &self, + b: &Matrix, + ) -> Option> + where + S2: Storage, + DefaultAllocator: Allocator, + ShapeConstraint: SameNumberOfRows, + { + let mut b = b.clone_owned(); + if self.tr_solve_lower_triangular_mut(&mut b) { + Some(b) + } else { + None + } + } + + /// Solve in-place a lower-triangular system with a dense right-hand-side. + pub fn solve_lower_triangular_mut( + &self, + b: &mut Matrix, + ) -> bool + where + S2: StorageMut, + ShapeConstraint: SameNumberOfRows, + { + let (nrows, ncols) = self.data.shape(); + assert_eq!(nrows.value(), ncols.value(), "The matrix must be square."); + assert_eq!(nrows.value(), b.len(), "Mismatched matrix dimensions."); + + for j2 in 0..b.ncols() { + let mut b = b.column_mut(j2); + + for j in 0..ncols.value() { + let mut column = self.data.column_entries(j); + let mut diag_found = false; + + while let Some((i, val)) = column.next() { + if i == j { + if val.is_zero() { + return false; + } + + b[j] /= val; + diag_found = true; + break; + } + } + + if !diag_found { + return false; + } + + for (i, val) in column { + b[i] -= b[j] * val; + } + } + } + + true + } + + /// Solve a lower-triangular system with `self` transposed and a dense right-hand-side. + pub fn tr_solve_lower_triangular_mut( + &self, + b: &mut Matrix, + ) -> bool + where + S2: StorageMut, + ShapeConstraint: SameNumberOfRows, + { + let (nrows, ncols) = self.data.shape(); + assert_eq!(nrows.value(), ncols.value(), "The matrix must be square."); + assert_eq!(nrows.value(), b.len(), "Mismatched matrix dimensions."); + + for j2 in 0..b.ncols() { + let mut b = b.column_mut(j2); + + for j in (0..ncols.value()).rev() { + let mut column = self.data.column_entries(j); + let mut diag = None; + + while let Some((i, val)) = column.next() { + if i == j { + if val.is_zero() { + return false; + } + + diag = Some(val); + break; + } + } + + if let Some(diag) = diag { + for (i, val) in column { + b[j] -= val * b[i]; + } + + b[j] /= diag; + } else { + return false; + } + } + } + + true + } + + /// Solve a lower-triangular system with a sparse right-hand-side. + pub fn solve_lower_triangular_cs( + &self, + b: &CsVector, + ) -> Option> + where + S2: CsStorage, + DefaultAllocator: Allocator + Allocator + Allocator, + ShapeConstraint: SameNumberOfRows, + { + let mut reach = Vec::new(); + // We don't compute a postordered reach here because it will be sorted after anyway. + self.lower_triangular_reach(b, &mut reach); + // We sort the reach so the result matrix has sorted indices. + reach.sort(); + let mut workspace = unsafe { VectorN::new_uninitialized_generic(b.data.shape().0, U1) }; + + for i in reach.iter().cloned() { + workspace[i] = N::zero(); + } + + for (i, val) in b.data.column_entries(0) { + workspace[i] = val; + } + + for j in reach.iter().cloned() { + let mut column = self.data.column_entries(j); + let mut diag_found = false; + + while let Some((i, val)) = column.next() { + if i == j { + if val.is_zero() { + break; + } + + workspace[j] /= val; + diag_found = true; + break; + } + } + + if !diag_found { + return None; + } + + for (i, val) in column { + workspace[i] -= workspace[j] * val; + } + } + + // Copy the result into a sparse vector. + let mut result = CsVector::new_uninitialized_generic(b.data.shape().0, U1, reach.len()); + + for (i, val) in reach.iter().zip(result.data.vals.iter_mut()) { + *val = workspace[*i]; + } + + result.data.i = reach; + Some(result) + } + + /* + // Computes the reachable, post-ordered, nodes from `b`. + fn lower_triangular_reach_postordered( + &self, + b: &CsVector, + xi: &mut Vec, + ) where + S2: CsStorage, + DefaultAllocator: Allocator, + { + let mut visited = VectorN::repeat_generic(self.data.shape().1, U1, false); + let mut stack = Vec::new(); + + for i in b.data.column_range(0) { + let row_index = b.data.row_index(i); + + if !visited[row_index] { + let rng = self.data.column_range(row_index); + stack.push((row_index, rng)); + self.lower_triangular_dfs(visited.as_mut_slice(), &mut stack, xi); + } + } + } + + fn lower_triangular_dfs( + &self, + visited: &mut [bool], + stack: &mut Vec<(usize, Range)>, + xi: &mut Vec, + ) + { + 'recursion: while let Some((j, rng)) = stack.pop() { + visited[j] = true; + + for i in rng.clone() { + let row_id = self.data.row_index(i); + if row_id > j && !visited[row_id] { + stack.push((j, (i + 1)..rng.end)); + stack.push((row_id, self.data.column_range(row_id))); + continue 'recursion; + } + } + + xi.push(j) + } + } + */ + + // Computes the nodes reachable from `b` in an arbitrary order. + fn lower_triangular_reach(&self, b: &CsVector, xi: &mut Vec) + where + S2: CsStorage, + DefaultAllocator: Allocator, + { + let mut visited = VectorN::repeat_generic(self.data.shape().1, U1, false); + let mut stack = Vec::new(); + + for irow in b.data.column_row_indices(0) { + self.lower_triangular_bfs(irow, visited.as_mut_slice(), &mut stack, xi); + } + } + + fn lower_triangular_bfs( + &self, + start: usize, + visited: &mut [bool], + stack: &mut Vec, + xi: &mut Vec, + ) + { + if !visited[start] { + stack.clear(); + stack.push(start); + xi.push(start); + visited[start] = true; + + while let Some(j) = stack.pop() { + for irow in self.data.column_row_indices(j) { + if irow > j && !visited[irow] { + stack.push(irow); + xi.push(irow); + visited[irow] = true; + } + } + } + } + } +} diff --git a/src/sparse/cs_utils.rs b/src/sparse/cs_utils.rs new file mode 100644 index 00000000..3c5db43e --- /dev/null +++ b/src/sparse/cs_utils.rs @@ -0,0 +1,16 @@ +use allocator::Allocator; +use {DefaultAllocator, Dim, VectorN}; + +pub fn cumsum(a: &mut VectorN, b: &mut VectorN) -> usize +where DefaultAllocator: Allocator { + assert!(a.len() == b.len()); + let mut sum = 0; + + for i in 0..a.len() { + b[i] = sum; + sum += a[i]; + a[i] = b[i]; + } + + sum +} diff --git a/src/sparse/mod.rs b/src/sparse/mod.rs new file mode 100644 index 00000000..5df2d75d --- /dev/null +++ b/src/sparse/mod.rs @@ -0,0 +1,13 @@ +//! Sparse matrices. + +pub use self::cs_matrix::{ + CsMatrix, CsStorage, CsStorageIter, CsStorageIterMut, CsStorageMut, CsVecStorage, CsVector, +}; +pub use self::cs_matrix_cholesky::CsCholesky; + +mod cs_matrix; +mod cs_matrix_cholesky; +mod cs_matrix_conversion; +mod cs_matrix_ops; +mod cs_matrix_solve; +pub(crate) mod cs_utils; diff --git a/tests/core/edition.rs b/tests/core/edition.rs index edaf2f08..37452a2d 100644 --- a/tests/core/edition.rs +++ b/tests/core/edition.rs @@ -565,4 +565,4 @@ fn resize_empty_matrix() { assert_eq!(m1, m5.resize(0, 0, 42)); assert_eq!(m1, m6.resize(0, 0, 42)); assert_eq!(m1, m7.resize(0, 0, 42)); -} \ No newline at end of file +} diff --git a/tests/core/matrix.rs b/tests/core/matrix.rs index 2569ea7a..9c6d468a 100644 --- a/tests/core/matrix.rs +++ b/tests/core/matrix.rs @@ -195,10 +195,10 @@ fn from_columns() { #[test] fn from_columns_dynamic() { let columns = &[ - DVector::from_row_slice(3, &[11, 21, 31]), - DVector::from_row_slice(3, &[12, 22, 32]), - DVector::from_row_slice(3, &[13, 23, 33]), - DVector::from_row_slice(3, &[14, 24, 34]), + DVector::from_row_slice(&[11, 21, 31]), + DVector::from_row_slice(&[12, 22, 32]), + DVector::from_row_slice(&[13, 23, 33]), + DVector::from_row_slice(&[14, 24, 34]), ]; let expected = DMatrix::from_row_slice(3, 4, &[11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34]); @@ -233,8 +233,8 @@ fn from_not_enough_columns() { #[should_panic] fn from_rows_with_different_dimensions() { let columns = &[ - DVector::from_row_slice(3, &[11, 21, 31]), - DVector::from_row_slice(3, &[12, 22, 32, 33]), + DVector::from_row_slice(&[11, 21, 31]), + DVector::from_row_slice(&[12, 22, 32, 33]), ]; let _ = DMatrix::from_columns(columns); @@ -272,8 +272,8 @@ fn to_homogeneous() { let a = Vector3::new(1.0, 2.0, 3.0); let expected_a = Vector4::new(1.0, 2.0, 3.0, 0.0); - let b = DVector::from_row_slice(3, &[1.0, 2.0, 3.0]); - let expected_b = DVector::from_row_slice(4, &[1.0, 2.0, 3.0, 0.0]); + let b = DVector::from_row_slice(&[1.0, 2.0, 3.0]); + let expected_b = DVector::from_row_slice(&[1.0, 2.0, 3.0, 0.0]); let c = Matrix2::new(1.0, 2.0, 3.0, 4.0); let expected_c = Matrix3::new(1.0, 2.0, 0.0, 3.0, 4.0, 0.0, 0.0, 0.0, 1.0); @@ -287,6 +287,18 @@ fn to_homogeneous() { assert_eq!(d.to_homogeneous(), expected_d); } +#[test] +fn push() { + let a = Vector3::new(1.0, 2.0, 3.0); + let expected_a = Vector4::new(1.0, 2.0, 3.0, 4.0); + + let b = DVector::from_row_slice(&[1.0, 2.0, 3.0]); + let expected_b = DVector::from_row_slice(&[1.0, 2.0, 3.0, 4.0]); + + assert_eq!(a.push(4.0), expected_a); + assert_eq!(b.push(4.0), expected_b); +} + #[test] fn simple_add() { let a = Matrix2x3::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0); diff --git a/tests/geometry/isometry.rs b/tests/geometry/isometry.rs index c72a2475..a0e00272 100644 --- a/tests/geometry/isometry.rs +++ b/tests/geometry/isometry.rs @@ -25,27 +25,35 @@ quickcheck!( let viewmatrix = Isometry3::look_at_rh(&eye, &target, &up); let origin = Point3::origin(); - relative_eq!(viewmatrix * eye, origin, epsilon = 1.0e-7) && - relative_eq!((viewmatrix * (target - eye)).normalize(), -Vector3::z(), epsilon = 1.0e-7) + relative_eq!(viewmatrix * eye, origin, epsilon = 1.0e-7) + && relative_eq!( + (viewmatrix * (target - eye)).normalize(), + -Vector3::z(), + epsilon = 1.0e-7 + ) } fn observer_frame_3(eye: Point3, target: Point3, up: Vector3) -> bool { - let observer = Isometry3::new_observer_frame(&eye, &target, &up); + let observer = Isometry3::face_towards(&eye, &target, &up); let origin = Point3::origin(); - relative_eq!(observer * origin, eye, epsilon = 1.0e-7) && - relative_eq!(observer * Vector3::z(), (target - eye).normalize(), epsilon = 1.0e-7) + relative_eq!(observer * origin, eye, epsilon = 1.0e-7) + && relative_eq!( + observer * Vector3::z(), + (target - eye).normalize(), + epsilon = 1.0e-7 + ) } fn inverse_is_identity(i: Isometry3, p: Point3, v: Vector3) -> bool { let ii = i.inverse(); - relative_eq!(i * ii, Isometry3::identity(), epsilon = 1.0e-7) && - relative_eq!(ii * i, Isometry3::identity(), epsilon = 1.0e-7) && - relative_eq!((i * ii) * p, p, epsilon = 1.0e-7) && - relative_eq!((ii * i) * p, p, epsilon = 1.0e-7) && - relative_eq!((i * ii) * v, v, epsilon = 1.0e-7) && - relative_eq!((ii * i) * v, v, epsilon = 1.0e-7) + relative_eq!(i * ii, Isometry3::identity(), epsilon = 1.0e-7) + && relative_eq!(ii * i, Isometry3::identity(), epsilon = 1.0e-7) + && relative_eq!((i * ii) * p, p, epsilon = 1.0e-7) + && relative_eq!((ii * i) * p, p, epsilon = 1.0e-7) + && relative_eq!((i * ii) * v, v, epsilon = 1.0e-7) + && relative_eq!((ii * i) * v, v, epsilon = 1.0e-7) } fn inverse_is_parts_inversion(t: Translation3, r: UnitQuaternion) -> bool { @@ -54,14 +62,29 @@ quickcheck!( } fn multiply_equals_alga_transform(i: Isometry3, v: Vector3, p: Point3) -> bool { - i * v == i.transform_vector(&v) && - i * p == i.transform_point(&p) && - relative_eq!(i.inverse() * v, i.inverse_transform_vector(&v), epsilon = 1.0e-7) && - relative_eq!(i.inverse() * p, i.inverse_transform_point(&p), epsilon = 1.0e-7) + i * v == i.transform_vector(&v) + && i * p == i.transform_point(&p) + && relative_eq!( + i.inverse() * v, + i.inverse_transform_vector(&v), + epsilon = 1.0e-7 + ) + && relative_eq!( + i.inverse() * p, + i.inverse_transform_point(&p), + epsilon = 1.0e-7 + ) } - fn composition2(i: Isometry2, uc: UnitComplex, r: Rotation2, - t: Translation2, v: Vector2, p: Point2) -> bool { + fn composition2( + i: Isometry2, + uc: UnitComplex, + r: Rotation2, + t: Translation2, + v: Vector2, + p: Point2, + ) -> bool + { // (rotation × translation) * point = rotation × (translation * point) relative_eq!((uc * t) * v, uc * v, epsilon = 1.0e-7) && relative_eq!((r * t) * v, r * v, epsilon = 1.0e-7) && @@ -91,8 +114,15 @@ quickcheck!( relative_eq!((i * t) * p, i * (t * p), epsilon = 1.0e-7) } - fn composition3(i: Isometry3, uq: UnitQuaternion, r: Rotation3, - t: Translation3, v: Vector3, p: Point3) -> bool { + fn composition3( + i: Isometry3, + uq: UnitQuaternion, + r: Rotation3, + t: Translation3, + v: Vector3, + p: Point3, + ) -> bool + { // (rotation × translation) * point = rotation × (translation * point) relative_eq!((uq * t) * v, uq * v, epsilon = 1.0e-7) && relative_eq!((r * t) * v, r * v, epsilon = 1.0e-7) && @@ -122,11 +152,18 @@ quickcheck!( relative_eq!((i * t) * p, i * (t * p), epsilon = 1.0e-7) } - fn all_op_exist(i: Isometry3, uq: UnitQuaternion, t: Translation3, - v: Vector3, p: Point3, r: Rotation3) -> bool { - let iMi = i * i; + fn all_op_exist( + i: Isometry3, + uq: UnitQuaternion, + t: Translation3, + v: Vector3, + p: Point3, + r: Rotation3, + ) -> bool + { + let iMi = i * i; let iMuq = i * uq; - let iDi = i / i; + let iDi = i / i; let iDuq = i / uq; let iMp = i * p; @@ -135,13 +172,13 @@ quickcheck!( let iMt = i * t; let tMi = t * i; - let tMr = t * r; + let tMr = t * r; let tMuq = t * uq; let uqMi = uq * i; let uqDi = uq / i; - let rMt = r * t; + let rMt = r * t; let uqMt = uq * t; let mut iMt1 = i; @@ -174,75 +211,57 @@ quickcheck!( iDuq1 /= uq; iDuq2 /= &uq; - iMt == iMt1 && - iMt == iMt2 && - - iMi == iMi1 && - iMi == iMi2 && - - iMuq == iMuq1 && - iMuq == iMuq2 && - - iDi == iDi1 && - iDi == iDi2 && - - iDuq == iDuq1 && - iDuq == iDuq2 && - - iMi == &i * &i && - iMi == i * &i && - iMi == &i * i && - - iMuq == &i * &uq && - iMuq == i * &uq && - iMuq == &i * uq && - - iDi == &i / &i && - iDi == i / &i && - iDi == &i / i && - - iDuq == &i / &uq && - iDuq == i / &uq && - iDuq == &i / uq && - - iMp == &i * &p && - iMp == i * &p && - iMp == &i * p && - - iMv == &i * &v && - iMv == i * &v && - iMv == &i * v && - - iMt == &i * &t && - iMt == i * &t && - iMt == &i * t && - - tMi == &t * &i && - tMi == t * &i && - tMi == &t * i && - - tMr == &t * &r && - tMr == t * &r && - tMr == &t * r && - - tMuq == &t * &uq && - tMuq == t * &uq && - tMuq == &t * uq && - - uqMi == &uq * &i && - uqMi == uq * &i && - uqMi == &uq * i && - - uqDi == &uq / &i && - uqDi == uq / &i && - uqDi == &uq / i && - - rMt == &r * &t && - rMt == r * &t && - rMt == &r * t && - - uqMt == &uq * &t && - uqMt == uq * &t && - uqMt == &uq * t + iMt == iMt1 + && iMt == iMt2 + && iMi == iMi1 + && iMi == iMi2 + && iMuq == iMuq1 + && iMuq == iMuq2 + && iDi == iDi1 + && iDi == iDi2 + && iDuq == iDuq1 + && iDuq == iDuq2 + && iMi == &i * &i + && iMi == i * &i + && iMi == &i * i + && iMuq == &i * &uq + && iMuq == i * &uq + && iMuq == &i * uq + && iDi == &i / &i + && iDi == i / &i + && iDi == &i / i + && iDuq == &i / &uq + && iDuq == i / &uq + && iDuq == &i / uq + && iMp == &i * &p + && iMp == i * &p + && iMp == &i * p + && iMv == &i * &v + && iMv == i * &v + && iMv == &i * v + && iMt == &i * &t + && iMt == i * &t + && iMt == &i * t + && tMi == &t * &i + && tMi == t * &i + && tMi == &t * i + && tMr == &t * &r + && tMr == t * &r + && tMr == &t * r + && tMuq == &t * &uq + && tMuq == t * &uq + && tMuq == &t * uq + && uqMi == &uq * &i + && uqMi == uq * &i + && uqMi == &uq * i + && uqDi == &uq / &i + && uqDi == uq / &i + && uqDi == &uq / i + && rMt == &r * &t + && rMt == r * &t + && rMt == &r * t + && uqMt == &uq * &t + && uqMt == uq * &t + && uqMt == &uq * t } ); diff --git a/tests/geometry/similarity.rs b/tests/geometry/similarity.rs index e9fde466..68b86943 100644 --- a/tests/geometry/similarity.rs +++ b/tests/geometry/similarity.rs @@ -8,33 +8,57 @@ quickcheck!( fn inverse_is_identity(i: Similarity3, p: Point3, v: Vector3) -> bool { let ii = i.inverse(); - relative_eq!(i * ii, Similarity3::identity(), epsilon = 1.0e-7) && - relative_eq!(ii * i, Similarity3::identity(), epsilon = 1.0e-7) && - relative_eq!((i * ii) * p, p, epsilon = 1.0e-7) && - relative_eq!((ii * i) * p, p, epsilon = 1.0e-7) && - relative_eq!((i * ii) * v, v, epsilon = 1.0e-7) && - relative_eq!((ii * i) * v, v, epsilon = 1.0e-7) + relative_eq!(i * ii, Similarity3::identity(), epsilon = 1.0e-7) + && relative_eq!(ii * i, Similarity3::identity(), epsilon = 1.0e-7) + && relative_eq!((i * ii) * p, p, epsilon = 1.0e-7) + && relative_eq!((ii * i) * p, p, epsilon = 1.0e-7) + && relative_eq!((i * ii) * v, v, epsilon = 1.0e-7) + && relative_eq!((ii * i) * v, v, epsilon = 1.0e-7) } - fn inverse_is_parts_inversion(t: Translation3, r: UnitQuaternion, scaling: f64) -> bool { + fn inverse_is_parts_inversion( + t: Translation3, + r: UnitQuaternion, + scaling: f64, + ) -> bool + { if relative_eq!(scaling, 0.0) { true - } - else { + } else { let s = Similarity3::from_isometry(t * r, scaling); s.inverse() == Similarity3::from_scaling(1.0 / scaling) * r.inverse() * t.inverse() } } - fn multiply_equals_alga_transform(s: Similarity3, v: Vector3, p: Point3) -> bool { - s * v == s.transform_vector(&v) && - s * p == s.transform_point(&p) && - relative_eq!(s.inverse() * v, s.inverse_transform_vector(&v), epsilon = 1.0e-7) && - relative_eq!(s.inverse() * p, s.inverse_transform_point(&p), epsilon = 1.0e-7) + fn multiply_equals_alga_transform( + s: Similarity3, + v: Vector3, + p: Point3, + ) -> bool + { + s * v == s.transform_vector(&v) + && s * p == s.transform_point(&p) + && relative_eq!( + s.inverse() * v, + s.inverse_transform_vector(&v), + epsilon = 1.0e-7 + ) + && relative_eq!( + s.inverse() * p, + s.inverse_transform_point(&p), + epsilon = 1.0e-7 + ) } - fn composition(i: Isometry3, uq: UnitQuaternion, - t: Translation3, v: Vector3, p: Point3, scaling: f64) -> bool { + fn composition( + i: Isometry3, + uq: UnitQuaternion, + t: Translation3, + v: Vector3, + p: Point3, + scaling: f64, + ) -> bool + { if relative_eq!(scaling, 0.0) { return true; } @@ -122,11 +146,18 @@ quickcheck!( relative_eq!((s * i * t) * p, scaling * (i * (t * p)), epsilon = 1.0e-7) } - fn all_op_exist(s: Similarity3, i: Isometry3, uq: UnitQuaternion, - t: Translation3, v: Vector3, p: Point3) -> bool { - let sMs = s * s; + fn all_op_exist( + s: Similarity3, + i: Isometry3, + uq: UnitQuaternion, + t: Translation3, + v: Vector3, + p: Point3, + ) -> bool + { + let sMs = s * s; let sMuq = s * uq; - let sDs = s / s; + let sDs = s / s; let sDuq = s / uq; let sMp = s * p; @@ -186,81 +217,61 @@ quickcheck!( sDi1 /= i; sDi2 /= &i; - sMt == sMt1 && - sMt == sMt2 && - - sMs == sMs1 && - sMs == sMs2 && - - sMuq == sMuq1 && - sMuq == sMuq2 && - - sMi == sMi1 && - sMi == sMi2 && - - sDs == sDs1 && - sDs == sDs2 && - - sDuq == sDuq1 && - sDuq == sDuq2 && - - sDi == sDi1 && - sDi == sDi2 && - - sMs == &s * &s && - sMs == s * &s && - sMs == &s * s && - - sMuq == &s * &uq && - sMuq == s * &uq && - sMuq == &s * uq && - - sDs == &s / &s && - sDs == s / &s && - sDs == &s / s && - - sDuq == &s / &uq && - sDuq == s / &uq && - sDuq == &s / uq && - - sMp == &s * &p && - sMp == s * &p && - sMp == &s * p && - - sMv == &s * &v && - sMv == s * &v && - sMv == &s * v && - - sMt == &s * &t && - sMt == s * &t && - sMt == &s * t && - - tMs == &t * &s && - tMs == t * &s && - tMs == &t * s && - - uqMs == &uq * &s && - uqMs == uq * &s && - uqMs == &uq * s && - - uqDs == &uq / &s && - uqDs == uq / &s && - uqDs == &uq / s && - - sMi == &s * &i && - sMi == s * &i && - sMi == &s * i && - - sDi == &s / &i && - sDi == s / &i && - sDi == &s / i && - - iMs == &i * &s && - iMs == i * &s && - iMs == &i * s && - - iDs == &i / &s && - iDs == i / &s && - iDs == &i / s + sMt == sMt1 + && sMt == sMt2 + && sMs == sMs1 + && sMs == sMs2 + && sMuq == sMuq1 + && sMuq == sMuq2 + && sMi == sMi1 + && sMi == sMi2 + && sDs == sDs1 + && sDs == sDs2 + && sDuq == sDuq1 + && sDuq == sDuq2 + && sDi == sDi1 + && sDi == sDi2 + && sMs == &s * &s + && sMs == s * &s + && sMs == &s * s + && sMuq == &s * &uq + && sMuq == s * &uq + && sMuq == &s * uq + && sDs == &s / &s + && sDs == s / &s + && sDs == &s / s + && sDuq == &s / &uq + && sDuq == s / &uq + && sDuq == &s / uq + && sMp == &s * &p + && sMp == s * &p + && sMp == &s * p + && sMv == &s * &v + && sMv == s * &v + && sMv == &s * v + && sMt == &s * &t + && sMt == s * &t + && sMt == &s * t + && tMs == &t * &s + && tMs == t * &s + && tMs == &t * s + && uqMs == &uq * &s + && uqMs == uq * &s + && uqMs == &uq * s + && uqDs == &uq / &s + && uqDs == uq / &s + && uqDs == &uq / s + && sMi == &s * &i + && sMi == s * &i + && sMi == &s * i + && sDi == &s / &i + && sDi == s / &i + && sDi == &s / i + && iMs == &i * &s + && iMs == i * &s + && iMs == &i * s + && iDs == &i / &s + && iDs == i / &s + && iDs == &i / s } ); diff --git a/tests/geometry/unit_complex.rs b/tests/geometry/unit_complex.rs index 7da0d20c..88988aa8 100644 --- a/tests/geometry/unit_complex.rs +++ b/tests/geometry/unit_complex.rs @@ -4,19 +4,17 @@ use na::{Point2, Rotation2, Unit, UnitComplex, Vector2}; quickcheck!( - /* * * From/to rotation matrix. * */ fn unit_complex_rotation_conversion(c: UnitComplex) -> bool { - let r = c.to_rotation_matrix(); + let r = c.to_rotation_matrix(); let cc = UnitComplex::from_rotation_matrix(&r); let rr = cc.to_rotation_matrix(); - relative_eq!(c, cc, epsilon = 1.0e-7) && - relative_eq!(r, rr, epsilon = 1.0e-7) + relative_eq!(c, cc, epsilon = 1.0e-7) && relative_eq!(r, rr, epsilon = 1.0e-7) } /* @@ -25,19 +23,18 @@ quickcheck!( * */ fn unit_complex_transformation(c: UnitComplex, v: Vector2, p: Point2) -> bool { - let r = c.to_rotation_matrix(); + let r = c.to_rotation_matrix(); let rv = r * v; let rp = r * p; - relative_eq!( c * v, rv, epsilon = 1.0e-7) && - relative_eq!( c * &v, rv, epsilon = 1.0e-7) && - relative_eq!(&c * v, rv, epsilon = 1.0e-7) && - relative_eq!(&c * &v, rv, epsilon = 1.0e-7) && - - relative_eq!( c * p, rp, epsilon = 1.0e-7) && - relative_eq!( c * &p, rp, epsilon = 1.0e-7) && - relative_eq!(&c * p, rp, epsilon = 1.0e-7) && - relative_eq!(&c * &p, rp, epsilon = 1.0e-7) + relative_eq!(c * v, rv, epsilon = 1.0e-7) + && relative_eq!(c * &v, rv, epsilon = 1.0e-7) + && relative_eq!(&c * v, rv, epsilon = 1.0e-7) + && relative_eq!(&c * &v, rv, epsilon = 1.0e-7) + && relative_eq!(c * p, rp, epsilon = 1.0e-7) + && relative_eq!(c * &p, rp, epsilon = 1.0e-7) + && relative_eq!(&c * p, rp, epsilon = 1.0e-7) + && relative_eq!(&c * &p, rp, epsilon = 1.0e-7) } /* @@ -47,15 +44,14 @@ quickcheck!( */ fn unit_complex_inv(c: UnitComplex) -> bool { let iq = c.inverse(); - relative_eq!(&iq * &c, UnitComplex::identity(), epsilon = 1.0e-7) && - relative_eq!( iq * &c, UnitComplex::identity(), epsilon = 1.0e-7) && - relative_eq!(&iq * c, UnitComplex::identity(), epsilon = 1.0e-7) && - relative_eq!( iq * c, UnitComplex::identity(), epsilon = 1.0e-7) && - - relative_eq!(&c * &iq, UnitComplex::identity(), epsilon = 1.0e-7) && - relative_eq!( c * &iq, UnitComplex::identity(), epsilon = 1.0e-7) && - relative_eq!(&c * iq, UnitComplex::identity(), epsilon = 1.0e-7) && - relative_eq!( c * iq, UnitComplex::identity(), epsilon = 1.0e-7) + relative_eq!(&iq * &c, UnitComplex::identity(), epsilon = 1.0e-7) + && relative_eq!(iq * &c, UnitComplex::identity(), epsilon = 1.0e-7) + && relative_eq!(&iq * c, UnitComplex::identity(), epsilon = 1.0e-7) + && relative_eq!(iq * c, UnitComplex::identity(), epsilon = 1.0e-7) + && relative_eq!(&c * &iq, UnitComplex::identity(), epsilon = 1.0e-7) + && relative_eq!(c * &iq, UnitComplex::identity(), epsilon = 1.0e-7) + && relative_eq!(&c * iq, UnitComplex::identity(), epsilon = 1.0e-7) + && relative_eq!(c * iq, UnitComplex::identity(), epsilon = 1.0e-7) } /* @@ -66,25 +62,30 @@ quickcheck!( fn unit_complex_mul_vector(c: UnitComplex, v: Vector2, p: Point2) -> bool { let r = c.to_rotation_matrix(); - relative_eq!(c * v, r * v, epsilon = 1.0e-7) && - relative_eq!(c * p, r * p, epsilon = 1.0e-7) + relative_eq!(c * v, r * v, epsilon = 1.0e-7) && relative_eq!(c * p, r * p, epsilon = 1.0e-7) } // Test that all operators (incl. all combinations of references) work. // See the top comment on `geometry/quaternion_ops.rs` for details on which operations are // supported. - fn all_op_exist(uc: UnitComplex, v: Vector2, p: Point2, r: Rotation2) -> bool { + fn all_op_exist( + uc: UnitComplex, + v: Vector2, + p: Point2, + r: Rotation2, + ) -> bool + { let uv = Unit::new_normalize(v); let ucMuc = uc * uc; - let ucMr = uc * r; - let rMuc = r * uc; + let ucMr = uc * r; + let rMuc = r * uc; let ucDuc = uc / uc; - let ucDr = uc / r; - let rDuc = r / uc; + let ucDr = uc / r; + let rDuc = r / uc; - let ucMp = uc * p; - let ucMv = uc * v; + let ucMp = uc * p; + let ucMv = uc * v; let ucMuv = uc * uv; let mut ucMuc1 = uc; @@ -111,52 +112,40 @@ quickcheck!( ucDr1 /= r; ucDr2 /= &r; - ucMuc1 == ucMuc && - ucMuc1 == ucMuc2 && - - ucMr1 == ucMr && - ucMr1 == ucMr2 && - - ucDuc1 == ucDuc && - ucDuc1 == ucDuc2 && - - ucDr1 == ucDr && - ucDr1 == ucDr2 && - - ucMuc == &uc * &uc && - ucMuc == uc * &uc && - ucMuc == &uc * uc && - - ucMr == &uc * &r && - ucMr == uc * &r && - ucMr == &uc * r && - - rMuc == &r * &uc && - rMuc == r * &uc && - rMuc == &r * uc && - - ucDuc == &uc / &uc && - ucDuc == uc / &uc && - ucDuc == &uc / uc && - - ucDr == &uc / &r && - ucDr == uc / &r && - ucDr == &uc / r && - - rDuc == &r / &uc && - rDuc == r / &uc && - rDuc == &r / uc && - - ucMp == &uc * &p && - ucMp == uc * &p && - ucMp == &uc * p && - - ucMv == &uc * &v && - ucMv == uc * &v && - ucMv == &uc * v && - - ucMuv == &uc * &uv && - ucMuv == uc * &uv && - ucMuv == &uc * uv + ucMuc1 == ucMuc + && ucMuc1 == ucMuc2 + && ucMr1 == ucMr + && ucMr1 == ucMr2 + && ucDuc1 == ucDuc + && ucDuc1 == ucDuc2 + && ucDr1 == ucDr + && ucDr1 == ucDr2 + && ucMuc == &uc * &uc + && ucMuc == uc * &uc + && ucMuc == &uc * uc + && ucMr == &uc * &r + && ucMr == uc * &r + && ucMr == &uc * r + && rMuc == &r * &uc + && rMuc == r * &uc + && rMuc == &r * uc + && ucDuc == &uc / &uc + && ucDuc == uc / &uc + && ucDuc == &uc / uc + && ucDr == &uc / &r + && ucDr == uc / &r + && ucDr == &uc / r + && rDuc == &r / &uc + && rDuc == r / &uc + && rDuc == &r / uc + && ucMp == &uc * &p + && ucMp == uc * &p + && ucMp == &uc * p + && ucMv == &uc * &v + && ucMv == uc * &v + && ucMv == &uc * v + && ucMuv == &uc * &uv + && ucMuv == uc * &uv + && ucMuv == &uc * uv } ); diff --git a/tests/lib.rs b/tests/lib.rs index bfe95a5e..c32f4066 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -16,3 +16,5 @@ extern crate serde_json; mod core; mod geometry; mod linalg; +#[cfg(feature = "sparse")] +mod sparse; diff --git a/tests/linalg/eigen.rs b/tests/linalg/eigen.rs index 0bcd672e..36855acf 100644 --- a/tests/linalg/eigen.rs +++ b/tests/linalg/eigen.rs @@ -104,3 +104,89 @@ fn symmetric_eigen_singular_24x24() { epsilon = 1.0e-5 )); } + +// #[cfg(feature = "arbitrary")] +// quickcheck! { +// FIXME: full eigendecomposition is not implemented yet because of its complexity when some +// eigenvalues have multiplicity > 1. +// +// /* +// * NOTE: for the following tests, we use only upper-triangular matrices. +// * Thes ensures the schur decomposition will work, and allows use to test the eigenvector +// * computation. +// */ +// fn eigen(n: usize) -> bool { +// let n = cmp::max(1, cmp::min(n, 10)); +// let m = DMatrix::::new_random(n, n).upper_triangle(); +// +// let eig = RealEigen::new(m.clone()).unwrap(); +// verify_eigenvectors(m, eig) +// } +// +// fn eigen_with_adjascent_duplicate_diagonals(n: usize) -> bool { +// let n = cmp::max(1, cmp::min(n, 10)); +// let mut m = DMatrix::::new_random(n, n).upper_triangle(); +// +// // Suplicate some adjascent diagonal elements. +// for i in 0 .. n / 2 { +// m[(i * 2 + 1, i * 2 + 1)] = m[(i * 2, i * 2)]; +// } +// +// let eig = RealEigen::new(m.clone()).unwrap(); +// verify_eigenvectors(m, eig) +// } +// +// fn eigen_with_nonadjascent_duplicate_diagonals(n: usize) -> bool { +// let n = cmp::max(3, cmp::min(n, 10)); +// let mut m = DMatrix::::new_random(n, n).upper_triangle(); +// +// // Suplicate some diagonal elements. +// for i in n / 2 .. n { +// m[(i, i)] = m[(i - n / 2, i - n / 2)]; +// } +// +// let eig = RealEigen::new(m.clone()).unwrap(); +// verify_eigenvectors(m, eig) +// } +// +// fn eigen_static_square_4x4(m: Matrix4) -> bool { +// let m = m.upper_triangle(); +// let eig = RealEigen::new(m.clone()).unwrap(); +// verify_eigenvectors(m, eig) +// } +// +// fn eigen_static_square_3x3(m: Matrix3) -> bool { +// let m = m.upper_triangle(); +// let eig = RealEigen::new(m.clone()).unwrap(); +// verify_eigenvectors(m, eig) +// } +// +// fn eigen_static_square_2x2(m: Matrix2) -> bool { +// let m = m.upper_triangle(); +// println!("{}", m); +// let eig = RealEigen::new(m.clone()).unwrap(); +// verify_eigenvectors(m, eig) +// } +// } +// +// fn verify_eigenvectors(m: MatrixN, mut eig: RealEigen) -> bool +// where DefaultAllocator: Allocator + +// Allocator + +// Allocator + +// Allocator, +// MatrixN: Display, +// VectorN: Display { +// let mv = &m * &eig.eigenvectors; +// +// println!("eigenvalues: {}eigenvectors: {}", eig.eigenvalues, eig.eigenvectors); +// +// let dim = m.nrows(); +// for i in 0 .. dim { +// let mut col = eig.eigenvectors.column_mut(i); +// col *= eig.eigenvalues[i]; +// } +// +// println!("{}{:.5}{:.5}", m, mv, eig.eigenvectors); +// +// relative_eq!(eig.eigenvectors, mv, epsilon = 1.0e-5) +// } diff --git a/tests/linalg/full_piv_lu.rs b/tests/linalg/full_piv_lu.rs index 06d87156..5a0ad75b 100644 --- a/tests/linalg/full_piv_lu.rs +++ b/tests/linalg/full_piv_lu.rs @@ -459,4 +459,4 @@ fn resize() { assert_eq!(add_del, m.resize(5, 2, 42)); assert_eq!(del_add, m.resize(1, 8, 42)); } -*/ \ No newline at end of file +*/ diff --git a/tests/linalg/svd.rs b/tests/linalg/svd.rs index 15054586..e84108ed 100644 --- a/tests/linalg/svd.rs +++ b/tests/linalg/svd.rs @@ -3,8 +3,10 @@ use na::{DMatrix, Matrix6}; #[cfg(feature = "arbitrary")] mod quickcheck_tests { + use na::{ + DMatrix, DVector, Matrix2, Matrix2x5, Matrix3, Matrix3x5, Matrix4, Matrix5x2, Matrix5x3, + }; use std::cmp; - use na::{DMatrix, Matrix2, Matrix3, Matrix4, Matrix5x2, Matrix5x3, Matrix2x5, Matrix3x5, DVector}; quickcheck! { fn svd(m: DMatrix) -> bool { @@ -258,7 +260,6 @@ fn svd_singular_horizontal() { assert!(relative_eq!(m, &u * ds * &v_t, epsilon = 1.0e-5)); } - #[test] fn svd_zeros() { let m = DMatrix::from_element(10, 10, 0.0); diff --git a/tests/sparse/cs_cholesky.rs b/tests/sparse/cs_cholesky.rs new file mode 100644 index 00000000..72a9a08f --- /dev/null +++ b/tests/sparse/cs_cholesky.rs @@ -0,0 +1,75 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] + +use na::{CsMatrix, CsVector, CsCholesky, Cholesky, Matrix5, Vector5}; + +#[test] +fn cs_cholesky() { + let mut a = Matrix5::new( + 40.0, 0.0, 0.0, 0.0, 0.0, + 2.0, 60.0, 0.0, 0.0, 0.0, + 1.0, 0.0, 11.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 50.0, 0.0, + 1.0, 0.0, 0.0, 4.0, 10.0 + ); + a.fill_upper_triangle_with_lower_triangle(); + test_cholesky(a); + + let a = Matrix5::from_diagonal(&Vector5::new(40.0, 60.0, 11.0, 50.0, 10.0)); + test_cholesky(a); + + let mut a = Matrix5::new( + 40.0, 0.0, 0.0, 0.0, 0.0, + 2.0, 60.0, 0.0, 0.0, 0.0, + 1.0, 0.0, 11.0, 0.0, 0.0, + 1.0, 0.0, 0.0, 50.0, 0.0, + 0.0, 0.0, 0.0, 4.0, 10.0 + ); + a.fill_upper_triangle_with_lower_triangle(); + test_cholesky(a); + + let mut a = Matrix5::new( + 2.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 2.0, 0.0, 0.0, 0.0, + 1.0, 1.0, 2.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 2.0, 0.0, + 1.0, 1.0, 0.0, 0.0, 2.0 + ); + a.fill_upper_triangle_with_lower_triangle(); + // Test ::new, left_looking, and up_looking implementations. + test_cholesky(a); +} + +fn test_cholesky(a: Matrix5) { + // Test ::new + test_cholesky_variant(a, 0); + // Test up-looking + test_cholesky_variant(a, 1); + // Test left-looking + test_cholesky_variant(a, 2); +} + +fn test_cholesky_variant(a: Matrix5, option: usize) { + let cs_a: CsMatrix<_, _, _> = a.into(); + + let chol_a = Cholesky::new(a).unwrap(); + let mut chol_cs_a; + + match option { + 0 => chol_cs_a = CsCholesky::new(&cs_a), + 1 => { + chol_cs_a = CsCholesky::new_symbolic(&cs_a); + chol_cs_a.decompose_up_looking(cs_a.data.values()); + } + _ => { + chol_cs_a = CsCholesky::new_symbolic(&cs_a); + chol_cs_a.decompose_left_looking(cs_a.data.values()); + } + }; + + let l = chol_a.l(); + let cs_l = chol_cs_a.unwrap_l().unwrap(); + assert!(cs_l.is_sorted()); + + let cs_l_mat: Matrix5<_> = cs_l.into(); + assert_relative_eq!(l, cs_l_mat); +} diff --git a/tests/sparse/cs_construction.rs b/tests/sparse/cs_construction.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/tests/sparse/cs_construction.rs @@ -0,0 +1 @@ + diff --git a/tests/sparse/cs_conversion.rs b/tests/sparse/cs_conversion.rs new file mode 100644 index 00000000..f08fe758 --- /dev/null +++ b/tests/sparse/cs_conversion.rs @@ -0,0 +1,89 @@ +use na::{CsMatrix, DMatrix, Matrix4x5}; + +#[test] +fn cs_from_to_matrix() { + #[cfg_attr(rustfmt, rustfmt_skip)] + let m = Matrix4x5::new( + 5.0, 6.0, 0.0, 8.0, 15.0, + 9.0, 10.0, 11.0, 12.0, 0.0, + 0.0, 0.0, 13.0, 0.0, 0.0, + 0.0, 1.0, 4.0, 0.0, 14.0, + ); + + let cs: CsMatrix<_, _, _> = m.into(); + assert!(cs.is_sorted()); + + let m2: Matrix4x5<_> = cs.into(); + assert_eq!(m2, m); +} + +#[test] +fn cs_matrix_from_triplet() { + let mut irows = vec![0, 0, 0, 0, 1, 1, 1, 1, 2, 3, 3, 3]; + let mut icols = vec![0, 1, 3, 4, 0, 1, 2, 3, 2, 1, 2, 4]; + let mut vals = vec![ + 5.0, 6.0, 8.0, 15.0, 9.0, 10.0, 11.0, 12.0, 13.0, 1.0, 4.0, 14.0, + ]; + + #[cfg_attr(rustfmt, rustfmt_skip)] + let expected = DMatrix::from_row_slice(4, 5, &[ + 5.0, 6.0, 0.0, 8.0, 15.0, + 9.0, 10.0, 11.0, 12.0, 0.0, + 0.0, 0.0, 13.0, 0.0, 0.0, + 0.0, 1.0, 4.0, 0.0, 14.0, + ]); + let cs_expected = CsMatrix::from_parts( + 4, + 5, + vec![0, 2, 5, 8, 10], + vec![0, 1, 0, 1, 3, 1, 2, 3, 0, 1, 0, 3], + vec![ + 5.0, 9.0, 6.0, 10.0, 1.0, 11.0, 13.0, 4.0, 8.0, 12.0, 15.0, 14.0, + ], + ); + + let cs_mat = CsMatrix::from_triplet(4, 5, &irows, &icols, &vals); + println!("Mat from triplet: {:?}", cs_mat); + assert!(cs_mat.is_sorted()); + assert_eq!(cs_mat, cs_expected); + + let m: DMatrix<_> = cs_mat.into(); + assert_eq!(m, expected); + + /* + * Try again with some permutations. + */ + let permutations = [(2, 5), (0, 4), (8, 10), (1, 11)]; + + for (i, j) in &permutations { + irows.swap(*i, *j); + icols.swap(*i, *j); + vals.swap(*i, *j); + } + + let cs_mat = CsMatrix::from_triplet(4, 5, &irows, &icols, &vals); + println!("Mat from triplet: {:?}", cs_mat); + assert!(cs_mat.is_sorted()); + assert_eq!(cs_mat, cs_expected); + + let m: DMatrix<_> = cs_mat.into(); + assert_eq!(m, expected); + + /* + * Try again, duplicating all entries. + */ + let mut ir = irows.clone(); + let mut ic = icols.clone(); + let mut va = vals.clone(); + irows.append(&mut ir); + icols.append(&mut ic); + vals.append(&mut va); + + let cs_mat = CsMatrix::from_triplet(4, 5, &irows, &icols, &vals); + println!("Mat from triplet: {:?}", cs_mat); + assert!(cs_mat.is_sorted()); + assert_eq!(cs_mat, cs_expected * 2.0); + + let m: DMatrix<_> = cs_mat.into(); + assert_eq!(m, expected * 2.0); +} diff --git a/tests/sparse/cs_matrix.rs b/tests/sparse/cs_matrix.rs new file mode 100644 index 00000000..b97260d4 --- /dev/null +++ b/tests/sparse/cs_matrix.rs @@ -0,0 +1,22 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] + +use na::{Matrix4x5, Matrix5x4, CsMatrix}; + +#[test] +fn cs_transpose() { + let m = Matrix4x5::new( + 4.0, 1.0, 4.0, 0.0, 9.0, + 5.0, 6.0, 0.0, 8.0, 10.0, + 9.0, 10.0, 11.0, 12.0, 0.0, + 0.0, 0.0, 1.0, 0.0, 10.0 + ); + + let cs: CsMatrix<_, _, _> = m.into(); + assert!(cs.is_sorted()); + + let cs_transposed = cs.transpose(); + assert!(cs_transposed.is_sorted()); + + let cs_transposed_mat: Matrix5x4<_> = cs_transposed.into(); + assert_eq!(cs_transposed_mat, m.transpose()) +} diff --git a/tests/sparse/cs_matrix_market.rs b/tests/sparse/cs_matrix_market.rs new file mode 100644 index 00000000..12414b37 --- /dev/null +++ b/tests/sparse/cs_matrix_market.rs @@ -0,0 +1,55 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] + + +use na::io; +use na::DMatrix; + +#[test] +fn cs_matrix_market() { + let file_str = r#" + %%MatrixMarket matrix coordinate real general +%================================================================================= +% +% This ASCII file represents a sparse MxN matrix with L +% nonzeros in the following Matrix Market format: +% +% +----------------------------------------------+ +% |%%MatrixMarket matrix coordinate real general | <--- header line +% |% | <--+ +% |% comments | |-- 0 or more comment lines +% |% | <--+ +% | M N L | <--- rows, columns, entries +% | I1 J1 A(I1, J1) | <--+ +% | I2 J2 A(I2, J2) | | +% | I3 J3 A(I3, J3) | |-- L lines +% | . . . | | +% | IL JL A(IL, JL) | <--+ +% +----------------------------------------------+ +% +% Indices are 1-based, i.e. A(1,1) is the first element. +% +%================================================================================= + 5 5 8 + 1 1 1.000e+00 + 2 2 1.050e+01 + 3 3 1.500e-02 + 1 4 6.000e+00 + 4 2 2.505e+02 + 4 4 -2.800e+02 + 4 5 3.332e+01 + 5 5 1.200e+01 +"#; + + let cs_mat = io::cs_matrix_from_matrix_market_str(file_str).unwrap(); + println!("CS mat: {:?}", cs_mat); + let mat: DMatrix<_> = cs_mat.into(); + let expected = DMatrix::from_row_slice(5, 5, &[ + 1.0, 0.0, 0.0, 6.0, 0.0, + 0.0, 10.5, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.015, 0.0, 0.0, + 0.0, 250.5, 0.0, -280.0, 33.32, + 0.0, 0.0, 0.0, 0.0, 12.0, + ]); + + assert_eq!(mat, expected); +} diff --git a/tests/sparse/cs_ops.rs b/tests/sparse/cs_ops.rs new file mode 100644 index 00000000..49dfc2bc --- /dev/null +++ b/tests/sparse/cs_ops.rs @@ -0,0 +1,72 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] + + +use na::{Matrix3x4, Matrix4x5, Matrix3x5, CsMatrix, Vector5, CsVector}; + +#[test] +fn axpy_cs() { + let mut v1 = Vector5::new(1.0, 2.0, 3.0, 4.0, 5.0); + let v2 = Vector5::new(10.0, 0.0, 30.0, 0.0, 50.0); + let expected = 5.0 * v2 + 10.0 * v1; + + let cs: CsVector<_, _> = v2.into(); + v1.axpy_cs(5.0, &cs, 10.0); + + assert!(cs.is_sorted()); + assert_eq!(v1, expected) +} + + +#[test] +fn cs_mat_mul() { + let m1 = Matrix3x4::new( + 0.0, 1.0, 4.0, 0.0, + 5.0, 6.0, 0.0, 8.0, + 9.0, 10.0, 11.0, 12.0, + ); + + let m2 = Matrix4x5::new( + 5.0, 6.0, 0.0, 8.0, 15.0, + 9.0, 10.0, 11.0, 12.0, 0.0, + 0.0, 0.0, 13.0, 0.0, 0.0, + 0.0, 1.0, 4.0, 0.0, 14.0, + ); + + let sm1: CsMatrix<_, _, _> = m1.into(); + let sm2: CsMatrix<_, _, _> = m2.into(); + + let mul = &sm1 * &sm2; + + assert!(sm1.is_sorted()); + assert!(sm2.is_sorted()); + assert!(mul.is_sorted()); + assert_eq!(Matrix3x5::from(mul), m1 * m2); +} + + +#[test] +fn cs_mat_add() { + let m1 = Matrix4x5::new( + 4.0, 1.0, 4.0, 0.0, 0.0, + 5.0, 6.0, 0.0, 8.0, 0.0, + 9.0, 10.0, 11.0, 12.0, 0.0, + 0.0, 0.0, 1.0, 0.0, 10.0 + ); + + let m2 = Matrix4x5::new( + 0.0, 1.0, 4.0, 0.0, 14.0, + 5.0, 6.0, 0.0, 8.0, 15.0, + 9.0, 10.0, 11.0, 12.0, 0.0, + 0.0, 0.0, 13.0, 0.0, 0.0, + ); + + let sm1: CsMatrix<_, _, _> = m1.into(); + let sm2: CsMatrix<_, _, _> = m2.into(); + + let sum = &sm1 + &sm2; + + assert!(sm1.is_sorted()); + assert!(sm2.is_sorted()); + assert!(sum.is_sorted()); + assert_eq!(Matrix4x5::from(sum), m1 + m2); +} diff --git a/tests/sparse/cs_solve.rs b/tests/sparse/cs_solve.rs new file mode 100644 index 00000000..b3415d79 --- /dev/null +++ b/tests/sparse/cs_solve.rs @@ -0,0 +1,106 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] + +use na::{CsMatrix, CsVector, Matrix5, Vector5}; + + +#[test] +fn cs_lower_triangular_solve() { + let a = Matrix5::new( + 4.0, 1.0, 4.0, 0.0, 9.0, + 5.0, 6.0, 0.0, 8.0, 10.0, + 9.0, 10.0, 11.0, 12.0, 0.0, + 0.0, -8.0, 3.0, 5.0, 9.0, + 0.0, 0.0, 1.0, 0.0, -10.0 + ); + let b = Vector5::new(1.0, 2.0, 3.0, 4.0, 5.0); + + let cs_a: CsMatrix<_, _, _> = a.into(); + + assert_eq!(cs_a.solve_lower_triangular(&b), a.solve_lower_triangular(&b)); +} + +#[test] +fn cs_tr_lower_triangular_solve() { + let a = Matrix5::new( + 4.0, 1.0, 4.0, 0.0, 9.0, + 5.0, 6.0, 0.0, 8.0, 10.0, + 9.0, 10.0, 11.0, 12.0, 0.0, + 0.0, -8.0, 3.0, 5.0, 9.0, + 0.0, 0.0, 1.0, 0.0, -10.0 + ); + let b = Vector5::new(1.0, 2.0, 3.0, 4.0, 5.0); + + let cs_a: CsMatrix<_, _, _> = a.into(); + + assert!(cs_a.tr_solve_lower_triangular(&b).is_some()); + assert_eq!(cs_a.tr_solve_lower_triangular(&b), a.tr_solve_lower_triangular(&b)); + + // Singular case. + let a = Matrix5::new( + 4.0, 1.0, 4.0, 0.0, 9.0, + 5.0, 6.0, 0.0, 8.0, 10.0, + 9.0, 10.0, 0.0, 12.0, 0.0, + 0.0, -8.0, 3.0, 5.0, 9.0, + 0.0, 0.0, 1.0, 0.0, -10.0 + ); + let cs_a: CsMatrix<_, _, _> = a.into(); + + assert!(cs_a.tr_solve_lower_triangular(&b).is_none()); +} + + +#[test] +fn cs_lower_triangular_solve_cs() { + let a = Matrix5::new( + 4.0, 1.0, 4.0, 0.0, 9.0, + 5.0, 6.0, 0.0, 8.0, 10.0, + 9.0, 10.0, 11.0, 12.0, 0.0, + 0.0, -8.0, 3.0, 5.0, 9.0, + 0.0, 0.0, 1.0, 0.0, -10.0 + ); + let b1 = Vector5::zeros(); + let b2 = Vector5::new(1.0, 2.0, 3.0, 4.0, 5.0); + let b3 = Vector5::new(1.0, 0.0, 0.0, 4.0, 0.0); + let b4 = Vector5::new(0.0, 1.0, 0.0, 4.0, 5.0); + let b5 = Vector5::x(); + let b6 = Vector5::y(); + let b7 = Vector5::z(); + let b8 = Vector5::w(); + let b9 = Vector5::a(); + + let cs_a: CsMatrix<_, _, _> = a.into(); + let cs_b1: CsVector<_, _> = Vector5::zeros().into(); + let cs_b2: CsVector<_, _> = Vector5::new(1.0, 2.0, 3.0, 4.0, 5.0).into(); + let cs_b3: CsVector<_, _> = Vector5::new(1.0, 0.0, 0.0, 4.0, 0.0).into(); + let cs_b4: CsVector<_, _> = Vector5::new(0.0, 1.0, 0.0, 4.0, 5.0).into(); + let cs_b5: CsVector<_, _> = Vector5::x().into(); + let cs_b6: CsVector<_, _> = Vector5::y().into(); + let cs_b7: CsVector<_, _> = Vector5::z().into(); + let cs_b8: CsVector<_, _> = Vector5::w().into(); + let cs_b9: CsVector<_, _> = Vector5::a().into(); + + assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b1).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b1)); + assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b5).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b5)); + assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b6).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b6)); + assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b7).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b7)); + assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b8).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b8)); + assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b9).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b9)); + assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b2).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b2)); + assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b3).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b3)); + assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b4).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b4)); + + + // Singular case. + let a = Matrix5::new( + 4.0, 1.0, 4.0, 0.0, 9.0, + 5.0, 0.0, 0.0, 8.0, 10.0, + 9.0, 10.0, 0.0, 12.0, 0.0, + 0.0, -8.0, 3.0, 5.0, 9.0, + 0.0, 0.0, 1.0, 0.0, -10.0 + ); + let cs_a: CsMatrix<_, _, _> = a.into(); + + assert!(cs_a.solve_lower_triangular_cs(&cs_b2).is_none()); + assert!(cs_a.solve_lower_triangular_cs(&cs_b3).is_none()); + assert!(cs_a.solve_lower_triangular_cs(&cs_b4).is_none()); +} diff --git a/tests/sparse/mod.rs b/tests/sparse/mod.rs new file mode 100644 index 00000000..df8e7e37 --- /dev/null +++ b/tests/sparse/mod.rs @@ -0,0 +1,8 @@ +mod cs_cholesky; +mod cs_construction; +mod cs_conversion; +mod cs_matrix; +#[cfg(feature = "io")] +mod cs_matrix_market; +mod cs_ops; +mod cs_solve;