1use crate::agreement::{LINE, Mapping, Walked};
3use crate::buffer::Element;
4use crate::error::{Error, Kind};
5use crate::gather::{self, Steps};
6use crate::layout::{Layout, count, strides};
7use crate::linalg::{Inner, Mapped, Orientation, Scalar};
8use crate::structure::{self, Plan, Windows};
9use std::borrow::Cow;
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13pub enum Mode {
14 Valid,
16 Full,
18 Same,
20}
21
22#[derive(Clone, Debug)]
24pub struct Correlation {
25 pub padding: Option<Plan>,
27 pub windows: Plan,
29 pub kernel: Plan,
31 pub size: [usize; 3],
33 pub shape: Vec<usize>,
35}
36
37pub fn correlation(input: &[usize], kernel: &[usize], slide: usize, mode: Mode, stride: &[usize], flip: bool) -> Result<Correlation, Error> {
39 let rank = input.len();
40 if slide == 0 || slide > rank { return Err(Error::new(Kind::Rank, format!("a correlation slides along 1 to {rank} axes, not {slide}"))); }
41 if kernel.len() < rank || kernel[slide..rank] != input[slide..] {
42 return Err(Error::new(
43 Kind::Length, format!("a kernel of shape {kernel:?} needs {slide} window sizes, then the array's other axes {:?}", &input[slide..])));
44 }
45 if stride.len() != slide || stride.contains(&0) {
46 return Err(Error::new(Kind::Domain, format!("a correlation needs a positive stride for each of its {slide} sliding axes")));
47 }
48 let window = &kernel[..slide];
49 for (&w, &n) in window.iter().zip(input) {
50 if w == 0 { return Err(Error::new(Kind::Length, "a window needs at least one item")); }
51 if mode == Mode::Valid && w > n { return Err(Error::new(Kind::Length, format!("a window of {w} doesn't fit an axis of {n} in valid mode"))); }
52 }
53 let widths: Vec<(usize, usize)> = (0..rank).map(|a| match (window.get(a), mode) {
54 (None, _) | (_, Mode::Valid) => (0, 0), (Some(&w), Mode::Full) => (w - 1, w - 1), (Some(&w), Mode::Same) => (w / 2, (w - 1) / 2)
55 }).collect();
56 let layout = Layout::from(input.to_vec());
57 let padding = widths.iter().any(|&w| w != (0, 0)).then(|| structure::pad(&layout, &widths, structure::Edge::Fill)).transpose()?;
58 let padded = padding.as_ref().map_or(&layout, |p| &p.layout);
59 let signed = |n: &[usize]| n.iter().map(|&n| i64::try_from(n).unwrap_or(i64::MAX)).collect::<Vec<_>>();
60 let slid = Windows::new(&signed(window), &signed(stride), |_| None, padded.shape())?;
61 let windows = slid.plan(padded, padded.shape())?;
62 let at = strides(kernel);
63 let steps = (0..kernel.len()).map(|a| match a < slide && flip {
64 true => Steps::Stride { start: (kernel[a] - 1) * at[a], len: kernel[a], step: -(at[a] as isize) }, false => Steps::along(kernel[a], at[a])
65 }).collect();
66 let outputs = &kernel[rank..];
67 let size = [count(slid.frame()), count(slid.cell()), count(outputs)];
68 Ok(Correlation { padding, windows, kernel: Plan { layout: Layout::from(kernel.to_vec()), steps }, size, shape: [slid.frame(), outputs].concat() })
69}
70
71pub fn correlate<T: Element, P: Inner<T>>(pair: &P, x: &[T], kernel: &[T], c: &Correlation) -> Vec<P::Out> {
73 let padded = match &c.padding {
74 None => Cow::Borrowed(x),
75 Some(p) => {
76 let mut out = Vec::with_capacity(p.layout.count());
77 gather::walk(&mut out, x, 0, &p.steps, T::FILL, gather::Same);
78 Cow::Owned(out)
79 }
80 };
81 let read = |plan: &Plan| Mapping::Steps { base: 0, steps: plan.steps.clone().into() };
82 let (windows, reversed) = (read(&c.windows), read(&c.kernel));
83 pair.inner_mapped(
84 Mapped { data: &padded, mapping: &Walked::from(&windows) }, Mapped { data: kernel, mapping: &Walked::from(&reversed) }, [Orientation::Normal; 2], c.size
85 )
86}
87
88pub fn overlap_add<T: Scalar>(frames: &[T], [outer, n, mid, w, inner]: [usize; 5], step: usize, len: usize) -> Vec<T> {
90 assert_eq!(frames.len(), outer * n * mid * w * inner, "the frames must fill their dimensions");
91 assert!(n == 0 || (n - 1) * step + w <= len, "the windows must lie inside the result");
92 let (block, row) = (w * inner, len * inner);
93 let mut out = vec![T::zero(); outer * mid * row];
94 if n == 0 || row == 0 { return out; }
95 for (i, out) in out.chunks_exact_mut(row).enumerate() {
97 let first = &frames[(i / mid * n * mid + i % mid) * block..];
98 if block * std::mem::size_of::<T>() >= LINE {
99 for (f, from) in first.chunks(mid * block).take(n).enumerate() {
100 for (y, &x) in out[f * step * inner..][..block].iter_mut().zip(&from[..block]) { *y = *y + x; }
101 }
102 } else {
103 for k in 0..block {
104 for (y, &x) in out[k..].iter_mut().step_by(step * inner).zip(first[k..].iter().step_by(mid * block)).take(n) { *y = *y + x; }
105 }
106 }
107 }
108 out
109}