use impl_prelude::*;
use lapack::c::{sgeqrf, sorgqr, dgeqrf, dorgqr, cgeqrf, cungqr, zgeqrf, zungqr};
use ndarray as nd;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum QRError {
BadLayout,
InconsistentDimensions,
IllegalParameter(i32),
}
#[derive(Debug)]
pub struct QRFactors<T: QR> {
mat: Array<T, Ix2>,
tau: Vec<T>,
}
impl<T: QR> QRFactors<T> {
fn from_raw<Matrix>(mat: Matrix, tau: Vec<T>) -> Result<QRFactors<T>, QRError>
where Matrix: Into<Array<T, Ix2>>
{
let mut ma = mat.into();
if slice_and_layout_mut(&mut ma).is_none() {
return Err(QRError::BadLayout);
}
Ok(QRFactors {
mat: ma,
tau: tau,
})
}
pub fn rows(&self) -> usize {
self.mat.rows()
}
pub fn cols(&self) -> usize {
self.mat.cols()
}
fn p(&self) -> usize {
cmp::min(self.rows(), self.cols())
}
pub fn qk<K: Into<Option<usize>>>(&self, k: K) -> Result<Array<T, Ix2>, QRError> {
let kr = k.into();
QR::compute_q(&self.mat, &self.tau, kr.unwrap_or(self.p()))
}
#[inline]
pub fn q(&self) -> Array<T, Ix2> {
self.qk(None).expect("Invalid implementation of Self::qk. Please report.")
}
pub fn rk<K: Into<Option<usize>>>(&self, k: K) -> Result<Array<T, Ix2>, QRError> {
let kr = k.into();
let p = kr.unwrap_or(self.p());
QR::compute_r(&self.mat, p)
}
#[inline]
pub fn r(&self) -> Array<T, Ix2> {
self.rk(None).expect("Invalid implementation of Self::rk. Please report.")
}
pub fn reconstruct(&self) -> Array<T, Ix2> {
self.q().dot(&self.r())
}
}
pub trait QR: nd::LinalgScalar {
fn compute_into(a: Array<Self, Ix2>) -> Result<QRFactors<Self>, QRError>;
fn compute<D1: Data>(a: &ArrayBase<D1, Ix2>) -> Result<QRFactors<Self>, QRError>
where D1: Data<Elem = Self>
{
Self::compute_into(a.to_owned())
}
fn compute_q<D1>(mat: &ArrayBase<D1, Ix2>,
tau: &[Self],
k: usize)
-> Result<Array<Self, Ix2>, QRError>
where D1: Data<Elem = Self>;
fn compute_r<D1>(mat: &ArrayBase<D1, Ix2>, k: usize) -> Result<Array<Self, Ix2>, QRError>
where D1: Data<Elem = Self>;
}
macro_rules! impl_qr {
($qr_type:ty, $qr_func:ident, $qr_to_q:ident) => (
impl QR for $qr_type {
fn compute_into(mut a: Array<Self, Ix2>) -> Result<QRFactors<Self>, QRError> {
let dim = a.dim();
let (info, tau) = {
let (mut slice, layout, lda) = match slice_and_layout_mut(&mut a) {
None => return Err(QRError::BadLayout),
Some(x) => x,
};
let mut tau = Vec::new();
tau.resize(cmp::min(dim.0, dim.1), <$qr_type as Zero>::zero());
($qr_func(layout,
dim.0 as i32,
dim.1 as i32,
&mut slice,
lda as i32,
&mut tau),
tau)
};
if info == 0 {
QRFactors::from_raw(a, tau)
} else if info < 0 {
Err(QRError::IllegalParameter(-info))
} else {
unreachable!();
}
}
fn compute_q<D1>(mat: &ArrayBase<D1, Ix2>,
tau: &[Self],
k: usize)
-> Result<Array<Self, Ix2>, QRError>
where D1: Data<Elem = Self>
{
let (m, n) = mat.dim();
if k > m {
return Err(QRError::InconsistentDimensions);
}
let mut q = mat.slice(s![.., ..k as isize]).to_owned();
let info = {
let (slice, layout, ldq) = match slice_and_layout_mut(&mut q) {
None => unreachable!(),
Some(fwd) => fwd,
};
$qr_to_q(layout,
m as i32,
k as i32,
cmp::min(k, n) as i32,
slice,
ldq as i32,
tau)
};
if info == 0 {
Ok(q)
} else {
Err(QRError::IllegalParameter(-info))
}
}
fn compute_r<D1>(mat: &ArrayBase<D1, Ix2>, k: usize) -> Result<Array<Self, Ix2>, QRError>
where D1: Data<Elem = Self>
{
let (m, n) = mat.dim();
let nn = cmp::min(m, n);
if k > nn {
return Err(QRError::InconsistentDimensions);
}
let mut r = Array::zeros((k as usize, n));
r.slice_mut(s![..k as isize, ..]).assign(&mat.slice(s![..k as isize, ..]));
let zero = <$qr_type as Zero>::zero();
for (i, mut row) in r.outer_iter_mut().enumerate().take(nn as usize) {
row.slice_mut(s![..i as isize]).fill(zero);
}
Ok(r)
}
}
)
}
impl_qr!(f32, sgeqrf, sorgqr);
impl_qr!(f64, dgeqrf, dorgqr);
impl_qr!(c32, cgeqrf, cungqr);
impl_qr!(c64, zgeqrf, zungqr);