From 6ad11edf9b453472806112258ad986be87a2a30e Mon Sep 17 00:00:00 2001 From: Vincent Barrielle Date: Sun, 11 May 2014 19:46:04 +0200 Subject: [PATCH 1/8] more generic QR: generalize the impl of the Indexable trait This allows the implementation of householder reflection without relying on knowledge of DVec. This required a new member in the Indexable trait: the shape() function, which returns the maximum index available. --- src/linalg/decompositions.rs | 55 ++++++++++++--------- src/na.rs | 3 +- src/structs/dmat.rs | 93 ++++++++++++++++++++++-------------- src/structs/dvec.rs | 48 ++++++++++++++----- src/structs/mat.rs | 15 +++++- src/structs/mat_macros.rs | 19 ++++++++ src/structs/spec/vec0.rs | 5 ++ src/structs/vec_macros.rs | 5 ++ src/traits/mod.rs | 2 +- src/traits/structure.rs | 9 ++++ 10 files changed, 182 insertions(+), 72 deletions(-) diff --git a/src/linalg/decompositions.rs b/src/linalg/decompositions.rs index 47409224..3cc1a0f5 100644 --- a/src/linalg/decompositions.rs +++ b/src/linalg/decompositions.rs @@ -1,11 +1,36 @@ 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. +fn householder_matrix, + V: Indexable> + (dim: uint, start: uint, vec: V) -> Mat { + let mut qk : Mat = 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 @@ -13,42 +38,26 @@ pub fn decomp_qr(m: &DMat) -> (DMat, DMat) { let rows = m.nrows(); let cols = m.ncols(); assert!(rows >= cols); - let mut q : DMat = DMat::new_identity(rows); + let mut q : DMat = 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: DMat = householder_matrix(rows, 0, v); r = qk * r; q = q * Transpose::transpose_cpy(&qk); } diff --git a/src/na.rs b/src/na.rs index 415eb8f2..6832fb18 100644 --- a/src/na.rs +++ b/src/na.rs @@ -40,7 +40,8 @@ pub use traits::{ UniformSphereSample, AnyVec, VecExt, - ColSlice, RowSlice + ColSlice, RowSlice, + Eye }; pub use structs::{ diff --git a/src/structs/dmat.rs b/src/structs/dmat.rs index 8fac029c..40a3dea3 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!(offset1 < 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,7 +576,7 @@ 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.buf, "{} ", self.at((i, j))); } let _ = write!(form.buf, "\n"); } diff --git a/src/structs/dvec.rs b/src/structs/dvec.rs index b45a1a47..4da43229 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] { @@ -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,7 +298,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)) * c.at_fast(i) }; + res = res + unsafe { (a.unsafe_at(i) - b.unsafe_at(i)) * c.unsafe_at(i) }; } res diff --git a/src/structs/mat.rs b/src/structs/mat.rs index c17831cf..0d180fcf 100644 --- a/src/structs/mat.rs +++ b/src/structs/mat.rs @@ -9,7 +9,8 @@ use std::slice::{Items, MutItems}; use structs::vec::{Vec1, Vec2, Vec3, Vec4, Vec5, Vec6, Vec1MulRhs, Vec4MulRhs, Vec5MulRhs, Vec6MulRhs}; -use traits::structure::{Cast, Row, Col, Iterable, IterableMut, Dim, Indexable}; +use traits::structure::{Cast, Row, Col, Iterable, IterableMut, Dim, Indexable, + Eye}; use traits::operations::{Absolute, Transpose, Inv, Outer}; use traits::geometry::{ToHomogeneous, FromHomogeneous}; @@ -34,6 +35,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) @@ -127,6 +130,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) @@ -225,6 +230,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) @@ -337,6 +344,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) @@ -501,6 +510,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) @@ -681,6 +692,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) diff --git a/src/structs/mat_macros.rs b/src/structs/mat_macros.rs index 2c13926d..6d08435b 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 { (*cast::transmute::<&$t, &[N, ..$dim * $dim]>(self).unsafe_ref(i + j * $dim)).clone() diff --git a/src/structs/spec/vec0.rs b/src/structs/spec/vec0.rs index 4843e3e1..ce493dda 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) { } diff --git a/src/structs/vec_macros.rs b/src/structs/vec_macros.rs index 0eeafb93..90e5ca9f 100644 --- a/src/structs/vec_macros.rs +++ b/src/structs/vec_macros.rs @@ -165,6 +165,11 @@ macro_rules! indexable_impl( } } + #[inline] + fn shape(&self) -> uint { + $dim + } + #[inline] fn swap(&mut self, i1: uint, i2: uint) { unsafe { 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. From dcf7b8ad018c074db9586bf139fba600e76a6016 Mon Sep 17 00:00:00 2001 From: Vincent Barrielle Date: Sun, 11 May 2014 21:20:41 +0200 Subject: [PATCH 2/8] generic implementation of QR decomposition But static matrices can't use it yet, they need to implement the Row/Col slicing traits. --- src/linalg/decompositions.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/linalg/decompositions.rs b/src/linalg/decompositions.rs index 3cc1a0f5..6c28b30f 100644 --- a/src/linalg/decompositions.rs +++ b/src/linalg/decompositions.rs @@ -1,5 +1,4 @@ use std::num::{Zero, Float}; -use na::DMat; use traits::operations::Transpose; use traits::structure::{ColSlice, Eye, Indexable}; use traits::geometry::Norm; @@ -34,11 +33,14 @@ fn householder_matrix(m: &DMat) -> (DMat, DMat) { - let rows = m.nrows(); - let cols = m.ncols(); +pub fn decomp_qr + Norm, + Mat: Clone + Eye + ColSlice + Transpose + + Indexable<(uint, uint), N> + Mul> + (m: &Mat) -> (Mat, Mat) { + let (rows, cols) = m.shape(); assert!(rows >= cols); - let mut q : DMat = Eye::new_identity(rows); + let mut q : Mat = Eye::new_identity(rows); let mut r = m.clone(); let iterations = min(rows - 1, cols); @@ -57,7 +59,7 @@ pub fn decomp_qr(m: &DMat) -> (DMat, DMat) { v.unsafe_set(0, x - alpha); } let _ = v.normalize(); - let qk: DMat = householder_matrix(rows, 0, v); + let qk: Mat = householder_matrix(rows, 0, v); r = qk * r; q = q * Transpose::transpose_cpy(&qk); } From 76a8bc3cf158f6fbc1cdf23ed90363385b873df7 Mon Sep 17 00:00:00 2001 From: Vincent Barrielle Date: Mon, 12 May 2014 14:06:25 +0200 Subject: [PATCH 3/8] QR factorization for fixed size matrices. The ColSlice implementation for fixed size matrices returns a DVec, while this is probably not optimal performance-wise, the dynamic nature of the result makes this necessary. Using a data type presenting the ImmutableVector trait would solve this, but it looks like a non-trivial change. --- src/structs/mat.rs | 15 ++++++++- src/structs/mat_macros.rs | 40 +++++++++++++++++++++++ src/tests/mat.rs | 69 ++++++++++++++++++++++++++++++--------- 3 files changed, 108 insertions(+), 16 deletions(-) diff --git a/src/structs/mat.rs b/src/structs/mat.rs index 0d180fcf..14dc5476 100644 --- a/src/structs/mat.rs +++ b/src/structs/mat.rs @@ -8,9 +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, - Eye}; + Eye, ColSlice, RowSlice}; use traits::operations::{Absolute, Transpose, Inv, Outer}; use traits::geometry::{ToHomogeneous, FromHomogeneous}; @@ -119,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) @@ -218,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) @@ -331,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) @@ -496,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) @@ -677,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) @@ -910,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 6d08435b..16005379 100644 --- a/src/structs/mat_macros.rs +++ b/src/structs/mat_macros.rs @@ -225,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 { + cast::transmute::<&$tv, & [N, ..$dim]> + (&col).slice(rstart, rend) + }); + res + } + } + ) +) + macro_rules! row_impl( ($t: ident, $tv: ident, $dim: expr) => ( impl Row<$tv> for $t { @@ -254,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 { + cast::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/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); } From 3dc0e27fd2d0a3cf8cb719711891763eff7244d5 Mon Sep 17 00:00:00 2001 From: Vincent Barrielle Date: Mon, 12 May 2014 21:54:18 +0200 Subject: [PATCH 4/8] fix typo --- src/structs/dmat.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/structs/dmat.rs b/src/structs/dmat.rs index 40a3dea3..6d101698 100644 --- a/src/structs/dmat.rs +++ b/src/structs/dmat.rs @@ -258,7 +258,7 @@ impl Indexable<(uint, uint), N> for DMat { let offset2 = self.offset(row2, col2); let count = self.mij.len(); assert!(offset1 < count); - assert!(offset1 < count); + assert!(offset2 < count); self.mij.as_mut_slice().swap(offset1, offset2); } From 73c6610048cd452f8f643d759cc1bf49cb096c24 Mon Sep 17 00:00:00 2001 From: Vincent Barrielle Date: Mon, 12 May 2014 21:54:59 +0200 Subject: [PATCH 5/8] new_identity and housholder matrix available under na:: --- src/linalg/decompositions.rs | 18 +++++++++--------- src/linalg/mod.rs | 2 +- src/na.rs | 7 ++++++- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/linalg/decompositions.rs b/src/linalg/decompositions.rs index 6c28b30f..6cb50153 100644 --- a/src/linalg/decompositions.rs +++ b/src/linalg/decompositions.rs @@ -11,11 +11,11 @@ use std::cmp::min; /// * `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. -fn householder_matrix, +pub fn householder_matrix, V: Indexable> - (dim: uint, start: uint, vec: V) -> Mat { - let mut qk : Mat = Eye::new_identity(dim); + (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) { @@ -35,12 +35,12 @@ fn householder_matrix + Norm, - Mat: Clone + Eye + ColSlice + Transpose - + Indexable<(uint, uint), N> + Mul> - (m: &Mat) -> (Mat, Mat) { + 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 : Mat = Eye::new_identity(rows); + let mut q : M = Eye::new_identity(rows); let mut r = m.clone(); let iterations = min(rows - 1, cols); @@ -59,7 +59,7 @@ pub fn decomp_qr>(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 */ From f8ac86effc44dac70b576be0f25275ecdad112b8 Mon Sep 17 00:00:00 2001 From: Vincent Barrielle Date: Tue, 13 May 2014 08:29:56 +0200 Subject: [PATCH 6/8] update to latest rust --- src/structs/mat_macros.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/structs/mat_macros.rs b/src/structs/mat_macros.rs index b3fb0513..e574a770 100644 --- a/src/structs/mat_macros.rs +++ b/src/structs/mat_macros.rs @@ -236,7 +236,7 @@ macro_rules! col_slice_impl( let res = DVec::from_vec( rend - rstart, unsafe { - cast::transmute::<&$tv, & [N, ..$dim]> + mem::transmute::<&$tv, & [N, ..$dim]> (&col).slice(rstart, rend) }); res @@ -285,7 +285,7 @@ macro_rules! row_slice_impl( let res = DVec::from_vec( cend - cstart, unsafe { - cast::transmute::<&$tv, & [N, ..$dim]> + mem::transmute::<&$tv, & [N, ..$dim]> (&row).slice(cstart, cend) }); res From b3bc4c66f179b1c67e743a6dedcf0261f658de8a Mon Sep 17 00:00:00 2001 From: Vincent Barrielle Date: Fri, 16 May 2014 19:55:20 +0200 Subject: [PATCH 7/8] QR decomposition mentionned in README --- README.md | 1 + 1 file changed, 1 insertion(+) 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/). From 987b91767acce1e2dd29cb10c01da396aafc80fa Mon Sep 17 00:00:00 2001 From: Vincent Barrielle Date: Fri, 16 May 2014 21:04:35 +0200 Subject: [PATCH 8/8] update to the latest rust: FloatMath for math functions (sin/exp/...) Also removed a bunch of duplicate trait usages --- src/na.rs | 4 ++-- src/structs/dmat.rs | 6 +++--- src/structs/dvec.rs | 4 ++-- src/structs/iso.rs | 2 +- src/structs/iso_macros.rs | 16 ++++++++-------- src/structs/rot.rs | 16 ++++++++-------- src/structs/rot_macros.rs | 2 +- src/structs/spec/vec0.rs | 2 +- src/structs/vec_macros.rs | 6 +++--- 9 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/na.rs b/src/na.rs index b3bab4e8..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::{ @@ -217,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(); diff --git a/src/structs/dmat.rs b/src/structs/dmat.rs index 6d101698..8f905d78 100644 --- a/src/structs/dmat.rs +++ b/src/structs/dmat.rs @@ -576,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 4da43229..662d3bfe 100644 --- a/src/structs/dvec.rs +++ b/src/structs/dvec.rs @@ -198,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. @@ -305,7 +305,7 @@ impl Dot for DVec { } } -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/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 1ea49bc2..3ca49091 100644 --- a/src/structs/spec/vec0.rs +++ b/src/structs/spec/vec0.rs @@ -164,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 a86fdfbf..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()) @@ -255,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) { @@ -465,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)