diff --git a/README.md b/README.md index aaade091..904cbd01 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ and keeps an optimized set of tools for computational graphics and physics. Thos * Dynamically sized vector: `DVec`. * Dynamically sized (square or rectangular) matrix: `DMat`. * A few methods for data analysis: `Cov`, `Mean`. +* Some matrix factorization algorithms: QR decomposition, ... * Almost one trait per functionality: useful for generic programming. * Operator overloading using the double trait dispatch [trick](http://smallcultfollowing.com/babysteps/blog/2012/10/04/refining-traits-slash-impls/). diff --git a/src/linalg/decompositions.rs b/src/linalg/decompositions.rs index 47409224..6cb50153 100644 --- a/src/linalg/decompositions.rs +++ b/src/linalg/decompositions.rs @@ -1,54 +1,65 @@ use std::num::{Zero, Float}; -use na::DVec; -use na::DMat; use traits::operations::Transpose; -use traits::structure::ColSlice; +use traits::structure::{ColSlice, Eye, Indexable}; use traits::geometry::Norm; use std::cmp::min; +/// Get the householder matrix corresponding to a reflexion to the hyperplane +/// defined by `vec̀ . It can be a reflexion contained in a subspace. +/// +/// # Arguments +/// * `dim` - the dimension of the space the resulting matrix operates in +/// * `start` - the starting dimension of the subspace of the reflexion +/// * `vec` - the vector defining the reflection. +pub fn householder_matrix, + V: Indexable> + (dim: uint, start: uint, vec: V) -> M { + let mut qk : M = Eye::new_identity(dim); + let stop = start + vec.shape(); + assert!(stop <= dim); + for j in range(start, stop) { + for i in range(start, stop) { + unsafe { + let vv = vec.unsafe_at(i) * vec.unsafe_at(j); + let qkij = qk.unsafe_at((i, j)); + qk.unsafe_set((i, j), qkij - vv - vv); + } + } + } + qk +} + /// QR decomposition using Householder reflections /// # Arguments /// * `m` - matrix to decompose -pub fn decomp_qr(m: &DMat) -> (DMat, DMat) { - let rows = m.nrows(); - let cols = m.ncols(); +pub fn decomp_qr + Norm, + M: Clone + Eye + ColSlice + Transpose + + Indexable<(uint, uint), N> + Mul> + (m: &M) -> (M, M) { + let (rows, cols) = m.shape(); assert!(rows >= cols); - let mut q : DMat = DMat::new_identity(rows); + let mut q : M = Eye::new_identity(rows); let mut r = m.clone(); - let subtract_reflection = |vec: DVec| -> DMat { - // FIXME: we don't handle the complex case here - let mut qk : DMat = DMat::new_identity(rows); - let start = rows - vec.at.len(); - for j in range(start, rows) { - for i in range(start, rows) { - unsafe { - let vv = vec.at_fast(i - start) * vec.at_fast(j - start); - let qkij = qk.at_fast(i, j); - qk.set_fast(i, j, qkij - vv - vv); - } - } - } - qk - }; - let iterations = min(rows - 1, cols); for ite in range(0u, iterations) { let mut v = r.col_slice(ite, ite, rows); let alpha = - if unsafe { v.at_fast(ite) } >= Zero::zero() { + if unsafe { v.unsafe_at(ite) } >= Zero::zero() { -Norm::norm(&v) } else { Norm::norm(&v) }; unsafe { - let x = v.at_fast(0); - v.set_fast(0, x - alpha); + let x = v.unsafe_at(0); + v.unsafe_set(0, x - alpha); } let _ = v.normalize(); - let qk = subtract_reflection(v); + let qk: M = householder_matrix(rows, 0, v); r = qk * r; q = q * Transpose::transpose_cpy(&qk); } diff --git a/src/linalg/mod.rs b/src/linalg/mod.rs index 6c2c6d63..e6e09f2a 100644 --- a/src/linalg/mod.rs +++ b/src/linalg/mod.rs @@ -1,4 +1,4 @@ -pub use self::decompositions::decomp_qr; +pub use self::decompositions::{decomp_qr, householder_matrix}; mod decompositions; diff --git a/src/na.rs b/src/na.rs index 415eb8f2..49c49b8d 100644 --- a/src/na.rs +++ b/src/na.rs @@ -1,6 +1,6 @@ //! **nalgebra** prelude. -use std::num::{Zero, One}; +use std::num::{Zero, One, FloatMath}; use std::cmp; pub use traits::{PartialLess, PartialEqual, PartialGreater, NotComparable}; pub use traits::{ @@ -40,7 +40,8 @@ pub use traits::{ UniformSphereSample, AnyVec, VecExt, - ColSlice, RowSlice + ColSlice, RowSlice, + Eye }; pub use structs::{ @@ -54,7 +55,8 @@ pub use structs::{ }; pub use linalg::{ - decomp_qr + decomp_qr, + householder_matrix }; /// Traits to work around the language limitations related to operator overloading. @@ -215,7 +217,7 @@ pub fn one() -> T { */ /// Computes a projection matrix given the frustrum near plane width, height, the field of /// view, and the distance to the clipping planes (`znear` and `zfar`). -pub fn perspective3d + Zero + One>(width: N, height: N, fov: N, znear: N, zfar: N) -> Mat4 { +pub fn perspective3d + Zero + One>(width: N, height: N, fov: N, znear: N, zfar: N) -> Mat4 { let aspect = width / height; let _1: N = one(); @@ -720,6 +722,10 @@ pub fn mean>(observations: &M) -> N { // // +/// Construct the identity matrix for a given dimension +#[inline(always)] +pub fn new_identity(dim: uint) -> M { Eye::new_identity(dim) } + /* * Basis */ diff --git a/src/structs/dmat.rs b/src/structs/dmat.rs index 8fac029c..8f905d78 100644 --- a/src/structs/dmat.rs +++ b/src/structs/dmat.rs @@ -9,7 +9,7 @@ use traits::operations::ApproxEq; use std::mem; use structs::dvec::{DVec, DVecMulRhs}; use traits::operations::{Inv, Transpose, Mean, Cov}; -use traits::structure::{Cast, ColSlice, RowSlice}; +use traits::structure::{Cast, ColSlice, RowSlice, Eye, Indexable}; use std::fmt::{Show, Formatter, Result}; #[doc(hidden)] @@ -181,19 +181,19 @@ impl DMat { // FIXME: add a function to modify the dimension (to avoid useless allocations)? -impl DMat { +impl Eye for DMat { /// Builds an identity matrix. /// /// # Arguments /// * `dim` - The dimension of the matrix. A `dim`-dimensional matrix contains `dim * dim` /// components. #[inline] - pub fn new_identity(dim: uint) -> DMat { + fn new_identity(dim: uint) -> DMat { let mut res = DMat::new_zeros(dim, dim); for i in range(0u, dim) { let _1: N = One::one(); - res.set(i, i, _1); + res.set((i, i), _1); } res @@ -206,13 +206,16 @@ impl DMat { i + j * self.nrows } +} + +impl Indexable<(uint, uint), N> for DMat { /// Changes the value of a component of the matrix. /// /// # Arguments - /// * `row` - 0-based index of the line to be changed - /// * `col` - 0-based index of the column to be changed + /// * `rowcol` - 0-based tuple (row, col) to be changed #[inline] - pub fn set(&mut self, row: uint, col: uint, val: N) { + fn set(&mut self, rowcol: (uint, uint), val: N) { + let (row, col) = rowcol; assert!(row < self.nrows); assert!(col < self.ncols); @@ -222,7 +225,8 @@ impl DMat { /// Just like `set` without bounds checking. #[inline] - pub unsafe fn set_fast(&mut self, row: uint, col: uint, val: N) { + unsafe fn unsafe_set(&mut self, rowcol: (uint, uint), val: N) { + let (row, col) = rowcol; let offset = self.offset(row, col); *self.mij.as_mut_slice().unsafe_mut_ref(offset) = val } @@ -230,20 +234,38 @@ impl DMat { /// Reads the value of a component of the matrix. /// /// # Arguments - /// * `row` - 0-based index of the line to be read - /// * `col` - 0-based index of the column to be read + /// * `rowcol` - 0-based tuple (row, col) to be read #[inline] - pub fn at(&self, row: uint, col: uint) -> N { + fn at(&self, rowcol: (uint, uint)) -> N { + let (row, col) = rowcol; assert!(row < self.nrows); assert!(col < self.ncols); - unsafe { self.at_fast(row, col) } + unsafe { self.unsafe_at((row, col)) } } /// Just like `at` without bounds checking. #[inline] - pub unsafe fn at_fast(&self, row: uint, col: uint) -> N { + unsafe fn unsafe_at(&self, rowcol: (uint, uint)) -> N { + let (row, col) = rowcol; (*self.mij.as_slice().unsafe_ref(self.offset(row, col))).clone() } + + #[inline] + fn swap(&mut self, rowcol1: (uint, uint), rowcol2: (uint, uint)) { + let (row1, col1) = rowcol1; + let (row2, col2) = rowcol2; + let offset1 = self.offset(row1, col1); + let offset2 = self.offset(row2, col2); + let count = self.mij.len(); + assert!(offset1 < count); + assert!(offset2 < count); + self.mij.as_mut_slice().swap(offset1, offset2); + } + + fn shape(&self) -> (uint, uint) { + (self.nrows, self.ncols) + } + } impl + Add + Zero> DMatMulRhs> for DMat { @@ -258,10 +280,11 @@ impl + Add + Zero> DMatMulRhs> for DMat unsafe { for k in range(0u, left.ncols) { - acc = acc + left.at_fast(i, k) * right.at_fast(k, j); + acc = acc + + left.unsafe_at((i, k)) * right.unsafe_at((k, j)); } - res.set_fast(i, j, acc); + res.unsafe_set((i, j), acc); } } } @@ -282,7 +305,7 @@ DMatMulRhs> for DVec { for j in range(0u, left.ncols) { unsafe { - acc = acc + left.at_fast(i, j) * right.at_fast(j); + acc = acc + left.unsafe_at((i, j)) * right.unsafe_at(j); } } @@ -306,7 +329,7 @@ DVecMulRhs> for DMat { for j in range(0u, right.nrows) { unsafe { - acc = acc + left.at_fast(j) * right.at_fast(j, i); + acc = acc + left.unsafe_at(j) * right.unsafe_at((j, i)); } } @@ -335,7 +358,7 @@ Inv for DMat { assert!(self.nrows == self.ncols); let dim = self.nrows; - let mut res: DMat = DMat::new_identity(dim); + let mut res: DMat = Eye::new_identity(dim); let _0T: N = Zero::zero(); // inversion using Gauss-Jordan elimination @@ -347,7 +370,7 @@ Inv for DMat { let mut n0 = k; // index of a non-zero entry while n0 != dim { - if unsafe { self.at_fast(n0, k) } != _0T { + if unsafe { self.unsafe_at((n0, k)) } != _0T { break; } @@ -370,30 +393,30 @@ Inv for DMat { } unsafe { - let pivot = self.at_fast(k, k); + let pivot = self.unsafe_at((k, k)); for j in range(k, dim) { - let selfval = self.at_fast(k, j) / pivot; - self.set_fast(k, j, selfval); + let selfval = self.unsafe_at((k, j)) / pivot; + self.unsafe_set((k, j), selfval); } for j in range(0u, dim) { - let resval = res.at_fast(k, j) / pivot; - res.set_fast(k, j, resval); + let resval = res.unsafe_at((k, j)) / pivot; + res.unsafe_set((k, j), resval); } for l in range(0u, dim) { if l != k { - let normalizer = self.at_fast(l, k); + let normalizer = self.unsafe_at((l, k)); for j in range(k, dim) { - let selfval = self.at_fast(l, j) - self.at_fast(k, j) * normalizer; - self.set_fast(l, j, selfval); + let selfval = self.unsafe_at((l, j)) - self.unsafe_at((k, j)) * normalizer; + self.unsafe_set((l, j), selfval); } for j in range(0u, dim) { - let resval = res.at_fast(l, j) - res.at_fast(k, j) * normalizer; - res.set_fast(l, j, resval); + let resval = res.unsafe_at((l, j)) - res.unsafe_at((k, j)) * normalizer; + res.unsafe_set((l, j), resval); } } } @@ -422,7 +445,7 @@ impl Transpose for DMat { for i in range(0u, m.nrows) { for j in range(0u, m.ncols) { unsafe { - res.set_fast(j, i, m.at_fast(i, j)) + res.unsafe_set((j, i), m.unsafe_at((i, j))) } } } @@ -460,8 +483,8 @@ impl + Clone> Mean> for DMat { for i in range(0u, m.nrows) { for j in range(0u, m.ncols) { unsafe { - let acc = res.at_fast(j) + m.at_fast(i, j) * normalizer; - res.set_fast(j, acc); + let acc = res.unsafe_at(j) + m.unsafe_at((i, j)) * normalizer; + res.unsafe_set(j, acc); } } } @@ -482,7 +505,7 @@ impl + DMatDivRhs> + ToStr > Cov> for i in range(0u, m.nrows) { for j in range(0u, m.ncols) { unsafe { - centered.set_fast(i, j, m.at_fast(i, j) - mean.at_fast(j)); + centered.unsafe_set((i, j), m.unsafe_at((i, j)) - mean.unsafe_at(j)); } } } @@ -520,7 +543,7 @@ impl RowSlice> for DMat { let mut slice_idx = 0u; for col_id in range(col_start, col_end) { unsafe { - slice.set_fast(slice_idx, self.at_fast(row_id, col_id)); + slice.unsafe_set(slice_idx, self.unsafe_at((row_id, col_id))); } slice_idx += 1; } @@ -553,11 +576,11 @@ impl Show for DMat { fn fmt(&self, form:&mut Formatter) -> Result { for i in range(0u, self.nrows()) { for j in range(0u, self.ncols()) { - let _ = write!(form.buf, "{} ", self.at(i, j)); + let _ = write!(form, "{} ", self.at((i, j))); } - let _ = write!(form.buf, "\n"); + let _ = write!(form, "\n"); } - write!(form.buf, "\n") + write!(form, "\n") } } diff --git a/src/structs/dvec.rs b/src/structs/dvec.rs index b45a1a47..662d3bfe 100644 --- a/src/structs/dvec.rs +++ b/src/structs/dvec.rs @@ -9,7 +9,7 @@ use std::slice::{Items, MutItems}; use traits::operations::ApproxEq; use std::iter::FromIterator; use traits::geometry::{Dot, Norm}; -use traits::structure::{Iterable, IterableMut}; +use traits::structure::{Iterable, IterableMut, Indexable}; #[doc(hidden)] mod metal; @@ -48,11 +48,42 @@ impl DVec { } } -impl DVec { - /// Indexing without bounds checking. - pub unsafe fn at_fast(&self, i: uint) -> N { +impl Indexable for DVec { + + fn at(&self, i: uint) -> N { + assert!(i < self.at.len()); + unsafe { + self.unsafe_at(i) + } + } + + fn set(&mut self, i: uint, val: N) { + assert!(i < self.at.len()); + unsafe { + self.unsafe_set(i, val); + } + } + + fn swap(&mut self, i: uint, j: uint) { + assert!(i < self.at.len()); + assert!(j < self.at.len()); + self.at.as_mut_slice().swap(i, j); + } + + fn shape(&self) -> uint { + self.at.len() + } + + #[inline] + unsafe fn unsafe_at(&self, i: uint) -> N { (*self.at.as_slice().unsafe_ref(i)).clone() } + + #[inline] + unsafe fn unsafe_set(&mut self, i: uint, val: N) { + *self.at.as_mut_slice().unsafe_mut_ref(i) = val + } + } impl DVec { @@ -86,11 +117,6 @@ impl DVec { } } - #[inline] - pub unsafe fn set_fast(&mut self, i: uint, val: N) { - *self.at.as_mut_slice().unsafe_mut_ref(i) = val - } - /// Gets a reference to of this vector data. #[inline] pub fn as_vec<'r>(&'r self) -> &'r [N] { @@ -172,7 +198,7 @@ impl FromIterator for DVec { } } -impl + DVecMulRhs>> DVec { +impl + DVecMulRhs>> DVec { /// Computes the canonical basis for the given dimension. A canonical basis is a set of /// vectors, mutually orthogonal, with all its component equal to 0.0 except one which is equal /// to 1.0. @@ -261,7 +287,7 @@ impl Dot for DVec { let mut res: N = Zero::zero(); for i in range(0u, a.at.len()) { - res = res + unsafe { a.at_fast(i) * b.at_fast(i) }; + res = res + unsafe { a.unsafe_at(i) * b.unsafe_at(i) }; } res @@ -272,14 +298,14 @@ impl Dot for DVec { let mut res: N = Zero::zero(); for i in range(0u, a.at.len()) { - res = res + unsafe { (a.at_fast(i) - b.at_fast(i)) * c.at_fast(i) }; + res = res + unsafe { (a.unsafe_at(i) - b.unsafe_at(i)) * c.unsafe_at(i) }; } res } } -impl Norm for DVec { +impl Norm for DVec { #[inline] fn sqnorm(v: &DVec) -> N { Dot::dot(v, v) diff --git a/src/structs/iso.rs b/src/structs/iso.rs index fc543ace..de4a78e2 100644 --- a/src/structs/iso.rs +++ b/src/structs/iso.rs @@ -51,7 +51,7 @@ pub struct Iso4 { pub translation: Vec4 } -impl Iso3 { +impl Iso3 { /// Reorient and translate this transformation such that its local `x` axis points to a given /// direction. Note that the usually known `look_at` function does the same thing but with the /// `z` axis. See `look_at_z` for that. diff --git a/src/structs/iso_macros.rs b/src/structs/iso_macros.rs index 43c05edb..1c618df4 100644 --- a/src/structs/iso_macros.rs +++ b/src/structs/iso_macros.rs @@ -2,7 +2,7 @@ macro_rules! iso_impl( ($t: ident, $submat: ident, $subvec: ident, $subrotvec: ident) => ( - impl $t { + impl $t { /// Creates a new isometry from a rotation matrix and a vector. #[inline] pub fn new(translation: $subvec, rotation: $subrotvec) -> $t { @@ -26,7 +26,7 @@ macro_rules! iso_impl( macro_rules! rotation_matrix_impl( ($t: ident, $trot: ident, $tlv: ident, $tav: ident) => ( - impl + Float + Float + Num + Clone> + impl + FloatMath + Num + Clone> RotationMatrix<$tlv, $tav, $trot> for $t { #[inline] fn to_rot_mat(&self) -> $trot { @@ -50,7 +50,7 @@ macro_rules! dim_impl( macro_rules! one_impl( ($t: ident) => ( - impl One for $t { + impl One for $t { #[inline] fn one() -> $t { $t::new_with_rotmat(Zero::zero(), One::one()) @@ -61,7 +61,7 @@ macro_rules! one_impl( macro_rules! iso_mul_iso_impl( ($t: ident, $tmul: ident) => ( - impl $tmul> for $t { + impl $tmul> for $t { #[inline] fn binop(left: &$t, right: &$t) -> $t { $t::new_with_rotmat( @@ -96,7 +96,7 @@ macro_rules! vec_mul_iso_impl( macro_rules! translation_impl( ($t: ident, $tv: ident) => ( - impl Translation<$tv> for $t { + impl Translation<$tv> for $t { #[inline] fn translation(&self) -> $tv { self.translation.clone() @@ -153,7 +153,7 @@ macro_rules! translate_impl( macro_rules! rotation_impl( ($t: ident, $trot: ident, $tav: ident) => ( - impl + Num + Float + Float + Clone> Rotation<$tav> for $t { + impl + FloatMath + Clone> Rotation<$tav> for $t { #[inline] fn rotation(&self) -> $tav { self.rotation.rotation() @@ -220,7 +220,7 @@ macro_rules! rotate_impl( macro_rules! transformation_impl( ($t: ident) => ( - impl Transformation<$t> for $t { + impl Transformation<$t> for $t { fn transformation(&self) -> $t { self.clone() } @@ -336,7 +336,7 @@ macro_rules! approx_eq_impl( macro_rules! rand_impl( ($t: ident) => ( - impl Rand for $t { + impl Rand for $t { #[inline] fn rand(rng: &mut R) -> $t { $t::new(rng.gen(), rng.gen()) diff --git a/src/structs/mat.rs b/src/structs/mat.rs index 1c64f448..d0f9ca7d 100644 --- a/src/structs/mat.rs +++ b/src/structs/mat.rs @@ -8,8 +8,10 @@ use traits::operations::ApproxEq; use std::slice::{Items, MutItems}; use structs::vec::{Vec1, Vec2, Vec3, Vec4, Vec5, Vec6, Vec1MulRhs, Vec4MulRhs, Vec5MulRhs, Vec6MulRhs}; +use structs::dvec::DVec; -use traits::structure::{Cast, Row, Col, Iterable, IterableMut, Dim, Indexable}; +use traits::structure::{Cast, Row, Col, Iterable, IterableMut, Dim, Indexable, + Eye, ColSlice, RowSlice}; use traits::operations::{Absolute, Transpose, Inv, Outer}; use traits::geometry::{ToHomogeneous, FromHomogeneous}; @@ -34,6 +36,8 @@ pub struct Mat1 { pub m11: N } +eye_impl!(Mat1, 1, m11) + double_dispatch_binop_decl_trait!(Mat1, Mat1MulRhs) double_dispatch_binop_decl_trait!(Mat1, Mat1DivRhs) double_dispatch_binop_decl_trait!(Mat1, Mat1AddRhs) @@ -116,6 +120,8 @@ transpose_impl!(Mat1, 1) approx_eq_impl!(Mat1) row_impl!(Mat1, Vec1, 1) col_impl!(Mat1, Vec1, 1) +col_slice_impl!(Mat1, Vec1, 1) +row_slice_impl!(Mat1, Vec1, 1) to_homogeneous_impl!(Mat1, Mat2, 1, 2) from_homogeneous_impl!(Mat1, Mat2, 1, 2) outer_impl!(Vec1, Mat1) @@ -127,6 +133,8 @@ pub struct Mat2 { pub m12: N, pub m22: N } +eye_impl!(Mat2, 2, m11, m22) + double_dispatch_binop_decl_trait!(Mat2, Mat2MulRhs) double_dispatch_binop_decl_trait!(Mat2, Mat2DivRhs) double_dispatch_binop_decl_trait!(Mat2, Mat2AddRhs) @@ -213,6 +221,8 @@ transpose_impl!(Mat2, 2) approx_eq_impl!(Mat2) row_impl!(Mat2, Vec2, 2) col_impl!(Mat2, Vec2, 2) +col_slice_impl!(Mat2, Vec2, 2) +row_slice_impl!(Mat2, Vec2, 2) to_homogeneous_impl!(Mat2, Mat3, 2, 3) from_homogeneous_impl!(Mat2, Mat3, 2, 3) outer_impl!(Vec2, Mat2) @@ -225,6 +235,8 @@ pub struct Mat3 { pub m13: N, pub m23: N, pub m33: N } +eye_impl!(Mat3, 3, m11, m22, m33) + double_dispatch_binop_decl_trait!(Mat3, Mat3MulRhs) double_dispatch_binop_decl_trait!(Mat3, Mat3DivRhs) double_dispatch_binop_decl_trait!(Mat3, Mat3AddRhs) @@ -324,6 +336,8 @@ transpose_impl!(Mat3, 3) approx_eq_impl!(Mat3) // (specialized) row_impl!(Mat3, Vec3, 3) // (specialized) col_impl!(Mat3, Vec3, 3) +col_slice_impl!(Mat3, Vec3, 3) +row_slice_impl!(Mat3, Vec3, 3) to_homogeneous_impl!(Mat3, Mat4, 3, 4) from_homogeneous_impl!(Mat3, Mat4, 3, 4) outer_impl!(Vec3, Mat3) @@ -337,6 +351,8 @@ pub struct Mat4 { pub m14: N, pub m24: N, pub m34: N, pub m44: N } +eye_impl!(Mat4, 4, m11, m22, m33, m44) + double_dispatch_binop_decl_trait!(Mat4, Mat4MulRhs) double_dispatch_binop_decl_trait!(Mat4, Mat4DivRhs) double_dispatch_binop_decl_trait!(Mat4, Mat4AddRhs) @@ -487,6 +503,8 @@ transpose_impl!(Mat4, 4) approx_eq_impl!(Mat4) row_impl!(Mat4, Vec4, 4) col_impl!(Mat4, Vec4, 4) +col_slice_impl!(Mat4, Vec4, 4) +row_slice_impl!(Mat4, Vec4, 4) to_homogeneous_impl!(Mat4, Mat5, 4, 5) from_homogeneous_impl!(Mat4, Mat5, 4, 5) outer_impl!(Vec4, Mat4) @@ -501,6 +519,8 @@ pub struct Mat5 { pub m15: N, pub m25: N, pub m35: N, pub m45: N, pub m55: N } +eye_impl!(Mat5, 5, m11, m22, m33, m44, m55) + double_dispatch_binop_decl_trait!(Mat5, Mat5MulRhs) double_dispatch_binop_decl_trait!(Mat5, Mat5DivRhs) double_dispatch_binop_decl_trait!(Mat5, Mat5AddRhs) @@ -666,6 +686,8 @@ transpose_impl!(Mat5, 5) approx_eq_impl!(Mat5) row_impl!(Mat5, Vec5, 5) col_impl!(Mat5, Vec5, 5) +col_slice_impl!(Mat5, Vec5, 5) +row_slice_impl!(Mat5, Vec5, 5) to_homogeneous_impl!(Mat5, Mat6, 5, 6) from_homogeneous_impl!(Mat5, Mat6, 5, 6) outer_impl!(Vec5, Mat5) @@ -681,6 +703,8 @@ pub struct Mat6 { pub m16: N, pub m26: N, pub m36: N, pub m46: N, pub m56: N, pub m66: N } +eye_impl!(Mat6, 6, m11, m22, m33, m44, m55, m66) + double_dispatch_binop_decl_trait!(Mat6, Mat6MulRhs) double_dispatch_binop_decl_trait!(Mat6, Mat6DivRhs) double_dispatch_binop_decl_trait!(Mat6, Mat6AddRhs) @@ -897,4 +921,6 @@ transpose_impl!(Mat6, 6) approx_eq_impl!(Mat6) row_impl!(Mat6, Vec6, 6) col_impl!(Mat6, Vec6, 6) +col_slice_impl!(Mat6, Vec6, 6) +row_slice_impl!(Mat6, Vec6, 6) outer_impl!(Vec6, Mat6) diff --git a/src/structs/mat_macros.rs b/src/structs/mat_macros.rs index 11304616..e574a770 100644 --- a/src/structs/mat_macros.rs +++ b/src/structs/mat_macros.rs @@ -98,6 +98,20 @@ macro_rules! scalar_add_impl( ) ) + +macro_rules! eye_impl( + ($t: ident, $ndim: expr, $($comp_diagN: ident),+) => ( + impl Eye for $t { + fn new_identity(dim: uint) -> $t { + assert!(dim == $ndim); + let mut eye: $t = Zero::zero(); + $(eye.$comp_diagN = One::one();)+ + eye + } + } + ) +) + macro_rules! scalar_sub_impl( ($t: ident, $n: ident, $trhs: ident, $comp0: ident $(,$compN: ident)*) => ( impl $trhs<$n, $t<$n>> for $n { @@ -193,6 +207,11 @@ macro_rules! indexable_impl( } } + #[inline] + fn shape(&self) -> (uint, uint) { + ($dim, $dim) + } + #[inline] unsafe fn unsafe_at(&self, (i, j): (uint, uint)) -> N { (*mem::transmute::<&$t, &[N, ..$dim * $dim]>(self).unsafe_ref(i + j * $dim)).clone() @@ -206,6 +225,26 @@ macro_rules! indexable_impl( ) ) +macro_rules! col_slice_impl( + ($t: ident, $tv: ident, $dim: expr) => ( + impl ColSlice> for $t { + fn col_slice(& self, cid: uint, rstart: uint, rend: uint) -> DVec { + assert!(cid < $dim); + assert!(rstart < rend); + assert!(rend <= $dim); + let col = self.col(cid); + let res = DVec::from_vec( + rend - rstart, + unsafe { + mem::transmute::<&$tv, & [N, ..$dim]> + (&col).slice(rstart, rend) + }); + res + } + } + ) +) + macro_rules! row_impl( ($t: ident, $tv: ident, $dim: expr) => ( impl Row<$tv> for $t { @@ -235,6 +274,26 @@ macro_rules! row_impl( ) ) +macro_rules! row_slice_impl( + ($t: ident, $tv: ident, $dim: expr) => ( + impl RowSlice> for $t { + fn row_slice(& self, rid: uint, cstart: uint, cend: uint) -> DVec { + assert!(rid < $dim); + assert!(cstart < cend); + assert!(cend <= $dim); + let row = self.row(rid); + let res = DVec::from_vec( + cend - cstart, + unsafe { + mem::transmute::<&$tv, & [N, ..$dim]> + (&row).slice(cstart, cend) + }); + res + } + } + ) +) + macro_rules! col_impl( ($t: ident, $tv: ident, $dim: expr) => ( impl Col<$tv> for $t { diff --git a/src/structs/rot.rs b/src/structs/rot.rs index 4bdcb84d..b184dd6d 100644 --- a/src/structs/rot.rs +++ b/src/structs/rot.rs @@ -20,7 +20,7 @@ pub struct Rot2 { submat: Mat2 } -impl> Rot2 { +impl> Rot2 { /// Builds a 2 dimensional rotation matrix from an angle in radian. pub fn new(angle: Vec1) -> Rot2 { let (sia, coa) = angle.x.sin_cos(); @@ -31,7 +31,7 @@ impl> Rot2 { } } -impl +impl Rotation> for Rot2 { #[inline] fn rotation(&self) -> Vec1 { @@ -69,7 +69,7 @@ Rotation> for Rot2 { } } -impl> Rand for Rot2 { +impl> Rand for Rot2 { #[inline] fn rand(rng: &mut R) -> Rot2 { Rot2::new(rng.gen()) @@ -99,7 +99,7 @@ pub struct Rot3 { } -impl Rot3 { +impl Rot3 { /// Builds a 3 dimensional rotation matrix from an axis and an angle. /// /// # Arguments @@ -140,7 +140,7 @@ impl Rot3 { } } -impl Rot3 { +impl Rot3 { /// Reorient this matrix such that its local `x` axis points to a given point. Note that the /// usually known `look_at` function does the same thing but with the `z` axis. See `look_at_z` /// for that. @@ -180,7 +180,7 @@ impl Rot3 { } } -impl> +impl> Rotation> for Rot3 { #[inline] fn rotation(&self) -> Vec3 { @@ -245,7 +245,7 @@ Rotation> for Rot3 { } } -impl +impl Rand for Rot3 { #[inline] fn rand(rng: &mut R) -> Rot3 { @@ -309,7 +309,7 @@ impl AbsoluteRotate> for Rot4 { } } -impl +impl Rotation> for Rot4 { #[inline] fn rotation(&self) -> Vec4 { diff --git a/src/structs/rot_macros.rs b/src/structs/rot_macros.rs index cee3972c..1a168080 100644 --- a/src/structs/rot_macros.rs +++ b/src/structs/rot_macros.rs @@ -56,7 +56,7 @@ macro_rules! dim_impl( macro_rules! rotation_matrix_impl( ($t: ident, $tlv: ident, $tav: ident) => ( - impl + Float + Float + Num + Clone> + impl + FloatMath + Clone> RotationMatrix<$tlv, $tav, $t> for $t { #[inline] fn to_rot_mat(&self) -> $t { diff --git a/src/structs/spec/vec0.rs b/src/structs/spec/vec0.rs index a758e4c5..3ca49091 100644 --- a/src/structs/spec/vec0.rs +++ b/src/structs/spec/vec0.rs @@ -25,6 +25,11 @@ impl Indexable for vec::Vec0 { fn set(&mut self, _: uint, _: N) { } + #[inline] + fn shape(&self) -> uint { + 0 + } + #[inline] fn swap(&mut self, _: uint, _: uint) { } @@ -159,7 +164,7 @@ impl + Neg> Translation> for vec::Vec0 { } } -impl Norm for vec::Vec0 { +impl Norm for vec::Vec0 { #[inline] fn sqnorm(_: &vec::Vec0) -> N { Zero::zero() diff --git a/src/structs/vec_macros.rs b/src/structs/vec_macros.rs index 2319b1a5..6f4e2595 100644 --- a/src/structs/vec_macros.rs +++ b/src/structs/vec_macros.rs @@ -38,7 +38,7 @@ macro_rules! at_fast_impl( // However, f32/f64 does not implement TotalOrd… macro_rules! ord_impl( ($t: ident, $comp0: ident $(,$compN: ident)*) => ( - impl PartialOrd for $t { + impl PartialOrd for $t { #[inline] fn inf(a: &$t, b: &$t) -> $t { $t::new(a.$comp0.min(b.$comp0.clone()) @@ -165,6 +165,11 @@ macro_rules! indexable_impl( } } + #[inline] + fn shape(&self) -> uint { + $dim + } + #[inline] fn swap(&mut self, i1: uint, i2: uint) { unsafe { @@ -250,7 +255,7 @@ macro_rules! container_impl( macro_rules! basis_impl( ($t: ident, $trhs: ident, $dim: expr) => ( - impl + $trhs>> Basis for $t { + impl + $trhs>> Basis for $t { #[inline] fn canonical_basis(f: |$t| -> bool) { for i in range(0u, $dim) { @@ -460,7 +465,7 @@ macro_rules! translation_impl( macro_rules! norm_impl( ($t: ident, $comp0: ident $(,$compN: ident)*) => ( - impl Norm for $t { + impl Norm for $t { #[inline] fn sqnorm(v: &$t) -> N { Dot::dot(v, v) diff --git a/src/tests/mat.rs b/src/tests/mat.rs index ca2be979..305f893d 100644 --- a/src/tests/mat.rs +++ b/src/tests/mat.rs @@ -3,6 +3,7 @@ use rand::random; use na::{Vec1, Vec3, Mat1, Mat2, Mat3, Mat4, Mat5, Mat6, Rot3, DMat, DVec, Indexable}; use na; use na::decomp_qr; +use std::cmp::{min, max}; macro_rules! test_inv_mat_impl( ($t: ty) => ( @@ -24,6 +25,19 @@ macro_rules! test_transpose_mat_impl( ); ) +macro_rules! test_decomp_qr_impl( + ($t: ty) => ( + for _ in range(0, 10000) { + let randmat : $t = random(); + + let (q, r) = decomp_qr(&randmat); + let recomp = q * r; + + assert!(na::approx_eq(&randmat, &recomp)); + } + ); +) + #[test] fn test_transpose_mat1() { test_transpose_mat_impl!(Mat1); @@ -210,20 +224,45 @@ fn test_dmat_from_vec() { #[test] fn test_decomp_qr() { - let mat = DMat::from_row_vec( - 5, - 3, - [ - 4.0, 2.0, 0.60, - 4.2, 2.1, 0.59, - 3.9, 2.0, 0.58, - 4.3, 2.1, 0.62, - 4.1, 2.2, 0.63 - ] - ); + for _ in range(0, 10) { + let dim1: uint = random(); + let dim2: uint = random(); + let rows = min(40, max(dim1, dim2)); + let cols = min(40, min(dim1, dim2)); + let randmat: DMat = DMat::new_random(rows, cols); + let (q, r) = decomp_qr(&randmat); + let recomp = q * r; - let (q, r) = decomp_qr(&mat); - let mat_ = q * r; - - assert!(na::approx_eq(&mat_, &mat)); + assert!(na::approx_eq(&randmat, &recomp)); + } +} + +#[test] +fn test_decomp_qr_mat1() { + test_decomp_qr_impl!(Mat1); +} + +#[test] +fn test_decomp_qr_mat2() { + test_decomp_qr_impl!(Mat2); +} + +#[test] +fn test_decomp_qr_mat3() { + test_decomp_qr_impl!(Mat3); +} + +#[test] +fn test_decomp_qr_mat4() { + test_decomp_qr_impl!(Mat4); +} + +#[test] +fn test_decomp_qr_mat5() { + test_decomp_qr_impl!(Mat5); +} + +#[test] +fn test_decomp_qr_mat6() { + test_decomp_qr_impl!(Mat6); } diff --git a/src/traits/mod.rs b/src/traits/mod.rs index 9c774d80..1c471b45 100644 --- a/src/traits/mod.rs +++ b/src/traits/mod.rs @@ -6,7 +6,7 @@ pub use self::geometry::{AbsoluteRotate, Cross, CrossMatrix, Dot, FromHomogeneou pub use self::structure::{FloatVec, FloatVecExt, Basis, Cast, Col, Dim, Indexable, Iterable, IterableMut, Mat, Row, AnyVec, VecExt, - ColSlice, RowSlice}; + ColSlice, RowSlice, Eye}; pub use self::operations::{Absolute, ApproxEq, Cov, Inv, LMul, Mean, Outer, PartialOrd, RMul, ScalarAdd, ScalarSub, Transpose}; diff --git a/src/traits/structure.rs b/src/traits/structure.rs index 953f13b9..0e94e6ad 100644 --- a/src/traits/structure.rs +++ b/src/traits/structure.rs @@ -19,6 +19,12 @@ pub trait Mat : Row + Col + RMul + LMul { } impl + Col + RMul + LMul, R, C> Mat for M { } +/// Trait for constructing the identity matrix +pub trait Eye { + /// Return the identity matrix of specified dimension + fn new_identity(dim: uint) -> Self; +} + // XXX: we keep ScalarAdd and ScalarSub here to avoid trait impl conflict (overriding) between the // different Add/Sub traits. This is _so_ unfortunate… @@ -126,6 +132,9 @@ pub trait Indexable { /// Swaps the `i`-th element of `self` with its `j`-th element. fn swap(&mut self, i: Index, j: Index); + /// Returns the shape of the iterable range + fn shape(&self) -> Index; + /// Reads the `i`-th element of `self`. /// /// `i` is not checked.