shape_runtime/simd_rolling/
window.rs1use wide::f64x4;
4
5use super::SIMD_THRESHOLD;
6
7#[cfg(feature = "simd")]
11#[inline]
12pub fn rolling_sum(data: &[f64], window: usize) -> Vec<f64> {
13 rolling_sum_simd(data, window)
14}
15
16#[cfg(not(feature = "simd"))]
17#[inline]
18pub fn rolling_sum(data: &[f64], window: usize) -> Vec<f64> {
19 rolling_sum_scalar(data, window)
20}
21
22#[cfg(feature = "simd")]
24#[inline]
25pub fn rolling_mean(data: &[f64], window: usize) -> Vec<f64> {
26 rolling_mean_simd(data, window)
27}
28
29#[cfg(not(feature = "simd"))]
30#[inline]
31pub fn rolling_mean(data: &[f64], window: usize) -> Vec<f64> {
32 rolling_mean_scalar(data, window)
33}
34
35fn rolling_sum_simd(data: &[f64], window: usize) -> Vec<f64> {
46 let n = data.len();
47
48 if window == 0 || window > n {
49 return vec![f64::NAN; n];
50 }
51
52 if n < SIMD_THRESHOLD {
54 return rolling_sum_scalar(data, window);
55 }
56
57 let mut result = vec![f64::NAN; n];
58
59 let mut sum: f64 = data[..window].iter().sum();
61 result[window - 1] = sum;
62
63 let remaining = n - window;
66 let simd_chunks = remaining / 4;
67
68 for chunk_idx in 0..simd_chunks {
69 let base = window + chunk_idx * 4;
70
71 let add_vals = f64x4::new([data[base], data[base + 1], data[base + 2], data[base + 3]]);
73
74 let sub_vals = f64x4::new([
76 data[base - window],
77 data[base - window + 1],
78 data[base - window + 2],
79 data[base - window + 3],
80 ]);
81
82 let deltas = add_vals - sub_vals;
84 let delta_arr = deltas.to_array();
85
86 for j in 0..4 {
88 sum += delta_arr[j];
89 result[base + j] = sum;
90 }
91 }
92
93 let processed = window + simd_chunks * 4;
95 for i in processed..n {
96 sum = sum - data[i - window] + data[i];
97 result[i] = sum;
98 }
99
100 result
101}
102
103fn rolling_sum_scalar(data: &[f64], window: usize) -> Vec<f64> {
105 let n = data.len();
106 let mut result = vec![f64::NAN; n];
107
108 if window == 0 || window > n {
109 return result;
110 }
111
112 let mut sum: f64 = data[..window].iter().sum();
113 result[window - 1] = sum;
114
115 for i in window..n {
116 sum = sum - data[i - window] + data[i];
117 result[i] = sum;
118 }
119
120 result
121}
122
123#[cfg(not(feature = "simd"))]
124fn rolling_mean_scalar(data: &[f64], window: usize) -> Vec<f64> {
125 let sums = rolling_sum_scalar(data, window);
126 sums.iter()
127 .map(|&s| {
128 if s.is_nan() {
129 f64::NAN
130 } else {
131 s / window as f64
132 }
133 })
134 .collect()
135}
136
137fn rolling_mean_simd(data: &[f64], window: usize) -> Vec<f64> {
145 let sums = rolling_sum_simd(data, window);
146 let window_f64 = window as f64;
147
148 let mut result = vec![f64::NAN; sums.len()];
150 let chunks = sums.len() / 4;
151
152 for chunk in 0..chunks {
153 let i = chunk * 4;
154 let sum_vec = f64x4::new([sums[i], sums[i + 1], sums[i + 2], sums[i + 3]]);
155 let window_vec = f64x4::splat(window_f64);
156 let mean = sum_vec / window_vec;
157 let arr = mean.to_array();
158 result[i..i + 4].copy_from_slice(&arr);
159 }
160
161 for i in (chunks * 4)..sums.len() {
163 result[i] = sums[i] / window_f64;
164 }
165
166 result
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172
173 #[test]
174 fn test_rolling_sum_simd() {
175 let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
176 let result = rolling_sum_simd(&data, 3);
177
178 assert!(result[0].is_nan());
180 assert!(result[1].is_nan());
181
182 assert_eq!(result[2], 6.0); assert_eq!(result[3], 9.0); assert_eq!(result[4], 12.0); assert_eq!(result[5], 15.0); assert_eq!(result[6], 18.0); assert_eq!(result[7], 21.0); }
190
191 #[test]
192 fn test_rolling_mean_simd() {
193 let data = vec![2.0, 4.0, 6.0, 8.0, 10.0];
194 let result = rolling_mean_simd(&data, 3);
195
196 assert!(result[0].is_nan());
197 assert!(result[1].is_nan());
198 assert_eq!(result[2], 4.0); assert_eq!(result[3], 6.0); assert_eq!(result[4], 8.0); }
202}