Merge remote-tracking branch 'upstream/master' into Implement_convolution_#520

This commit is contained in:
Nathan 2019-02-23 08:29:41 -06:00
commit a3d571ea6b
108 changed files with 4698 additions and 938 deletions

View File

@ -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,6 +44,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
* Implement `Extend<Matrix<...>>` for matrices with dynamic storage. This will concatenate the columns of both matrices.
* Implement `Into<Vec>` 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
@ -47,6 +57,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
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).

View File

@ -1,6 +1,6 @@
[package]
name = "nalgebra"
version = "0.16.13"
version = "0.17.2"
authors = [ "Sébastien Crozet <developer@crozet.re>" ]
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"

View File

@ -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

View File

@ -1,6 +1,6 @@
[package]
name = "nalgebra-glm"
version = "0.2.1"
version = "0.3.0"
authors = ["sebcrozet <developer@crozet.re>"]
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 }

View File

@ -9,7 +9,7 @@ where DefaultAllocator: Alloc<N, D, D> {
TMat::<N, D, D>::identity()
}
/// Build a right hand look at view matrix
/// Build a look at view matrix based on the right handedness.
///
/// # Parameters:
///

View File

@ -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;

View File

@ -1,6 +1,6 @@
[package]
name = "nalgebra-lapack"
version = "0.8.0"
version = "0.9.0"
authors = [ "Sébastien Crozet <developer@crozet.re>", "Andrew Straw <strawman@astraw.com>" ]
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"

View File

@ -62,7 +62,7 @@ where DefaultAllocator: Allocator<N, D, D>
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.

View File

@ -127,7 +127,7 @@ where DefaultAllocator: Allocator<N, D, D> + Allocator<N, D>
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<N, D, D> + Allocator<N, D>
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<N, D, D> + Allocator<N, D>
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<N, D, D> + Allocator<N, D>
lapack_check!(info);
if wi.iter().all(|e| e.is_zero()) {
return Some(Eigen {
return Some(Self {
eigenvalues: wr,
left_eigenvectors: None,
eigenvectors: None,

View File

@ -48,7 +48,7 @@ impl<N: HessenbergScalar + Zero, D: DimSub<U1>> Hessenberg<N, D>
where DefaultAllocator: Allocator<N, D, D> + Allocator<N, DimDiff<D, U1>>
{
/// Computes the hessenberg decomposition of the matrix `m`.
pub fn new(mut m: MatrixN<N, D>) -> Hessenberg<N, D> {
pub fn new(mut m: MatrixN<N, D>) -> Self {
let nrows = m.data.shape().0;
let n = nrows.value() as i32;
@ -83,7 +83,7 @@ where DefaultAllocator: Allocator<N, D, D> + Allocator<N, DimDiff<D, U1>>
);
lapack_panic!(info);
Hessenberg { h: m, tau: tau }
Self { h: m, tau: tau }
}
/// Computes the hessenberg matrix of this decomposition.

View File

@ -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;

View File

@ -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.

View File

@ -54,14 +54,14 @@ where DefaultAllocator: Allocator<N, R, C>
+ Allocator<N, DimMinimum<R, C>>
{
/// Computes the QR decomposition of the matrix `m`.
pub fn new(mut m: MatrixMN<N, R, C>) -> QR<N, R, C> {
pub fn new(mut m: MatrixMN<N, R, C>) -> 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<N, R, C>
&mut info,
);
QR { qr: m, tau: tau }
Self { qr: m, tau: tau }
}
/// Retrieves the upper trapezoidal submatrix `R` of this decomposition.

View File

@ -62,7 +62,7 @@ where DefaultAllocator: Allocator<N, D, D> + Allocator<N, D>
pub fn new(m: MatrixN<N, D>) -> Self {
let (vals, vecs) =
Self::do_decompose(m, true).expect("SymmetricEigen: convergence failure.");
SymmetricEigen {
Self {
eigenvalues: vals,
eigenvectors: vecs.unwrap(),
}

View File

@ -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<N, D, U1, SliceStorage<'a, N, D, U1, Stride, D>>;
pub type VectorSliceN<'a, N, D, RStride = U1, CStride = D> =
Matrix<N, D, U1, SliceStorage<'a, N, D, U1, RStride, CStride>>;
/// 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<N, D, U1, SliceStorageMut<'a, N, D, U1, Stride, D>>;
pub type VectorSliceMutN<'a, N, D, RStride = U1, CStride = D> =
Matrix<N, D, U1, SliceStorageMut<'a, N, D, U1, RStride, CStride>>;
/// 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>;

View File

@ -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;
}

View File

@ -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<N: Scalar + PartialOrd + Signed, D: Dim, S: Storage<N, D>> Vector<N, D, S> {
/// Computes the index of the vector component with the largest value.
impl<N: Scalar + PartialOrd, D: Dim, S: Storage<N, D>> Vector<N, D, S> {
/// 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<N: Scalar + PartialOrd + Signed, D: Dim, S: Storage<N, D>> Vector<N, D, S>
}
}
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<N: Scalar + PartialOrd + Signed, D: Dim, S: Storage<N, D>> Vector<N, D, S>
/// 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<N: Scalar + PartialOrd + Signed, D: Dim, S: Storage<N, D>> Vector<N, D, S>
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<N: Scalar + PartialOrd + Signed, D: Dim, S: Storage<N, D>> Vector<N, D, S>
/// ```
#[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<N: Scalar + PartialOrd + Signed, D: Dim, S: Storage<N, D>> Vector<N, D, S>
/// 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,

View File

@ -130,8 +130,14 @@ impl<N: Real> Matrix4<N> {
/// 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<N>, target: &Point3<N>, up: &Vector3<N>) -> 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<N>, target: &Point3<N>, up: &Vector3<N>) -> Self {
IsometryMatrix3::new_observer_frame(eye, target, up).to_homogeneous()
Matrix4::face_towards(eye, target, up)
}
/// Builds a right-handed look-at view matrix.

View File

@ -1,4 +1,4 @@
// Non-conventional componentwise operators.
// Non-conventional component-wise operators.
use num::{Signed, Zero};
use std::ops::{Add, Mul};

View File

@ -270,7 +270,7 @@ where DefaultAllocator: Allocator<N, R, C>
/// 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<N>) -> Self {
Self::from_vec_generic($($gargs, )* data)
}
}
impl<N: Scalar, $($DimIdent: $DimBound, )*> MatrixMN<N $(, $Dims)*>
@ -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<N: Scalar, $($DimIdent: $DimBound, )*> MatrixMN<N $(, $Dims)*>
where DefaultAllocator: Allocator<N $(, $Dims)*> {
/// 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<N>) -> 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<N, ..., S>
=> R: DimName, => C: DimName; // Type parameters for impl<N, ..., S>
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.

View File

@ -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"))]

View File

@ -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<usize> 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<usize> 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<UInt<UInt<UInt<UInt<UInt<UInt<UInt<UTerm, B1>, A>, B>, C>, D>, E>, F>, G>
{}
{
}
impl<U: Unsigned + DimName, B: Bit + Any + Debug + Copy + PartialEq + Send + Sync> NamedDim
for UInt<U, B>
@ -405,4 +406,5 @@ impl<U: Unsigned + DimName, B: Bit + Any + Debug + Copy + PartialEq + Send + Syn
impl<U: Unsigned + DimName, B: Bit + Any + Debug + Copy + PartialEq + Send + Sync> IsNotStaticOne
for UInt<U, B>
{}
{
}

View File

@ -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<N: Scalar + Zero, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
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<N, R, C>
where DefaultAllocator: Allocator<N, R, C> {
@ -32,6 +38,54 @@ impl<N: Scalar + Zero, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
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<N, Dynamic, C>
where I: IntoIterator<Item = &'a usize>,
I::IntoIter: ExactSizeIterator + Clone,
DefaultAllocator: Allocator<N, Dynamic, C> {
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<N, R, Dynamic>
where I: IntoIterator<Item = &'a usize>,
I::IntoIter: ExactSizeIterator,
DefaultAllocator: Allocator<N, R, Dynamic> {
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<N: Scalar, R: Dim, C: Dim, S: StorageMut<N, R, C>> Matrix<N, R, C, S> {
@ -248,6 +302,7 @@ impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// 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<N, R, Dynamic>
where
C: DimSub<Dynamic, Output = Dynamic>,
@ -330,6 +385,7 @@ impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// 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<N, Dynamic, C>
where
R: DimSub<Dynamic, Output = Dynamic>,
@ -407,6 +463,7 @@ impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// 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<N, R, Dynamic>
where
C: DimAdd<Dynamic, Output = Dynamic>,
@ -484,6 +541,7 @@ impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// 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<N, Dynamic, C>
where
R: DimAdd<Dynamic, Output = Dynamic>,
@ -549,6 +607,29 @@ impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
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<N, Dynamic, C>
where DefaultAllocator: Reallocator<N, R, C, Dynamic, C> {
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<N, R, Dynamic>
where DefaultAllocator: Reallocator<N, R, C, R, Dynamic> {
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<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
}
}
#[cfg(any(feature = "std", feature = "alloc"))]
impl<N: Scalar> DMatrix<N> {
/// 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<N, Dynamic, Dynamic, Dynamic, Dynamic> {
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<N: Scalar, C: Dim> MatrixMN<N, Dynamic, C>
where DefaultAllocator: Allocator<N, Dynamic, C> {
/// 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<N, Dynamic, C, Dynamic, C> {
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<N: Scalar, R: Dim> MatrixMN<N, R, Dynamic>
where DefaultAllocator: Allocator<N, R, Dynamic> {
/// 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<N, R, Dynamic, R, Dynamic> {
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<N: Scalar>(
data: &mut [N],
nrows: usize,
@ -706,6 +842,7 @@ unsafe fn extend_rows<N: Scalar>(
/// Extend the number of columns of the `Matrix` with elements from
/// a given iterator.
#[cfg(any(feature = "std", feature = "alloc"))]
impl<N, R, S> Extend<N> for Matrix<N, R, Dynamic, S>
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<N, S> Extend<N> for Matrix<N, Dynamic, U1, S>
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<I: IntoIterator<Item=N>>(&mut self, iter: I) {
self.data.extend(iter);
}
}
#[cfg(any(feature = "std", feature = "alloc"))]
impl<N, R, S, RV, SV> Extend<Vector<N, RV, SV>> for Matrix<N, R, Dynamic, S>
where
N: Scalar,

View File

@ -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<N, R, C>> {
mat: &'a Matrix<N, R, C, S>,
curr: usize
}
impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + Storage<N, R, C>> RowIter<'a, N, R, C, S> {
pub(crate) fn new(mat: &'a Matrix<N, R, C, S>) -> Self {
RowIter {
mat, curr: 0
}
}
}
impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + Storage<N, R, C>> 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<Self::Item> {
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<usize>) {
(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<N, R, C>> 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<N, R, C>> {
mat: *mut Matrix<N, R, C, S>,
curr: usize,
phantom: PhantomData<&'a mut Matrix<N, R, C, S>>
}
impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + StorageMut<N, R, C>> RowIterMut<'a, N, R, C, S> {
pub(crate) fn new(mat: &'a mut Matrix<N, R, C, S>) -> 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<N, R, C>> 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<Self::Item> {
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<usize>) {
(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<N, R, C>> 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<N, R, C>> {
mat: &'a Matrix<N, R, C, S>,
curr: usize
}
impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + Storage<N, R, C>> ColumnIter<'a, N, R, C, S> {
pub(crate) fn new(mat: &'a Matrix<N, R, C, S>) -> Self {
ColumnIter {
mat, curr: 0
}
}
}
impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + Storage<N, R, C>> 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<Self::Item> {
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<usize>) {
(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<N, R, C>> 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<N, R, C>> {
mat: *mut Matrix<N, R, C, S>,
curr: usize,
phantom: PhantomData<&'a mut Matrix<N, R, C, S>>
}
impl<'a, N: Scalar, R: Dim, C: Dim, S: 'a + StorageMut<N, R, C>> ColumnIterMut<'a, N, R, C, S> {
pub(crate) fn new(mat: &'a mut Matrix<N, R, C, S>) -> 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<N, R, C>> 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<Self::Item> {
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<usize>) {
(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<N, R, C>> ExactSizeIterator for ColumnIterMut<'a, N, R, C, S> {
#[inline]
fn len(&self) -> usize {
self.ncols() - self.curr
}
}

View File

@ -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<N: Scalar, R: Dim, C: Dim, S> Matrix<N, R, C, S> {
impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// Creates a new matrix with the given data.
#[inline]
pub fn from_data(data: S) -> Matrix<N, R, C, S> {
pub fn from_data(data: S) -> Self {
unsafe { Self::from_data_statically_unchecked(data) }
}
@ -247,6 +247,37 @@ impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
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<N, R, C, S> {
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<N, R, C, S> {
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<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
}
}
/// 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<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
}
/// 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: Scalar, F: FnMut(usize, usize, N) -> N2>(
&self,
@ -493,6 +533,57 @@ impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
res
}
/// Folds a function `f` on each entry of `self`.
#[inline]
pub fn fold<Acc>(&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<N2, R2, C2, S2, Acc>(&self, rhs: &Matrix<N2, R2, C2, S2>, init: Acc, mut f: impl FnMut(Acc, N, N2) -> Acc) -> Acc
where
N2: Scalar,
R2: Dim,
C2: Dim,
S2: Storage<N2, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>
{
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<R2, C2, SB>(&self, out: &mut Matrix<N, R2, C2, SB>)
@ -540,6 +631,55 @@ impl<N: Scalar, R: Dim, C: Dim, S: StorageMut<N, R, C>> Matrix<N, R, C, S> {
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<N, R, C, S> {
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<N, R, C, S> {
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<N: Scalar, R: Dim, C: Dim, S: StorageMut<N, R, C>> Matrix<N, R, C, S> {
/// Replaces each component of `self` by the result of a closure `f` applied on it.
#[inline]
pub fn apply<F: FnMut(N) -> N>(&mut self, mut f: F)
where DefaultAllocator: Allocator<N, R, C> {
pub fn apply<F: FnMut(N) -> N>(&mut self, mut f: F) {
let (nrows, ncols) = self.shape();
for j in 0..ncols {
@ -646,6 +785,71 @@ impl<N: Scalar, R: Dim, C: Dim, S: StorageMut<N, R, C>> Matrix<N, R, C, S> {
}
}
}
/// 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<N2, R2, C2, S2>(&mut self, rhs: &Matrix<N2, R2, C2, S2>, mut f: impl FnMut(N, N2) -> N)
where N2: Scalar,
R2: Dim,
C2: Dim,
S2: Storage<N2, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2> {
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<N2, R2, C2, S2, N3, R3, C3, S3>(&mut self, b: &Matrix<N2, R2, C2, S2>, c: &Matrix<N3, R3, C3, S3>, mut f: impl FnMut(N, N2, N3) -> N)
where N2: Scalar,
R2: Dim,
C2: Dim,
S2: Storage<N2, R2, C2>,
N3: Scalar,
R3: Dim,
C3: Dim,
S3: Storage<N3, R3, C3>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2> {
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<N: Scalar, D: Dim, S: Storage<N, D>> Vector<N, D, S> {
@ -832,14 +1036,7 @@ impl<N: Scalar + Zero, D: DimAdd<U1>, S: Storage<N, D>> Vector<N, D, S> {
#[inline]
pub fn to_homogeneous(&self) -> VectorN<N, DimSum<D, U1>>
where DefaultAllocator: Allocator<N, DimSum<D, U1>> {
let len = self.len();
let hnrows = DimSum::<D, U1>::from_usize(len + 1);
let mut res = unsafe { VectorN::<N, _>::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<N: Scalar + Zero, D: DimAdd<U1>, S: Storage<N, D>> Vector<N, D, S> {
}
}
impl<N: Scalar + Zero, D: DimAdd<U1>, S: Storage<N, D>> Vector<N, D, S> {
/// Constructs a new vector of higher dimension by appending `element` to the end of `self`.
#[inline]
pub fn push(&self, element: N) -> VectorN<N, DimSum<D, U1>>
where DefaultAllocator: Allocator<N, DimSum<D, U1>> {
let len = self.len();
let hnrows = DimSum::<D, U1>::from_usize(len + 1);
let mut res = unsafe { VectorN::<N, _>::new_uninitialized_generic(hnrows, U1) };
res.generic_slice_mut((0, 0), self.data.shape())
.copy_from(self);
res[(len, 0)] = element;
res
}
}
impl<N, R: Dim, C: Dim, S> AbsDiffEq for Matrix<N, R, C, S>
where
N: Scalar + AbsDiffEq,
@ -1242,67 +1455,6 @@ impl<N: Real, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
}
}
impl<N: Real, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// 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<N, R, C>
where DefaultAllocator: Allocator<N, R, C> {
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<MatrixMN<N, R, C>>
where DefaultAllocator: Allocator<N, R, C> {
let n = self.norm();
if n <= min_norm {
None
} else {
Some(self / n)
}
}
}
impl<N: Scalar + Zero + One + ClosedAdd + ClosedSub + ClosedMul, D: Dim, S: Storage<N, D>>
Vector<N, D, S>
{
@ -1377,32 +1529,6 @@ impl<N: Real, D: Dim, S: Storage<N, D>> Unit<Vector<N, D, S>> {
}
}
impl<N: Real, R: Dim, C: Dim, S: StorageMut<N, R, C>> Matrix<N, R, C, S> {
/// 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<N> {
let n = self.norm();
if n <= min_norm {
None
} else {
*self /= n;
Some(n)
}
}
}
impl<N, R: Dim, C: Dim, S> AbsDiffEq for Unit<Matrix<N, R, C, S>>
where
N: Scalar + AbsDiffEq,

View File

@ -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<N, R: DimName, C: DimName> Inverse<Additive> for MatrixMN<N, R, C>
impl<N, R: DimName, C: DimName> TwoSidedInverse<Additive> for MatrixMN<N, R, C>
where
N: Scalar + ClosedNeg,
DefaultAllocator: Allocator<N, R, C>,
{
#[inline]
fn inverse(&self) -> MatrixMN<N, R, C> {
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<N: Real, R: DimName, C: DimName> FiniteDimInnerSpace for MatrixMN<N, R, C>
where DefaultAllocator: Allocator<N, R, C>
{
#[inline]
fn orthonormalize(vs: &mut [MatrixMN<N, R, C>]) -> usize {
fn orthonormalize(vs: &mut [Self]) -> usize {
let mut nbasis_elements = 0;
for i in 0..vs.len() {

View File

@ -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<N, R, C>
where DefaultAllocator: Allocator<N, R, C> {
@ -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<N, R, U1> for SliceStorage<'a, N, R, U1, U1, CStride> { }
unsafe impl<'a, N: Scalar, R: Dim, CStride: Dim> ContiguousStorage<N, R, U1> for SliceStorageMut<'a, N, R, U1, U1, CStride> { }
unsafe impl<'a, N: Scalar, R: Dim, CStride: Dim> ContiguousStorageMut<N, R, U1> for SliceStorageMut<'a, N, R, U1, U1, CStride> { }
unsafe impl<'a, N: Scalar, R: DimName, C: Dim + IsNotStaticOne> ContiguousStorage<N, R, C> for SliceStorage<'a, N, R, C, U1, R> { }
unsafe impl<'a, N: Scalar, R: DimName, C: Dim + IsNotStaticOne> ContiguousStorage<N, R, C> for SliceStorageMut<'a, N, R, C, U1, R> { }
unsafe impl<'a, N: Scalar, R: DimName, C: Dim + IsNotStaticOne> ContiguousStorageMut<N, R, C> for SliceStorageMut<'a, N, R, C, U1, R> { }
impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
#[inline]
fn assert_slice_index(
@ -859,3 +866,25 @@ impl<N: Scalar, R: Dim, C: Dim, S: StorageMut<N, R, C>> Matrix<N, R, C, S> {
self.slice_range_mut(.., cols)
}
}
impl<'a, N, R, C, RStride, CStride> From<MatrixSliceMut<'a, N, R, C, RStride, CStride>>
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) }
}
}

View File

@ -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::*;

238
src/base/norm.rs Normal file
View File

@ -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<N: Scalar> {
/// Apply this norm to the given matrix.
fn norm<R, C, S>(&self, m: &Matrix<N, R, C, S>) -> N
where R: Dim, C: Dim, S: Storage<N, R, C>;
/// Use the metric induced by this norm to compute the metric distance between the two given matrices.
fn metric_distance<R1, C1, S1, R2, C2, S2>(&self, m1: &Matrix<N, R1, C1, S1>, m2: &Matrix<N, R2, C2, S2>) -> N
where R1: Dim, C1: Dim, S1: Storage<N, R1, C1>,
R2: Dim, C2: Dim, S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2>;
}
/// 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<N: Real> Norm<N> for EuclideanNorm {
#[inline]
fn norm<R, C, S>(&self, m: &Matrix<N, R, C, S>) -> N
where R: Dim, C: Dim, S: Storage<N, R, C> {
m.norm_squared().sqrt()
}
#[inline]
fn metric_distance<R1, C1, S1, R2, C2, S2>(&self, m1: &Matrix<N, R1, C1, S1>, m2: &Matrix<N, R2, C2, S2>) -> N
where R1: Dim, C1: Dim, S1: Storage<N, R1, C1>,
R2: Dim, C2: Dim, S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2> {
m1.zip_fold(m2, N::zero(), |acc, a, b| {
let diff = a - b;
acc + diff * diff
}).sqrt()
}
}
impl<N: Real> Norm<N> for LpNorm {
#[inline]
fn norm<R, C, S>(&self, m: &Matrix<N, R, C, S>) -> N
where R: Dim, C: Dim, S: Storage<N, R, C> {
m.fold(N::zero(), |a, b| {
a + b.abs().powi(self.0)
}).powf(::convert(1.0 / (self.0 as f64)))
}
#[inline]
fn metric_distance<R1, C1, S1, R2, C2, S2>(&self, m1: &Matrix<N, R1, C1, S1>, m2: &Matrix<N, R2, C2, S2>) -> N
where R1: Dim, C1: Dim, S1: Storage<N, R1, C1>,
R2: Dim, C2: Dim, S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2> {
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<N: Scalar + PartialOrd + Signed> Norm<N> for UniformNorm {
#[inline]
fn norm<R, C, S>(&self, m: &Matrix<N, R, C, S>) -> N
where R: Dim, C: Dim, S: Storage<N, R, C> {
m.amax()
}
#[inline]
fn metric_distance<R1, C1, S1, R2, C2, S2>(&self, m1: &Matrix<N, R1, C1, S1>, m2: &Matrix<N, R2, C2, S2>) -> N
where R1: Dim, C1: Dim, S1: Storage<N, R1, C1>,
R2: Dim, C2: Dim, S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2> {
m1.zip_fold(m2, N::zero(), |acc, a, b| {
let val = (a - b).abs();
if val > acc {
val
} else {
acc
}
})
}
}
impl<N: Real, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// 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<R2, C2, S2>(&self, rhs: &Matrix<N, R2, C2, S2>) -> N
where R2: Dim, C2: Dim, S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2> {
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>) -> 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<R2, C2, S2>(&self, rhs: &Matrix<N, R2, C2, S2>, norm: &impl Norm<N>) -> N
where R2: Dim, C2: Dim, S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2> {
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<N, R, C>
where DefaultAllocator: Allocator<N, R, C> {
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<MatrixMN<N, R, C>>
where DefaultAllocator: Allocator<N, R, C> {
let n = self.norm();
if n <= min_norm {
None
} else {
Some(self / n)
}
}
}
impl<N: Real, R: Dim, C: Dim, S: StorageMut<N, R, C>> Matrix<N, R, C, S> {
/// 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<N> {
let n = self.norm();
if n <= min_norm {
None
} else {
*self /= n;
Some(n)
}
}
}

View File

@ -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<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Index<usize> for Matrix<N,
type Output = N;
#[inline]
fn index(&self, i: usize) -> &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<N, C: Dim> iter::Sum for MatrixMN<N, Dynamic, C>
where
N: Scalar + ClosedAdd + Zero,
DefaultAllocator: Allocator<N, Dynamic, C>,
{
/// # 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<f64>>(),
/// 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::<DMatrix<f64>>().sum::<DMatrix<f64>>(); // panics!
/// ```
fn sum<I: Iterator<Item = MatrixMN<N, Dynamic, C>>>(mut iter: I) -> MatrixMN<N, Dynamic, C> {
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<N, R, C>> for MatrixMN<N, R, C>
where
N: Scalar + ClosedAdd + Zero,
@ -394,6 +424,36 @@ where
}
}
impl<'a, N, C: Dim> iter::Sum<&'a MatrixMN<N, Dynamic, C>> for MatrixMN<N, Dynamic, C>
where
N: Scalar + ClosedAdd + Zero,
DefaultAllocator: Allocator<N, Dynamic, C>,
{
/// # Example
/// ```
/// # use nalgebra::DVector;
/// let v = &DVector::repeat(3, 1.0f64);
///
/// assert_eq!(vec![v, v, v].into_iter().sum::<DVector<f64>>(),
/// v + v + v);
/// ```
///
/// # Panics
/// Panics if the iterator is empty:
/// ```should_panic
/// # use std::iter;
/// # use nalgebra::DMatrix;
/// iter::empty::<&DMatrix<f64>>().sum::<DMatrix<f64>>(); // panics!
/// ```
fn sum<I: Iterator<Item = &'a MatrixMN<N, Dynamic, C>>>(mut iter: I) -> MatrixMN<N, Dynamic, C> {
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<N: Scalar + PartialOrd + Signed, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// 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<N: Scalar + PartialOrd + Signed, R: Dim, C: Dim, S: Storage<N, R, C>> 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<N: Scalar + PartialOrd + Signed, R: Dim, C: Dim, S: Storage<N, R, C>> 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
}
}

309
src/base/statistics.rs Normal file
View File

@ -0,0 +1,309 @@
use ::{Real, Dim, Matrix, VectorN, RowVectorN, DefaultAllocator, U1, VectorSliceN};
use storage::Storage;
use allocator::Allocator;
impl<N: Real, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// 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, R, S::RStride, S::CStride>) -> N) -> RowVectorN<N, C>
where DefaultAllocator: Allocator<N, U1, C> {
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, R, S::RStride, S::CStride>) -> N) -> VectorN<N, C>
where DefaultAllocator: Allocator<N, C> {
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<N, R>, f: impl Fn(&mut VectorN<N, R>, VectorSliceN<N, R, S::RStride, S::CStride>)) -> VectorN<N, R>
where DefaultAllocator: Allocator<N, R> {
let mut res = init;
for i in 0..self.ncols() {
f(&mut res, self.column(i))
}
res
}
}
impl<N: Real, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/*
*
* 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<N, C>
where DefaultAllocator: Allocator<N, U1, C> {
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<N, C>
where DefaultAllocator: Allocator<N, C> {
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<N, R>
where DefaultAllocator: Allocator<N, R> {
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<N, C>
where DefaultAllocator: Allocator<N, U1, C> {
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<N, C>
where DefaultAllocator: Allocator<N, C> {
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<N, R>
where DefaultAllocator: Allocator<N, R> {
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<N, C>
where DefaultAllocator: Allocator<N, U1, C> {
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<N, C>
where DefaultAllocator: Allocator<N, C> {
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<N, R>
where DefaultAllocator: Allocator<N, R> {
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())
})
}
}

View File

@ -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<T: NormedSpace> Unit<T> {
/// 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<T> Unit<T> {

View File

@ -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<N, R, C> = VecStorage<N, R, C>;
impl<N, R: Dim, C: Dim> VecStorage<N, R, C> {
/// Creates a new dynamic matrix data storage from the given vector and shape.
#[inline]
pub fn new(nrows: R, ncols: C, data: Vec<N>) -> VecStorage<N, R, C> {
pub fn new(nrows: R, ncols: C, data: Vec<N>) -> 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<N, R: Dim, C: Dim> VecStorage<N, R, C> {
/// The underlying data storage.
#[inline]
pub fn data(&self) -> &Vec<N> {
pub fn as_vec(&self) -> &Vec<N> {
&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<N> {
pub unsafe fn as_vec_mut(&mut self) -> &mut Vec<N> {
&mut self.data
}
@ -81,14 +81,11 @@ impl<N, R: Dim, C: Dim> VecStorage<N, R, C> {
self.data
}
}
impl<N, R: Dim, C: Dim> Deref for VecStorage<N, R, C> {
type Target = Vec<N>;
/// 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<N, Dynamic, C, Buffer = Self>
#[inline]
fn as_slice(&self) -> &[N] {
&self[..]
&self.data
}
}
@ -189,7 +186,7 @@ where DefaultAllocator: Allocator<N, R, Dynamic, Buffer = Self>
#[inline]
fn as_slice(&self) -> &[N] {
&self[..]
&self.data
}
}

View File

@ -100,7 +100,7 @@ where DefaultAllocator: Allocator<N, D>
{
#[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<N, D>
///
/// ```
/// # #[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<N, D>
/// 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<N, D>, rotation: R) -> Isometry<N, D, R> {
Isometry {
pub fn from_parts(translation: Translation<N, D>, rotation: R) -> Self {
Self {
rotation: rotation,
translation: translation,
_noconstruct: PhantomData,
@ -145,7 +144,7 @@ where DefaultAllocator: Allocator<N, D>
/// assert_eq!(inv * (iso * pt), pt);
/// ```
#[inline]
pub fn inverse(&self) -> Isometry<N, D, R> {
pub fn inverse(&self) -> Self {
let mut res = self.clone();
res.inverse_mut();
res
@ -167,7 +166,7 @@ where DefaultAllocator: Allocator<N, D>
/// ```
#[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<N, D>
///
/// ```
/// # #[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<N, D>
///
/// ```
/// # #[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<N, D>
///
/// ```
/// # #[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<N, D>,
{
#[inline]
fn eq(&self, right: &Isometry<N, D, R>) -> bool {
fn eq(&self, right: &Self) -> bool {
self.translation == right.translation && self.rotation == right.rotation
}
}

View File

@ -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<N: Real, D: DimName, R> Inverse<Multiplicative> for Isometry<N, D, R>
impl<N: Real, D: DimName, R> TwoSidedInverse<Multiplicative> for Isometry<N, D, R>
where
R: Rotation<Point<N, D>>,
DefaultAllocator: Allocator<N, D>,
{
#[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<N, D>;
#[inline]
fn decompose(&self) -> (Translation<N, D>, R, Id, R) {
fn decompose(&self) -> (Self::Translation, R, Id, R) {
(
self.translation.clone(),
self.rotation.clone(),

View File

@ -16,7 +16,7 @@ use base::{DefaultAllocator, Vector2, Vector3};
use geometry::{
Isometry, Point, Point3, Rotation, Rotation2, Rotation3, Translation, UnitComplex,
UnitQuaternion,
UnitQuaternion, Translation2, Translation3
};
impl<N: Real, D: DimName, R: AlgaRotation<Point<N, D>>> Isometry<N, D, R>
@ -49,7 +49,6 @@ where DefaultAllocator: Allocator<N, D>
///
/// ```
/// # #[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<N: Real> Isometry<N, U2, Rotation2<N>> {
Rotation::<N, U2>::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<N: Real> Isometry<N, U2, UnitComplex<N>> {
@ -153,6 +164,18 @@ impl<N: Real> Isometry<N, U2, UnitComplex<N>> {
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<N>) -> 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<N>,
pub fn face_towards(eye: &Point3<N>,
target: &Point3<N>,
up: &Vector3<N>)
-> 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<N>,
target: &Point3<N>,
up: &Vector3<N>)
-> 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);

View File

@ -200,8 +200,8 @@ isometry_binop_assign_impl_all!(
DivAssign, div_assign;
self: Isometry<N, D, R>, 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

View File

@ -37,6 +37,7 @@ mod translation_alga;
mod translation_alias;
mod translation_construction;
mod translation_conversion;
mod translation_coordinates;
mod translation_ops;
mod isometry;

View File

@ -26,7 +26,7 @@ impl<N: Real> Copy for Orthographic3<N> {}
impl<N: Real> Clone for Orthographic3<N> {
#[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<N> {
where Des: Deserializer<'a> {
let matrix = Matrix4::<N>::deserialize(deserializer)?;
Ok(Orthographic3::from_matrix_unchecked(matrix))
Ok(Self::from_matrix_unchecked(matrix))
}
}
@ -69,7 +69,6 @@ impl<N: Real> Orthographic3<N> {
/// # 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<N: Real> Orthographic3<N> {
/// ```
#[inline]
pub fn from_matrix_unchecked(matrix: Matrix4<N>) -> 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<N: Real> Orthographic3<N> {
/// # 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<N: Real> Orthographic3<N> {
/// # 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<N: Real> Orthographic3<N> {
///
/// ```
/// # #[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<N: Real> Orthographic3<N> {
///
/// ```
/// # #[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<N: Real> Orthographic3<N> {
///
/// ```
/// # #[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<N: Real> Orthographic3<N> {
///
/// ```
/// # #[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<N: Real> Orthographic3<N> {
///
/// ```
/// # #[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<N: Real> Orthographic3<N> {
///
/// ```
/// # #[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<N: Real> Orthographic3<N> {
/// # 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<N: Real> Orthographic3<N> {
/// # 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<N: Real> Orthographic3<N> {
/// # 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<N: Real> Orthographic3<N> {
///
/// ```
/// # #[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<N: Real> Orthographic3<N> {
///
/// ```
/// # #[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<N: Real> Orthographic3<N> {
///
/// ```
/// # #[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<N: Real> Orthographic3<N> {
///
/// ```
/// # #[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<N: Real> Orthographic3<N> {
///
/// ```
/// # #[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<N: Real> Orthographic3<N> {
///
/// ```
/// # #[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<N: Real> Orthographic3<N> {
///
/// ```
/// # #[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<N: Real> Orthographic3<N> {
///
/// ```
/// # #[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<N: Real> Orthographic3<N> {
///
/// ```
/// # #[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);

View File

@ -27,7 +27,7 @@ impl<N: Real> Copy for Perspective3<N> {}
impl<N: Real> Clone for Perspective3<N> {
#[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<N> {
where Des: Deserializer<'a> {
let matrix = Matrix4::<N>::deserialize(deserializer)?;
Ok(Perspective3::from_matrix_unchecked(matrix))
Ok(Self::from_matrix_unchecked(matrix))
}
}
@ -75,7 +75,7 @@ impl<N: Real> Perspective3<N> {
);
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<N: Real> Perspective3<N> {
/// projection.
#[inline]
pub fn from_matrix_unchecked(matrix: Matrix4<N>) -> Self {
Perspective3 { matrix: matrix }
Self { matrix: matrix }
}
/// Retrieves the inverse of the underlying homogeneous matrix.

View File

@ -66,7 +66,7 @@ where
where Des: Deserializer<'a> {
let coords = VectorN::<N, D>::deserialize(deserializer)?;
Ok(Point::from(coords))
Ok(Self::from(coords))
}
}
@ -126,8 +126,8 @@ where DefaultAllocator: Allocator<N, D>
/// Creates a new point with the given coordinates.
#[deprecated(note = "Use Point::from(vector) instead.")]
#[inline]
pub fn from_coordinates(coords: VectorN<N, D>) -> Point<N, D> {
Point { coords: coords }
pub fn from_coordinates(coords: VectorN<N, D>) -> Self {
Self { coords: coords }
}
/// The dimension of this point.

View File

@ -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))
}
}

View File

@ -145,7 +145,7 @@ where
{
#[inline]
fn arbitrary<G: Gen>(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<N, $D> {
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 <N: Scalar> From<[N; $len]> for Point<N, $D> {
fn from (coords: [N; $len]) -> Self {
Point {
Self {
coords: coords.into()
}
}

View File

@ -45,7 +45,7 @@ where
#[inline]
unsafe fn from_superset_unchecked(m: &Point<N2, D>) -> Self {
Point::from(Matrix::from_superset_unchecked(&m.coords))
Self::from(Matrix::from_superset_unchecked(&m.coords))
}
}

View File

@ -46,11 +46,11 @@ where DefaultAllocator: Allocator<N, D>
impl<N: Scalar + ClosedNeg, D: DimName> Neg for Point<N, D>
where DefaultAllocator: Allocator<N, D>
{
type Output = Point<N, D>;
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<N, D>
#[inline]
fn neg(self) -> Self::Output {
Point::from(-&self.coords)
Self::Output::from(-&self.coords)
}
}

View File

@ -68,7 +68,7 @@ impl<N: Real> Copy for Quaternion<N> {}
impl<N: Real> Clone for Quaternion<N> {
#[inline]
fn clone(&self) -> Self {
Quaternion::from(self.coords.clone())
Self::from(self.coords.clone())
}
}
@ -90,7 +90,7 @@ where Owned<N, U4>: Deserialize<'a>
where Des: Deserializer<'a> {
let coords = Vector4::<N>::deserialize(deserializer)?;
Ok(Quaternion::from(coords))
Ok(Self::from(coords))
}
}
@ -98,15 +98,15 @@ impl<N: Real> Quaternion<N> {
/// 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<N> {
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<N> {
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<N: Real> Quaternion<N> {
/// # 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<N> {
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<N: Real> Quaternion<N> {
/// assert!(conj.i == -2.0 && conj.j == -3.0 && conj.k == -4.0 && conj.w == 1.0);
/// ```
#[inline]
pub fn conjugate(&self) -> Quaternion<N> {
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<N: Real> Quaternion<N> {
/// # 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<N: Real> Quaternion<N> {
/// assert!(inv_q.is_none());
/// ```
#[inline]
pub fn try_inverse(&self) -> Option<Quaternion<N>> {
let mut res = Quaternion::from(self.coords.clone_owned());
pub fn try_inverse(&self) -> Option<Self> {
let mut res = Self::from(self.coords.clone_owned());
if res.try_inverse_mut() {
Some(res)
@ -188,7 +186,7 @@ impl<N: Real> Quaternion<N> {
/// assert_eq!(q1.lerp(&q2, 0.1), Quaternion::new(1.9, 3.8, 5.7, 7.6));
/// ```
#[inline]
pub fn lerp(&self, other: &Quaternion<N>, t: N) -> Quaternion<N> {
pub fn lerp(&self, other: &Self, t: N) -> Self {
self * (N::one() - t) + other * t
}
@ -240,7 +238,6 @@ impl<N: Real> Quaternion<N> {
/// # 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<N: Real> Quaternion<N> {
/// # 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<N: Real> Quaternion<N> {
/// # 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<N> {
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<N: Real> Quaternion<N> {
/// # 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<N> {
pub fn exp(&self) -> Self {
self.exp_eps(N::default_epsilon())
}
@ -380,7 +374,6 @@ impl<N: Real> Quaternion<N> {
/// # 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<N: Real> Quaternion<N> {
/// assert_eq!(q.exp_eps(1.0e-6), Quaternion::identity());
/// ```
#[inline]
pub fn exp_eps(&self, eps: N) -> Quaternion<N> {
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<N: Real> Quaternion<N> {
/// # 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<N> {
pub fn powf(&self, n: N) -> Self {
(self.ln() * n).exp()
}
@ -476,7 +468,6 @@ impl<N: Real> Quaternion<N> {
/// # 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<N: Real> Quaternion<N> {
/// # 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();
@ -587,7 +577,7 @@ impl<N: Real> UnitQuaternion<N> {
#[deprecated(
note = "This method is unnecessary and will be removed in a future release. Use `.clone()` instead."
)]
pub fn into_owned(self) -> UnitQuaternion<N> {
pub fn into_owned(self) -> Self {
self
}
@ -596,7 +586,7 @@ impl<N: Real> UnitQuaternion<N> {
#[deprecated(
note = "This method is unnecessary and will be removed in a future release. Use `.clone()` instead."
)]
pub fn clone_owned(&self) -> UnitQuaternion<N> {
pub fn clone_owned(&self) -> Self {
*self
}
@ -647,8 +637,8 @@ impl<N: Real> UnitQuaternion<N> {
/// assert_eq!(conj, UnitQuaternion::from_axis_angle(&-axis, 1.78));
/// ```
#[inline]
pub fn conjugate(&self) -> UnitQuaternion<N> {
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<N: Real> UnitQuaternion<N> {
/// assert_eq!(inv * rot, UnitQuaternion::identity());
/// ```
#[inline]
pub fn inverse(&self) -> UnitQuaternion<N> {
pub fn inverse(&self) -> Self {
self.conjugate()
}
@ -672,14 +662,13 @@ impl<N: Real> UnitQuaternion<N> {
/// # 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>) -> N {
pub fn angle_to(&self, other: &Self) -> N {
let delta = self.rotation_to(other);
delta.angle()
}
@ -691,7 +680,6 @@ impl<N: Real> UnitQuaternion<N> {
/// # 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<N: Real> UnitQuaternion<N> {
/// assert_relative_eq!(rot_to * rot1, rot2, epsilon = 1.0e-6);
/// ```
#[inline]
pub fn rotation_to(&self, other: &UnitQuaternion<N>) -> UnitQuaternion<N> {
pub fn rotation_to(&self, other: &Self) -> Self{
other / self
}
@ -715,7 +703,7 @@ impl<N: Real> UnitQuaternion<N> {
/// assert_eq!(q1.lerp(&q2, 0.1), Quaternion::new(0.9, 0.1, 0.0, 0.0));
/// ```
#[inline]
pub fn lerp(&self, other: &UnitQuaternion<N>, t: N) -> Quaternion<N> {
pub fn lerp(&self, other: &Self, t: N) -> Quaternion<N> {
self.as_ref().lerp(other.as_ref(), t)
}
@ -731,11 +719,11 @@ impl<N: Real> UnitQuaternion<N> {
/// 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<N>, t: N) -> UnitQuaternion<N> {
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<N: Real> UnitQuaternion<N> {
/// 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<N>, t: N) -> UnitQuaternion<N> {
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<N: Real> UnitQuaternion<N> {
#[inline]
pub fn try_slerp(
&self,
other: &UnitQuaternion<N>,
other: &Self,
t: N,
epsilon: N,
) -> Option<UnitQuaternion<N>>
) -> Option<Self>
{
Unit::new_unchecked(self.coords)
.try_slerp(&Unit::new_unchecked(other.coords), t, epsilon)
@ -785,7 +773,6 @@ impl<N: Real> UnitQuaternion<N> {
/// # 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<N: Real> UnitQuaternion<N> {
/// # 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<N: Real> UnitQuaternion<N> {
/// # 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<N: Real> UnitQuaternion<N> {
/// # 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<N: Real> UnitQuaternion<N> {
/// assert_eq!(pow.angle(), 2.4);
/// ```
#[inline]
pub fn powf(&self, n: N) -> UnitQuaternion<N> {
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<N: Real> UnitQuaternion<N> {
///
/// ```
/// # #[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<N: Real> UnitQuaternion<N> {
/// # 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<N: Real> UnitQuaternion<N> {
///
/// ```
/// # #[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);

View File

@ -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<N: Real> AbstractMagma<Additive> for Quaternion<N> {
}
}
impl<N: Real> Inverse<Additive> for Quaternion<N> {
impl<N: Real> TwoSidedInverse<Additive> for Quaternion<N> {
#[inline]
fn inverse(&self) -> Self {
fn two_sided_inverse(&self) -> Self {
-self
}
}
@ -173,14 +173,14 @@ impl<N: Real> AbstractMagma<Multiplicative> for UnitQuaternion<N> {
}
}
impl<N: Real> Inverse<Multiplicative> for UnitQuaternion<N> {
impl<N: Real> TwoSidedInverse<Multiplicative> for UnitQuaternion<N> {
#[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()
}
}

View File

@ -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<N: Real> Quaternion<N> {
/// Creates a quaternion from a 4D vector. The quaternion scalar part corresponds to the `w`
@ -25,7 +25,7 @@ impl<N: Real> Quaternion<N> {
#[inline]
#[deprecated(note = "Use `::from` instead.")]
pub fn from_vector(vector: Vector4<N>) -> 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<N, U4>: Send
{
#[inline]
fn arbitrary<G: Gen>(g: &mut G) -> Self {
Quaternion::new(
Self::new(
N::arbitrary(g),
N::arbitrary(g),
N::arbitrary(g),
@ -166,7 +166,6 @@ impl<N: Real> UnitQuaternion<N> {
/// # 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<N: Real> UnitQuaternion<N> {
/// # 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<N: Real> UnitQuaternion<N> {
/// # 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<N: Real> UnitQuaternion<N> {
/// assert_relative_eq!(q.angle(), rot.angle(), epsilon = 1.0e-6);
/// ```
#[inline]
pub fn from_rotation_matrix(rotmat: &Rotation<N, U3>) -> Self {
pub fn from_rotation_matrix(rotmat: &Rotation3<N>) -> 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<N: Real> UnitQuaternion<N> {
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<N>) -> 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<N>, 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<N: Real> UnitQuaternion<N> {
/// # 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<N: Real> UnitQuaternion<N> {
/// # 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<N: Real> UnitQuaternion<N> {
/// # 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<N: Real> UnitQuaternion<N> {
/// # 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<SB, SC>(dir: &Vector<N, U3, SB>, up: &Vector<N, U3, SC>) -> Self
pub fn face_towards<SB, SC>(dir: &Vector<N, U3, SB>, up: &Vector<N, U3, SC>) -> Self
where
SB: Storage<N, U3>,
SC: Storage<N, U3>,
{
Self::from_rotation_matrix(&Rotation::<N, U3>::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<SB, SC>(dir: &Vector<N, U3, SB>, up: &Vector<N, U3, SC>) -> Self
where
SB: Storage<N, U3>,
SC: Storage<N, U3>,
{
Self::face_towards(dir, up)
}
/// Builds a right-handed look-at view matrix without translation.
@ -478,7 +506,6 @@ impl<N: Real> UnitQuaternion<N> {
/// # 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<N: Real> UnitQuaternion<N> {
SB: Storage<N, U3>,
SC: Storage<N, U3>,
{
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<N: Real> UnitQuaternion<N> {
/// # 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<N: Real> UnitQuaternion<N> {
SB: Storage<N, U3>,
SC: Storage<N, U3>,
{
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<N: Real> UnitQuaternion<N> {
/// # 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<N: Real> UnitQuaternion<N> {
/// # 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<N: Real> UnitQuaternion<N> {
/// # 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<N: Real> UnitQuaternion<N> {
/// # 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: Gen>(g: &mut G) -> Self {
let axisangle = Vector3::arbitrary(g);
UnitQuaternion::from_scaled_axis(axisangle)
Self::from_scaled_axis(axisangle)
}
}

View File

@ -103,7 +103,7 @@ impl<N1, N2, R> SubsetOf<Isometry<N2, U3, R>> for UnitQuaternion<N1>
where
N1: Real,
N2: Real + SupersetOf<N1>,
R: AlgaRotation<Point3<N2>> + SupersetOf<UnitQuaternion<N1>>,
R: AlgaRotation<Point3<N2>> + SupersetOf<Self>,
{
#[inline]
fn to_superset(&self) -> Isometry<N2, U3, R> {
@ -125,7 +125,7 @@ impl<N1, N2, R> SubsetOf<Similarity<N2, U3, R>> for UnitQuaternion<N1>
where
N1: Real,
N2: Real + SupersetOf<N1>,
R: AlgaRotation<Point3<N2>> + SupersetOf<UnitQuaternion<N1>>,
R: AlgaRotation<Point3<N2>> + SupersetOf<Self>,
{
#[inline]
fn to_superset(&self) -> Similarity<N2, U3, R> {
@ -186,7 +186,7 @@ impl<N1: Real, N2: Real + SupersetOf<N1>> SubsetOf<Matrix4<N2>> for UnitQuaterni
#[cfg(feature = "mint")]
impl<N: Real> From<mint::Quaternion<N>> for Quaternion<N> {
fn from(q: mint::Quaternion<N>) -> 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<N: Real> Into<mint::Quaternion<N>> for UnitQuaternion<N> {
impl<N: Real> From<UnitQuaternion<N>> for Matrix4<N> {
#[inline]
fn from(q: UnitQuaternion<N>) -> Matrix4<N> {
fn from(q: UnitQuaternion<N>) -> Self {
q.to_homogeneous()
}
}
impl<N: Real> From<UnitQuaternion<N>> for Rotation3<N> {
#[inline]
fn from(q: UnitQuaternion<N>) -> Self {
q.to_rotation_matrix()
}
}
impl<N: Real> From<Rotation3<N>> for UnitQuaternion<N> {
#[inline]
fn from(q: Rotation3<N>) -> Self {
Self::from_rotation_matrix(&q)
}
}
impl<N: Real> From<UnitQuaternion<N>> for Matrix3<N> {
#[inline]
fn from(q: UnitQuaternion<N>) -> Matrix3<N> {
fn from(q: UnitQuaternion<N>) -> Self {
q.to_rotation_matrix().into_inner()
}
}
@ -235,6 +249,6 @@ impl<N: Real> From<UnitQuaternion<N>> for Matrix3<N> {
impl<N: Real> From<Vector4<N>> for Quaternion<N> {
#[inline]
fn from(coords: Vector4<N>) -> Self {
Quaternion { coords }
Self { coords }
}
}

View File

@ -67,7 +67,7 @@ impl<N: Real> Index<usize> for Quaternion<N> {
type Output = N;
#[inline]
fn index(&self, i: usize) -> &N {
fn index(&self, i: usize) -> &Self::Output {
&self.coords[i]
}
}

View File

@ -18,8 +18,8 @@ impl<N: Real, D: Dim, S: Storage<N, D>> Reflection<N, D, S> {
///
/// 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<Vector<N, D, S>>, bias: N) -> Reflection<N, D, S> {
Reflection {
pub fn new(axis: Unit<Vector<N, D, S>>, bias: N) -> Self {
Self {
axis: axis.into_inner(),
bias: bias,
}
@ -30,7 +30,7 @@ impl<N: Real, D: Dim, S: Storage<N, D>> Reflection<N, D, S> {
pub fn new_containing_point(
axis: Unit<Vector<N, D, S>>,
pt: &Point<N, D>,
) -> Reflection<N, D, S>
) -> Self
where
D: DimName,
DefaultAllocator: Allocator<N, D>,

View File

@ -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::<N, D>::deserialize(deserializer)?;
Ok(Rotation::from_matrix_unchecked(matrix))
Ok(Self::from_matrix_unchecked(matrix))
}
}
@ -241,13 +241,13 @@ where DefaultAllocator: Allocator<N, D, D>
/// assert_eq!(*rot.matrix(), mat);
/// ```
#[inline]
pub fn from_matrix_unchecked(matrix: MatrixN<N, D>) -> Rotation<N, D> {
pub fn from_matrix_unchecked(matrix: MatrixN<N, D>) -> 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<N, D, D>
/// # 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<N, D, D>
/// assert_relative_eq!(tr_rot * rot, Rotation2::identity(), epsilon = 1.0e-6);
/// ```
#[inline]
pub fn transpose(&self) -> Rotation<N, D> {
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<N, D, D>
/// # 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<N, D, D>
/// assert_relative_eq!(inv * rot, Rotation2::identity(), epsilon = 1.0e-6);
/// ```
#[inline]
pub fn inverse(&self) -> Rotation<N, D> {
pub fn inverse(&self) -> Self {
self.transpose()
}
@ -305,7 +303,6 @@ where DefaultAllocator: Allocator<N, D, D>
/// # 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<N, D, D>
/// # 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<N: Scalar + PartialEq, D: DimName> PartialEq for Rotation<N, D>
where DefaultAllocator: Allocator<N, D, D>
{
#[inline]
fn eq(&self, right: &Rotation<N, D>) -> bool {
fn eq(&self, right: &Self) -> bool {
self.matrix == right.matrix
}
}

View File

@ -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<N, D, D>
}
}
impl<N: Real, D: DimName> Inverse<Multiplicative> for Rotation<N, D>
impl<N: Real, D: DimName> TwoSidedInverse<Multiplicative> for Rotation<N, D>
where DefaultAllocator: Allocator<N, D, D>
{
#[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()
}
}

View File

@ -102,7 +102,7 @@ impl<N1, N2, D: DimName, R> SubsetOf<Isometry<N2, D, R>> for Rotation<N1, D>
where
N1: Real,
N2: Real + SupersetOf<N1>,
R: AlgaRotation<Point<N2, D>> + SupersetOf<Rotation<N1, D>>,
R: AlgaRotation<Point<N2, D>> + SupersetOf<Self>,
DefaultAllocator: Allocator<N1, D, D> + Allocator<N2, D>,
{
#[inline]
@ -125,7 +125,7 @@ impl<N1, N2, D: DimName, R> SubsetOf<Similarity<N2, D, R>> for Rotation<N1, D>
where
N1: Real,
N2: Real + SupersetOf<N1>,
R: AlgaRotation<Point<N2, D>> + SupersetOf<Rotation<N1, D>>,
R: AlgaRotation<Point<N2, D>> + SupersetOf<Self>,
DefaultAllocator: Allocator<N1, D, D> + Allocator<N2, D>,
{
#[inline]
@ -219,28 +219,28 @@ impl<N: Real> From<mint::EulerAngles<N, mint::IntraXYZ>> for Rotation3<N> {
impl<N: Real> From<Rotation2<N>> for Matrix3<N> {
#[inline]
fn from(q: Rotation2<N>) -> Matrix3<N> {
fn from(q: Rotation2<N>) ->Self {
q.to_homogeneous()
}
}
impl<N: Real> From<Rotation2<N>> for Matrix2<N> {
#[inline]
fn from(q: Rotation2<N>) -> Matrix2<N> {
fn from(q: Rotation2<N>) -> Self {
q.into_inner()
}
}
impl<N: Real> From<Rotation3<N>> for Matrix4<N> {
#[inline]
fn from(q: Rotation3<N>) -> Matrix4<N> {
fn from(q: Rotation3<N>) -> Self {
q.to_homogeneous()
}
}
impl<N: Real> From<Rotation3<N>> for Matrix3<N> {
#[inline]
fn from(q: Rotation3<N>) -> Matrix3<N> {
fn from(q: Rotation3<N>) -> Self {
q.into_inner()
}
}

View File

@ -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<N: Real> Rotation2<N> {
///
/// ```
/// # #[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<N: Real> Rotation2<N> {
/// ```
pub fn new(angle: N) -> Self {
let (sia, coa) = angle.sin_cos();
Self::from_matrix_unchecked(MatrixN::<N, U2>::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<N: Real> Rotation2<N> {
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<N>) -> 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<N>, 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<N: Real> Rotation2<N> {
/// # 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<N: Real> Rotation2<N> {
/// # 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<N: Real> Rotation2<N> {
{
::convert(UnitComplex::scaled_rotation_between(a, b, s).to_rotation_matrix())
}
}
impl<N: Real> Rotation2<N> {
/// 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<N: Real> Rotation2<N> {
/// # 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>) -> N {
pub fn angle_to(&self, other: &Self) -> N {
self.rotation_to(other).angle()
}
@ -141,7 +179,6 @@ impl<N: Real> Rotation2<N> {
/// # 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<N: Real> Rotation2<N> {
/// assert_relative_eq!(rot_to.inverse() * rot2, rot1);
/// ```
#[inline]
pub fn rotation_to(&self, other: &Rotation2<N>) -> Rotation2<N> {
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<N> {
pub fn powf(&self, n: N) -> Self {
Self::new(self.angle() * n)
}
@ -217,7 +267,6 @@ impl<N: Real> Rotation3<N> {
/// # 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<N: Real> Rotation3<N> {
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<N>) -> 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<N>, 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<N: Real> Rotation3<N> {
/// # 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<N: Real> Rotation3<N> {
/// # 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<N: Real> Rotation3<N> {
/// # 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<N: Real> Rotation3<N> {
/// # 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<N: Real> Rotation3<N> {
}
}
/// 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<N: Real> Rotation3<N> {
/// # 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<SB, SC>(dir: &Vector<N, U3, SB>, up: &Vector<N, U3, SC>) -> Self
pub fn face_towards<SB, SC>(dir: &Vector<N, U3, SB>, up: &Vector<N, U3, SC>) -> Self
where
SB: Storage<N, U3>,
SC: Storage<N, U3>,
@ -427,6 +529,16 @@ impl<N: Real> Rotation3<N> {
))
}
/// Deprecated: Use [Rotation3::face_towards] instead.
#[deprecated(note="renamed to `face_towards`")]
pub fn new_observer_frames<SB, SC>(dir: &Vector<N, U3, SB>, up: &Vector<N, U3, SC>) -> Self
where
SB: Storage<N, U3>,
SC: Storage<N, U3>,
{
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<N: Real> Rotation3<N> {
/// # 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<N: Real> Rotation3<N> {
SB: Storage<N, U3>,
SC: Storage<N, U3>,
{
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<N: Real> Rotation3<N> {
/// # 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<N: Real> Rotation3<N> {
SB: Storage<N, U3>,
SC: Storage<N, U3>,
{
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<N: Real> Rotation3<N> {
/// # 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<N: Real> Rotation3<N> {
/// # 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<N: Real> Rotation3<N> {
/// # 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<N: Real> Rotation3<N> {
/// # 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<N: Real> Rotation3<N> {
/// # 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<N: Real> Rotation3<N> {
/// # 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<N: Real> Rotation3<N> {
/// # 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>) -> N {
pub fn angle_to(&self, other: &Self) -> N {
self.rotation_to(other).angle()
}
@ -678,7 +781,6 @@ impl<N: Real> Rotation3<N> {
/// # 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<N: Real> Rotation3<N> {
/// assert_relative_eq!(rot_to * rot1, rot2, epsilon = 1.0e-6);
/// ```
#[inline]
pub fn rotation_to(&self, other: &Rotation3<N>) -> Rotation3<N> {
pub fn rotation_to(&self, other: &Self) -> Self {
other * self.inverse()
}
@ -696,7 +798,6 @@ impl<N: Real> Rotation3<N> {
/// # 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<N: Real> Rotation3<N> {
/// assert_eq!(pow.angle(), 2.4);
/// ```
#[inline]
pub fn powf(&self, n: N) -> Rotation3<N> {
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() {

View File

@ -106,20 +106,20 @@ where
translation: Translation<N, D>,
rotation: R,
scaling: N,
) -> Similarity<N, D, R>
) -> 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<N, D, R>, scaling: N) -> Similarity<N, D, R> {
pub fn from_isometry(isometry: Isometry<N, D, R>, 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<N, D, R> {
pub fn from_scaling(scaling: N) -> Self {
Self::from_isometry(Isometry::identity(), scaling)
}
/// Inverts `self`.
#[inline]
pub fn inverse(&self) -> Similarity<N, D, R> {
pub fn inverse(&self) -> Self {
let mut res = self.clone();
res.inverse_mut();
res
@ -277,7 +277,7 @@ where
DefaultAllocator: Allocator<N, D>,
{
#[inline]
fn eq(&self, right: &Similarity<N, D, R>) -> bool {
fn eq(&self, right: &Self) -> bool {
self.isometry == right.isometry && self.scaling == right.scaling
}
}

View File

@ -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<N: Real, D: DimName, R> Inverse<Multiplicative> for Similarity<N, D, R>
impl<N: Real, D: DimName, R> TwoSidedInverse<Multiplicative> for Similarity<N, D, R>
where
R: Rotation<Point<N, D>>,
DefaultAllocator: Allocator<N, D>,
{
#[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()
}
}

View File

@ -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<N: Real> Similarity<N, U2, Rotation2<N>> {
///
/// ```
/// # #[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<N: Real> Similarity<N, U2, UnitComplex<N>> {
///
/// ```
/// # #[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<N>,
pub fn face_towards(eye: &Point3<N>,
target: &Point3<N>,
up: &Vector3<N>,
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<N>,
target: &Point3<N>,
up: &Vector3<N>,
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);

View File

@ -222,8 +222,8 @@ similarity_binop_assign_impl_all!(
DivAssign, div_assign;
self: Similarity<N, D, R>, 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

View File

@ -350,7 +350,6 @@ where DefaultAllocator: Allocator<N, DimNameSum<D, U1>, DimNameSum<D, U1>>
/// # 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<N, DimNameSum<D, U1>, DimNameSum<D, U1>>
/// # 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<N, DimNameSum<D, U1>, DimNameSum<D, U1>>
/// # 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<N, DimNameSum<D, U1>, DimNameSum<D, U1>>
/// # Examples
/// ```
/// # #[macro_use] extern crate approx;
/// # extern crate nalgebra;
/// # use nalgebra::{Matrix3, Projective2};
///
/// let m = Matrix3::new(2.0, 2.0, -0.3,

View File

@ -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<N: Real, D: DimNameAdd<U1>, C> Inverse<Multiplicative> for Transform<N, D, C>
impl<N: Real, D: DimNameAdd<U1>, C> TwoSidedInverse<Multiplicative> for Transform<N, D, C>
where
C: SubTCategoryOf<TProjective>,
DefaultAllocator: Allocator<N, DimNameSum<D, U1>, DimNameSum<D, U1>>,
{
#[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<N, D>) -> Point<N, D> {
self.inverse() * pt
self.two_sided_inverse() * pt
}
#[inline]
fn inverse_transform_vector(&self, v: &VectorN<N, D>) -> VectorN<N, D> {
self.inverse() * v
self.two_sided_inverse() * v
}
}

View File

@ -57,7 +57,7 @@ where
#[inline]
unsafe fn from_superset_unchecked(m: &MatrixN<N2, DimNameSum<D, U1>>) -> Self {
Transform::from_matrix_unchecked(::convert_ref_unchecked(m))
Self::from_matrix_unchecked(::convert_ref_unchecked(m))
}
}

View File

@ -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<N, D>
}
}
impl<N: Real, D: DimName> Inverse<Multiplicative> for Translation<N, D>
impl<N: Real, D: DimName> TwoSidedInverse<Multiplicative> for Translation<N, D>
where DefaultAllocator: Allocator<N, D>
{
#[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()
}
}

View File

@ -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<N: Scalar> Deref for Translation<N, $D>
where DefaultAllocator: Allocator<N, $D> {
type Target = $Target<N>;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { mem::transmute(self) }
}
}
impl<N: Scalar> DerefMut for Translation<N, $D>
where DefaultAllocator: Allocator<N, $D> {
#[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);

View File

@ -85,7 +85,6 @@ impl<N: Real> UnitComplex<N> {
/// # Example
/// ```
/// # extern crate num_complex;
/// # extern crate nalgebra;
/// # use num_complex::Complex;
/// # use nalgebra::UnitComplex;
/// let angle = 1.78f32;
@ -109,7 +108,7 @@ impl<N: Real> UnitComplex<N> {
/// ```
#[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<N: Real> UnitComplex<N> {
/// # 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<N: Real> UnitComplex<N> {
/// # 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<N: Real> UnitComplex<N> {
/// # 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<N: Real> UnitComplex<N> {
/// # 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<N: Real> UnitComplex<N> {
/// # 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<N: Real> UnitComplex<N> {
/// # Example
/// ```
/// # #[macro_use] extern crate approx;
/// # extern crate nalgebra;
/// # use nalgebra::UnitComplex;
/// let rot = UnitComplex::new(0.78);
/// let pow = rot.powf(2.0);
@ -311,17 +304,3 @@ impl<N: Real> UlpsEq for UnitComplex<N> {
&& self.im.ulps_eq(&other.im, epsilon, max_ulps)
}
}
impl<N: Real> From<UnitComplex<N>> for Matrix3<N> {
#[inline]
fn from(q: UnitComplex<N>) -> Matrix3<N> {
q.to_homogeneous()
}
}
impl<N: Real> From<UnitComplex<N>> for Matrix2<N> {
#[inline]
fn from(q: UnitComplex<N>) -> Matrix2<N> {
q.to_rotation_matrix().into_inner()
}
}

View File

@ -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<N: Real> AbstractMagma<Multiplicative> for UnitComplex<N> {
}
}
impl<N: Real> Inverse<Multiplicative> for UnitComplex<N> {
impl<N: Real> TwoSidedInverse<Multiplicative> for UnitComplex<N> {
#[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()
}
}

View File

@ -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<N: Real> UnitComplex<N> {
@ -35,7 +35,6 @@ impl<N: Real> UnitComplex<N> {
///
/// ```
/// # #[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<N: Real> UnitComplex<N> {
///
/// ```
/// # #[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<N: Real> UnitComplex<N> {
///
/// ```
/// # #[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<N: Real> UnitComplex<N> {
/// ```
#[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<N: Real> UnitComplex<N> {
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<N>) -> 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<N>, 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<N: Real> UnitComplex<N> {
/// # 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<N: Real> UnitComplex<N> {
/// # 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<N: Real> UnitComplex<N> {
/// # 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));

View File

@ -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<N1, N2, R> SubsetOf<Isometry<N2, U2, R>> for UnitComplex<N1>
where
N1: Real,
N2: Real + SupersetOf<N1>,
R: AlgaRotation<Point2<N2>> + SupersetOf<UnitComplex<N1>>,
R: AlgaRotation<Point2<N2>> + SupersetOf<Self>,
{
#[inline]
fn to_superset(&self) -> Isometry<N2, U2, R> {
@ -96,7 +96,7 @@ impl<N1, N2, R> SubsetOf<Similarity<N2, U2, R>> for UnitComplex<N1>
where
N1: Real,
N2: Real + SupersetOf<N1>,
R: AlgaRotation<Point2<N2>> + SupersetOf<UnitComplex<N1>>,
R: AlgaRotation<Point2<N2>> + SupersetOf<Self>,
{
#[inline]
fn to_superset(&self) -> Similarity<N2, U2, R> {
@ -153,3 +153,32 @@ impl<N1: Real, N2: Real + SupersetOf<N1>> SubsetOf<Matrix3<N2>> for UnitComplex<
Self::from_rotation_matrix(&rot)
}
}
impl<N: Real> From<UnitComplex<N>> for Rotation2<N> {
#[inline]
fn from(q: UnitComplex<N>) -> Self {
q.to_rotation_matrix()
}
}
impl<N: Real> From<Rotation2<N>> for UnitComplex<N> {
#[inline]
fn from(q: Rotation2<N>) -> Self {
Self::from_rotation_matrix(&q)
}
}
impl<N: Real> From<UnitComplex<N>> for Matrix3<N> {
#[inline]
fn from(q: UnitComplex<N>) -> Matrix3<N> {
q.to_homogeneous()
}
}
impl<N: Real> From<UnitComplex<N>> for Matrix2<N> {
#[inline]
fn from(q: UnitComplex<N>) -> Self {
q.to_rotation_matrix().into_inner()
}
}

View File

@ -45,11 +45,11 @@ use geometry::{Isometry, Point2, Rotation, Similarity, Translation, UnitComplex}
*/
// UnitComplex × UnitComplex
impl<N: Real> Mul<UnitComplex<N>> for UnitComplex<N> {
type Output = UnitComplex<N>;
impl<N: Real> Mul<Self> for UnitComplex<N> {
type Output = Self;
#[inline]
fn mul(self, rhs: UnitComplex<N>) -> UnitComplex<N> {
fn mul(self, rhs: Self) -> Self {
Unit::new_unchecked(self.into_inner() * rhs.into_inner())
}
}
@ -58,16 +58,16 @@ impl<'a, N: Real> Mul<UnitComplex<N>> for &'a UnitComplex<N> {
type Output = UnitComplex<N>;
#[inline]
fn mul(self, rhs: UnitComplex<N>) -> UnitComplex<N> {
fn mul(self, rhs: UnitComplex<N>) -> Self::Output {
Unit::new_unchecked(self.complex() * rhs.into_inner())
}
}
impl<'b, N: Real> Mul<&'b UnitComplex<N>> for UnitComplex<N> {
type Output = UnitComplex<N>;
type Output = Self;
#[inline]
fn mul(self, rhs: &'b UnitComplex<N>) -> UnitComplex<N> {
fn mul(self, rhs: &'b UnitComplex<N>) -> Self::Output {
Unit::new_unchecked(self.into_inner() * rhs.complex())
}
}
@ -76,17 +76,17 @@ impl<'a, 'b, N: Real> Mul<&'b UnitComplex<N>> for &'a UnitComplex<N> {
type Output = UnitComplex<N>;
#[inline]
fn mul(self, rhs: &'b UnitComplex<N>) -> UnitComplex<N> {
fn mul(self, rhs: &'b UnitComplex<N>) -> Self::Output {
Unit::new_unchecked(self.complex() * rhs.complex())
}
}
// UnitComplex ÷ UnitComplex
impl<N: Real> Div<UnitComplex<N>> for UnitComplex<N> {
type Output = UnitComplex<N>;
impl<N: Real> Div<Self> for UnitComplex<N> {
type Output = Self;
#[inline]
fn div(self, rhs: UnitComplex<N>) -> UnitComplex<N> {
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<UnitComplex<N>> for &'a UnitComplex<N> {
type Output = UnitComplex<N>;
#[inline]
fn div(self, rhs: UnitComplex<N>) -> UnitComplex<N> {
fn div(self, rhs: UnitComplex<N>) -> Self::Output {
Unit::new_unchecked(self.complex() * rhs.conjugate().into_inner())
}
}
impl<'b, N: Real> Div<&'b UnitComplex<N>> for UnitComplex<N> {
type Output = UnitComplex<N>;
type Output = Self;
#[inline]
fn div(self, rhs: &'b UnitComplex<N>) -> UnitComplex<N> {
fn div(self, rhs: &'b UnitComplex<N>) -> Self::Output {
Unit::new_unchecked(self.into_inner() * rhs.conjugate().into_inner())
}
}
@ -113,7 +113,7 @@ impl<'a, 'b, N: Real> Div<&'b UnitComplex<N>> for &'a UnitComplex<N> {
type Output = UnitComplex<N>;
#[inline]
fn div(self, rhs: &'b UnitComplex<N>) -> UnitComplex<N> {
fn div(self, rhs: &'b UnitComplex<N>) -> Self::Output {
Unit::new_unchecked(self.complex() * rhs.conjugate().into_inner())
}
}

16
src/io/matrix_market.pest Normal file
View File

@ -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 }

53
src/io/matrix_market.rs Normal file
View File

@ -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<N: Real, P: AsRef<Path>>(path: P) -> Option<CsMatrix<N>> {
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<N: Real>(data: &str) -> Option<CsMatrix<N>> {
let file = MatrixMarketParser::parse(Rule::Document, data)
.unwrap()
.next()?;
let mut shape = (0, 0, 0);
let mut rows: Vec<usize> = Vec::new();
let mut cols: Vec<usize> = Vec::new();
let mut data: Vec<N> = 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::<usize>().ok()?;
shape.1 = inner.next()?.as_str().parse::<usize>().ok()?;
shape.2 = inner.next()?.as_str().parse::<usize>().ok()?;
}
Rule::Entry => {
let mut inner = line.into_inner();
// NOTE: indices are 1-based.
rows.push(inner.next()?.as_str().parse::<usize>().ok()? - 1);
cols.push(inner.next()?.as_str().parse::<usize>().ok()? - 1);
data.push(::convert(inner.next()?.as_str().parse::<f64>().ok()?));
}
_ => return None, // FIXME: return an Err instead.
}
}
Some(CsMatrix::from_triplet(
shape.0, shape.1, &rows, &cols, &data,
))
}

5
src/io/mod.rs Normal file
View File

@ -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;

View File

@ -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: AlgaSquareMatrix>(m: &M) -> Option<M> {
///
/// * [`try_inverse`](fn.try_inverse.html)
#[inline]
pub fn inverse<M: Inverse<Multiplicative>>(m: &M) -> M {
m.inverse()
pub fn inverse<M: TwoSidedInverse<Multiplicative>>(m: &M) -> M {
m.two_sided_inverse()
}
/*

View File

@ -61,7 +61,7 @@ where DefaultAllocator: Allocator<N, R, C> + 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<N, R, C> + Allocator<(usize, usize), DimMinimu
}
}
FullPivLU {
Self {
lu: matrix,
p: p,
q: q,

View File

@ -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),
}

View File

@ -55,7 +55,7 @@ where
+ Allocator<N, D>,
{
/// Computes the Schur decomposition of a square matrix.
pub fn new(m: MatrixN<N, D>) -> RealSchur<N, D> {
pub fn new(m: MatrixN<N, D>) -> 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<N, D>, eps: N, max_niter: usize) -> Option<RealSchur<N, D>> {
pub fn try_new(m: MatrixN<N, D>, eps: N, max_niter: usize) -> Option<Self> {
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 {

View File

@ -271,7 +271,7 @@ where
}
}
Some(SVD {
Some(Self {
u: u,
v_t: v_t,
singular_values: b.diagonal,

View File

@ -83,7 +83,7 @@ where DefaultAllocator: Allocator<N, D, D> + Allocator<N, DimDiff<D, U1>>
}
}
SymmetricTridiagonal {
Self {
tri: m,
off_diagonal: off_diagonal,
}

517
src/sparse/cs_matrix.rs Normal file
View File

@ -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<Self::Item> {
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<Item = (usize, N)>;
/// Iterator through the row indices of a specific column.
type ColumnRowIndices: Iterator<Item = usize>;
/// 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<Item = &'a mut N>;
/// Mutable iterator through all the rows of a specific columns.
///
/// The elements are given as a tuple (row_index, value).
type ColumnEntriesMut: Iterator<Item = (usize, &'a mut N)>;
/// 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<N, R, C = U1>: 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<usize>;
/// 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<N, R, C = U1>:
CsStorage<N, R, C> + 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<N: Scalar, R: Dim, C: Dim>
where DefaultAllocator: Allocator<usize, C>
{
pub(crate) shape: (R, C),
pub(crate) p: VectorN<usize, C>,
pub(crate) i: Vec<usize>,
pub(crate) vals: Vec<N>,
}
impl<N: Scalar, R: Dim, C: Dim> CsVecStorage<N, R, C>
where DefaultAllocator: Allocator<usize, C>
{
/// 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<N: Scalar, R: Dim, C: Dim> CsVecStorage<N, R, C> where DefaultAllocator: Allocator<usize, C> {}
impl<'a, N: Scalar, R: Dim, C: Dim> CsStorageIter<'a, N, R, C> for CsVecStorage<N, R, C>
where DefaultAllocator: Allocator<usize, C>
{
type ColumnEntries = ColumnEntries<'a, N>;
type ColumnRowIndices = iter::Cloned<slice::Iter<'a, usize>>;
#[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<N: Scalar, R: Dim, C: Dim> CsStorage<N, R, C> for CsVecStorage<N, R, C>
where DefaultAllocator: Allocator<usize, C>
{
#[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<usize> {
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<N, R, C>
where DefaultAllocator: Allocator<usize, C>
{
type ValuesMut = slice::IterMut<'a, N>;
type ColumnEntriesMut = iter::Zip<iter::Cloned<slice::Iter<'a, usize>>, 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<N: Scalar, R: Dim, C: Dim> CsStorageMut<N, R, C> for CsVecStorage<N, R, C> where DefaultAllocator: Allocator<usize, C>
{}
/*
pub struct CsSliceStorage<'a, N: Scalar, R: Dim, C: DimAdd<U1>> {
shape: (R, C),
p: VectorSlice<usize, DimSum<C, U1>>,
i: VectorSlice<usize, Dynamic>,
vals: VectorSlice<N, Dynamic>,
}*/
/// A compressed sparse column matrix.
#[derive(Clone, Debug, PartialEq)]
pub struct CsMatrix<
N: Scalar,
R: Dim = Dynamic,
C: Dim = Dynamic,
S: CsStorage<N, R, C> = CsVecStorage<N, R, C>,
> {
pub(crate) data: S,
_phantoms: PhantomData<(N, R, C)>,
}
/// A column compressed sparse vector.
pub type CsVector<N, R = Dynamic, S = CsVecStorage<N, R, U1>> = CsMatrix<N, R, U1, S>;
impl<N: Scalar, R: Dim, C: Dim> CsMatrix<N, R, C>
where DefaultAllocator: Allocator<usize, C>
{
/// 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<usize, C>,
i: Vec<usize>,
vals: Vec<N>,
) -> Self
where
N: Zero + ClosedAdd,
DefaultAllocator: Allocator<N, R>,
{
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<N: Scalar + Zero + ClosedAdd> CsMatrix<N> {
pub(crate) fn from_parts(
nrows: usize,
ncols: usize,
p: Vec<usize>,
i: Vec<usize>,
vals: Vec<N>,
) -> 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<N: Scalar, R: Dim, C: Dim, S: CsStorage<N, R, C>> CsMatrix<N, R, C, S> {
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<N, C, R>
where DefaultAllocator: Allocator<usize, R> {
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<N: Scalar, R: Dim, C: Dim, S: CsStorageMut<N, R, C>> CsMatrix<N, R, C, S> {
/// Iterator through all the mutable values of this sparse matrix.
#[inline]
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut N> {
self.data.values_mut()
}
}
impl<N: Scalar, R: Dim, C: Dim> CsMatrix<N, R, C>
where DefaultAllocator: Allocator<usize, C>
{
pub(crate) fn sort(&mut self)
where DefaultAllocator: Allocator<N, R> {
// 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();
}
}

View File

@ -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<N: Real, D: Dim>
where DefaultAllocator: Allocator<usize, D> + Allocator<N, D>
{
// 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<usize>,
original_i: Vec<usize>,
// Decomposition result.
l: CsMatrix<N, D, D>,
// Used only for the pattern.
// FIXME: store only the nonzero pattern instead.
u: CsMatrix<N, D, D>,
ok: bool,
// Workspaces.
work_x: VectorN<N, D>,
work_c: VectorN<usize, D>,
}
impl<N: Real, D: Dim> CsCholesky<N, D>
where DefaultAllocator: Allocator<usize, D> + Allocator<N, D>
{
/// Computes the cholesky decomposition of the sparse matrix `m`.
pub fn new(m: &CsMatrix<N, D, D>) -> 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<N, D, D>) -> 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<N, D, D>> {
if self.ok {
Some(&self.l)
} else {
None
}
}
/// Extracts the lower-triangular matrix of the cholesky decomposition.
pub fn unwrap_l(self) -> Option<CsMatrix<N, D, D>> {
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<S: CsStorage<N, D, D>>(m: &CsMatrix<N, D, D, S>) -> Vec<usize> {
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<S: CsStorage<N, D, D>>(
m: &CsMatrix<N, D, D, S>,
j: usize,
max_j: usize,
tree: &[usize],
marks: &mut Vec<bool>,
out: &mut Vec<usize>,
)
{
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<S: CsStorage<N, D, D>>(
m: &CsMatrix<N, D, D, S>,
) -> (CsMatrix<N, D, D>, CsMatrix<N, D, D>) {
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<S: CsStorage<N, D, D>>(
m: &CsMatrix<N, D, D, S>,
tree: &[usize],
) -> Vec<usize> {
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<usize> {
// 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<usize>,
result: &mut Vec<usize>,
) {
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];
}
}
}
*/
}

View File

@ -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<N> {
/// 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<N, R, C>
where DefaultAllocator: Allocator<usize, C> + Allocator<N, R>
{
/// 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<CsMatrix<N, R, C, S>> for MatrixMN<N, R, C>
where
S: CsStorage<N, R, C>,
DefaultAllocator: Allocator<N, R, C>,
{
fn from(m: CsMatrix<N, R, C, S>) -> 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<Matrix<N, R, C, S>> for CsMatrix<N, R, C>
where
S: Storage<N, R, C>,
DefaultAllocator: Allocator<N, R, C> + Allocator<usize, C>,
{
fn from(m: Matrix<N, R, C, S>) -> 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
}
}

304
src/sparse/cs_matrix_ops.rs Normal file
View File

@ -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<N: Scalar, R: Dim, C: Dim, S: CsStorage<N, R, C>> CsMatrix<N, R, C, S> {
fn scatter<R2: Dim, C2: Dim>(
&self,
j: usize,
beta: N,
timestamps: &mut [usize],
timestamp: usize,
workspace: &mut [N],
mut nz: usize,
res: &mut CsMatrix<N, R2, C2>,
) -> usize
where
N: ClosedAdd + ClosedMul,
DefaultAllocator: Allocator<usize, C2>,
{
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<N: Scalar, R, S> CsVector<N, R, S> {
pub fn axpy(&mut self, alpha: N, x: CsVector<N, R, S>, 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<N: Scalar + Zero + ClosedAdd + ClosedMul, D: Dim, S: StorageMut<N, D>> Vector<N, D, S> {
/// Perform a sparse axpy operation: `self = alpha * x + beta * self` operation.
pub fn axpy_cs<D2: Dim, S2>(&mut self, alpha: N, x: &CsVector<N, D2, S2>, beta: N)
where
S2: CsStorage<N, D2>,
ShapeConstraint: DimEq<D, D2>,
{
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<R2: Dim, C2: Dim, S2>(&mut self, alpha: N, a: &CsMatrix<N, R2, C2, S2>, x: &DVector<N>, beta: N)
where
S2: CsStorage<N, R2, C2> {
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<N, R2, C2, S2>>
for &'a CsMatrix<N, R1, C1, S1>
where
N: Scalar + ClosedAdd + ClosedMul + Zero,
R1: Dim,
C1: Dim,
R2: Dim,
C2: Dim,
S1: CsStorage<N, R1, C1>,
S2: CsStorage<N, R2, C2>,
ShapeConstraint: AreMultipliable<R1, C1, R2, C2>,
DefaultAllocator: Allocator<usize, C2> + Allocator<usize, R1> + Allocator<N, R1>,
{
type Output = CsMatrix<N, R1, C2>;
fn mul(self, rhs: &'b CsMatrix<N, R2, C2, S2>) -> 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::<N, R1>::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<N, R2, C2, S2>>
for &'a CsMatrix<N, R1, C1, S1>
where
N: Scalar + ClosedAdd + ClosedMul + One,
R1: Dim,
C1: Dim,
R2: Dim,
C2: Dim,
S1: CsStorage<N, R1, C1>,
S2: CsStorage<N, R2, C2>,
ShapeConstraint: DimEq<R1, R2> + DimEq<C1, C2>,
DefaultAllocator: Allocator<usize, C2> + Allocator<usize, R1> + Allocator<N, R1>,
{
type Output = CsMatrix<N, R1, C2>;
fn add(self, rhs: &'b CsMatrix<N, R2, C2, S2>) -> 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<N> for CsMatrix<N, R, C, S>
where
N: Scalar + ClosedAdd + ClosedMul + Zero,
R: Dim,
C: Dim,
S: CsStorageMut<N, R, C>,
{
type Output = Self;
fn mul(mut self, rhs: N) -> Self::Output {
for e in self.values_mut() {
*e *= rhs
}
self
}
}

View File

@ -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<N: Real, D: Dim, S: CsStorage<N, D, D>> CsMatrix<N, D, D, S> {
/// Solve a lower-triangular system with a dense right-hand-side.
pub fn solve_lower_triangular<R2: Dim, C2: Dim, S2>(
&self,
b: &Matrix<N, R2, C2, S2>,
) -> Option<MatrixMN<N, R2, C2>>
where
S2: Storage<N, R2, C2>,
DefaultAllocator: Allocator<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<D, R2>,
{
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<R2: Dim, C2: Dim, S2>(
&self,
b: &Matrix<N, R2, C2, S2>,
) -> Option<MatrixMN<N, R2, C2>>
where
S2: Storage<N, R2, C2>,
DefaultAllocator: Allocator<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<D, R2>,
{
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<R2: Dim, C2: Dim, S2>(
&self,
b: &mut Matrix<N, R2, C2, S2>,
) -> bool
where
S2: StorageMut<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<D, R2>,
{
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<R2: Dim, C2: Dim, S2>(
&self,
b: &mut Matrix<N, R2, C2, S2>,
) -> bool
where
S2: StorageMut<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<D, R2>,
{
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<D2: Dim, S2>(
&self,
b: &CsVector<N, D2, S2>,
) -> Option<CsVector<N, D2>>
where
S2: CsStorage<N, D2>,
DefaultAllocator: Allocator<bool, D> + Allocator<N, D2> + Allocator<usize, D2>,
ShapeConstraint: SameNumberOfRows<D, D2>,
{
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<D2: Dim, S2>(
&self,
b: &CsVector<N, D2, S2>,
xi: &mut Vec<usize>,
) where
S2: CsStorage<N, D2>,
DefaultAllocator: Allocator<bool, D>,
{
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<usize>)>,
xi: &mut Vec<usize>,
)
{
'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<D2: Dim, S2>(&self, b: &CsVector<N, D2, S2>, xi: &mut Vec<usize>)
where
S2: CsStorage<N, D2>,
DefaultAllocator: Allocator<bool, D>,
{
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<usize>,
xi: &mut Vec<usize>,
)
{
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;
}
}
}
}
}
}

16
src/sparse/cs_utils.rs Normal file
View File

@ -0,0 +1,16 @@
use allocator::Allocator;
use {DefaultAllocator, Dim, VectorN};
pub fn cumsum<D: Dim>(a: &mut VectorN<usize, D>, b: &mut VectorN<usize, D>) -> usize
where DefaultAllocator: Allocator<usize, D> {
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
}

13
src/sparse/mod.rs Normal file
View File

@ -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;

View File

@ -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);

View File

@ -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<f64>, target: Point3<f64>, up: Vector3<f64>) -> 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<f64>, p: Point3<f64>, v: Vector3<f64>) -> 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<f64>, r: UnitQuaternion<f64>) -> bool {
@ -54,14 +62,29 @@ quickcheck!(
}
fn multiply_equals_alga_transform(i: Isometry3<f64>, v: Vector3<f64>, p: Point3<f64>) -> 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<f64>, uc: UnitComplex<f64>, r: Rotation2<f64>,
t: Translation2<f64>, v: Vector2<f64>, p: Point2<f64>) -> bool {
fn composition2(
i: Isometry2<f64>,
uc: UnitComplex<f64>,
r: Rotation2<f64>,
t: Translation2<f64>,
v: Vector2<f64>,
p: Point2<f64>,
) -> 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<f64>, uq: UnitQuaternion<f64>, r: Rotation3<f64>,
t: Translation3<f64>, v: Vector3<f64>, p: Point3<f64>) -> bool {
fn composition3(
i: Isometry3<f64>,
uq: UnitQuaternion<f64>,
r: Rotation3<f64>,
t: Translation3<f64>,
v: Vector3<f64>,
p: Point3<f64>,
) -> 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,8 +152,15 @@ quickcheck!(
relative_eq!((i * t) * p, i * (t * p), epsilon = 1.0e-7)
}
fn all_op_exist(i: Isometry3<f64>, uq: UnitQuaternion<f64>, t: Translation3<f64>,
v: Vector3<f64>, p: Point3<f64>, r: Rotation3<f64>) -> bool {
fn all_op_exist(
i: Isometry3<f64>,
uq: UnitQuaternion<f64>,
t: Translation3<f64>,
v: Vector3<f64>,
p: Point3<f64>,
r: Rotation3<f64>,
) -> bool
{
let iMi = i * i;
let iMuq = i * uq;
let iDi = i / 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
}
);

View File

@ -8,33 +8,57 @@ quickcheck!(
fn inverse_is_identity(i: Similarity3<f64>, p: Point3<f64>, v: Vector3<f64>) -> 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<f64>, r: UnitQuaternion<f64>, scaling: f64) -> bool {
fn inverse_is_parts_inversion(
t: Translation3<f64>,
r: UnitQuaternion<f64>,
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<f64>, v: Vector3<f64>, p: Point3<f64>) -> 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<f64>,
v: Vector3<f64>,
p: Point3<f64>,
) -> 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<f64>, uq: UnitQuaternion<f64>,
t: Translation3<f64>, v: Vector3<f64>, p: Point3<f64>, scaling: f64) -> bool {
fn composition(
i: Isometry3<f64>,
uq: UnitQuaternion<f64>,
t: Translation3<f64>,
v: Vector3<f64>,
p: Point3<f64>,
scaling: f64,
) -> bool
{
if relative_eq!(scaling, 0.0) {
return true;
}
@ -122,8 +146,15 @@ quickcheck!(
relative_eq!((s * i * t) * p, scaling * (i * (t * p)), epsilon = 1.0e-7)
}
fn all_op_exist(s: Similarity3<f64>, i: Isometry3<f64>, uq: UnitQuaternion<f64>,
t: Translation3<f64>, v: Vector3<f64>, p: Point3<f64>) -> bool {
fn all_op_exist(
s: Similarity3<f64>,
i: Isometry3<f64>,
uq: UnitQuaternion<f64>,
t: Translation3<f64>,
v: Vector3<f64>,
p: Point3<f64>,
) -> bool
{
let sMs = s * s;
let sMuq = s * uq;
let sDs = s / s;
@ -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
}
);

View File

@ -4,7 +4,6 @@
use na::{Point2, Rotation2, Unit, UnitComplex, Vector2};
quickcheck!(
/*
*
* From/to rotation matrix.
@ -15,8 +14,7 @@ quickcheck!(
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)
}
/*
@ -29,15 +27,14 @@ quickcheck!(
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<f64>) -> 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,14 +62,19 @@ quickcheck!(
fn unit_complex_mul_vector(c: UnitComplex<f64>, v: Vector2<f64>, p: Point2<f64>) -> 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<f64>, v: Vector2<f64>, p: Point2<f64>, r: Rotation2<f64>) -> bool {
fn all_op_exist(
uc: UnitComplex<f64>,
v: Vector2<f64>,
p: Point2<f64>,
r: Rotation2<f64>,
) -> bool
{
let uv = Unit::new_normalize(v);
let ucMuc = uc * 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
}
);

View File

@ -16,3 +16,5 @@ extern crate serde_json;
mod core;
mod geometry;
mod linalg;
#[cfg(feature = "sparse")]
mod sparse;

View File

@ -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::<f64>::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::<f64>::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::<f64>::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<f64>) -> bool {
// let m = m.upper_triangle();
// let eig = RealEigen::new(m.clone()).unwrap();
// verify_eigenvectors(m, eig)
// }
//
// fn eigen_static_square_3x3(m: Matrix3<f64>) -> bool {
// let m = m.upper_triangle();
// let eig = RealEigen::new(m.clone()).unwrap();
// verify_eigenvectors(m, eig)
// }
//
// fn eigen_static_square_2x2(m: Matrix2<f64>) -> bool {
// let m = m.upper_triangle();
// println!("{}", m);
// let eig = RealEigen::new(m.clone()).unwrap();
// verify_eigenvectors(m, eig)
// }
// }
//
// fn verify_eigenvectors<D: Dim>(m: MatrixN<f64, D>, mut eig: RealEigen<f64, D>) -> bool
// where DefaultAllocator: Allocator<f64, D, D> +
// Allocator<f64, D> +
// Allocator<usize, D, D> +
// Allocator<usize, D>,
// MatrixN<f64, D>: Display,
// VectorN<f64, D>: 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)
// }

View File

@ -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<f64>) -> 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);

View File

@ -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<f32>) {
// 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<f32>, 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);
}

View File

@ -0,0 +1 @@

View File

@ -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);
}

22
tests/sparse/cs_matrix.rs Normal file
View File

@ -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())
}

View File

@ -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);
}

72
tests/sparse/cs_ops.rs Normal file
View File

@ -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);
}

106
tests/sparse/cs_solve.rs Normal file
View File

@ -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());
}

8
tests/sparse/mod.rs Normal file
View File

@ -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;