1use crate::prelude_dev::*;
2use core::ops::{Add, Mul};
3use num::complex::ComplexFloat;
4use num::{FromPrimitive, One, Zero};
5use rstsr_dtype_traits::ExtReal;
6
7impl<T, D> OpSumAPI<T, D> for DeviceCpuSerial
8where
9 T: Zero + Add<Output = T> + Clone,
10 D: DimAPI,
11{
12 type TOut = T;
13
14 fn sum_all(&self, a: &Vec<T>, la: &Layout<D>) -> Result<T> {
15 let f_init = T::zero;
16 let f = |acc, x| acc + x;
17 let f_sum = |acc1, acc2| acc1 + acc2;
18 let f_out = |acc| acc;
19
20 reduce_all_cpu_serial(a, la, f_init, f, f_sum, f_out)
21 }
22
23 fn sum_axes(
24 &self,
25 a: &Vec<T>,
26 la: &Layout<D>,
27 axes: &[isize],
28 ) -> Result<(Storage<DataOwned<Vec<T>>, T, Self>, Layout<IxD>)> {
29 let f_init = T::zero;
30 let f = |acc, x| acc + x;
31 let f_sum = |acc1, acc2| acc1 + acc2;
32 let f_out = |acc| acc;
33
34 let (out, layout_out) = reduce_axes_cpu_serial(a, &la.to_dim()?, axes, f_init, f, f_sum, f_out)?;
35 Ok((Storage::new(out.into(), self.clone()), layout_out))
36 }
37}
38
39impl<T, D> OpMinAPI<T, D> for DeviceCpuSerial
40where
41 T: ExtReal,
42 D: DimAPI,
43{
44 type TOut = T;
45
46 fn min_all(&self, a: &Vec<T>, la: &Layout<D>) -> Result<T> {
47 if la.size() == 0 {
48 rstsr_raise!(InvalidValue, "zero-size array is not supported for min")?;
49 }
50
51 let f_init = T::ext_max_value;
52 let f = |acc: T, x: T| acc.ext_min(x);
53 let f_sum = |acc1: T, acc2: T| acc1.ext_min(acc2);
54 let f_out = |acc| acc;
55 reduce_all_cpu_serial(a, la, f_init, f, f_sum, f_out)
56 }
57
58 fn min_axes(
59 &self,
60 a: &Vec<T>,
61 la: &Layout<D>,
62 axes: &[isize],
63 ) -> Result<(Storage<DataOwned<Vec<T>>, T, Self>, Layout<IxD>)> {
64 if la.size() == 0 {
65 rstsr_raise!(InvalidValue, "zero-size array is not supported for min")?;
66 }
67
68 let f_init = T::ext_max_value;
69 let f = |acc: T, x: T| acc.ext_min(x);
70 let f_sum = |acc1: T, acc2: T| acc1.ext_min(acc2);
71 let f_out = |acc| acc;
72
73 let (out, layout_out) = reduce_axes_cpu_serial(a, &la.to_dim()?, axes, f_init, f, f_sum, f_out)?;
74 Ok((Storage::new(out.into(), self.clone()), layout_out))
75 }
76}
77
78impl<T, D> OpMaxAPI<T, D> for DeviceCpuSerial
79where
80 T: ExtReal,
81 D: DimAPI,
82{
83 type TOut = T;
84
85 fn max_all(&self, a: &Vec<T>, la: &Layout<D>) -> Result<T> {
86 if la.size() == 0 {
87 rstsr_raise!(InvalidValue, "zero-size array is not supported for max")?;
88 }
89
90 let f_init = T::ext_min_value;
91 let f = |acc: T, x: T| acc.ext_max(x);
92 let f_sum = |acc1: T, acc2: T| acc1.ext_max(acc2);
93 let f_out = |acc| acc;
94
95 reduce_all_cpu_serial(a, la, f_init, f, f_sum, f_out)
96 }
97
98 fn max_axes(
99 &self,
100 a: &Vec<T>,
101 la: &Layout<D>,
102 axes: &[isize],
103 ) -> Result<(Storage<DataOwned<Vec<T>>, T, Self>, Layout<IxD>)> {
104 if la.size() == 0 {
105 rstsr_raise!(InvalidValue, "zero-size array is not supported for max")?;
106 }
107
108 let f_init = T::ext_min_value;
109 let f = |acc: T, x: T| acc.ext_max(x);
110 let f_sum = |acc1: T, acc2: T| acc1.ext_max(acc2);
111 let f_out = |acc| acc;
112
113 let (out, layout_out) = reduce_axes_cpu_serial(a, &la.to_dim()?, axes, f_init, f, f_sum, f_out)?;
114 Ok((Storage::new(out.into(), self.clone()), layout_out))
115 }
116}
117
118impl<T, D> OpProdAPI<T, D> for DeviceCpuSerial
119where
120 T: Clone + One + Mul<Output = T>,
121 D: DimAPI,
122{
123 type TOut = T;
124
125 fn prod_all(&self, a: &Vec<T>, la: &Layout<D>) -> Result<T> {
126 let f_init = T::one;
127 let f = |acc, x| acc * x;
128 let f_sum = |acc1, acc2| acc1 * acc2;
129 let f_out = |acc| acc;
130
131 reduce_all_cpu_serial(a, la, f_init, f, f_sum, f_out)
132 }
133
134 fn prod_axes(
135 &self,
136 a: &Vec<T>,
137 la: &Layout<D>,
138 axes: &[isize],
139 ) -> Result<(Storage<DataOwned<Vec<T>>, T, Self>, Layout<IxD>)> {
140 let f_init = T::one;
141 let f = |acc, x| acc * x;
142 let f_sum = |acc1, acc2| acc1 * acc2;
143 let f_out = |acc| acc;
144
145 let (out, layout_out) = reduce_axes_cpu_serial(a, &la.to_dim()?, axes, f_init, f, f_sum, f_out)?;
146 Ok((Storage::new(out.into(), self.clone()), layout_out))
147 }
148}
149
150impl<T, D> OpMeanAPI<T, D> for DeviceCpuSerial
151where
152 T: Clone + ComplexFloat + FromPrimitive,
153 D: DimAPI,
154{
155 type TOut = T;
156
157 fn mean_all(&self, a: &Vec<T>, la: &Layout<D>) -> Result<T> {
158 let size = la.size();
159 let f_init = T::zero;
160 let f = |acc, x| acc + x;
161 let f_sum = |acc, x| acc + x;
162 let f_out = |acc| acc / T::from_usize(size).unwrap();
163
164 let sum = reduce_all_cpu_serial(a, la, f_init, f, f_sum, f_out)?;
165 Ok(sum)
166 }
167
168 fn mean_axes(
169 &self,
170 a: &Vec<T>,
171 la: &Layout<D>,
172 axes: &[isize],
173 ) -> Result<(Storage<DataOwned<Vec<T>>, T, Self>, Layout<IxD>)> {
174 let (layout_axes, _) = la.dim_split_axes(axes)?;
175 let size = layout_axes.size();
176 let f_init = T::zero;
177 let f = |acc, x| acc + x;
178 let f_sum = |acc, x| acc + x;
179 let f_out = |acc| acc / T::from_usize(size).unwrap();
180
181 let (out, layout_out) = reduce_axes_cpu_serial(a, &la.to_dim()?, axes, f_init, f, f_sum, f_out)?;
182 Ok((Storage::new(out.into(), self.clone()), layout_out))
183 }
184}
185
186impl<T, D> OpVarAPI<T, D> for DeviceCpuSerial
187where
188 T: Clone + ComplexFloat + FromPrimitive,
189 T::Real: Clone + ComplexFloat + FromPrimitive,
190 D: DimAPI,
191{
192 type TOut = T::Real;
193
194 fn var_all(&self, a: &Vec<T>, la: &Layout<D>) -> Result<T::Real> {
195 let size = la.size();
196
197 let f_init = || (T::zero(), T::Real::zero());
198 let f = |(acc_1, acc_2): (T, T::Real), x: T| (acc_1 + x, acc_2 + (x * x.conj()).re());
199 let f_sum = |(acc_1, acc_2): (T, T::Real), (x_1, x_2)| (acc_1 + x_1, acc_2 + x_2);
200 let f_out = |(acc_1, acc_2): (T, T::Real)| {
201 let size_1 = T::from_usize(size).unwrap();
202 let size_2 = T::Real::from_usize(size).unwrap();
203 let mean = acc_1 / size_1;
204 acc_2 / size_2 - (mean * mean.conj()).re()
205 };
206
207 let result = reduce_all_cpu_serial(a, la, f_init, f, f_sum, f_out)?;
208 Ok(result)
209 }
210
211 fn var_axes(
212 &self,
213 a: &Vec<T>,
214 la: &Layout<D>,
215 axes: &[isize],
216 ) -> Result<(Storage<DataOwned<Vec<T::Real>>, T::Real, Self>, Layout<IxD>)> {
217 let (layout_axes, _) = la.dim_split_axes(axes)?;
218 let size = layout_axes.size();
219
220 let f_init = || (T::zero(), T::Real::zero());
221 let f = |(acc_1, acc_2): (T, T::Real), x: T| (acc_1 + x, acc_2 + (x * x.conj()).re());
222 let f_sum = |(acc_1, acc_2): (T, T::Real), (x_1, x_2)| (acc_1 + x_1, acc_2 + x_2);
223 let f_out = |(acc_1, acc_2): (T, T::Real)| {
224 let size_1 = T::from_usize(size).unwrap();
225 let size_2 = T::Real::from_usize(size).unwrap();
226 let mean = acc_1 / size_1;
227 acc_2 / size_2 - (mean * mean.conj()).re()
228 };
229
230 let (out, layout_out) = reduce_axes_difftype_cpu_serial(a, &la.to_dim()?, axes, f_init, f, f_sum, f_out)?;
231
232 Ok((Storage::new(out.into(), self.clone()), layout_out))
233 }
234}
235
236impl<T, D> OpStdAPI<T, D> for DeviceCpuSerial
237where
238 T: Clone + ComplexFloat + FromPrimitive,
239 T::Real: Clone + ComplexFloat + FromPrimitive,
240 D: DimAPI,
241{
242 type TOut = T::Real;
243
244 fn std_all(&self, a: &Vec<T>, la: &Layout<D>) -> Result<T::Real> {
245 let size = la.size();
246
247 let f_init = || (T::zero(), T::Real::zero());
248 let f = |(acc_1, acc_2): (T, T::Real), x: T| (acc_1 + x, acc_2 + (x * x.conj()).re());
249 let f_sum = |(acc_1, acc_2): (T, T::Real), (x_1, x_2)| (acc_1 + x_1, acc_2 + x_2);
250 let f_out = |(acc_1, acc_2): (T, T::Real)| {
251 let size_1 = T::from_usize(size).unwrap();
252 let size_2 = T::Real::from_usize(size).unwrap();
253 let mean = acc_1 / size_1;
254 let var = acc_2 / size_2 - (mean * mean.conj()).re();
255 var.sqrt()
256 };
257
258 let result = reduce_all_cpu_serial(a, la, f_init, f, f_sum, f_out)?;
259 Ok(result)
260 }
261
262 fn std_axes(
263 &self,
264 a: &Vec<T>,
265 la: &Layout<D>,
266 axes: &[isize],
267 ) -> Result<(Storage<DataOwned<Vec<T::Real>>, T::Real, Self>, Layout<IxD>)> {
268 let (layout_axes, _) = la.dim_split_axes(axes)?;
269 let size = layout_axes.size();
270
271 let f_init = || (T::zero(), T::Real::zero());
272 let f = |(acc_1, acc_2): (T, T::Real), x: T| (acc_1 + x, acc_2 + (x * x.conj()).re());
273 let f_sum = |(acc_1, acc_2): (T, T::Real), (x_1, x_2)| (acc_1 + x_1, acc_2 + x_2);
274 let f_out = |(acc_1, acc_2): (T, T::Real)| {
275 let size_1 = T::from_usize(size).unwrap();
276 let size_2 = T::Real::from_usize(size).unwrap();
277 let mean = acc_1 / size_1;
278 let var = acc_2 / size_2 - (mean * mean.conj()).re();
279 var.sqrt()
280 };
281
282 let (out, layout_out) = reduce_axes_difftype_cpu_serial(a, &la.to_dim()?, axes, f_init, f, f_sum, f_out)?;
283
284 Ok((Storage::new(out.into(), self.clone()), layout_out))
285 }
286}
287
288impl<T, D> OpL2NormAPI<T, D> for DeviceCpuSerial
289where
290 T: Clone + ComplexFloat + FromPrimitive,
291 T::Real: Clone + ComplexFloat + FromPrimitive,
292 D: DimAPI,
293{
294 type TOut = T::Real;
295
296 fn l2_norm_all(&self, a: &Vec<T>, la: &Layout<D>) -> Result<T::Real> {
297 let f_init = || T::Real::zero();
298 let f = |acc: T::Real, x: T| acc + (x * x.conj()).re();
299 let f_sum = |acc: T::Real, x: T::Real| acc + x;
300 let f_out = |acc: T::Real| acc.sqrt();
301
302 let result = reduce_all_cpu_serial(a, la, f_init, f, f_sum, f_out)?;
303 Ok(result)
304 }
305
306 fn l2_norm_axes(
307 &self,
308 a: &Vec<T>,
309 la: &Layout<D>,
310 axes: &[isize],
311 ) -> Result<(Storage<DataOwned<Vec<T::Real>>, T::Real, Self>, Layout<IxD>)> {
312 let f_init = || T::Real::zero();
313 let f = |acc: T::Real, x: T| acc + (x * x.conj()).re();
314 let f_sum = |acc: T::Real, x: T::Real| acc + x;
315 let f_out = |acc: T::Real| acc.sqrt();
316
317 let (out, layout_out) = reduce_axes_cpu_serial(a, &la.to_dim()?, axes, f_init, f, f_sum, f_out)?;
318
319 Ok((Storage::new(out.into(), self.clone()), layout_out))
320 }
321}
322
323impl<T, D> OpArgMinAPI<T, D> for DeviceCpuSerial
324where
325 T: Clone + PartialOrd,
326 D: DimAPI,
327{
328 type TOut = usize;
329
330 fn argmin_axes(
331 &self,
332 a: &Vec<T>,
333 la: &Layout<D>,
334 axes: &[isize],
335 ) -> Result<(Storage<DataOwned<Vec<usize>>, Self::TOut, Self>, Layout<IxD>)> {
336 let f_comp = |x: Option<T>, y: T| -> Option<bool> {
337 if let Some(x) = x {
338 Some(y < x)
339 } else {
340 Some(true)
341 }
342 };
343 let f_eq = |x: Option<T>, y: T| -> Option<bool> {
344 if let Some(x) = x {
345 Some(y == x)
346 } else {
347 Some(false)
348 }
349 };
350 let (out, layout_out) = reduce_axes_arg_cpu_serial(a, la, axes, f_comp, f_eq, RowMajor)?;
351 Ok((Storage::new(out.into(), self.clone()), layout_out))
352 }
353
354 fn argmin_all(&self, a: &Vec<T>, la: &Layout<D>) -> Result<Self::TOut> {
355 let f_comp = |x: Option<T>, y: T| -> Option<bool> {
356 if let Some(x) = x {
357 Some(y < x)
358 } else {
359 Some(true)
360 }
361 };
362 let f_eq = |x: Option<T>, y: T| -> Option<bool> {
363 if let Some(x) = x {
364 Some(y == x)
365 } else {
366 Some(false)
367 }
368 };
369 let result = reduce_all_arg_cpu_serial(a, la, f_comp, f_eq, RowMajor)?;
370 Ok(result)
371 }
372}
373
374impl<T, D> OpArgMaxAPI<T, D> for DeviceCpuSerial
375where
376 T: Clone + PartialOrd,
377 D: DimAPI,
378{
379 type TOut = usize;
380
381 fn argmax_axes(
382 &self,
383 a: &Vec<T>,
384 la: &Layout<D>,
385 axes: &[isize],
386 ) -> Result<(Storage<DataOwned<Vec<usize>>, Self::TOut, Self>, Layout<IxD>)> {
387 let f_comp = |x: Option<T>, y: T| -> Option<bool> {
388 if let Some(x) = x {
389 Some(y > x)
390 } else {
391 Some(true)
392 }
393 };
394 let f_eq = |x: Option<T>, y: T| -> Option<bool> {
395 if let Some(x) = x {
396 Some(y == x)
397 } else {
398 Some(false)
399 }
400 };
401 let (out, layout_out) = reduce_axes_arg_cpu_serial(a, la, axes, f_comp, f_eq, RowMajor)?;
402 Ok((Storage::new(out.into(), self.clone()), layout_out))
403 }
404
405 fn argmax_all(&self, a: &Vec<T>, la: &Layout<D>) -> Result<Self::TOut> {
406 let f_comp = |x: Option<T>, y: T| -> Option<bool> {
407 if let Some(x) = x {
408 Some(y > x)
409 } else {
410 Some(true)
411 }
412 };
413 let f_eq = |x: Option<T>, y: T| -> Option<bool> {
414 if let Some(x) = x {
415 Some(y == x)
416 } else {
417 Some(false)
418 }
419 };
420 let result = reduce_all_arg_cpu_serial(a, la, f_comp, f_eq, RowMajor)?;
421 Ok(result)
422 }
423}
424
425impl<D> OpAllAPI<bool, D> for DeviceCpuSerial
426where
427 D: DimAPI,
428{
429 type TOut = bool;
430
431 fn all_all(&self, a: &Vec<bool>, la: &Layout<D>) -> Result<bool> {
432 let f_init = || true;
433 let f = |acc, x| acc && x;
434 let f_sum = |acc1, acc2| acc1 && acc2;
435 let f_out = |acc| acc;
436
437 reduce_all_cpu_serial(a, la, f_init, f, f_sum, f_out)
438 }
439
440 fn all_axes(
441 &self,
442 a: &Vec<bool>,
443 la: &Layout<D>,
444 axes: &[isize],
445 ) -> Result<(Storage<DataOwned<Vec<bool>>, bool, Self>, Layout<IxD>)> {
446 let f_init = || true;
447 let f = |acc, x| acc && x;
448 let f_sum = |acc1, acc2| acc1 && acc2;
449 let f_out = |acc| acc;
450
451 let (out, layout_out) = reduce_axes_cpu_serial(a, &la.to_dim()?, axes, f_init, f, f_sum, f_out)?;
452 Ok((Storage::new(out.into(), self.clone()), layout_out))
453 }
454}
455
456impl<D> OpAnyAPI<bool, D> for DeviceCpuSerial
457where
458 D: DimAPI,
459{
460 type TOut = bool;
461
462 fn any_all(&self, a: &Vec<bool>, la: &Layout<D>) -> Result<bool> {
463 let f_init = || false;
464 let f = |acc, x| acc || x;
465 let f_sum = |acc1, acc2| acc1 || acc2;
466 let f_out = |acc| acc;
467
468 reduce_all_cpu_serial(a, la, f_init, f, f_sum, f_out)
469 }
470
471 fn any_axes(
472 &self,
473 a: &Vec<bool>,
474 la: &Layout<D>,
475 axes: &[isize],
476 ) -> Result<(Storage<DataOwned<Vec<bool>>, bool, Self>, Layout<IxD>)> {
477 let f_init = || false;
478 let f = |acc, x| acc || x;
479 let f_sum = |acc1, acc2| acc1 || acc2;
480 let f_out = |acc| acc;
481
482 let (out, layout_out) = reduce_axes_cpu_serial(a, &la.to_dim()?, axes, f_init, f, f_sum, f_out)?;
483 Ok((Storage::new(out.into(), self.clone()), layout_out))
484 }
485}
486
487impl<T, D> OpCountNonZeroAPI<T, D> for DeviceCpuSerial
488where
489 T: Clone + PartialEq + Zero,
490 D: DimAPI,
491{
492 type TOut = usize;
493
494 fn count_nonzero_all(&self, a: &Vec<T>, la: &Layout<D>) -> Result<usize> {
495 let f_init = || 0;
496 let f = |acc, x| if x != T::zero() { acc + 1 } else { acc };
497 let f_sum = |acc1, acc2| acc1 + acc2;
498 let f_out = |acc| acc;
499
500 reduce_all_cpu_serial(a, la, f_init, f, f_sum, f_out)
501 }
502
503 fn count_nonzero_axes(
504 &self,
505 a: &Vec<T>,
506 la: &Layout<D>,
507 axes: &[isize],
508 ) -> Result<(Storage<DataOwned<Vec<usize>>, usize, Self>, Layout<IxD>)> {
509 let f_init = || 0;
510 let f = |acc, x| if x != T::zero() { acc + 1 } else { acc };
511 let f_sum = |acc1, acc2| acc1 + acc2;
512 let f_out = |acc| acc;
513
514 let (out, layout_out) = reduce_axes_cpu_serial(a, &la.to_dim()?, axes, f_init, f, f_sum, f_out)?;
515 Ok((Storage::new(out.into(), self.clone()), layout_out))
516 }
517}
518
519impl<T, D> OpUnraveledArgMinAPI<T, D> for DeviceCpuSerial
520where
521 T: Clone + PartialOrd,
522 D: DimAPI,
523{
524 fn unraveled_argmin_axes(
525 &self,
526 a: &Vec<T>,
527 la: &Layout<D>,
528 axes: &[isize],
529 ) -> Result<(Storage<DataOwned<Vec<IxD>>, IxD, Self>, Layout<IxD>)> {
530 let f_comp = |x: Option<T>, y: T| -> Option<bool> {
531 if let Some(x) = x {
532 Some(y < x)
533 } else {
534 Some(true)
535 }
536 };
537 let f_eq = |x: Option<T>, y: T| -> Option<bool> {
538 if let Some(x) = x {
539 Some(y == x)
540 } else {
541 Some(false)
542 }
543 };
544 let (out, layout_out) = reduce_axes_unraveled_arg_cpu_serial(a, la, axes, f_comp, f_eq)?;
545 Ok((Storage::new(out.into(), self.clone()), layout_out))
546 }
547
548 fn unraveled_argmin_all(&self, a: &<Self as DeviceRawAPI<T>>::Raw, la: &Layout<D>) -> Result<D> {
549 let f_comp = |x: Option<T>, y: T| -> Option<bool> {
550 if let Some(x) = x {
551 Some(y < x)
552 } else {
553 Some(true)
554 }
555 };
556 let f_eq = |x: Option<T>, y: T| -> Option<bool> {
557 if let Some(x) = x {
558 Some(y == x)
559 } else {
560 Some(false)
561 }
562 };
563 let result = reduce_all_unraveled_arg_cpu_serial(a, la, f_comp, f_eq)?;
564 Ok(result)
565 }
566}
567
568impl<T, D> OpUnraveledArgMaxAPI<T, D> for DeviceCpuSerial
569where
570 T: Clone + PartialOrd,
571 D: DimAPI,
572{
573 fn unraveled_argmax_axes(
574 &self,
575 a: &Vec<T>,
576 la: &Layout<D>,
577 axes: &[isize],
578 ) -> Result<(Storage<DataOwned<Vec<IxD>>, IxD, Self>, Layout<IxD>)> {
579 let f_comp = |x: Option<T>, y: T| -> Option<bool> {
580 if let Some(x) = x {
581 Some(y > x)
582 } else {
583 Some(true)
584 }
585 };
586 let f_eq = |x: Option<T>, y: T| -> Option<bool> {
587 if let Some(x) = x {
588 Some(y == x)
589 } else {
590 Some(false)
591 }
592 };
593 let (out, layout_out) = reduce_axes_unraveled_arg_cpu_serial(a, la, axes, f_comp, f_eq)?;
594 Ok((Storage::new(out.into(), self.clone()), layout_out))
595 }
596
597 fn unraveled_argmax_all(&self, a: &<Self as DeviceRawAPI<T>>::Raw, la: &Layout<D>) -> Result<D> {
598 let f_comp = |x: Option<T>, y: T| -> Option<bool> {
599 if let Some(x) = x {
600 Some(y > x)
601 } else {
602 Some(true)
603 }
604 };
605 let f_eq = |x: Option<T>, y: T| -> Option<bool> {
606 if let Some(x) = x {
607 Some(y == x)
608 } else {
609 Some(false)
610 }
611 };
612 let result = reduce_all_unraveled_arg_cpu_serial(a, la, f_comp, f_eq)?;
613 Ok(result)
614 }
615}
616
617impl<D> OpSumBoolAPI<D> for DeviceCpuSerial
618where
619 D: DimAPI,
620{
621 fn sum_all(&self, a: &Vec<bool>, la: &Layout<D>) -> Result<usize> {
622 let f_init = || 0;
623 let f = |acc, x| match x {
624 true => acc + 1,
625 false => acc,
626 };
627 let f_sum = |acc1, acc2| acc1 + acc2;
628 let f_out = |acc| acc;
629
630 reduce_all_cpu_serial(a, la, f_init, f, f_sum, f_out)
631 }
632
633 fn sum_axes(
634 &self,
635 a: &Vec<bool>,
636 la: &Layout<D>,
637 axes: &[isize],
638 ) -> Result<(Storage<DataOwned<Vec<usize>>, usize, Self>, Layout<IxD>)> {
639 let f_init = || 0;
640 let f = |acc, x| match x {
641 true => acc + 1,
642 false => acc,
643 };
644 let f_sum = |acc1, acc2| acc1 + acc2;
645 let f_out = |acc| acc;
646
647 let (out, layout_out) = reduce_axes_cpu_serial(a, &la.to_dim()?, axes, f_init, f, f_sum, f_out)?;
648 Ok((Storage::new(out.into(), self.clone()), layout_out))
649 }
650}
651
652impl<TA, TB, TE, D> OpAllCloseAPI<TA, TB, TE, D> for DeviceCpuSerial
653where
654 TA: Clone + DTypePromoteAPI<TB>,
655 TB: Clone,
656 <TA as DTypePromoteAPI<TB>>::Res: ExtNum<AbsOut: DTypeCastAPI<TE>>,
657 TE: ExtFloat + Add<TE, Output = TE> + Mul<TE, Output = TE> + PartialOrd + Clone,
658 D: DimAPI,
659{
660 fn allclose_all(
661 &self,
662 a: &<Self as DeviceRawAPI<TA>>::Raw,
663 la: &Layout<D>,
664 b: &<Self as DeviceRawAPI<TB>>::Raw,
665 lb: &Layout<D>,
666 isclose_args: &IsCloseArgs<TE>,
667 ) -> Result<bool> {
668 use rstsr_dtype_traits::isclose;
669
670 if la.size() == 0 || lb.size() == 0 {
671 rstsr_raise!(InvalidValue, "zero-size array is not supported for allclose")?;
672 }
673
674 let f_init = || true;
675 let f = |acc: bool, (a_elem, b_elem): (TA, TB)| {
676 let result = isclose(&a_elem, &b_elem, isclose_args);
677 acc && result
678 };
679 let f_sum = |acc1: bool, acc2: bool| acc1 && acc2;
680 let f_out = |acc: bool| acc;
681
682 reduce_all_binary_cpu_serial(a, la, b, lb, f_init, f, f_sum, f_out)
683 }
684
685 fn allclose_axes(
686 &self,
687 _a: &<Self as DeviceRawAPI<TA>>::Raw,
688 _la: &Layout<D>,
689 _b: &<Self as DeviceRawAPI<TB>>::Raw,
690 _lb: &Layout<D>,
691 _axes: &[isize],
692 _isclose_args: &IsCloseArgs<TE>,
693 ) -> Result<(Storage<DataOwned<<Self as DeviceRawAPI<bool>>::Raw>, bool, Self>, Layout<IxD>)> {
694 unimplemented!("This function (`allclose_axes`) is not planned to be implemented yet.");
695 }
696}