mesh_sieve/accelerator/
reduction.rs1use super::{AcceleratorBackend, AcceleratorError, CpuBackend, DeviceBuffer, FvmScalar};
4
5pub struct DeviceReduction<T: FvmScalar, B: AcceleratorBackend> {
7 pub(crate) input_len: usize,
9 pub(crate) result: B::Buffer<T>,
11 pub(crate) workspace: B::Buffer<T>,
12 pub(crate) backend_id: u64,
13}
14
15impl<T: FvmScalar, B: AcceleratorBackend> DeviceReduction<T, B> {
16 pub fn new(backend: &B, input_len: usize) -> Result<Self, AcceleratorError> {
18 let result = backend
19 .allocate(1)
20 .map_err(|e| AcceleratorError::AllocationFailed {
21 bytes: std::mem::size_of::<T>(),
22 reason: e.to_string(),
23 })?;
24 let workspace_len = input_len.div_ceil(256).clamp(1, 4096);
25 let workspace =
26 backend
27 .allocate(workspace_len)
28 .map_err(|e| AcceleratorError::AllocationFailed {
29 bytes: workspace_len.saturating_mul(std::mem::size_of::<T>()),
30 reason: e.to_string(),
31 })?;
32 Ok(Self {
33 input_len,
34 result,
35 workspace,
36 backend_id: backend.identity(),
37 })
38 }
39
40 pub fn download(&self, backend: &B) -> Result<T, AcceleratorError> {
42 let mut value = [T::zeroed()];
43 backend
44 .download(&self.result, &mut value)
45 .map_err(|e| AcceleratorError::DeviceTransferFailed(e.to_string()))?;
46 Ok(value[0])
47 }
48
49 pub(crate) fn validate(&self, found: usize) -> Result<(), AcceleratorError> {
50 if self.input_len == found {
51 Ok(())
52 } else {
53 Err(AcceleratorError::LengthMismatch {
54 expected: self.input_len,
55 found,
56 })
57 }
58 }
59}
60
61impl<T: FvmScalar> DeviceReduction<T, CpuBackend> {
62 pub fn sum(
64 &mut self,
65 input: &<CpuBackend as AcceleratorBackend>::Buffer<T>,
66 ) -> Result<T, AcceleratorError> {
67 self.validate(input.len())?;
68 let value = input
69 .as_slice()
70 .iter()
71 .fold(0.0, |sum, value| sum + value.to_f64());
72 self.result.as_mut_slice()[0] = T::from_f64(value);
73 Ok(T::from_f64(value))
74 }
75
76 pub fn dot(
78 &mut self,
79 lhs: &<CpuBackend as AcceleratorBackend>::Buffer<T>,
80 rhs: &<CpuBackend as AcceleratorBackend>::Buffer<T>,
81 ) -> Result<T, AcceleratorError> {
82 self.validate(lhs.len())?;
83 self.validate(rhs.len())?;
84 let value = lhs
85 .as_slice()
86 .iter()
87 .zip(rhs.as_slice())
88 .fold(0.0, |sum, (&a, &b)| sum + a.to_f64() * b.to_f64());
89 self.result.as_mut_slice()[0] = T::from_f64(value);
90 Ok(T::from_f64(value))
91 }
92
93 pub fn l2_norm(
95 &mut self,
96 input: &<CpuBackend as AcceleratorBackend>::Buffer<T>,
97 ) -> Result<T, AcceleratorError> {
98 self.validate(input.len())?;
99 let value = input
100 .as_slice()
101 .iter()
102 .fold(0.0, |sum, value| sum + value.to_f64() * value.to_f64())
103 .sqrt();
104 self.result.as_mut_slice()[0] = T::from_f64(value);
105 Ok(T::from_f64(value))
106 }
107
108 pub fn max_abs(
110 &mut self,
111 input: &<CpuBackend as AcceleratorBackend>::Buffer<T>,
112 ) -> Result<T, AcceleratorError> {
113 self.validate(input.len())?;
114 let value = input
115 .as_slice()
116 .iter()
117 .fold(0.0_f64, |max, value| max.max(value.to_f64().abs()));
118 self.result.as_mut_slice()[0] = T::from_f64(value);
119 Ok(T::from_f64(value))
120 }
121}