mod kernels;
use super::blas::{Op, Side, Uplo, gemm, trsm};
use super::{Matrix, Scalar};
use kernels::{axpy, axpys4, dots, dots4, reflect, reflector};
#[derive(Clone, Debug)]
pub struct Qr<T> {
qr: Matrix<T>,
taus: Vec<T>,
ts: Vec<T>,
nb: usize,
}
const SMALL: usize = 4096;
const LEAF: usize = 8;
const FOURS: usize = 4;
const SUBSTITUTE: usize = 128;
const ONE_BLOCK: usize = 48;
const LEAF_T: usize = 64;
fn block_width<T: Scalar>(m: usize, n: usize) -> usize {
let size = m * n;
if size <= SMALL || (T::IS_COMPLEX && n <= 64 && size <= 8 * SMALL) {
return 0;
}
let e = n * if T::IS_COMPLEX {
4
} else if core::mem::size_of::<T>() == 4 {
2
} else {
1
};
let nb = match e {
0..=384 => 8,
385..=640 => 16,
641..=896 => 24,
_ => 32,
};
if m >= 4 * n {
let tall = match m {
0..=511 => 8,
512..=2047 => 16,
2048..=4095 => 48,
_ => 64,
};
nb.max(tall)
} else {
nb
}
}
impl<T: Scalar> Matrix<T> {
pub fn qr(&self) -> Qr<T> {
let (m, n) = self.shape();
let mut qr = self.clone();
let k = m.min(n);
let mut taus = vec![T::zero(); k];
let nb = block_width::<T>(m, n);
if nb == 0 {
unblocked(&mut qr.data, m, m, n, &mut taus, None, &mut []);
return Qr {
qr,
taus,
ts: Vec::new(),
nb: 0,
};
}
let mut ts = vec![T::zero(); nb * k];
let mut work = Work::new(nb, n);
let mut j0 = 0;
while j0 < k {
let kb = nb.min(k - j0);
let j1 = j0 + kb;
let (left, right) = qr.data.split_at_mut(j1 * m);
let block = &mut left[j0 + j0 * m..];
panel(
block,
m,
m - j0,
kb,
&mut taus[j0..j1],
&mut ts[j0 * nb..],
nb,
&mut work,
);
if j1 < n {
let Work { save, w, .. } = &mut work;
let t = &ts[j0 * nb..];
with_v(block, m, kb, save, |v| {
apply_block(v, m, m - j0, kb, t, nb, &mut right[j0..], m, n - j1, w);
});
}
j0 = j1;
}
Qr { qr, taus, ts, nb }
}
}
impl<T: Scalar> Qr<T> {
pub fn q(&self) -> Matrix<T> {
self.form_q(self.taus.len())
}
pub fn q_full(&self) -> Matrix<T> {
self.form_q(self.qr.rows())
}
pub fn r(&self) -> Matrix<T> {
let (m, n) = self.qr.shape();
let k = m.min(n);
let mut r = vec![T::zero(); k * n];
for (j, col) in r.chunks_exact_mut(k.max(1)).take(n).enumerate() {
let h = k.min(j + 1);
col[..h].copy_from_slice(&self.qr.data[j * m..j * m + h]);
}
Matrix::from_column_major(k, n, r)
}
#[track_caller]
pub fn solve(&self, b: &Matrix<T>) -> Option<Matrix<T>> {
let (m, n) = self.qr.shape();
assert!(m >= n, "the matrix has fewer rows than columns");
assert_eq!(b.rows(), m, "the right-hand side has not as many rows");
if (0..n).any(|i| self.qr[(i, i)] == T::zero()) {
return None;
}
let cols = b.cols();
let mut y = b.clone();
let a = &self.qr.data;
if cols == 0 {
} else if self.nb == 0 {
for (j, &tau) in self.taus.iter().enumerate() {
if tau != T::zero() {
let v = &a[j * m + j + 1..(j + 1) * m];
reflect(v, tau.conj(), &mut y.data[j..], m, cols);
}
}
} else if cols <= FOURS {
for y in y.data.chunks_exact_mut(m) {
self.reflect_fours(y);
}
} else {
self.apply_blocks(&mut y.data, cols);
}
for j in 1..cols {
y.data.copy_within(j * m..j * m + n, j * n);
}
y.data.truncate(n * cols);
let mut x = Matrix::from_column_major(n, cols, y.data);
if n == 0 {
} else if cols == 1 || n <= SUBSTITUTE {
for x in x.data.chunks_exact_mut(n) {
back_substitute(a, m, n, x);
}
} else {
trsm(
Side::Left,
Uplo::Upper,
Op::N,
false,
n,
cols,
a,
m,
&mut x.data,
n,
);
}
Some(x)
}
fn apply_blocks(&self, y: &mut [T], cols: usize) {
let m = self.qr.rows();
let (k, nb) = (self.taus.len(), self.nb);
let a = &self.qr.data;
let mut e1 = vec![T::zero(); nb * nb];
let mut w = vec![T::zero(); 2 * nb * cols];
let mut j0 = 0;
while j0 < k {
let kb = nb.min(k - j0);
let j1 = j0 + kb;
unit_lower(&a[j0 + j0 * m..], m, kb, &mut e1);
let v2 = &a[j1 + j0 * m..];
let t = &self.ts[j0 * nb..];
let (w1, w2) = w.split_at_mut(kb * cols);
let w2 = &mut w2[..kb * cols];
gemm(
kb,
cols,
kb,
T::one(),
&e1,
kb,
Op::C,
&y[j0..],
m,
Op::N,
T::zero(),
w1,
kb,
);
gemm(
kb,
cols,
m - j1,
T::one(),
v2,
m,
Op::C,
&y[j1..],
m,
Op::N,
T::one(),
w1,
kb,
);
gemm(
kb,
cols,
kb,
T::one(),
t,
nb,
Op::C,
w1,
kb,
Op::N,
T::zero(),
w2,
kb,
);
gemm(
kb,
cols,
kb,
-T::one(),
&e1,
kb,
Op::N,
w2,
kb,
Op::N,
T::one(),
&mut y[j0..],
m,
);
gemm(
m - j1,
cols,
kb,
-T::one(),
v2,
m,
Op::N,
w2,
kb,
Op::N,
T::one(),
&mut y[j1..],
m,
);
j0 = j1;
}
}
fn reflect_fours(&self, y: &mut [T]) {
let m = self.qr.rows();
let (k, nb) = (self.taus.len(), self.nb);
let a = &self.qr.data;
let mut j0 = 0;
while j0 < k {
let j1 = (j0 + nb).min(k);
let t = &self.ts[j0 * nb..];
let mut j = j0;
while j + 4 <= j1 && j + 4 <= m {
let at = |r: usize, c: usize| a[j + r + (j + c) * m];
let col = |c: usize| &a[(j + c) * m + j + 4..(j + c + 1) * m];
let cols = [col(0), col(1), col(2), col(3)];
let (top, bottom) = y[j..].split_at_mut(4);
let mut w = dots4(bottom, cols);
for (c, w) in w.iter_mut().enumerate() {
*w += top[c];
for (r, &y) in top.iter().enumerate().skip(c + 1) {
*w += at(r, c).conj() * y;
}
}
let d = j - j0;
let tt = |r: usize, c: usize| t[d + r + (d + c) * nb];
for c in (0..4).rev() {
let mut x = T::zero();
for (r, &w) in w.iter().enumerate().take(c + 1) {
x += tt(r, c).conj() * w;
}
w[c] = x;
}
for r in 0..4 {
let mut x = w[r];
for (c, &w) in w.iter().enumerate().take(r) {
x += at(r, c) * w;
}
top[r] -= x;
}
axpys4(bottom, cols, w);
j += 4;
}
for j in j..j1 {
let tau = self.taus[j];
if tau != T::zero() {
reflect(
&a[j * m + j + 1..(j + 1) * m],
tau.conj(),
&mut y[j..],
m,
1,
);
}
}
j0 = j1;
}
}
fn whole_t(&self) -> Vec<T> {
let m = self.qr.rows();
let k = self.taus.len();
let a = &self.qr.data;
let mut s = vec![T::zero(); k * k];
{
let mut e = vec![T::zero(); k * k];
unit_lower(a, m, k, &mut e);
gemm(
k,
k,
k,
T::one(),
&e,
k,
Op::C,
&e,
k,
Op::N,
T::zero(),
&mut s,
k,
);
gemm(
k,
k,
m - k,
T::one(),
&a[k..],
m,
Op::C,
&a[k..],
m,
Op::N,
T::one(),
&mut s,
k,
);
}
let mut t = vec![T::zero(); k * k];
let mut y = vec![T::zero(); k * LEAF_T];
let mut z = vec![T::zero(); k * LEAF_T];
let mut j0 = 0;
while j0 < k {
let j1 = (j0 + LEAF_T).min(k);
let kb = j1 - j0;
for j in j0..j1 {
let tau = self.taus[j];
t[j + j * k] = tau;
if tau == T::zero() {
continue;
}
for i in j0..j {
let mut x = T::zero();
for p in i..j {
x += t[i + p * k] * s[p + j * k];
}
t[i + j * k] = -tau * x;
}
}
if j0 > 0 {
let (y, z) = (&mut y[..j0 * kb], &mut z[..j0 * kb]);
gemm(
j0,
kb,
j0,
T::one(),
&t,
k,
Op::N,
&s[j0 * k..],
k,
Op::N,
T::zero(),
y,
j0,
);
gemm(
j0,
kb,
kb,
-T::one(),
y,
j0,
Op::N,
&t[j0 + j0 * k..],
k,
Op::N,
T::zero(),
z,
j0,
);
for (c, z) in z.chunks_exact(j0).enumerate() {
t[(j0 + c) * k..(j0 + c) * k + j0].copy_from_slice(z);
}
}
j0 = j1;
}
t
}
fn form_q(&self, cols: usize) -> Matrix<T> {
let m = self.qr.rows();
let k = self.taus.len();
let a = &self.qr.data;
let mut q = Matrix::from_column_major(m, cols, vec![T::zero(); m * cols]);
let one = cols > k && m >= 4 * k && m >= ONE_BLOCK;
let nb = if one { k } else { self.nb };
if nb == 0 || k == 0 {
for j in k..cols {
q.data[j + j * m] = T::one();
}
for j in (0..k).rev() {
let tau = self.taus[j];
let v = &a[j * m + j + 1..(j + 1) * m];
let (left, right) = q.data.split_at_mut((j + 1) * m);
let rest = cols - j - 1;
if rest > 0 && tau != T::zero() {
reflect(v, tau, &mut right[j..], m, rest);
}
left[j + j * m] = T::one() - tau;
for (q, &v) in left[j * m + j + 1..].iter_mut().zip(v) {
*q = -tau * v;
}
}
return q;
}
let built;
let ts: &[T] = if one {
built = self.whole_t();
&built
} else {
&self.ts
};
let mut e1 = vec![T::zero(); nb * nb];
let mut y = vec![T::zero(); nb * nb];
let mut w = vec![T::zero(); 2 * nb * cols];
let mut end = k;
while end > 0 {
let j0 = (end - 1) / nb * nb;
let j1 = end;
let kb = j1 - j0;
unit_lower(&a[j0 + j0 * m..], m, kb, &mut e1);
let v2 = &a[j1 + j0 * m..];
let t = &ts[j0 * nb..];
let (left, right) = q.data.split_at_mut(j1 * m);
let rest = cols - j1;
let last = j1 == k;
if rest > 0 {
let (w1, w2) = w.split_at_mut(kb * rest);
let w2 = &mut w2[..kb * rest];
if last {
for (c, w) in w1.chunks_exact_mut(kb).enumerate() {
for (i, w) in w.iter_mut().enumerate() {
*w = v2[c + i * m].conj();
}
}
} else {
gemm(
kb,
rest,
m - j1,
T::one(),
v2,
m,
Op::C,
&right[j1..],
m,
Op::N,
T::zero(),
w1,
kb,
);
}
gemm(
kb,
rest,
kb,
T::one(),
t,
nb,
Op::N,
w1,
kb,
Op::N,
T::zero(),
w2,
kb,
);
gemm(
kb,
rest,
kb,
-T::one(),
&e1,
kb,
Op::N,
w2,
kb,
Op::N,
T::zero(),
&mut right[j0..],
m,
);
gemm(
m - j1,
rest,
kb,
-T::one(),
v2,
m,
Op::N,
w2,
kb,
Op::N,
if last { T::zero() } else { T::one() },
&mut right[j1..],
m,
);
if last {
for c in 0..rest.min(m - j1) {
right[j1 + c + c * m] += T::one();
}
}
}
gemm(
kb,
kb,
kb,
T::one(),
t,
nb,
Op::N,
&e1,
kb,
Op::C,
T::zero(),
&mut y,
kb,
);
gemm(
kb,
kb,
kb,
-T::one(),
&e1,
kb,
Op::N,
&y,
kb,
Op::N,
T::zero(),
&mut left[j0 + j0 * m..],
m,
);
gemm(
m - j1,
kb,
kb,
-T::one(),
v2,
m,
Op::N,
&y,
kb,
Op::N,
T::zero(),
&mut left[j1 + j0 * m..],
m,
);
for i in j0..j1 {
left[i + i * m] += T::one();
}
end = j0;
}
q
}
}
fn back_substitute<T: Scalar>(r: &[T], ldr: usize, n: usize, x: &mut [T]) {
for j in (0..n).rev() {
let d = r[j + j * ldr].re();
let xj = T::from_parts(x[j].re() / d, x[j].im() / d);
x[j] = xj;
axpy(&r[j * ldr..j * ldr + j], xj, &mut x[..j]);
}
}
struct Work<T> {
save: Vec<T>,
w: Vec<T>,
u: Vec<T>,
}
impl<T: Scalar> Work<T> {
fn new(nb: usize, n: usize) -> Work<T> {
Work {
save: vec![T::zero(); nb * nb],
w: vec![T::zero(); 2 * nb * n.max(nb)],
u: vec![T::zero(); nb],
}
}
}
#[allow(clippy::too_many_arguments)]
fn panel<T: Scalar>(
a: &mut [T],
lda: usize,
m: usize,
n: usize,
taus: &mut [T],
t: &mut [T],
ldt: usize,
work: &mut Work<T>,
) {
if n <= LEAF {
unblocked(a, lda, m, n, taus, Some((t, ldt)), &mut work.u);
return;
}
let n1 = n / 2;
let n2 = n - n1;
panel(a, lda, m, n1, &mut taus[..n1], t, ldt, work);
let (l, r) = a.split_at_mut(n1 * lda);
{
let Work { save, w, .. } = &mut *work;
let t1: &[T] = t;
with_v(l, lda, n1, save, |v| {
apply_block(v, lda, m, n1, t1, ldt, r, lda, n2, w);
});
}
let (t1, t2) = t.split_at_mut(n1 * ldt);
panel(
&mut r[n1..],
lda,
m - n1,
n2,
&mut taus[n1..],
&mut t2[n1..],
ldt,
work,
);
let Work { save, w, .. } = work;
let (x, y) = w.split_at_mut(n1 * n2);
let y = &mut y[..n1 * n2];
let l: &[T] = l;
with_v(&mut r[n1..], lda, n2, save, |v2| {
gemm(
n1,
n2,
m - n1,
T::one(),
&l[n1..],
lda,
Op::C,
v2,
lda,
Op::N,
T::zero(),
x,
n1,
);
});
gemm(
n1,
n2,
n1,
T::one(),
t1,
ldt,
Op::N,
x,
n1,
Op::N,
T::zero(),
y,
n1,
);
gemm(
n1,
n2,
n2,
-T::one(),
y,
n1,
Op::N,
&t2[n1..],
ldt,
Op::N,
T::zero(),
x,
n1,
);
for (t, x) in t2.chunks_mut(ldt).zip(x.chunks(n1)) {
t[..n1].copy_from_slice(x);
}
}
fn unblocked<T: Scalar>(
a: &mut [T],
lda: usize,
m: usize,
n: usize,
taus: &mut [T],
mut t: Option<(&mut [T], usize)>,
u: &mut [T],
) {
let k = m.min(n);
for j in 0..k {
let (left, right) = a.split_at_mut(((j + 1) * lda).min(a.len()));
let col = &mut left[j * lda..j * lda + m];
let (tau, beta) = reflector(col[j], &mut col[j + 1..]);
col[j] = beta;
taus[j] = tau;
if tau == T::zero() {
continue;
}
let v = &left[j * lda + j + 1..j * lda + m];
let rest = n - j - 1;
if rest > 0 {
reflect(v, tau.conj(), &mut right[j..], lda, rest);
}
if let Some((t, ldt)) = &mut t {
let ldt = *ldt;
t[j + j * ldt] = tau;
if j > 0 {
let s = &mut u[..j];
dots(v, &left[j + 1..], lda, j, s);
for (i, s) in s.iter_mut().enumerate() {
*s = (left[j + i * lda] + *s).conj();
}
for i in 0..j {
let mut x = T::zero();
for p in i..j {
x += t[i + p * ldt] * s[p];
}
t[i + j * ldt] = -tau * x;
}
}
}
}
}
#[allow(clippy::too_many_arguments)]
fn apply_block<T: Scalar>(
v: &[T],
ldv: usize,
m: usize,
k: usize,
t: &[T],
ldt: usize,
c: &mut [T],
ldc: usize,
n: usize,
w: &mut [T],
) {
if n == 0 || k == 0 {
return;
}
let (w1, w2) = w[..2 * k * n].split_at_mut(k * n);
gemm(
k,
n,
m,
T::one(),
v,
ldv,
Op::C,
c,
ldc,
Op::N,
T::zero(),
w1,
k,
);
gemm(
k,
n,
k,
T::one(),
t,
ldt,
Op::C,
w1,
k,
Op::N,
T::zero(),
w2,
k,
);
gemm(
m,
n,
k,
-T::one(),
v,
ldv,
Op::N,
w2,
k,
Op::N,
T::one(),
c,
ldc,
);
}
fn with_v<T: Scalar, X>(
a: &mut [T],
lda: usize,
k: usize,
save: &mut [T],
f: impl FnOnce(&[T]) -> X,
) -> X {
for j in 0..k {
let col = &mut a[j * lda..j * lda + j + 1];
save[j * k..j * k + j + 1].copy_from_slice(col);
col[..j].fill(T::zero());
col[j] = T::one();
}
let x = f(a);
for j in 0..k {
a[j * lda..j * lda + j + 1].copy_from_slice(&save[j * k..j * k + j + 1]);
}
x
}
fn unit_lower<T: Scalar>(a: &[T], lda: usize, k: usize, e: &mut [T]) {
for j in 0..k {
let col = &mut e[j * k..(j + 1) * k];
col[..j].fill(T::zero());
col[j] = T::one();
col[j + 1..].copy_from_slice(&a[j * lda + j + 1..j * lda + k]);
}
}