2014-05-10 04:14:37 +08:00
|
|
|
|
use std::num::{Zero, Float};
|
2014-08-16 20:53:25 +08:00
|
|
|
|
use traits::operations::{Transpose, ApproxEq};
|
|
|
|
|
use traits::structure::{ColSlice, Eye, Indexable, Diag};
|
2014-05-10 04:14:37 +08:00
|
|
|
|
use traits::geometry::Norm;
|
|
|
|
|
use std::cmp::min;
|
|
|
|
|
|
2014-05-12 01:46:04 +08:00
|
|
|
|
/// Get the householder matrix corresponding to a reflexion to the hyperplane
|
2014-07-12 15:30:49 +08:00
|
|
|
|
/// defined by `vec`. It can be a reflexion contained in a subspace.
|
2014-05-12 01:46:04 +08:00
|
|
|
|
///
|
|
|
|
|
/// # 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.
|
2014-10-26 21:40:27 +08:00
|
|
|
|
pub fn householder_matrix<N, V, M>(dim: uint, start: uint, vec: V) -> M
|
|
|
|
|
where N: Float,
|
|
|
|
|
M: Eye + Indexable<(uint, uint), N>,
|
|
|
|
|
V: Indexable<uint, N> {
|
2014-05-13 03:54:59 +08:00
|
|
|
|
let mut qk : M = Eye::new_identity(dim);
|
2014-08-16 20:53:25 +08:00
|
|
|
|
let subdim = vec.shape();
|
|
|
|
|
|
|
|
|
|
let stop = subdim + start;
|
|
|
|
|
|
|
|
|
|
assert!(dim >= stop);
|
|
|
|
|
|
2014-05-12 01:46:04 +08:00
|
|
|
|
for j in range(start, stop) {
|
|
|
|
|
for i in range(start, stop) {
|
|
|
|
|
unsafe {
|
2014-08-16 20:53:25 +08:00
|
|
|
|
let vv = vec.unsafe_at(i - start) * vec.unsafe_at(j - start);
|
2014-05-12 01:46:04 +08:00
|
|
|
|
let qkij = qk.unsafe_at((i, j));
|
|
|
|
|
qk.unsafe_set((i, j), qkij - vv - vv);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
qk
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-10 03:48:24 +08:00
|
|
|
|
/// QR decomposition using Householder reflections.
|
|
|
|
|
///
|
2014-05-10 04:14:37 +08:00
|
|
|
|
/// # Arguments
|
2014-05-10 18:48:25 +08:00
|
|
|
|
/// * `m` - matrix to decompose
|
2014-10-26 21:40:27 +08:00
|
|
|
|
pub fn qr<N, V, M>(m: &M) -> (M, M)
|
|
|
|
|
where N: Float,
|
2014-08-16 18:16:26 +08:00
|
|
|
|
V: Indexable<uint, N> + Norm<N>,
|
2014-10-26 21:40:27 +08:00
|
|
|
|
M: Clone + Eye + ColSlice<V> + Transpose + Indexable<(uint, uint), N> + Mul<M, M> {
|
2014-05-12 03:20:41 +08:00
|
|
|
|
let (rows, cols) = m.shape();
|
2014-05-10 04:14:37 +08:00
|
|
|
|
assert!(rows >= cols);
|
2014-05-13 03:54:59 +08:00
|
|
|
|
let mut q : M = Eye::new_identity(rows);
|
2014-05-10 04:14:37 +08:00
|
|
|
|
let mut r = m.clone();
|
|
|
|
|
|
2014-05-10 18:48:25 +08:00
|
|
|
|
let iterations = min(rows - 1, cols);
|
2014-05-10 04:14:37 +08:00
|
|
|
|
|
|
|
|
|
for ite in range(0u, iterations) {
|
|
|
|
|
let mut v = r.col_slice(ite, ite, rows);
|
|
|
|
|
let alpha =
|
2014-05-12 01:46:04 +08:00
|
|
|
|
if unsafe { v.unsafe_at(ite) } >= Zero::zero() {
|
2014-05-10 04:14:37 +08:00
|
|
|
|
-Norm::norm(&v)
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
Norm::norm(&v)
|
|
|
|
|
};
|
|
|
|
|
unsafe {
|
2014-05-12 01:46:04 +08:00
|
|
|
|
let x = v.unsafe_at(0);
|
|
|
|
|
v.unsafe_set(0, x - alpha);
|
2014-05-10 04:14:37 +08:00
|
|
|
|
}
|
2014-08-16 20:53:25 +08:00
|
|
|
|
if !v.normalize().is_zero() {
|
|
|
|
|
let qk: M = householder_matrix(rows, ite, v);
|
|
|
|
|
r = qk * r;
|
|
|
|
|
q = q * Transpose::transpose_cpy(&qk);
|
|
|
|
|
}
|
2014-05-10 04:14:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(q, r)
|
|
|
|
|
}
|
2014-08-16 20:53:25 +08:00
|
|
|
|
|
|
|
|
|
/// Eigendecomposition of a square matrix using the qr algorithm.
|
2014-10-26 21:40:27 +08:00
|
|
|
|
pub fn eigen_qr<N, V, V2, M>(m: &M, eps: &N, niter: uint) -> (M, V2)
|
|
|
|
|
where N: Float,
|
|
|
|
|
V: Indexable<uint, N> + Norm<N>,
|
|
|
|
|
V2: Zero,
|
|
|
|
|
M: Clone + Eye + ColSlice<V> + Transpose + Indexable<(uint, uint), N> + Mul<M, M>
|
|
|
|
|
+ Diag<V2> + ApproxEq<N> + Add<M, M> + Sub<M, M> {
|
2014-08-16 20:53:25 +08:00
|
|
|
|
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()) }
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-18 04:42:16 +08:00
|
|
|
|
let (q, r) = qr(&eigenvalues);// - shifter));
|
2014-08-16 20:53:25 +08:00
|
|
|
|
|
2014-08-18 04:42:16 +08:00
|
|
|
|
eigenvalues = r * q /*+ shifter*/;
|
2014-08-16 20:53:25 +08:00
|
|
|
|
eigenvectors = eigenvectors * q;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(eigenvectors, eigenvalues.diag())
|
|
|
|
|
}
|