1use crate::{DispatchDevice, backends::*};
2
3#[cfg(feature = "autodiff")]
4use burn_autodiff::checkpoint::strategy::{
5 BalancedCheckpointing, CheckpointStrategy, NoCheckpointing,
6};
7use burn_backend::{Backend, BackendTypes, DType, Shape, TensorMetadata};
8
9use crate::GradientCheckpointingStrategy;
10#[cfg(feature = "autodiff")]
11use alloc::boxed::Box;
12#[cfg(feature = "autodiff")]
13use burn_backend::tensor::FloatTensor;
14
15use alloc::{format, string::String};
16
17#[derive(Clone, Debug)]
22pub enum BackendTensor<B: BackendTypes> {
23 Float(B::FloatTensorPrimitive),
25 Int(B::IntTensorPrimitive),
27 Bool(B::BoolTensorPrimitive),
29 Quantized(B::QuantizedTensorPrimitive),
31 #[cfg(feature = "autodiff")]
32 Autodiff(FloatTensor<Autodiff<B>>),
34}
35
36impl<B: Backend> BackendTensor<B> {
37 pub fn float(self) -> B::FloatTensorPrimitive {
39 match self {
40 BackendTensor::Float(tensor) => tensor,
41 BackendTensor::Int(_) => panic!("Should be float, got int"),
42 BackendTensor::Bool(_) => panic!("Should be float, got bool"),
43 BackendTensor::Quantized(_) => panic!("Should be float, got quantized"),
44 #[cfg(feature = "autodiff")]
45 BackendTensor::Autodiff(_) => panic!("Should be float, got autodiff"),
46 }
47 }
48 pub fn as_float(&self) -> &B::FloatTensorPrimitive {
50 match self {
51 BackendTensor::Float(tensor) => tensor,
52 BackendTensor::Int(_) => panic!("Should be float, got int"),
53 BackendTensor::Bool(_) => panic!("Should be float, got bool"),
54 BackendTensor::Quantized(_) => panic!("Should be float, got quantized"),
55 #[cfg(feature = "autodiff")]
56 BackendTensor::Autodiff(_) => panic!("Should be float, got autodiff"),
57 }
58 }
59
60 pub fn int(self) -> B::IntTensorPrimitive {
62 match self {
63 BackendTensor::Int(tensor) => tensor,
64 BackendTensor::Float(_) => panic!("Should be int, got float"),
65 BackendTensor::Bool(_) => panic!("Should be int, got bool"),
66 BackendTensor::Quantized(_) => panic!("Should be int, got quantized"),
67 #[cfg(feature = "autodiff")]
68 BackendTensor::Autodiff(_) => panic!("Should be int, got autodiff"),
69 }
70 }
71
72 pub fn bool(self) -> B::BoolTensorPrimitive {
74 match self {
75 BackendTensor::Bool(tensor) => tensor,
76 BackendTensor::Float(_) => panic!("Should be bool, got float"),
77 BackendTensor::Int(_) => panic!("Should be bool, got int"),
78 BackendTensor::Quantized(_) => panic!("Should be bool, got quantized"),
79 #[cfg(feature = "autodiff")]
80 BackendTensor::Autodiff(_) => panic!("Should be bool, got autodiff"),
81 }
82 }
83
84 pub fn quantized(self) -> B::QuantizedTensorPrimitive {
86 match self {
87 BackendTensor::Quantized(tensor) => tensor,
88 _ => unreachable!(),
89 }
90 }
91
92 #[cfg(feature = "autodiff")]
93 pub fn autodiff(self) -> FloatTensor<Autodiff<B>> {
95 match self {
96 BackendTensor::Autodiff(tensor) => tensor,
97 _ => unreachable!(),
99 }
100 }
101
102 #[cfg(feature = "autodiff")]
103 pub fn as_autodiff(&self) -> &FloatTensor<Autodiff<B>> {
105 match self {
106 BackendTensor::Autodiff(tensor) => tensor,
107 _ => unreachable!(),
108 }
109 }
110
111 #[cfg(feature = "autodiff")]
112 pub fn autodiff_inner(self) -> B::FloatTensorPrimitive {
114 match self {
115 BackendTensor::Autodiff(tensor) => tensor.primitive,
116 _ => unreachable!(),
117 }
118 }
119
120 #[cfg(feature = "autodiff")]
127 pub fn into_autodiff(self) -> BackendTensor<Autodiff<B>> {
128 match self {
129 BackendTensor::Autodiff(tensor) => BackendTensor::Float(tensor),
130 BackendTensor::Int(tensor) => BackendTensor::Int(tensor),
131 BackendTensor::Bool(tensor) => BackendTensor::Bool(tensor),
132 BackendTensor::Quantized(tensor) => BackendTensor::Quantized(tensor),
133 BackendTensor::Float(_) => {
134 unreachable!("an untracked float handle can't be lifted to Autodiff<B>")
135 }
136 }
137 }
138
139 pub fn name(&self) -> &'static str {
141 match self {
142 BackendTensor::Float(_) => "Float",
143 BackendTensor::Int(_) => "Int",
144 BackendTensor::Bool(_) => "Bool",
145 BackendTensor::Quantized(_) => "Quantized",
146 #[cfg(feature = "autodiff")]
147 BackendTensor::Autodiff(_) => "Autodiff",
148 }
149 }
150}
151
152impl<B: BackendTypes> TensorMetadata for BackendTensor<B> {
153 type Device = B::Device;
154 fn device(&self) -> Self::Device {
155 match self {
156 BackendTensor::Float(tensor) => tensor.device(),
157 BackendTensor::Int(tensor) => tensor.device(),
158 BackendTensor::Bool(tensor) => tensor.device(),
159 BackendTensor::Quantized(tensor) => tensor.device(),
160 #[cfg(feature = "autodiff")]
161 BackendTensor::Autodiff(tensor) => tensor.device(),
162 }
163 }
164 fn dtype(&self) -> DType {
165 match self {
166 BackendTensor::Float(tensor) => tensor.dtype(),
167 BackendTensor::Int(tensor) => tensor.dtype(),
168 BackendTensor::Bool(tensor) => tensor.dtype(),
169 BackendTensor::Quantized(tensor) => tensor.dtype(),
170 #[cfg(feature = "autodiff")]
171 BackendTensor::Autodiff(tensor) => tensor.dtype(),
172 }
173 }
174
175 fn shape(&self) -> Shape {
176 match self {
177 BackendTensor::Float(tensor) => tensor.shape(),
178 BackendTensor::Int(tensor) => tensor.shape(),
179 BackendTensor::Bool(tensor) => tensor.shape(),
180 BackendTensor::Quantized(tensor) => tensor.shape(),
181 #[cfg(feature = "autodiff")]
182 BackendTensor::Autodiff(tensor) => tensor.shape(),
183 }
184 }
185
186 fn can_mut(&self) -> bool {
187 match self {
188 BackendTensor::Float(tensor) => tensor.can_mut(),
189 BackendTensor::Int(tensor) => tensor.can_mut(),
190 BackendTensor::Bool(tensor) => tensor.can_mut(),
191 BackendTensor::Quantized(tensor) => tensor.can_mut(),
192 #[cfg(feature = "autodiff")]
193 BackendTensor::Autodiff(tensor) => tensor.can_mut(),
194 }
195 }
196}
197
198#[derive(Clone, Debug)]
204pub struct DispatchTensor {
205 pub kind: DispatchTensorKind,
207 pub checkpointing: Option<GradientCheckpointingStrategy>,
215}
216
217#[derive(Clone, Debug)]
225pub enum DispatchTensorKind {
226 #[cfg(feature = "cpu")]
228 Cpu(BackendTensor<Cpu>),
229
230 #[cfg(feature = "cuda")]
232 Cuda(BackendTensor<Cuda>),
233
234 #[cfg(feature = "metal")]
236 Metal(BackendTensor<Metal>),
237
238 #[cfg(feature = "rocm")]
240 Rocm(BackendTensor<Rocm>),
241
242 #[cfg(feature = "vulkan")]
244 Vulkan(BackendTensor<Vulkan>),
245
246 #[cfg(feature = "wgpu")]
248 Wgpu(BackendTensor<Wgpu>),
249
250 #[cfg(feature = "webgpu")]
252 WebGpu(BackendTensor<WebGpu>),
253
254 #[cfg(any(feature = "flex", default_backend))]
256 Flex(BackendTensor<Flex>),
257
258 #[cfg(feature = "ndarray")]
260 NdArray(BackendTensor<NdArray>),
261
262 #[cfg(feature = "tch")]
264 LibTorch(BackendTensor<LibTorch>),
265
266 #[cfg(feature = "remote")]
268 Remote(BackendTensor<Remote>),
269 #[cfg(feature = "capture")]
271 Capture(BackendTensor<Capture>),
272
273 #[cfg(feature = "autodiff")]
275 Autodiff(Box<DispatchTensorKind>),
276}
277
278impl TensorMetadata for DispatchTensorKind {
279 type Device = DispatchDevice;
280
281 fn dtype(&self) -> DType {
282 match self {
283 #[cfg(feature = "cpu")]
284 Self::Cpu(tensor) => tensor.dtype(),
285 #[cfg(feature = "cuda")]
286 Self::Cuda(tensor) => tensor.dtype(),
287 #[cfg(feature = "metal")]
288 Self::Metal(tensor) => tensor.dtype(),
289 #[cfg(feature = "rocm")]
290 Self::Rocm(tensor) => tensor.dtype(),
291 #[cfg(feature = "vulkan")]
292 Self::Vulkan(tensor) => tensor.dtype(),
293 #[cfg(feature = "wgpu")]
294 Self::Wgpu(tensor) => tensor.dtype(),
295 #[cfg(feature = "webgpu")]
296 Self::WebGpu(tensor) => tensor.dtype(),
297 #[cfg(any(feature = "flex", default_backend))]
298 Self::Flex(tensor) => tensor.dtype(),
299 #[cfg(feature = "ndarray")]
300 Self::NdArray(tensor) => tensor.dtype(),
301 #[cfg(feature = "tch")]
302 Self::LibTorch(tensor) => tensor.dtype(),
303 #[cfg(feature = "remote")]
304 Self::Remote(tensor) => tensor.dtype(),
305 #[cfg(feature = "capture")]
306 Self::Capture(tensor) => tensor.dtype(),
307 #[cfg(feature = "autodiff")]
308 Self::Autodiff(tensor) => tensor.dtype(),
309 }
310 }
311
312 fn shape(&self) -> Shape {
313 match self {
314 #[cfg(feature = "cpu")]
315 Self::Cpu(tensor) => tensor.shape(),
316 #[cfg(feature = "cuda")]
317 Self::Cuda(tensor) => tensor.shape(),
318 #[cfg(feature = "metal")]
319 Self::Metal(tensor) => tensor.shape(),
320 #[cfg(feature = "rocm")]
321 Self::Rocm(tensor) => tensor.shape(),
322 #[cfg(feature = "vulkan")]
323 Self::Vulkan(tensor) => tensor.shape(),
324 #[cfg(feature = "wgpu")]
325 Self::Wgpu(tensor) => tensor.shape(),
326 #[cfg(feature = "webgpu")]
327 Self::WebGpu(tensor) => tensor.shape(),
328 #[cfg(any(feature = "flex", default_backend))]
329 Self::Flex(tensor) => tensor.shape(),
330 #[cfg(feature = "ndarray")]
331 Self::NdArray(tensor) => tensor.shape(),
332 #[cfg(feature = "tch")]
333 Self::LibTorch(tensor) => tensor.shape(),
334 #[cfg(feature = "remote")]
335 Self::Remote(tensor) => tensor.shape(),
336 #[cfg(feature = "capture")]
337 Self::Capture(tensor) => tensor.shape(),
338 #[cfg(feature = "autodiff")]
339 Self::Autodiff(tensor) => tensor.shape(),
340 }
341 }
342
343 fn device(&self) -> DispatchDevice {
344 match self {
345 #[cfg(feature = "cpu")]
346 DispatchTensorKind::Cpu(tensor) => DispatchDevice::Cpu(tensor.device()),
347 #[cfg(feature = "cuda")]
348 DispatchTensorKind::Cuda(tensor) => DispatchDevice::Cuda(tensor.device()),
349 #[cfg(feature = "metal")]
350 DispatchTensorKind::Metal(tensor) => DispatchDevice::Metal(tensor.device()),
351 #[cfg(feature = "rocm")]
352 DispatchTensorKind::Rocm(tensor) => DispatchDevice::Rocm(tensor.device()),
353 #[cfg(feature = "vulkan")]
354 DispatchTensorKind::Vulkan(tensor) => DispatchDevice::Vulkan(tensor.device()),
355 #[cfg(feature = "wgpu")]
356 DispatchTensorKind::Wgpu(tensor) => DispatchDevice::Wgpu(tensor.device()),
357 #[cfg(feature = "webgpu")]
358 DispatchTensorKind::WebGpu(tensor) => DispatchDevice::WebGpu(tensor.device()),
359 #[cfg(any(feature = "flex", default_backend))]
360 DispatchTensorKind::Flex(tensor) => DispatchDevice::Flex(tensor.device()),
361 #[cfg(feature = "ndarray")]
362 DispatchTensorKind::NdArray(tensor) => DispatchDevice::NdArray(tensor.device()),
363 #[cfg(feature = "tch")]
364 DispatchTensorKind::LibTorch(tensor) => DispatchDevice::LibTorch(tensor.device()),
365 #[cfg(feature = "remote")]
366 DispatchTensorKind::Remote(tensor) => DispatchDevice::Remote(tensor.device()),
367 #[cfg(feature = "capture")]
368 DispatchTensorKind::Capture(tensor) => DispatchDevice::Capture(tensor.device()),
369 #[cfg(feature = "autodiff")]
370 DispatchTensorKind::Autodiff(tensor) => DispatchDevice::autodiff(tensor.device()),
371 }
372 }
373
374 fn can_mut(&self) -> bool {
375 match self {
376 #[cfg(feature = "cpu")]
377 Self::Cpu(tensor) => tensor.can_mut(),
378 #[cfg(feature = "cuda")]
379 Self::Cuda(tensor) => tensor.can_mut(),
380 #[cfg(feature = "metal")]
381 Self::Metal(tensor) => tensor.can_mut(),
382 #[cfg(feature = "rocm")]
383 Self::Rocm(tensor) => tensor.can_mut(),
384 #[cfg(feature = "vulkan")]
385 Self::Vulkan(tensor) => tensor.can_mut(),
386 #[cfg(feature = "wgpu")]
387 Self::Wgpu(tensor) => tensor.can_mut(),
388 #[cfg(feature = "webgpu")]
389 Self::WebGpu(tensor) => tensor.can_mut(),
390 #[cfg(any(feature = "flex", default_backend))]
391 Self::Flex(tensor) => tensor.can_mut(),
392 #[cfg(feature = "ndarray")]
393 Self::NdArray(tensor) => tensor.can_mut(),
394 #[cfg(feature = "tch")]
395 Self::LibTorch(tensor) => tensor.can_mut(),
396 #[cfg(feature = "remote")]
397 Self::Remote(tensor) => tensor.can_mut(),
398 #[cfg(feature = "capture")]
399 Self::Capture(tensor) => tensor.can_mut(),
400 #[cfg(feature = "autodiff")]
401 Self::Autodiff(tensor) => tensor.can_mut(),
402 }
403 }
404}
405
406impl TensorMetadata for DispatchTensor {
407 fn dtype(&self) -> DType {
408 self.kind.dtype()
409 }
410
411 fn shape(&self) -> Shape {
412 self.kind.shape()
413 }
414
415 fn can_mut(&self) -> bool {
416 self.kind.can_mut()
417 }
418
419 type Device = DispatchDevice;
420
421 fn device(&self) -> Self::Device {
422 #[allow(unused_mut)]
423 let mut device = self.kind.device();
424
425 #[cfg(feature = "autodiff")]
426 if let Some(checkpointing) = &self.checkpointing {
427 if !self.dtype().is_float() && !matches!(device, DispatchDevice::Autodiff(_)) {
434 device = DispatchDevice::autodiff(device);
435 }
436 if let DispatchDevice::Autodiff(device) = &mut device {
437 device.checkpointing = *checkpointing;
438 }
439 }
440
441 device
442 }
443}
444
445impl DispatchTensorKind {
446 pub(crate) fn name(&self) -> &'static str {
448 match self {
449 #[cfg(feature = "cpu")]
450 DispatchTensorKind::Cpu(_) => "Cpu",
451 #[cfg(feature = "cuda")]
452 DispatchTensorKind::Cuda(_) => "Cuda",
453 #[cfg(feature = "metal")]
454 DispatchTensorKind::Metal(_) => "Metal",
455 #[cfg(feature = "rocm")]
456 DispatchTensorKind::Rocm(_) => "Rocm",
457 #[cfg(feature = "vulkan")]
458 DispatchTensorKind::Vulkan(_) => "Vulkan",
459 #[cfg(feature = "wgpu")]
460 DispatchTensorKind::Wgpu(_) => "Wgpu",
461 #[cfg(feature = "webgpu")]
462 DispatchTensorKind::WebGpu(_) => "WebGpu",
463 #[cfg(any(feature = "flex", default_backend))]
464 DispatchTensorKind::Flex(_) => "Flex",
465 #[cfg(feature = "ndarray")]
466 DispatchTensorKind::NdArray(_) => "NdArray",
467 #[cfg(feature = "tch")]
468 DispatchTensorKind::LibTorch(_) => "LibTorch",
469 #[cfg(feature = "remote")]
470 DispatchTensorKind::Remote(_) => "Remote",
471 #[cfg(feature = "capture")]
472 DispatchTensorKind::Capture(_) => "Capture",
473 #[cfg(feature = "autodiff")]
474 DispatchTensorKind::Autodiff(_) => "Autodiff",
475 }
476 }
477}
478
479#[cfg(feature = "autodiff")]
480trait IntoGradientCheckpointingStrategy {
481 const STRATEGY: GradientCheckpointingStrategy;
482}
483
484#[cfg(feature = "autodiff")]
485impl IntoGradientCheckpointingStrategy for NoCheckpointing {
486 const STRATEGY: GradientCheckpointingStrategy = GradientCheckpointingStrategy::Disabled;
487}
488
489#[cfg(feature = "autodiff")]
490impl IntoGradientCheckpointingStrategy for BalancedCheckpointing {
491 const STRATEGY: GradientCheckpointingStrategy = GradientCheckpointingStrategy::Balanced;
492}
493
494pub trait DispatchKindConversion<B: Backend> {
496 fn try_into_backend(tensor: DispatchTensor) -> Result<BackendTensor<B>, String>;
502
503 fn from_backend(tensor: BackendTensor<B>) -> DispatchTensor;
505}
506
507macro_rules! impl_dispatch_conversion {
508 ($backend:ident, $cfg:meta) => {
509 #[cfg($cfg)]
510 impl DispatchKindConversion<$backend> for DispatchTensor {
511 fn try_into_backend(tensor: DispatchTensor) -> Result<BackendTensor<$backend>, String> {
512 #[allow(unreachable_patterns)]
515 match tensor.kind {
516 DispatchTensorKind::$backend(t) => Ok(t),
517 other => Err(format!(
518 "Expected {} tensor, got variant: {}",
519 stringify!($backend),
520 other.name()
521 )),
522 }
523 }
524
525 fn from_backend(tensor: BackendTensor<$backend>) -> DispatchTensor {
526 DispatchTensor {
527 kind: DispatchTensorKind::$backend(tensor),
528 checkpointing: None,
529 }
530 }
531 }
532
533 #[cfg(all($cfg, feature = "autodiff"))]
534 impl<C: CheckpointStrategy + IntoGradientCheckpointingStrategy>
535 DispatchKindConversion<Autodiff<$backend, C>> for DispatchTensor
536 {
537 fn try_into_backend(
538 tensor: DispatchTensor,
539 ) -> Result<BackendTensor<Autodiff<$backend, C>>, String> {
540 match tensor.kind {
541 DispatchTensorKind::Autodiff(t) => match *t {
542 DispatchTensorKind::$backend(t) => match t {
543 BackendTensor::Autodiff(t) => Ok(BackendTensor::Float(t)),
545 other => Err(format!(
546 "Expected Autodiff {} float tensor, got Autodiff variant: {}",
547 stringify!($backend),
548 other.name()
549 )),
550 },
551 other => Err(format!(
552 "Expected Autodiff {} tensor, got Autodiff variant: {}",
553 stringify!($backend),
554 other.name()
555 )),
556 },
557 other => Err(format!(
558 "Expected Autodiff tensor, got backend: {}",
559 other.name()
560 )),
561 }
562 }
563
564 fn from_backend(tensor: BackendTensor<Autodiff<$backend, C>>) -> DispatchTensor {
565 let kind = match tensor {
567 BackendTensor::Float(t) => {
569 let ad_tensor = BackendTensor::Autodiff(t);
570 let inner_dispatch = DispatchTensorKind::$backend(ad_tensor);
572 DispatchTensorKind::Autodiff(Box::new(inner_dispatch))
574 }
575
576 BackendTensor::Int(t) => DispatchTensorKind::$backend(BackendTensor::Int(t)),
578 BackendTensor::Bool(t) => DispatchTensorKind::$backend(BackendTensor::Bool(t)),
579 BackendTensor::Quantized(t) => {
580 DispatchTensorKind::$backend(BackendTensor::Quantized(t))
581 }
582
583 BackendTensor::Autodiff(_) => {
584 panic!("Unexpected Autodiff variant provided to `from_backend`",)
585 }
586 };
587
588 DispatchTensor {
589 kind,
590 checkpointing: Some(C::STRATEGY),
591 }
592 }
593 }
594 };
595}
596
597impl_dispatch_conversion!(Flex, any(feature = "flex", default_backend));
598impl_dispatch_conversion!(Cpu, feature = "cpu");
599impl_dispatch_conversion!(Cuda, feature = "cuda");
600impl_dispatch_conversion!(Rocm, feature = "rocm");
601impl_dispatch_conversion!(Remote, feature = "remote");
602impl_dispatch_conversion!(Capture, feature = "capture");
603impl_dispatch_conversion!(Metal, feature = "metal");
604impl_dispatch_conversion!(Vulkan, feature = "vulkan");
605impl_dispatch_conversion!(Wgpu, feature = "wgpu");
606impl_dispatch_conversion!(WebGpu, feature = "webgpu");
607impl_dispatch_conversion!(NdArray, feature = "ndarray");
608impl_dispatch_conversion!(LibTorch, feature = "tch");