use super::complex_kernel::divide;
use ruda_kernel::dsl as kernel_dsl;
use ruda_kernel::dsl::prelude::*;
#[ruda]
pub(super) fn combine_norm<F: Float>(scale: F, sum: F, other: F, other_sum: F) -> (F, F) {
let mut s = scale; let mut q = sum;
if scale != scale || other != other { s = scale + other; q = s; }
else if scale == F::cast_from(f32::INFINITY) || other == F::cast_from(f32::INFINITY) {
s = F::cast_from(f32::INFINITY); q = F::new(1.0);
} else if scale < other {
let ratio = scale / other; s = other; q = other_sum + sum * ratio * ratio;
} else if scale != F::new(0.0) {
let ratio = other / scale; q = sum + other_sum * ratio * ratio;
}
(s, q)
}
#[ruda]
fn tail_norm<F: Float>(real: &Array<F>, imag: &Array<F>, base: usize, m: usize, n: usize, j: usize) -> F {
let lane = UNIT_POS as usize;
let mut scale = F::new(0.0); let mut sum = F::new(0.0); let mut row = j + 1 + lane;
while row < m {
let p = base + row * n + j;
let (sr, qr) = combine_norm::<F>(scale, sum, real[p].abs(), F::new(1.0));
let (si, qi) = combine_norm::<F>(sr, qr, imag[p].abs(), F::new(1.0));
scale = si; sum = qi; row += 256;
}
let mut scales = SharedMemory::<F>::new(256usize); let mut sums = SharedMemory::<F>::new(256usize);
scales[lane] = scale; sums[lane] = sum; sync_ruda();
let mut width = 128usize;
while width > 0 {
if lane < width {
let (s, q) = combine_norm::<F>(scales[lane], sums[lane], scales[lane + width], sums[lane + width]);
scales[lane] = s; sums[lane] = q;
}
sync_ruda(); width /= 2;
}
let value = scales[0usize] * sums[0usize].sqrt(); sync_ruda(); value
}
#[ruda]
fn beta<F: Float>(ar: F, ai: F, norm: F, #[comptime] wide: bool) -> F {
let (s, q) = combine_norm::<F>(ar.abs(), F::new(1.0), ai.abs(), F::new(1.0));
let (s, q) = combine_norm::<F>(s, q, norm, F::new(1.0));
let mut negative = false;
if comptime!(wide) { negative = (u64::reinterpret(ar) >> 63u32) != 0u64; }
else { negative = (u32::reinterpret(ar) >> 31u32) != 0u32; }
let mut value = -s * q.sqrt(); if negative { value = -value; } value
}
#[ruda(launch)]
pub(super) fn panel<F: Float>(
real: &mut Array<F>, imag: &mut Array<F>, tau_real: &mut Array<F>, tau_imag: &mut Array<F>,
batches: u32, rows: u32, cols: u32, column: u32, #[comptime] wide: bool,
) {
let batch = RUDA_POS_X as usize + RUDA_POS_Y as usize * RUDA_COUNT_X as usize;
if batch >= batches as usize { terminate!(); }
let lane = UNIT_POS as usize; let m = rows as usize; let n = cols as usize; let j = column as usize;
let base = batch * m * n; let diagonal = base + j * n + j; let t = batch * m.min(n) + j;
let mut ar = real[diagonal]; let mut ai = imag[diagonal];
let mut norm = tail_norm::<F>(real, imag, base, m, n, j);
if norm == F::new(0.0) && ai == F::new(0.0) {
if lane == 0 { tau_real[t] = F::new(0.0); tau_imag[t] = F::new(0.0); }
} else {
let mut b = beta::<F>(ar, ai, norm, wide);
let mut safmin = F::cast_from(f32::MIN_POSITIVE / (f32::EPSILON / 2.0));
if comptime!(wide) { safmin = F::cast_from(f64::MIN_POSITIVE / (f64::EPSILON / 2.0)); }
let reciprocal = F::new(1.0) / safmin; let mut count = 0u32;
if b.abs() < safmin {
while b.abs() < safmin && count < 20u32 {
let mut row = j + 1 + lane;
while row < m {
let p = base + row * n + j; real[p] *= reciprocal; imag[p] *= reciprocal; row += 256;
}
ar *= reciprocal; ai *= reciprocal; b *= reciprocal; count += 1;
sync_ruda();
}
norm = tail_norm::<F>(real, imag, base, m, n, j);
b = beta::<F>(ar, ai, norm, wide);
}
let tr = (b - ar) / b; let ti = -ai / b;
let (rr, ri) = divide::<F>(F::new(1.0), F::new(0.0), ar - b, ai, wide);
let mut row = j + 1 + lane;
while row < m {
let p = base + row * n + j; let r = real[p]; let i = imag[p];
real[p] = r * rr - i * ri; imag[p] = r * ri + i * rr; row += 256;
}
if lane == 0 {
tau_real[t] = tr; tau_imag[t] = ti;
for _ in 0..count { b *= safmin; }
real[diagonal] = b; imag[diagonal] = F::new(0.0);
}
}
}
#[ruda(launch)]
pub(super) fn apply<F: Float>(
packed_real: &Array<F>, packed_imag: &Array<F>, tau_real: &Array<F>, tau_imag: &Array<F>,
real: &mut Array<F>, imag: &mut Array<F>, batches: u32, rows: u32, cols: u32,
reflectors: u32, output_cols: u32, column: u32, first_col: u32, #[comptime] adjoint: bool,
) {
let work = RUDA_POS_X as usize + RUDA_POS_Y as usize * RUDA_COUNT_X as usize;
let m = rows as usize; let n = cols as usize; let nrhs = output_cols as usize;
let width = nrhs - first_col as usize;
if work >= batches as usize * width { terminate!(); }
let batch = work / width; let col = work % width + first_col as usize;
let lane = UNIT_POS as usize; let j = column as usize; let factor = batch * m * n; let out = batch * m * nrhs;
let t = batch * reflectors as usize + j; let tr = tau_real[t]; let mut ti = tau_imag[t];
if tr == F::new(0.0) && ti == F::new(0.0) { terminate!(); }
if comptime!(adjoint) { ti = -ti; }
let mut dr = F::new(0.0); let mut di = F::new(0.0); let mut row = j + lane;
while row < m {
let mut vr = F::new(1.0); let mut vi = F::new(0.0);
if row != j { vr = packed_real[factor + row * n + j]; vi = packed_imag[factor + row * n + j]; }
let r = real[out + row * nrhs + col]; let i = imag[out + row * nrhs + col];
dr += vr * r + vi * i; di += vr * i - vi * r; row += 256;
}
let mut reals = SharedMemory::<F>::new(256usize); let mut imags = SharedMemory::<F>::new(256usize);
reals[lane] = dr; imags[lane] = di; sync_ruda();
let mut step = 128usize;
while step > 0 {
if lane < step { reals[lane] = reals[lane] + reals[lane + step]; imags[lane] = imags[lane] + imags[lane + step]; }
sync_ruda(); step /= 2;
}
let wr = tr * reals[0usize] - ti * imags[0usize]; let wi = tr * imags[0usize] + ti * reals[0usize];
row = j + lane;
while row < m {
let mut vr = F::new(1.0); let mut vi = F::new(0.0);
if row != j { vr = packed_real[factor + row * n + j]; vi = packed_imag[factor + row * n + j]; }
let p = out + row * nrhs + col; real[p] -= vr * wr - vi * wi; imag[p] -= vr * wi + vi * wr;
row += 256;
}
}
#[ruda(launch)]
pub(super) fn resize<F: Float>(
ar: &Array<F>, ai: &Array<F>, real: &mut Array<F>, imag: &mut Array<F>,
rows: u32, cols: u32, output_rows: u32, output_cols: u32, #[comptime] upper: bool,
) {
let p = (RUDA_POS_X as usize + RUDA_POS_Y as usize * RUDA_COUNT_X as usize) * 256usize + UNIT_POS as usize;
if p < real.len() {
let om = output_rows as usize; let on = output_cols as usize;
let m = rows as usize; let n = cols as usize;
let batch = p / (om * on); let row = p / on % om; let col = p % on;
let mut r = F::new(0.0); let mut i = F::new(0.0);
let mut take = row < m && col < n; if comptime!(upper) { take = take && row <= col; }
if take { let q = (batch * m + row) * n + col; r = ar[q]; i = ai[q]; }
real[p] = r; imag[p] = i;
}
}
#[ruda(launch)]
pub(super) fn residuals<F: Float>(real: &Array<F>, imag: &Array<F>, out: &mut Array<F>, rows: u32, cols: u32, start: u32) {
let work = RUDA_POS_X as usize + RUDA_POS_Y as usize * RUDA_COUNT_X as usize;
if work >= out.len() { terminate!(); }
let lane = UNIT_POS as usize; let m = rows as usize; let nrhs = cols as usize;
let batch = work / nrhs; let col = work % nrhs; let mut row = start as usize + lane;
let mut value = F::new(0.0);
while row < m {
let p = (batch * m + row) * nrhs + col;
value += real[p] * real[p] + imag[p] * imag[p]; row += 256;
}
let mut sums = SharedMemory::<F>::new(256usize); sums[lane] = value; sync_ruda();
let mut width = 128usize;
while width > 0 {
if lane < width { sums[lane] = sums[lane] + sums[lane + width]; }
sync_ruda(); width /= 2;
}
if lane == 0 { out[work] = sums[0usize]; }
}