1use crate::rand::NdArrayRng;
2use crate::{NdArrayQTensor, NdArrayTensor};
3use alloc::string::String;
4use burn_backend::quantization::{QuantLevel, QuantMode, QuantScheme, QuantStore, QuantValue};
5use burn_backend::tensor::{BoolTensor, FloatTensor, IntTensor, QuantizedTensor};
6use burn_backend::{Backend, BackendTypes, DType, DeviceId, DeviceOps};
7use burn_ir::{BackendIr, HandleKind, TensorHandle};
8use burn_std::stub::Mutex;
9use burn_std::{BoolStore, DeviceSettings, QuantConfig};
10use rand::SeedableRng;
11
12pub(crate) static SEED: Mutex<Option<NdArrayRng>> = Mutex::new(None);
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
16pub enum NdArrayDevice {
17 #[default]
19 Cpu,
20}
21
22impl DeviceOps for NdArrayDevice {
23 fn defaults(&self) -> DeviceSettings {
24 DeviceSettings::new(
26 DType::F32,
27 DType::I64,
28 DType::Bool(BoolStore::Native),
29 QuantConfig::new(
30 QuantScheme::default().with_store(QuantStore::Native),
31 Default::default(),
32 ),
33 )
34 }
35}
36
37impl burn_backend::Device for NdArrayDevice {
38 fn from_id(_device_id: DeviceId) -> Self {
39 Self::Cpu
40 }
41
42 fn to_id(&self) -> DeviceId {
43 DeviceId {
44 type_id: 0,
45 index_id: 0,
46 }
47 }
48}
49
50#[derive(Clone, Copy, Default, Debug)]
55pub struct NdArray;
56
57impl BackendTypes for NdArray {
58 type Device = NdArrayDevice;
59
60 type FloatTensorPrimitive = NdArrayTensor;
61 type IntTensorPrimitive = NdArrayTensor;
62 type BoolTensorPrimitive = NdArrayTensor;
63 type QuantizedTensorPrimitive = NdArrayQTensor;
64
65 type GraphPrimitive = burn_backend::GraphUnsupported;
66}
67
68impl Backend for NdArray {
69 fn ad_enabled(_device: &Self::Device) -> bool {
70 false
71 }
72
73 fn name(_device: &Self::Device) -> String {
74 String::from("ndarray")
75 }
76
77 fn seed(_device: &Self::Device, seed: u64) {
78 let rng = NdArrayRng::seed_from_u64(seed);
79 let mut seed = SEED.lock().unwrap();
80 *seed = Some(rng);
81 }
82
83 fn dtype_usage(_device: &Self::Device, dtype: DType) -> burn_backend::DTypeUsageSet {
84 match dtype {
85 DType::F64
86 | DType::F32
87 | DType::Flex32
88 | DType::I64
89 | DType::I32
90 | DType::I16
91 | DType::I8
92 | DType::U64
93 | DType::U32
94 | DType::U16
95 | DType::U8
96 | DType::Bool(BoolStore::Native) => burn_backend::DTypeUsage::general(),
97 DType::F16 | DType::BF16 | DType::Bool(_) => burn_backend::DTypeUsageSet::empty(),
98 DType::QFloat(scheme) => {
99 match scheme {
100 QuantScheme {
101 level: QuantLevel::Tensor | QuantLevel::Block(_),
102 mode: QuantMode::Symmetric,
103 #[cfg(not(feature = "export_tests"))]
104 value: QuantValue::Q8F | QuantValue::Q8S,
105 #[cfg(feature = "export_tests")]
108 value:
109 QuantValue::Q8F
110 | QuantValue::Q8S
111 | QuantValue::Q4F
112 | QuantValue::Q4S
113 | QuantValue::Q2F
114 | QuantValue::Q2S,
115 store: QuantStore::Native,
116 ..
117 } => burn_backend::DTypeUsage::general(),
118 _scheme => burn_backend::DTypeUsageSet::empty(),
119 }
120 }
121 }
122 }
123
124 fn device_count(_: u16) -> usize {
125 1
126 }
127
128 fn flush(_device: &Self::Device) {}
129}
130
131impl BackendIr for NdArray {
132 type Handle = HandleKind<Self>;
133
134 fn float_tensor(handle: TensorHandle<Self::Handle>) -> FloatTensor<Self> {
135 match handle.handle {
136 HandleKind::Float(handle) => handle,
137 _ => panic!("Expected float handle, got {}", handle.handle.name()),
138 }
139 }
140
141 fn int_tensor(handle: TensorHandle<Self::Handle>) -> IntTensor<Self> {
142 match handle.handle {
143 HandleKind::Int(handle) => handle,
144 _ => panic!("Expected int handle, got {}", handle.handle.name()),
145 }
146 }
147
148 fn bool_tensor(handle: TensorHandle<Self::Handle>) -> BoolTensor<Self> {
149 match handle.handle {
150 HandleKind::Bool(handle) => handle,
151 _ => panic!("Expected bool handle, got {}", handle.handle.name()),
152 }
153 }
154
155 fn quantized_tensor(handle: TensorHandle<Self::Handle>) -> QuantizedTensor<Self> {
156 match handle.handle {
157 HandleKind::Quantized(handle) => handle,
158 _ => panic!("Expected quantized handle, got {}", handle.handle.name()),
159 }
160 }
161
162 fn float_tensor_handle(tensor: FloatTensor<Self>) -> Self::Handle {
163 HandleKind::Float(tensor)
164 }
165
166 fn int_tensor_handle(tensor: IntTensor<Self>) -> Self::Handle {
167 HandleKind::Int(tensor)
168 }
169
170 fn bool_tensor_handle(tensor: BoolTensor<Self>) -> Self::Handle {
171 HandleKind::Bool(tensor)
172 }
173
174 fn quantized_tensor_handle(tensor: QuantizedTensor<Self>) -> Self::Handle {
175 HandleKind::Quantized(tensor)
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182
183 #[test]
184 fn should_support_dtypes() {
185 type B = NdArray;
186 let device = NdArrayDevice::Cpu;
187 let scheme = device.defaults().quantization.scheme;
188
189 assert!(B::supports_dtype(&device, DType::F64));
190 assert!(B::supports_dtype(&device, DType::F32));
191 assert!(B::supports_dtype(&device, DType::Flex32));
192 assert!(B::supports_dtype(&device, DType::I64));
193 assert!(B::supports_dtype(&device, DType::I32));
194 assert!(B::supports_dtype(&device, DType::I16));
195 assert!(B::supports_dtype(&device, DType::I8));
196 assert!(B::supports_dtype(&device, DType::U64));
197 assert!(B::supports_dtype(&device, DType::U32));
198 assert!(B::supports_dtype(&device, DType::U16));
199 assert!(B::supports_dtype(&device, DType::U8));
200 assert!(B::supports_dtype(&device, DType::Bool(BoolStore::Native)));
201 assert!(B::supports_dtype(&device, DType::QFloat(scheme)));
202
203 assert!(!B::supports_dtype(&device, DType::F16));
204 assert!(!B::supports_dtype(&device, DType::BF16));
205 assert!(!B::supports_dtype(
207 &device,
208 DType::QFloat(QuantScheme::default())
209 ));
210 }
211}