diff --git a/src/linalg/decompositions.rs b/src/linalg/decompositions.rs index 44471f57..650718eb 100644 --- a/src/linalg/decompositions.rs +++ b/src/linalg/decompositions.rs @@ -1,6 +1,6 @@ use std::num::{Zero, Float}; -use traits::operations::Transpose; -use traits::structure::{ColSlice, Eye, Indexable}; +use traits::operations::{Transpose, ApproxEq}; +use traits::structure::{ColSlice, Eye, Indexable, Diag}; use traits::geometry::Norm; use std::cmp::min; @@ -16,12 +16,16 @@ pub fn householder_matrix> (dim: uint, start: uint, vec: V) -> M { let mut qk : M = Eye::new_identity(dim); - let stop = start + vec.shape(); - assert!(stop <= dim); + let subdim = vec.shape(); + + let stop = subdim + start; + + assert!(dim >= stop); + for j in range(start, stop) { for i in range(start, stop) { unsafe { - let vv = vec.unsafe_at(i) * vec.unsafe_at(j); + let vv = vec.unsafe_at(i - start) * vec.unsafe_at(j - start); let qkij = qk.unsafe_at((i, j)); qk.unsafe_set((i, j), qkij - vv - vv); } @@ -60,11 +64,70 @@ pub fn qr + Norm, + V2: Zero, + M: Clone + Eye + ColSlice + Transpose + + Indexable<(uint, uint), N> + Mul + + Diag + ApproxEq + Add + + Sub> + (m: &M, eps: &N, niter: uint) -> (M, V2) { + let (rows, cols) = m.shape(); + + assert!(rows == cols, "The matrix being decomposed must be square."); + + let mut eigenvectors: M = Eye::new_identity(rows); + let mut eigenvalues = m.clone(); + let mut shifter: M = Eye::new_identity(rows); + + let mut iter = 0u; + for _ in range(0, niter) { + let mut stop = true; + + for j in range(0, cols) { + for i in range(0, j) { + if unsafe { eigenvalues.unsafe_at((i, j)) }.abs() >= *eps { + stop = false; + break; + } + } + + for i in range(j + 1, rows) { + if unsafe { eigenvalues.unsafe_at((i, j)) }.abs() >= *eps { + stop = false; + break; + } + } + } + + if stop { + break; + } + iter = iter + 1; + + // FIXME: This is a very naive implementation. + let shift = unsafe { eigenvalues.unsafe_at((rows - 1, rows - 1)) }; + + for i in range(0, rows) { + unsafe { shifter.unsafe_set((i, i), shift.clone()) } + } + + let (q, r) = qr(&(eigenvalues - shifter)); + + eigenvalues = r * q + shifter; + eigenvectors = eigenvectors * q; + } + + (eigenvectors, eigenvalues.diag()) +} diff --git a/src/tests/mat.rs b/src/tests/mat.rs index b5f9b677..882947ab 100644 --- a/src/tests/mat.rs +++ b/src/tests/mat.rs @@ -37,34 +37,28 @@ macro_rules! test_qr_impl( ); ) -macro_rules! test_eigen_qr_impl( - ($t: ty) => { - for _ in range(0u, 10000) { - let randmat : $t = random(); - - let (eigenvalues, eigenvectors) = na::eigen_qr(&randmat, &Float::epsilon(), 1000); - - // FIXME: provide a method to initialize a matrix from its diagonal! - let diag: $t = na::zero(); - - for i in range(0, na::dim::<$t>()) { - diag.set((i, i), eigenvalues.at(i)); - } - - let recomp = na::transpose(&eigenvectors) * diag * eigenvectors; - - println!("mat: {}", randmat); - println!("eigenvectors: {}", eigenvectors); - println!("eigenvalues: {}", eigenvalues); - println!("recomp: {}", recomp); - - assert!(false); - fail!("what!"); - - assert!(na::approx_eq(&randmat, &recomp)); - } - } -) +// NOTE: deactivated untile we get a better convergence rate. +// macro_rules! test_eigen_qr_impl( +// ($t: ty) => { +// for _ in range(0u, 10000) { +// let randmat : $t = random(); +// // Make it symetric so that we can recompose the matrix to test at the end. +// let randmat = na::transpose(&randmat) * randmat; +// +// let (eigenvectors, eigenvalues) = na::eigen_qr(&randmat, &Float::epsilon(), 100); +// +// let diag: $t = Diag::from_diag(&eigenvalues); +// +// let recomp = eigenvectors * diag * na::transpose(&eigenvectors); +// +// println!("eigenvalues: {}", eigenvalues); +// println!(" mat: {}", randmat); +// println!("recomp: {}", recomp); +// +// assert!(na::approx_eq_eps(&randmat, &recomp, &1.0e-2)); +// } +// } +// ) #[test] fn test_transpose_mat1() { @@ -295,6 +289,7 @@ fn test_qr_mat6() { test_qr_impl!(Mat6); } +// NOTE: deactivated untile we get a better convergence rate. // #[test] // fn test_eigen_qr_mat1() { // test_eigen_qr_impl!(Mat1); diff --git a/src/traits/structure.rs b/src/traits/structure.rs index 39e4a65c..94d16aae 100644 --- a/src/traits/structure.rs +++ b/src/traits/structure.rs @@ -144,7 +144,7 @@ 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 + /// Returns the shape of the iterable range. fn shape(&self) -> Index; /// Reads the `i`-th element of `self`.