1use crate::{DType, HostBuffer, Result, Shape, WithDType};
11use fuel_backend_contract::dyn_backend::DynBackendDevice;
12use fuel_cpu_backend::dyn_impl::CpuBackendDevice;
13use std::sync::Arc;
14
15pub use fuel_ir::DeviceLocation;
16
17#[derive(Clone, Debug)]
34pub struct Device {
35 pub(crate) inner: Arc<dyn DynBackendDevice>,
36}
37
38pub trait NdArray {
56 fn shape(&self) -> Result<Shape>;
58
59 fn to_cpu_storage(&self) -> HostBuffer;
61}
62
63impl<S: WithDType> NdArray for S {
64 fn shape(&self) -> Result<Shape> {
65 Ok(Shape::from(()))
66 }
67
68 fn to_cpu_storage(&self) -> HostBuffer {
69 S::to_cpu_storage(&[*self])
70 }
71}
72
73impl<S: WithDType, const N: usize> NdArray for &[S; N] {
74 fn shape(&self) -> Result<Shape> {
75 Ok(Shape::from(self.len()))
76 }
77
78 fn to_cpu_storage(&self) -> HostBuffer {
79 S::to_cpu_storage(self.as_slice())
80 }
81}
82
83impl<S: WithDType> NdArray for &[S] {
84 fn shape(&self) -> Result<Shape> {
85 Ok(Shape::from(self.len()))
86 }
87
88 fn to_cpu_storage(&self) -> HostBuffer {
89 S::to_cpu_storage(self)
90 }
91}
92
93impl<S: WithDType, const N: usize, const M: usize> NdArray for &[[S; N]; M] {
94 fn shape(&self) -> Result<Shape> {
95 Ok(Shape::from((M, N)))
96 }
97
98 fn to_cpu_storage(&self) -> HostBuffer {
99 S::to_cpu_storage_owned(self.concat())
100 }
101}
102
103impl<S: WithDType, const N1: usize, const N2: usize, const N3: usize> NdArray
104 for &[[[S; N3]; N2]; N1]
105{
106 fn shape(&self) -> Result<Shape> {
107 Ok(Shape::from((N1, N2, N3)))
108 }
109
110 fn to_cpu_storage(&self) -> HostBuffer {
111 let mut vec = Vec::with_capacity(N1 * N2 * N3);
112 for i1 in 0..N1 {
113 for i2 in 0..N2 {
114 vec.extend(self[i1][i2])
115 }
116 }
117 S::to_cpu_storage_owned(vec)
118 }
119}
120
121impl<S: WithDType, const N1: usize, const N2: usize, const N3: usize, const N4: usize> NdArray
122 for &[[[[S; N4]; N3]; N2]; N1]
123{
124 fn shape(&self) -> Result<Shape> {
125 Ok(Shape::from((N1, N2, N3, N4)))
126 }
127
128 fn to_cpu_storage(&self) -> HostBuffer {
129 let mut vec = Vec::with_capacity(N1 * N2 * N3 * N4);
130 for i1 in 0..N1 {
131 for i2 in 0..N2 {
132 for i3 in 0..N3 {
133 vec.extend(self[i1][i2][i3])
134 }
135 }
136 }
137 S::to_cpu_storage_owned(vec)
138 }
139}
140
141impl<S: WithDType> NdArray for Vec<S> {
142 fn shape(&self) -> Result<Shape> {
143 Ok(Shape::from(self.len()))
144 }
145
146 fn to_cpu_storage(&self) -> HostBuffer {
147 S::to_cpu_storage(self.as_slice())
148 }
149}
150
151impl<S: WithDType> NdArray for Vec<&[S]> {
152 fn shape(&self) -> Result<Shape> {
153 if self.is_empty() {
154 crate::bail!("empty array")
155 }
156 let n = self.len();
157 let m = self[0].len();
158 for v in self.iter() {
159 if v.len() != m {
160 crate::bail!("two elements have different len {m} {}", v.len())
161 }
162 }
163 Ok(Shape::from((n, m)))
164 }
165
166 fn to_cpu_storage(&self) -> HostBuffer {
167 let data = self.iter().copied().flatten().copied().collect::<Vec<_>>();
168 S::to_cpu_storage_owned(data)
169 }
170}
171
172impl<S: WithDType> NdArray for Vec<Vec<S>> {
173 fn shape(&self) -> Result<Shape> {
174 if self.is_empty() {
175 crate::bail!("empty array")
176 }
177 let n = self.len();
178 let m = self[0].len();
179 for v in self.iter() {
180 if v.len() != m {
181 crate::bail!("two elements have different len {m} {}", v.len())
182 }
183 }
184 Ok(Shape::from((n, m)))
185 }
186
187 fn to_cpu_storage(&self) -> HostBuffer {
188 let len: usize = self.iter().map(|v| v.len()).sum();
189 let mut dst = Vec::with_capacity(len);
190 for v in self.iter() {
191 dst.extend(v.iter().copied());
192 }
193 S::to_cpu_storage_owned(dst)
194 }
195}
196
197impl<S: WithDType> NdArray for Vec<Vec<Vec<S>>> {
198 fn shape(&self) -> Result<Shape> {
199 if self.is_empty() {
200 crate::bail!("empty array")
201 }
202 let shape0 = self[0].shape()?;
203 let n = self.len();
204 for v in self.iter() {
205 let shape = v.shape()?;
206 if shape != shape0 {
207 crate::bail!("two elements have different shapes {shape:?} {shape0:?}")
208 }
209 }
210 Ok(Shape::from([[n].as_slice(), shape0.dims()].concat()))
211 }
212
213 fn to_cpu_storage(&self) -> HostBuffer {
214 if self.is_empty() {
215 return S::to_cpu_storage_owned(vec![]);
216 }
217 let len: usize = self
218 .iter()
219 .map(|v| v.iter().map(|v| v.len()).sum::<usize>())
220 .sum();
221 let mut dst = Vec::with_capacity(len);
222 for v1 in self.iter() {
223 for v2 in v1.iter() {
224 dst.extend(v2.iter().copied());
225 }
226 }
227 S::to_cpu_storage_owned(dst)
228 }
229}
230
231impl<S: WithDType> NdArray for Vec<Vec<Vec<Vec<S>>>> {
232 fn shape(&self) -> Result<Shape> {
233 if self.is_empty() {
234 crate::bail!("empty array")
235 }
236 let shape0 = self[0].shape()?;
237 let n = self.len();
238 for v in self.iter() {
239 let shape = v.shape()?;
240 if shape != shape0 {
241 crate::bail!("two elements have different shapes {shape:?} {shape0:?}")
242 }
243 }
244 Ok(Shape::from([[n].as_slice(), shape0.dims()].concat()))
245 }
246
247 fn to_cpu_storage(&self) -> HostBuffer {
248 let len: usize = self
249 .iter()
250 .map(|v| {
251 v.iter()
252 .map(|v| v.iter().map(|v| v.len()).sum::<usize>())
253 .sum::<usize>()
254 })
255 .sum();
256 let mut dst = Vec::with_capacity(len);
257 for v1 in self.iter() {
258 for v2 in v1.iter() {
259 for v3 in v2.iter() {
260 dst.extend(v3.iter().copied());
261 }
262 }
263 }
264 S::to_cpu_storage_owned(dst)
265 }
266}
267
268impl Device {
269 pub fn cpu() -> Self {
279 Device {
280 inner: Arc::new(CpuBackendDevice),
281 }
282 }
283
284 pub fn custom(device: Arc<dyn DynBackendDevice>) -> Self {
289 Device { inner: device }
290 }
291
292 pub fn as_dyn(&self) -> &Arc<dyn DynBackendDevice> {
301 &self.inner
302 }
303
304 pub fn is_custom(&self) -> bool {
306 !self.is_cpu() && !self.is_cuda() && !self.is_metal()
307 }
308
309 pub fn set_seed(&self, seed: u64) -> Result<()> {
311 self.inner.set_seed_dyn(seed)
312 }
313
314 pub fn get_current_seed(&self) -> Result<u64> {
316 self.inner.get_current_seed_dyn()
317 }
318
319 pub fn same_device(&self, rhs: &Self) -> bool {
328 self.inner.same_device_dyn(&*rhs.inner)
329 }
330
331 pub fn location(&self) -> DeviceLocation {
340 self.inner.location_dyn()
341 }
342
343 pub fn is_cpu(&self) -> bool {
352 matches!(self.location(), DeviceLocation::Cpu)
353 }
354
355 pub fn is_cuda(&self) -> bool {
364 matches!(self.location(), DeviceLocation::Cuda { .. })
365 }
366
367 pub fn is_metal(&self) -> bool {
376 matches!(self.location(), DeviceLocation::Metal { .. })
377 }
378
379 pub fn supports_bf16(&self) -> bool {
389 self.inner.supports_bf16()
390 }
391
392 pub fn bf16_default_to_f32(&self) -> DType {
401 if self.supports_bf16() {
402 DType::BF16
403 } else {
404 DType::F32
405 }
406 }
407
408 pub fn synchronize(&self) -> Result<()> {
418 self.inner.synchronize_dyn()
419 }
420}