use crate::math::reduction::DET_REDUCE_BLOCK;
use crate::neural_network::Tensor;
use ndarray::Array1;
use rayon::iter::{IndexedParallelIterator, ParallelIterator};
use rayon::slice::ParallelSlice;
pub(super) fn segment_sum(seg: &[f32], scale: f32) -> f32 {
let mut lanes = [0.0f32; 8];
let mut chunks = seg.chunks_exact(8);
for ch in chunks.by_ref() {
for (l, &v) in lanes.iter_mut().zip(ch) {
*l += v * scale;
}
}
let mut tail = 0.0f32;
for &v in chunks.remainder() {
tail += v * scale;
}
((lanes[0] + lanes[1]) + (lanes[2] + lanes[3]))
+ ((lanes[4] + lanes[5]) + (lanes[6] + lanes[7]))
+ tail
}
pub(super) fn segment_dot(a: &[f32], b: &[f32], scale: f32) -> f32 {
let mut lanes = [0.0f32; 8];
let mut chunks_a = a.chunks_exact(8);
let mut chunks_b = b.chunks_exact(8);
for (ca, cb) in chunks_a.by_ref().zip(chunks_b.by_ref()) {
for ((l, &va), &vb) in lanes.iter_mut().zip(ca).zip(cb) {
*l += va * vb * scale;
}
}
let mut tail = 0.0f32;
for (&va, &vb) in chunks_a.remainder().iter().zip(chunks_b.remainder()) {
tail += va * vb * scale;
}
((lanes[0] + lanes[1]) + (lanes[2] + lanes[3]))
+ ((lanes[4] + lanes[5]) + (lanes[6] + lanes[7]))
+ tail
}
pub(super) fn segment_sq_dev(seg: &[f32], mean: f32) -> f32 {
let mut lanes = [0.0f32; 8];
let mut chunks = seg.chunks_exact(8);
for ch in chunks.by_ref() {
for (l, &v) in lanes.iter_mut().zip(ch) {
let d = v - mean;
*l += d * d;
}
}
let mut tail = 0.0f32;
for &v in chunks.remainder() {
let d = v - mean;
tail += d * d;
}
((lanes[0] + lanes[1]) + (lanes[2] + lanes[3]))
+ ((lanes[4] + lanes[5]) + (lanes[6] + lanes[7]))
+ tail
}
pub(super) fn segment_dot3(a: &[f32], b: &[f32], c: &[f32], scale: f32) -> f32 {
let mut lanes = [0.0f32; 8];
let mut chunks_a = a.chunks_exact(8);
let mut chunks_b = b.chunks_exact(8);
let mut chunks_c = c.chunks_exact(8);
for ((ca, cb), cc) in chunks_a
.by_ref()
.zip(chunks_b.by_ref())
.zip(chunks_c.by_ref())
{
for (((l, &va), &vb), &vc) in lanes.iter_mut().zip(ca).zip(cb).zip(cc) {
*l += va * vb * vc * scale;
}
}
let mut tail = 0.0f32;
for ((&va, &vb), &vc) in chunks_a
.remainder()
.iter()
.zip(chunks_b.remainder())
.zip(chunks_c.remainder())
{
tail += va * vb * vc * scale;
}
((lanes[0] + lanes[1]) + (lanes[2] + lanes[3]))
+ ((lanes[4] + lanes[5]) + (lanes[6] + lanes[7]))
+ tail
}
pub(super) fn rows_per_block(c: usize) -> usize {
(DET_REDUCE_BLOCK / c).max(1)
}
fn col_sum_chunk(chunk: &[f32], c: usize, scale: f32) -> Vec<f32> {
let mut acc = vec![0.0f32; c];
for row in chunk.chunks_exact(c) {
for (a, &v) in acc.iter_mut().zip(row) {
*a += v * scale;
}
}
acc
}
fn col_dot_chunk(chunk_a: &[f32], chunk_b: &[f32], c: usize, scale: f32) -> Vec<f32> {
let mut acc = vec![0.0f32; c];
for (row_a, row_b) in chunk_a.chunks_exact(c).zip(chunk_b.chunks_exact(c)) {
for ((s, &va), &vb) in acc.iter_mut().zip(row_a).zip(row_b) {
*s += va * vb * scale;
}
}
acc
}
fn merge_col_parts(parts: Vec<Vec<f32>>, c: usize) -> Tensor {
let mut out = vec![0.0f32; c];
for part in parts {
for (o, p) in out.iter_mut().zip(part) {
*o += p;
}
}
Array1::from_vec(out).into_dyn()
}
pub(super) fn par_col_sum(x: &[f32], c: usize, parallel: bool, scale: f32) -> Tensor {
let block = rows_per_block(c) * c;
let parts: Vec<Vec<f32>> = if parallel {
x.par_chunks(block)
.map(|chunk| col_sum_chunk(chunk, c, scale))
.collect()
} else {
x.chunks(block)
.map(|chunk| col_sum_chunk(chunk, c, scale))
.collect()
};
merge_col_parts(parts, c)
}
pub(super) fn par_col_dot(a: &[f32], b: &[f32], c: usize, parallel: bool, scale: f32) -> Tensor {
let block = rows_per_block(c) * c;
let parts: Vec<Vec<f32>> = if parallel {
a.par_chunks(block)
.zip(b.par_chunks(block))
.map(|(ca, cb)| col_dot_chunk(ca, cb, c, scale))
.collect()
} else {
a.chunks(block)
.zip(b.chunks(block))
.map(|(ca, cb)| col_dot_chunk(ca, cb, c, scale))
.collect()
};
merge_col_parts(parts, c)
}