diff --git a/src/lib.rs b/src/lib.rs index 3650a4d6..125c679a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,6 +119,7 @@ extern crate test; pub mod na; mod structs; mod traits; +mod linalg; // mod lower_triangular; // mod chol; diff --git a/src/linalg/decompositions.rs b/src/linalg/decompositions.rs new file mode 100644 index 00000000..47409224 --- /dev/null +++ b/src/linalg/decompositions.rs @@ -0,0 +1,58 @@ +use std::num::{Zero, Float}; +use na::DVec; +use na::DMat; +use traits::operations::Transpose; +use traits::structure::ColSlice; +use traits::geometry::Norm; +use std::cmp::min; + +/// 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(); + assert!(rows >= cols); + let mut q : DMat = DMat::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() { + -Norm::norm(&v) + } + else { + Norm::norm(&v) + }; + unsafe { + let x = v.at_fast(0); + v.set_fast(0, x - alpha); + } + let _ = v.normalize(); + let qk = subtract_reflection(v); + r = qk * r; + q = q * Transpose::transpose_cpy(&qk); + } + + (q, r) +} + diff --git a/src/linalg/mod.rs b/src/linalg/mod.rs new file mode 100644 index 00000000..6c2c6d63 --- /dev/null +++ b/src/linalg/mod.rs @@ -0,0 +1,4 @@ + +pub use self::decompositions::decomp_qr; + +mod decompositions; diff --git a/src/na.rs b/src/na.rs index 98211927..415eb8f2 100644 --- a/src/na.rs +++ b/src/na.rs @@ -39,7 +39,8 @@ pub use traits::{ Transpose, UniformSphereSample, AnyVec, - VecExt + VecExt, + ColSlice, RowSlice }; pub use structs::{ @@ -52,6 +53,10 @@ pub use structs::{ Vec0, Vec1, Vec2, Vec3, Vec4, Vec5, Vec6 }; +pub use linalg::{ + decomp_qr +}; + /// Traits to work around the language limitations related to operator overloading. /// /// The trait names are formed by: diff --git a/src/structs/dmat.rs b/src/structs/dmat.rs index 83d36a39..8fac029c 100644 --- a/src/structs/dmat.rs +++ b/src/structs/dmat.rs @@ -9,7 +9,8 @@ use traits::operations::ApproxEq; use std::mem; use structs::dvec::{DVec, DVecMulRhs}; use traits::operations::{Inv, Transpose, Mean, Cov}; -use traits::structure::Cast; +use traits::structure::{Cast, ColSlice, RowSlice}; +use std::fmt::{Show, Formatter, Result}; #[doc(hidden)] mod metal; @@ -494,6 +495,39 @@ impl + DMatDivRhs> + ToStr > Cov> } } +impl ColSlice> for DMat { + fn col_slice(&self, col_id :uint, row_start: uint, row_end: uint) -> DVec { + assert!(col_id < self.ncols); + assert!(row_start < row_end); + assert!(row_end <= self.nrows); + // we can init from slice thanks to the matrix being column major + let start= self.offset(row_start, col_id); + let stop = self.offset(row_end, col_id); + let slice = DVec::from_vec( + row_end - row_start, self.mij.slice(start, stop)); + slice + } +} + +impl RowSlice> for DMat { + fn row_slice(&self, row_id :uint, col_start: uint, col_end: uint) -> DVec { + assert!(row_id < self.nrows); + assert!(col_start < col_end); + assert!(col_end <= self.ncols); + let mut slice : DVec = unsafe { + DVec::new_uninitialized(self.nrows) + }; + 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_idx += 1; + } + slice + } +} + impl> ApproxEq for DMat { #[inline] fn approx_epsilon(_: Option>) -> N { @@ -515,6 +549,18 @@ impl> ApproxEq for DMat { } } +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, "\n"); + } + write!(form.buf, "\n") + } +} + macro_rules! scalar_mul_impl ( ($n: ident) => ( impl DMatMulRhs<$n, DMat<$n>> for $n { diff --git a/src/tests/mat.rs b/src/tests/mat.rs index 6d623bdc..ca2be979 100644 --- a/src/tests/mat.rs +++ b/src/tests/mat.rs @@ -2,6 +2,7 @@ use std::num::{Float, abs}; use rand::random; use na::{Vec1, Vec3, Mat1, Mat2, Mat3, Mat4, Mat5, Mat6, Rot3, DMat, DVec, Indexable}; use na; +use na::decomp_qr; macro_rules! test_inv_mat_impl( ($t: ty) => ( @@ -206,3 +207,23 @@ fn test_dmat_from_vec() { assert!(mat1 == mat2); } + +#[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 + ] + ); + + let (q, r) = decomp_qr(&mat); + let mat_ = q * r; + + assert!(na::approx_eq(&mat_, &mat)); +} diff --git a/src/traits/mod.rs b/src/traits/mod.rs index 8a62fab5..9c774d80 100644 --- a/src/traits/mod.rs +++ b/src/traits/mod.rs @@ -5,7 +5,8 @@ pub use self::geometry::{AbsoluteRotate, Cross, CrossMatrix, Dot, FromHomogeneou Transform, Transformation, Translate, Translation, UniformSphereSample}; pub use self::structure::{FloatVec, FloatVecExt, Basis, Cast, Col, Dim, Indexable, - Iterable, IterableMut, Mat, Row, AnyVec, VecExt}; + Iterable, IterableMut, Mat, Row, AnyVec, VecExt, + ColSlice, RowSlice}; pub use self::operations::{Absolute, ApproxEq, Cov, Inv, LMul, Mean, Outer, PartialOrd, RMul, ScalarAdd, ScalarSub, Transpose}; diff --git a/src/traits/operations.rs b/src/traits/operations.rs index 0f836fbc..b71a1ff2 100644 --- a/src/traits/operations.rs +++ b/src/traits/operations.rs @@ -244,6 +244,7 @@ pub trait Mean { fn mean(&Self) -> N; } + // /// Cholesky decomposition. // pub trait Chol { // /// Performs the cholesky decomposition on `self`. The resulting upper-triangular matrix is diff --git a/src/traits/structure.rs b/src/traits/structure.rs index db0f1be9..953f13b9 100644 --- a/src/traits/structure.rs +++ b/src/traits/structure.rs @@ -91,6 +91,18 @@ pub trait Col { // a lot of operations. } +/// Trait to access part of a column of a matrix +pub trait ColSlice { + /// Returns a view to a slice of a column of a matrix. + fn col_slice(&self, col_id: uint, row_start: uint, row_end: uint) -> C; +} + +/// Trait to access part of a row of a matrix +pub trait RowSlice { + /// Returns a view to a slice of a row of a matrix. + fn row_slice(&self, row_id: uint, col_start: uint, col_end: uint) -> R; +} + /// Trait of objects having a spacial dimension known at compile time. pub trait Dim { /// The dimension of the object.