1pub mod max {
3 pub use tract_data::internal::f16;
4
5 routine_reduce_rust!(generic;
6 f32,
7 SMax4,
8 4,
9 4,
10 fn run(x: &[f32], _: ()) -> f32 {
11 debug_assert!(x.len() % Self::nr() == 0);
12 debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0);
13 *x.iter().max_by(|a, b| a.total_cmp(b)).unwrap()
14 },
15 op(Max)
16 );
17
18 routine_reduce_rust!(generic;
19 f16,
20 HMax8,
21 8,
22 8,
23 fn run(x: &[f16], _: ()) -> f16 {
24 debug_assert!(x.len() % Self::nr() == 0);
25 debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0);
26 *x.iter().max_by(|a, b| a.total_cmp(b)).unwrap()
27 },
28 op(Max)
29 );
30}
31
32pub mod min {
34 pub use tract_data::internal::f16;
35
36 routine_reduce_rust!(generic;
37 f32,
38 SMin4,
39 4,
40 4,
41 fn run(x: &[f32], _: ()) -> f32 {
42 debug_assert!(x.len() % Self::nr() == 0);
43 debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0);
44 *x.iter().min_by(|a, b| a.total_cmp(b)).unwrap()
45 },
46 op(Min)
47 );
48}
49
50pub mod sum {
52 pub use tract_data::internal::f16;
53
54 routine_reduce_rust!(generic;
55 f32,
56 SSum4,
57 4,
58 4,
59 fn run(x: &[f32], _: ()) -> f32 {
60 debug_assert!(x.len() % Self::nr() == 0);
61 debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0);
62 x.iter().sum::<f32>()
63 },
64 op(Sum)
65 );
66
67 routine_reduce_rust!(generic;
68 f16,
69 HSum8,
70 8,
71 8,
72 fn run(x: &[f16], _: ()) -> f16 {
73 debug_assert!(x.len() % Self::nr() == 0);
74 debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0);
75 f16::from_f32(x.iter().map(|v| v.to_f32()).sum::<f32>())
79 },
80 op(Sum)
81 );
82}
83
84pub mod softmax_l2 {
86
87 #[inline(always)]
90 pub fn accurate_exp_f32(x: f32) -> f32 {
91 const LOG2E: f32 = 1.442_695_04;
92 const LN2_HI: f32 = 0.693_145_75;
93 const LN2_LO: f32 = 1.428_606_8e-6;
94 const MAGIC: f32 = 12_582_912.0;
95 let kf = (x * LOG2E + MAGIC) - MAGIC;
96 let rr = kf.mul_add(-LN2_LO, kf.mul_add(-LN2_HI, x));
97 let mut q = 1.383684405e-03f32;
98 q = q.mul_add(rr, 8.374815793e-03);
99 q = q.mul_add(rr, 4.166822560e-02);
100 q = q.mul_add(rr, 1.666642017e-01);
101 q = q.mul_add(rr, 4.999999208e-01);
102 q = q.mul_add(rr, 1.000000036e+00);
103 q = q.mul_add(rr, 1.000000001e+00);
104 let k = kf as i32;
105 let scale = f32::from_bits(((k + 127).clamp(1, 254) as u32) << 23);
106 #[allow(clippy::manual_range_contains)]
113 if x < -103.0 || x > 0.0 { 0.0 } else { q * scale }
114 }
115
116 #[cfg(target_arch = "aarch64")]
121 #[inline]
122 fn exp_sum_impl(x: &mut [f32], max: f32) -> f32 {
123 use std::arch::aarch64::*;
124 unsafe {
125 #[inline(always)]
126 unsafe fn exp_ps(x: float32x4_t) -> float32x4_t {
127 unsafe {
128 let kf = vrndnq_f32(vmulq_f32(x, vdupq_n_f32(1.442_695_04)));
129 let mut rr = vfmsq_f32(x, kf, vdupq_n_f32(0.693_145_75));
130 rr = vfmsq_f32(rr, kf, vdupq_n_f32(1.428_606_8e-6));
131 let mut q = vdupq_n_f32(8.297653546e-03);
132 q = vfmaq_f32(vdupq_n_f32(4.191538191e-02), q, rr);
133 q = vfmaq_f32(vdupq_n_f32(1.666757475e-01), q, rr);
134 q = vfmaq_f32(vdupq_n_f32(4.999889485e-01), q, rr);
135 q = vfmaq_f32(vdupq_n_f32(9.999996920e-01), q, rr);
136 q = vfmaq_f32(vdupq_n_f32(1.000000072e+00), q, rr);
137 let k = vcvtq_s32_f32(kf);
138 let biased = vmaxq_s32(
142 vminq_s32(vaddq_s32(k, vdupq_n_s32(127)), vdupq_n_s32(254)),
143 vdupq_n_s32(1),
144 );
145 let scale = vreinterpretq_f32_s32(vshlq_n_s32(biased, 23));
146 let out = vorrq_u32(
147 vcltq_f32(x, vdupq_n_f32(-103.0)),
148 vcgtq_f32(x, vdupq_n_f32(0.0)),
149 );
150 vbslq_f32(out, vdupq_n_f32(0.0), vmulq_f32(q, scale))
151 }
152 }
153 let vm = vdupq_n_f32(max);
154 let mut vsum = vdupq_n_f32(0.0);
155 let mut i = 0;
156 while i + 4 <= x.len() {
157 let y = exp_ps(vsubq_f32(vld1q_f32(x.as_ptr().add(i)), vm));
158 vst1q_f32(x.as_mut_ptr().add(i), y);
159 vsum = vaddq_f32(vsum, y);
160 i += 4;
161 }
162 let mut sum = vaddvq_f32(vsum);
163 for v in &mut x[i..] {
164 let y = accurate_exp_f32(*v - max);
165 *v = y;
166 sum += y;
167 }
168 sum
169 }
170 }
171
172 #[cfg(all(target_family = "wasm", target_feature = "simd128"))]
177 #[inline]
178 fn exp_sum_impl(x: &mut [f32], max: f32) -> f32 {
179 use std::arch::wasm32::*;
180 #[inline(always)]
181 fn exp_ps(x: v128) -> v128 {
182 let kf = f32x4_nearest(f32x4_mul(x, f32x4_splat(1.442_695_04)));
183 let mut rr = f32x4_sub(x, f32x4_mul(kf, f32x4_splat(0.693_145_75)));
184 rr = f32x4_sub(rr, f32x4_mul(kf, f32x4_splat(1.428_606_8e-6)));
185 let mut q = f32x4_splat(8.297653546e-03);
186 q = f32x4_add(f32x4_splat(4.191538191e-02), f32x4_mul(q, rr));
187 q = f32x4_add(f32x4_splat(1.666757475e-01), f32x4_mul(q, rr));
188 q = f32x4_add(f32x4_splat(4.999889485e-01), f32x4_mul(q, rr));
189 q = f32x4_add(f32x4_splat(9.999996920e-01), f32x4_mul(q, rr));
190 q = f32x4_add(f32x4_splat(1.000000072e+00), f32x4_mul(q, rr));
191 let k = i32x4_trunc_sat_f32x4(kf);
192 let biased = i32x4_max(
195 i32x4_min(i32x4_add(k, i32x4_splat(127)), i32x4_splat(254)),
196 i32x4_splat(1),
197 );
198 let scale = i32x4_shl(biased, 23);
199 let out = v128_or(f32x4_lt(x, f32x4_splat(-103.0)), f32x4_gt(x, f32x4_splat(0.0)));
200 v128_bitselect(f32x4_splat(0.0), f32x4_mul(q, scale), out)
201 }
202 let vm = f32x4_splat(max);
203 let mut vsum = f32x4_splat(0.0);
204 let mut i = 0;
205 while i + 4 <= x.len() {
206 let y = exp_ps(f32x4_sub(unsafe { v128_load(x.as_ptr().add(i) as *const v128) }, vm));
207 unsafe { v128_store(x.as_mut_ptr().add(i) as *mut v128, y) };
208 vsum = f32x4_add(vsum, y);
209 i += 4;
210 }
211 let mut sum = f32x4_extract_lane::<0>(vsum)
212 + f32x4_extract_lane::<1>(vsum)
213 + f32x4_extract_lane::<2>(vsum)
214 + f32x4_extract_lane::<3>(vsum);
215 for v in &mut x[i..] {
216 let y = accurate_exp_f32(*v - max);
217 *v = y;
218 sum += y;
219 }
220 sum
221 }
222
223 #[cfg(not(any(
224 target_arch = "aarch64",
225 all(target_family = "wasm", target_feature = "simd128")
226 )))]
227 #[inline]
228 fn exp_sum_impl(x: &mut [f32], max: f32) -> f32 {
229 let mut acc = [0f32; 4];
230 let mut it = x.chunks_exact_mut(4);
231 for c in &mut it {
232 for (j, v) in c.iter_mut().enumerate() {
233 let y = accurate_exp_f32(*v - max);
234 *v = y;
235 acc[j] += y;
236 }
237 }
238 let mut sum = (acc[0] + acc[1]) + (acc[2] + acc[3]);
239 for v in it.into_remainder().iter_mut() {
240 let y = accurate_exp_f32(*v - max);
241 *v = y;
242 sum += y;
243 }
244 sum
245 }
246
247 routine_map_reduce_rust!(generic;
248 f32,
249 SSoftMaxL2Accurate,
250 4,
251 4,
252 fn run(x: &mut [f32], max: f32) -> f32 {
253 debug_assert!(x.len() % Self::nr() == 0);
254 debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0);
255 exp_sum_impl(x, max)
256 },
257 op(Softmax2)
258 );
259}
260
261#[cfg(test)]
262mod f16_accumulators {
263 use super::*;
264 use crate::frame::reduce::ReduceKer;
265 use tract_data::internal::f16;
266
267 #[test]
271 fn plain_sum_keeps_long_rows() {
272 for len in [1024usize, 4096, 8192] {
273 let row: Vec<f16> = vec![f16::from_f32(1.0); len];
274 let got = sum::HSum8::red().run(&row).unwrap().to_f32();
275 let err = (got - len as f32).abs() / len as f32;
276 assert!(err < 0.01, "len {len}: summed to {got}, rel {err}");
277 }
278 }
279}