1use alloc::format;
2use alloc::string::String;
3use alloc::vec::Vec;
4
5#[cfg(any(
6 feature = "cpu",
7 feature = "ndarray",
8 feature = "flex",
9 default_backend
10))]
11use alloc::vec;
12
13#[cfg(feature = "autodiff")]
14use burn_backend::distributed::{DistributedParamId, DistributedParams};
15use burn_backend::{
16 AutodiffBackend, Backend, BackendGraph, BackendTypes, DType, ExecutionError,
17 InstallMemoryPoolsError, MemoryPoolLayout, MemoryPoolUsage, SlicedPoolReport,
18};
19
20#[derive(Debug, Clone)]
26pub enum DispatchGraph {
27 #[cfg(feature = "cpu")]
29 Cpu(BackendGraph<Cpu>),
30
31 #[cfg(feature = "cuda")]
33 Cuda(BackendGraph<Cuda>),
34
35 #[cfg(feature = "metal")]
37 Metal(BackendGraph<Metal>),
38
39 #[cfg(feature = "rocm")]
41 Rocm(BackendGraph<Rocm>),
42
43 #[cfg(feature = "vulkan")]
45 Vulkan(BackendGraph<Vulkan>),
46
47 #[cfg(feature = "wgpu")]
49 Wgpu(BackendGraph<Wgpu>),
50
51 #[cfg(feature = "webgpu")]
53 WebGpu(BackendGraph<WebGpu>),
54
55 #[cfg(any(feature = "flex", default_backend))]
57 Flex(BackendGraph<Flex>),
58
59 #[cfg(feature = "ndarray")]
61 NdArray(BackendGraph<NdArray>),
62
63 #[cfg(feature = "tch")]
65 LibTorch(BackendGraph<LibTorch>),
66
67 #[cfg(feature = "remote")]
69 Remote(BackendGraph<Remote>),
70 #[cfg(feature = "capture")]
72 Capture(BackendGraph<Capture>),
73}
74
75fn graph_dispatch_err(reason: alloc::string::String) -> ExecutionError {
77 ExecutionError::WithContext { reason }
78}
79
80macro_rules! graph_stop_capture_arms {
83 ($device:expr; $([$Backend:ident, $cfg:meta]),*) => {
84 match $device {
85 $(
86 #[cfg($cfg)]
87 $crate::DispatchDevice::$Backend(device) => {
88 <$crate::backends::$Backend as Backend>::graph_stop_capture(device)
89 .map(DispatchGraph::$Backend)
90 }
91 )*
92 #[allow(unreachable_patterns)]
93 other => Err(graph_dispatch_err(format!(
94 "Graph capture is not supported for device {other:?}"
95 ))),
96 }
97 };
98}
99
100macro_rules! graph_replay_arms {
104 ($device:expr, $graph:expr; $([$Backend:ident, $cfg:meta]),*) => {
105 match ($device, $graph) {
106 $(
107 #[cfg($cfg)]
108 ($crate::DispatchDevice::$Backend(device), DispatchGraph::$Backend(graph)) => {
109 unsafe {
112 <$crate::backends::$Backend as Backend>::graph_replay(device, graph)
113 }
114 }
115 )*
116 #[allow(unreachable_patterns)]
117 (device, _) => Err(graph_dispatch_err(format!(
118 "The graph was not captured on the backend of device {device:?}"
119 ))),
120 }
121 };
122}
123
124#[cfg(feature = "autodiff")]
125use alloc::boxed::Box;
126#[cfg(feature = "autodiff")]
127use burn_autodiff::grads::Gradients;
128
129#[allow(unused)]
130use crate::DispatchDeviceId;
131#[allow(unused)]
132use crate::DispatchTensorKind;
133use crate::backends::*;
134use crate::{DispatchDevice, DispatchTensor};
135
136#[derive(Debug, Default, Clone)]
161pub struct Dispatch;
162
163impl BackendTypes for Dispatch {
164 type Device = DispatchDevice;
165
166 type FloatTensorPrimitive = DispatchTensor;
167 type IntTensorPrimitive = DispatchTensor;
168 type BoolTensorPrimitive = DispatchTensor;
169 type QuantizedTensorPrimitive = DispatchTensor;
170
171 type GraphPrimitive = DispatchGraph;
172}
173
174impl Backend for Dispatch {
175 fn name(device: &Self::Device) -> String {
176 let inner = dispatch_device!(device, |device| B::name(device));
177 format!("dispatch<{inner}>")
178 }
179
180 fn seed(device: &Self::Device, seed: u64) {
181 dispatch_device!(device, |device| B::seed(device, seed))
182 }
183
184 fn sync(device: &Self::Device) -> Result<(), ExecutionError> {
185 dispatch_device!(device, |device| B::sync(device))
186 }
187
188 fn graph_prepare(device: &Self::Device) -> Result<(), ExecutionError> {
189 dispatch_device!(device, |device| B::graph_prepare(device))
190 }
191
192 fn graph_start_capture(device: &Self::Device) -> Result<(), ExecutionError> {
193 dispatch_device!(device, |device| B::graph_start_capture(device))
194 }
195
196 fn graph_stop_capture(device: &Self::Device) -> Result<DispatchGraph, ExecutionError> {
197 backend_list!(graph_stop_capture_arms, device)
198 }
199
200 unsafe fn graph_replay(
201 device: &Self::Device,
202 graph: &DispatchGraph,
203 ) -> Result<(), ExecutionError> {
204 backend_list!(graph_replay_arms, device, graph)
205 }
206
207 fn dtype_usage(device: &Self::Device, dtype: DType) -> burn_backend::DTypeUsageSet {
208 dispatch_device!(device, |device| B::dtype_usage(device, dtype))
209 }
210
211 fn ad_enabled(device: &Self::Device) -> bool {
212 match device {
213 #[cfg(feature = "autodiff")]
214 DispatchDevice::Autodiff(_) => true,
215 _ => false,
216 }
217 }
218
219 fn device_count(type_id: u16) -> usize {
220 let (dispatch_id, backend_type_id) = DispatchDevice::decode_type_id(type_id);
221 match dispatch_id {
222 #[cfg(feature = "cpu")]
223 DispatchDeviceId::Cpu => Cpu::device_count(backend_type_id),
224 #[cfg(feature = "cuda")]
225 DispatchDeviceId::Cuda => Cuda::device_count(backend_type_id),
226 #[cfg(feature = "metal")]
227 DispatchDeviceId::Metal => Metal::device_count(backend_type_id),
228 #[cfg(feature = "rocm")]
229 DispatchDeviceId::Rocm => Rocm::device_count(backend_type_id),
230 #[cfg(feature = "vulkan")]
231 DispatchDeviceId::Vulkan => Vulkan::device_count(backend_type_id),
232 #[cfg(feature = "wgpu")]
233 DispatchDeviceId::Wgpu => Wgpu::device_count(backend_type_id),
234 #[cfg(feature = "webgpu")]
235 DispatchDeviceId::WebGpu => WebGpu::device_count(backend_type_id),
236 #[cfg(any(feature = "flex", default_backend))]
237 DispatchDeviceId::Flex => Flex::device_count(backend_type_id),
238 #[cfg(feature = "ndarray")]
239 DispatchDeviceId::NdArray => NdArray::device_count(backend_type_id),
240 #[cfg(feature = "tch")]
241 DispatchDeviceId::LibTorch => LibTorch::device_count(backend_type_id),
242 #[cfg(feature = "remote")]
243 DispatchDeviceId::Remote => Remote::device_count(backend_type_id),
244 #[cfg(feature = "capture")]
245 DispatchDeviceId::Capture => Capture::device_count(backend_type_id),
246 _ => unreachable!("No backend feature enabled."),
247 }
248 }
249
250 fn memory_persistent_allocations<
251 Output: Send,
252 Input: Send,
253 Func: Fn(Input) -> Output + Send,
254 >(
255 device: &Self::Device,
256 input: Input,
257 func: Func,
258 ) -> Output {
259 dispatch_device!(device, |device| B::memory_persistent_allocations(
260 device, input, func
261 ))
262 }
263
264 fn memory_cleanup(device: &Self::Device) {
265 dispatch_device!(device, |device| B::memory_cleanup(device))
266 }
267
268 fn memory_install_pools(
269 device: &Self::Device,
270 layout: MemoryPoolLayout,
271 ) -> Result<(), InstallMemoryPoolsError> {
272 dispatch_device!(device, |device| B::memory_install_pools(
273 device,
274 layout.clone()
275 ))
276 }
277
278 fn memory_pool_report(device: &Self::Device) -> Option<Vec<SlicedPoolReport>> {
279 dispatch_device!(device, |device| B::memory_pool_report(device))
280 }
281
282 fn memory_pool_usage(device: &Self::Device) -> Option<MemoryPoolUsage> {
283 dispatch_device!(device, |device| B::memory_pool_usage(device))
284 }
285
286 fn staging<'a, Iter>(data: Iter, device: &Self::Device)
287 where
288 Iter: Iterator<Item = &'a mut burn_backend::TensorData>,
289 {
290 dispatch_device!(device, |device| B::staging(data, device))
291 }
292
293 fn supports_dtype(device: &Self::Device, dtype: DType) -> bool {
294 dispatch_device!(device, |device| B::supports_dtype(device, dtype))
295 }
296
297 fn flush(device: &Self::Device) {
298 dispatch_device!(device, |device| B::flush(device))
299 }
300}
301
302#[cfg(feature = "autodiff")]
303impl AutodiffBackend for Dispatch {
304 type InnerBackend = Dispatch;
305
306 type Gradients = Gradients;
307
308 fn backward(tensor: DispatchTensor) -> Self::Gradients {
309 let DispatchTensor { kind, .. } = tensor;
310
311 match kind {
312 DispatchTensorKind::Autodiff(tensor) => match *tensor {
313 #[cfg(feature = "cpu")]
314 DispatchTensorKind::Cpu(tensor) => tensor.autodiff().backward(),
315 #[cfg(feature = "cuda")]
316 DispatchTensorKind::Cuda(tensor) => tensor.autodiff().backward(),
317 #[cfg(feature = "metal")]
318 DispatchTensorKind::Metal(tensor) => tensor.autodiff().backward(),
319 #[cfg(feature = "rocm")]
320 DispatchTensorKind::Rocm(tensor) => tensor.autodiff().backward(),
321 #[cfg(feature = "vulkan")]
322 DispatchTensorKind::Vulkan(tensor) => tensor.autodiff().backward(),
323 #[cfg(feature = "wgpu")]
324 DispatchTensorKind::Wgpu(tensor) => tensor.autodiff().backward(),
325 #[cfg(feature = "webgpu")]
326 DispatchTensorKind::WebGpu(tensor) => tensor.autodiff().backward(),
327 #[cfg(any(feature = "flex", default_backend))]
328 DispatchTensorKind::Flex(tensor) => tensor.autodiff().backward(),
329 #[cfg(feature = "ndarray")]
330 DispatchTensorKind::NdArray(tensor) => tensor.autodiff().backward(),
331 #[cfg(feature = "tch")]
332 DispatchTensorKind::LibTorch(tensor) => tensor.autodiff().backward(),
333 #[cfg(feature = "remote")]
334 DispatchTensorKind::Remote(tensor) => tensor.autodiff().backward(),
335 #[cfg(feature = "capture")]
336 DispatchTensorKind::Capture(_) => {
337 panic!("Capture tensors do not support autodiff")
338 }
339 DispatchTensorKind::Autodiff(_) => {
340 panic!("Autodiff should not wrap an autodiff tensor.")
341 }
342 },
343 _ => panic!("Requires autodiff tensor."),
344 }
345 }
346
347 fn grad(tensor: &DispatchTensor, grads: &Self::Gradients) -> Option<DispatchTensor> {
348 let DispatchTensor {
349 kind,
350 checkpointing,
351 } = tensor;
352 let grad: Option<DispatchTensorKind> = match &kind {
353 DispatchTensorKind::Autodiff(inner_kind) => match &**inner_kind {
354 #[cfg(feature = "cpu")]
355 DispatchTensorKind::Cpu(tensor) => tensor
356 .as_autodiff()
357 .grad(grads)
358 .map(|t| DispatchTensorKind::Cpu(crate::BackendTensor::Float(t))),
359 #[cfg(feature = "cuda")]
360 DispatchTensorKind::Cuda(tensor) => tensor
361 .as_autodiff()
362 .grad(grads)
363 .map(|t| DispatchTensorKind::Cuda(crate::BackendTensor::Float(t))),
364 #[cfg(feature = "metal")]
365 DispatchTensorKind::Metal(tensor) => tensor
366 .as_autodiff()
367 .grad(grads)
368 .map(|t| DispatchTensorKind::Metal(crate::BackendTensor::Float(t))),
369 #[cfg(feature = "rocm")]
370 DispatchTensorKind::Rocm(tensor) => tensor
371 .as_autodiff()
372 .grad(grads)
373 .map(|t| DispatchTensorKind::Rocm(crate::BackendTensor::Float(t))),
374 #[cfg(feature = "vulkan")]
375 DispatchTensorKind::Vulkan(tensor) => tensor
376 .as_autodiff()
377 .grad(grads)
378 .map(|t| DispatchTensorKind::Vulkan(crate::BackendTensor::Float(t))),
379 #[cfg(feature = "wgpu")]
380 DispatchTensorKind::Wgpu(tensor) => tensor
381 .as_autodiff()
382 .grad(grads)
383 .map(|t| DispatchTensorKind::Wgpu(crate::BackendTensor::Float(t))),
384 #[cfg(feature = "webgpu")]
385 DispatchTensorKind::WebGpu(tensor) => tensor
386 .as_autodiff()
387 .grad(grads)
388 .map(|t| DispatchTensorKind::WebGpu(crate::BackendTensor::Float(t))),
389 #[cfg(any(feature = "flex", default_backend))]
390 DispatchTensorKind::Flex(tensor) => tensor
391 .as_autodiff()
392 .grad(grads)
393 .map(|t| DispatchTensorKind::Flex(crate::BackendTensor::Float(t))),
394 #[cfg(feature = "ndarray")]
395 DispatchTensorKind::NdArray(tensor) => tensor
396 .as_autodiff()
397 .grad(grads)
398 .map(|t| DispatchTensorKind::NdArray(crate::BackendTensor::Float(t))),
399 #[cfg(feature = "tch")]
400 DispatchTensorKind::LibTorch(tensor) => tensor
401 .as_autodiff()
402 .grad(grads)
403 .map(|t| DispatchTensorKind::LibTorch(crate::BackendTensor::Float(t))),
404 #[cfg(feature = "remote")]
405 DispatchTensorKind::Remote(tensor) => tensor
406 .as_autodiff()
407 .grad(grads)
408 .map(|t| DispatchTensorKind::Remote(crate::BackendTensor::Float(t))),
409 #[cfg(feature = "capture")]
410 DispatchTensorKind::Capture(_) => {
411 panic!("Capture tensors do not support autodiff")
412 }
413 DispatchTensorKind::Autodiff(_) => {
414 panic!("Autodiff should not wrap an autodiff tensor.")
415 }
416 },
417 _ => panic!("Requires autodiff tensor."),
418 };
419 grad.map(|kind| DispatchTensor {
420 kind,
421 checkpointing: *checkpointing,
422 })
423 }
424
425 fn grad_remove(tensor: &DispatchTensor, grads: &mut Self::Gradients) -> Option<DispatchTensor> {
426 let DispatchTensor {
427 kind,
428 checkpointing,
429 } = tensor;
430 let grad: Option<DispatchTensorKind> = match &kind {
431 DispatchTensorKind::Autodiff(inner_kind) => match &**inner_kind {
432 #[cfg(feature = "cpu")]
433 DispatchTensorKind::Cpu(tensor) => tensor
434 .as_autodiff()
435 .grad_remove(grads)
436 .map(|t| DispatchTensorKind::Cpu(crate::BackendTensor::Float(t))),
437 #[cfg(feature = "cuda")]
438 DispatchTensorKind::Cuda(tensor) => tensor
439 .as_autodiff()
440 .grad_remove(grads)
441 .map(|t| DispatchTensorKind::Cuda(crate::BackendTensor::Float(t))),
442 #[cfg(feature = "metal")]
443 DispatchTensorKind::Metal(tensor) => tensor
444 .as_autodiff()
445 .grad_remove(grads)
446 .map(|t| DispatchTensorKind::Metal(crate::BackendTensor::Float(t))),
447 #[cfg(feature = "rocm")]
448 DispatchTensorKind::Rocm(tensor) => tensor
449 .as_autodiff()
450 .grad_remove(grads)
451 .map(|t| DispatchTensorKind::Rocm(crate::BackendTensor::Float(t))),
452 #[cfg(feature = "vulkan")]
453 DispatchTensorKind::Vulkan(tensor) => tensor
454 .as_autodiff()
455 .grad_remove(grads)
456 .map(|t| DispatchTensorKind::Vulkan(crate::BackendTensor::Float(t))),
457 #[cfg(feature = "wgpu")]
458 DispatchTensorKind::Wgpu(tensor) => tensor
459 .as_autodiff()
460 .grad_remove(grads)
461 .map(|t| DispatchTensorKind::Wgpu(crate::BackendTensor::Float(t))),
462 #[cfg(feature = "webgpu")]
463 DispatchTensorKind::WebGpu(tensor) => tensor
464 .as_autodiff()
465 .grad_remove(grads)
466 .map(|t| DispatchTensorKind::WebGpu(crate::BackendTensor::Float(t))),
467 #[cfg(any(feature = "flex", default_backend))]
468 DispatchTensorKind::Flex(tensor) => tensor
469 .as_autodiff()
470 .grad_remove(grads)
471 .map(|t| DispatchTensorKind::Flex(crate::BackendTensor::Float(t))),
472 #[cfg(feature = "ndarray")]
473 DispatchTensorKind::NdArray(tensor) => tensor
474 .as_autodiff()
475 .grad_remove(grads)
476 .map(|t| DispatchTensorKind::NdArray(crate::BackendTensor::Float(t))),
477 #[cfg(feature = "tch")]
478 DispatchTensorKind::LibTorch(tensor) => tensor
479 .as_autodiff()
480 .grad_remove(grads)
481 .map(|t| DispatchTensorKind::LibTorch(crate::BackendTensor::Float(t))),
482 #[cfg(feature = "remote")]
483 DispatchTensorKind::Remote(tensor) => tensor
484 .as_autodiff()
485 .grad_remove(grads)
486 .map(|t| DispatchTensorKind::Remote(crate::BackendTensor::Float(t))),
487 #[cfg(feature = "capture")]
488 DispatchTensorKind::Capture(_) => {
489 panic!("Capture tensors do not support autodiff")
490 }
491 DispatchTensorKind::Autodiff(_) => {
492 panic!("Autodiff should not wrap an autodiff tensor.")
493 }
494 },
495 _ => panic!("Requires autodiff tensor."),
496 };
497 grad.map(|kind| DispatchTensor {
498 kind,
499 checkpointing: *checkpointing,
500 })
501 }
502
503 fn grad_replace(tensor: &DispatchTensor, grads: &mut Self::Gradients, grad: DispatchTensor) {
504 let DispatchTensor { kind, .. } = tensor;
510 let DispatchTensor { kind: grad, .. } = grad;
511
512 match &kind {
513 DispatchTensorKind::Autodiff(inner_kind) => match (&**inner_kind, grad) {
514 #[cfg(feature = "cpu")]
515 (DispatchTensorKind::Cpu(tensor), DispatchTensorKind::Cpu(grad)) => {
516 tensor.as_autodiff().grad_replace(grads, grad.float())
517 }
518 #[cfg(feature = "cuda")]
519 (DispatchTensorKind::Cuda(tensor), DispatchTensorKind::Cuda(grad)) => {
520 tensor.as_autodiff().grad_replace(grads, grad.float())
521 }
522 #[cfg(feature = "metal")]
523 (DispatchTensorKind::Metal(tensor), DispatchTensorKind::Metal(grad)) => {
524 tensor.as_autodiff().grad_replace(grads, grad.float())
525 }
526 #[cfg(feature = "rocm")]
527 (DispatchTensorKind::Rocm(tensor), DispatchTensorKind::Rocm(grad)) => {
528 tensor.as_autodiff().grad_replace(grads, grad.float())
529 }
530 #[cfg(feature = "vulkan")]
531 (DispatchTensorKind::Vulkan(tensor), DispatchTensorKind::Vulkan(grad)) => {
532 tensor.as_autodiff().grad_replace(grads, grad.float())
533 }
534 #[cfg(feature = "wgpu")]
535 (DispatchTensorKind::Wgpu(tensor), DispatchTensorKind::Wgpu(grad)) => {
536 tensor.as_autodiff().grad_replace(grads, grad.float())
537 }
538 #[cfg(feature = "webgpu")]
539 (DispatchTensorKind::WebGpu(tensor), DispatchTensorKind::WebGpu(grad)) => {
540 tensor.as_autodiff().grad_replace(grads, grad.float())
541 }
542 #[cfg(any(feature = "flex", default_backend))]
543 (DispatchTensorKind::Flex(tensor), DispatchTensorKind::Flex(grad)) => {
544 tensor.as_autodiff().grad_replace(grads, grad.float())
545 }
546 #[cfg(feature = "ndarray")]
547 (DispatchTensorKind::NdArray(tensor), DispatchTensorKind::NdArray(grad)) => {
548 tensor.as_autodiff().grad_replace(grads, grad.float())
549 }
550 #[cfg(feature = "remote")]
551 (DispatchTensorKind::Remote(tensor), DispatchTensorKind::Remote(grad)) => {
552 tensor.as_autodiff().grad_replace(grads, grad.float())
553 }
554 (DispatchTensorKind::Autodiff(_), _) => {
555 panic!("Autodiff should not wrap an autodiff tensor.")
556 }
557 (t, g) => panic!(
559 "The provided tensors are not on the same backend. Got backends {t:?} and {g:?}."
560 ),
561 },
562 _ => panic!("Requires autodiff tensor."),
563 }
564 }
565
566 fn inner(tensor: DispatchTensor) -> DispatchTensor {
567 let DispatchTensor {
568 kind,
569 checkpointing: _,
570 } = tensor;
571
572 let kind = match kind {
573 DispatchTensorKind::Autodiff(inner_kind) => match *inner_kind {
574 #[cfg(feature = "cpu")]
575 DispatchTensorKind::Cpu(tensor) => DispatchTensorKind::Cpu(
576 crate::BackendTensor::Float(tensor.autodiff().primitive),
577 ),
578 #[cfg(feature = "cuda")]
579 DispatchTensorKind::Cuda(tensor) => DispatchTensorKind::Cuda(
580 crate::BackendTensor::Float(tensor.autodiff().primitive),
581 ),
582 #[cfg(feature = "metal")]
583 DispatchTensorKind::Metal(tensor) => DispatchTensorKind::Metal(
584 crate::BackendTensor::Float(tensor.autodiff().primitive),
585 ),
586 #[cfg(feature = "rocm")]
587 DispatchTensorKind::Rocm(tensor) => DispatchTensorKind::Rocm(
588 crate::BackendTensor::Float(tensor.autodiff().primitive),
589 ),
590 #[cfg(feature = "vulkan")]
591 DispatchTensorKind::Vulkan(tensor) => DispatchTensorKind::Vulkan(
592 crate::BackendTensor::Float(tensor.autodiff().primitive),
593 ),
594 #[cfg(feature = "wgpu")]
595 DispatchTensorKind::Wgpu(tensor) => DispatchTensorKind::Wgpu(
596 crate::BackendTensor::Float(tensor.autodiff().primitive),
597 ),
598 #[cfg(feature = "webgpu")]
599 DispatchTensorKind::WebGpu(tensor) => DispatchTensorKind::WebGpu(
600 crate::BackendTensor::Float(tensor.autodiff().primitive),
601 ),
602 #[cfg(any(feature = "flex", default_backend))]
603 DispatchTensorKind::Flex(tensor) => DispatchTensorKind::Flex(
604 crate::BackendTensor::Float(tensor.autodiff().primitive),
605 ),
606 #[cfg(feature = "ndarray")]
607 DispatchTensorKind::NdArray(tensor) => DispatchTensorKind::NdArray(
608 crate::BackendTensor::Float(tensor.autodiff().primitive),
609 ),
610 #[cfg(feature = "tch")]
611 DispatchTensorKind::LibTorch(tensor) => DispatchTensorKind::LibTorch(
612 crate::BackendTensor::Float(tensor.autodiff().primitive),
613 ),
614 #[cfg(feature = "remote")]
615 DispatchTensorKind::Remote(tensor) => DispatchTensorKind::Remote(
616 crate::BackendTensor::Float(tensor.autodiff().primitive),
617 ),
618 #[cfg(feature = "capture")]
619 DispatchTensorKind::Capture(_) => {
620 panic!("Capture tensors do not support autodiff")
621 }
622 DispatchTensorKind::Autodiff(_) => {
623 panic!("Autodiff should not wrap an autodiff tensor.")
624 }
625 },
626 _ => panic!("Requires autodiff tensor."),
627 };
628 DispatchTensor {
629 kind,
630 checkpointing: None,
631 }
632 }
633
634 fn int_inner(tensor: DispatchTensor) -> DispatchTensor {
635 tensor
636 }
637
638 fn bool_inner(tensor: DispatchTensor) -> DispatchTensor {
639 tensor
640 }
641
642 fn q_inner(tensor: DispatchTensor) -> DispatchTensor {
643 tensor
644 }
645
646 fn from_inner(tensor: DispatchTensor) -> DispatchTensor {
647 let DispatchTensor {
648 kind,
649 checkpointing,
650 } = tensor;
651
652 let kind = match kind {
653 #[cfg(feature = "cpu")]
654 DispatchTensorKind::Cpu(tensor) => {
655 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::Cpu(
656 crate::BackendTensor::Autodiff(Autodiff::<Cpu>::from_inner(tensor.float())),
657 )))
658 }
659 #[cfg(feature = "cuda")]
660 DispatchTensorKind::Cuda(tensor) => {
661 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::Cuda(
662 crate::BackendTensor::Autodiff(Autodiff::<Cuda>::from_inner(tensor.float())),
663 )))
664 }
665 #[cfg(feature = "metal")]
666 DispatchTensorKind::Metal(tensor) => {
667 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::Metal(
668 crate::BackendTensor::Autodiff(Autodiff::<Metal>::from_inner(tensor.float())),
669 )))
670 }
671 #[cfg(feature = "rocm")]
672 DispatchTensorKind::Rocm(tensor) => {
673 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::Rocm(
674 crate::BackendTensor::Autodiff(Autodiff::<Rocm>::from_inner(tensor.float())),
675 )))
676 }
677 #[cfg(feature = "vulkan")]
678 DispatchTensorKind::Vulkan(tensor) => {
679 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::Vulkan(
680 crate::BackendTensor::Autodiff(Autodiff::<Vulkan>::from_inner(tensor.float())),
681 )))
682 }
683 #[cfg(feature = "wgpu")]
684 DispatchTensorKind::Wgpu(tensor) => {
685 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::Wgpu(
686 crate::BackendTensor::Autodiff(Autodiff::<Wgpu>::from_inner(tensor.float())),
687 )))
688 }
689 #[cfg(feature = "webgpu")]
690 DispatchTensorKind::WebGpu(tensor) => {
691 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::WebGpu(
692 crate::BackendTensor::Autodiff(Autodiff::<WebGpu>::from_inner(tensor.float())),
693 )))
694 }
695 #[cfg(any(feature = "flex", default_backend))]
696 DispatchTensorKind::Flex(tensor) => {
697 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::Flex(
698 crate::BackendTensor::Autodiff(Autodiff::<Flex>::from_inner(tensor.float())),
699 )))
700 }
701 #[cfg(feature = "ndarray")]
702 DispatchTensorKind::NdArray(tensor) => {
703 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::NdArray(
704 crate::BackendTensor::Autodiff(Autodiff::<NdArray>::from_inner(tensor.float())),
705 )))
706 }
707 #[cfg(feature = "tch")]
708 DispatchTensorKind::LibTorch(tensor) => DispatchTensorKind::Autodiff(Box::new(
709 DispatchTensorKind::LibTorch(crate::BackendTensor::Autodiff(
710 Autodiff::<LibTorch>::from_inner(tensor.float()),
711 )),
712 )),
713 #[cfg(feature = "remote")]
714 DispatchTensorKind::Remote(tensor) => {
715 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::Remote(
716 crate::BackendTensor::Autodiff(Autodiff::<Remote>::from_inner(tensor.float())),
717 )))
718 }
719 #[cfg(feature = "capture")]
720 DispatchTensorKind::Capture(_) => {
721 panic!("Capture tensors do not support autodiff")
722 }
723 DispatchTensorKind::Autodiff(_) => {
724 panic!("Autodiff should not wrap an autodiff tensor.")
725 }
726 };
727
728 let checkpointing = if let Some(strategy) = checkpointing {
730 Some(strategy)
731 } else {
732 Some(crate::GradientCheckpointingStrategy::Disabled)
733 };
734 DispatchTensor {
735 kind,
736 checkpointing,
737 }
738 }
739
740 fn int_from_inner(tensor: DispatchTensor) -> DispatchTensor {
741 tensor
742 }
743
744 fn bool_from_inner(tensor: DispatchTensor) -> DispatchTensor {
745 tensor
746 }
747
748 fn q_from_inner(tensor: DispatchTensor) -> DispatchTensor {
749 tensor
750 }
751
752 #[allow(unused_variables, unreachable_code)]
755 fn set_distributed_params(
756 tensor: DispatchTensor,
757 param_id: DistributedParamId,
758 ) -> DispatchTensor {
759 let DispatchTensor {
760 kind,
761 checkpointing,
762 } = tensor;
763
764 let kind = match kind {
765 DispatchTensorKind::Autodiff(inner_kind) => match *inner_kind {
766 #[cfg(feature = "cuda")]
767 DispatchTensorKind::Cuda(tensor) => {
768 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::Cuda(
769 crate::BackendTensor::Autodiff(Autodiff::<Cuda>::set_distributed_params(
770 tensor.as_autodiff().clone(),
771 param_id,
772 )),
773 )))
774 }
775 #[cfg(feature = "remote")]
776 DispatchTensorKind::Remote(tensor) => {
777 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::Remote(
778 crate::BackendTensor::Autodiff(Autodiff::<Remote>::set_distributed_params(
779 tensor.as_autodiff().clone(),
780 param_id,
781 )),
782 )))
783 }
784 DispatchTensorKind::Autodiff(_) => {
785 panic!("Autodiff should not wrap an autodiff tensor.")
786 }
787 other => {
788 panic!("Distributed operations are not supported for tensor kind {other:?}")
789 }
790 },
791 _ => panic!("Requires autodiff tensor."),
792 };
793
794 let checkpointing = if let Some(strategy) = checkpointing {
795 Some(strategy)
796 } else {
797 Some(crate::GradientCheckpointingStrategy::Disabled)
798 };
799 DispatchTensor {
800 kind,
801 checkpointing,
802 }
803 }
804
805 #[allow(unused_variables)]
806 fn distributed_params(tensor: &DispatchTensor) -> Option<DistributedParams> {
807 let DispatchTensor {
808 kind,
809 checkpointing: _,
810 } = tensor;
811
812 match &kind {
813 DispatchTensorKind::Autodiff(inner_kind) => match &**inner_kind {
814 #[cfg(feature = "cuda")]
815 DispatchTensorKind::Cuda(tensor) => {
816 tensor.as_autodiff().node.distributed_params.clone()
817 }
818 #[cfg(feature = "remote")]
819 DispatchTensorKind::Remote(tensor) => {
820 tensor.as_autodiff().node.distributed_params.clone()
821 }
822
823 DispatchTensorKind::Autodiff(_) => {
824 panic!("Autodiff should not wrap an autodiff tensor.")
825 }
826 _ => None,
828 },
829 _ => panic!("Requires autodiff tensor."),
830 }
831 }
832
833 #[allow(unused_variables)]
834 fn is_distributed(tensor: &DispatchTensor) -> bool {
835 let DispatchTensor {
836 kind,
837 checkpointing: _,
838 } = tensor;
839
840 match &kind {
841 DispatchTensorKind::Autodiff(inner_kind) => match &**inner_kind {
842 #[cfg(feature = "cuda")]
843 DispatchTensorKind::Cuda(tensor) => {
844 tensor.as_autodiff().node.distributed_params.is_some()
845 }
846 #[cfg(feature = "remote")]
847 DispatchTensorKind::Remote(tensor) => {
848 tensor.as_autodiff().node.distributed_params.is_some()
849 }
850
851 DispatchTensorKind::Autodiff(_) => {
852 panic!("Autodiff should not wrap an autodiff tensor.")
853 }
854 _ => false,
856 },
857 _ => panic!("Requires autodiff tensor."),
858 }
859 }
860}
861
862#[cfg(not(feature = "autodiff"))]
864impl AutodiffBackend for Dispatch {
865 type InnerBackend = Dispatch;
866
867 type Gradients = bool;
868
869 fn backward(_tensor: DispatchTensor) -> Self::Gradients {
870 unimplemented!("Requires `autodiff` feature")
871 }
872
873 fn grad(_tensor: &DispatchTensor, _grads: &Self::Gradients) -> Option<DispatchTensor> {
874 unimplemented!("Requires `autodiff` feature")
875 }
876
877 fn grad_remove(
878 _tensor: &DispatchTensor,
879 _grads: &mut Self::Gradients,
880 ) -> Option<DispatchTensor> {
881 unimplemented!("Requires `autodiff` feature")
882 }
883
884 fn grad_replace(_tensor: &DispatchTensor, _grads: &mut Self::Gradients, _grad: DispatchTensor) {
885 unimplemented!("Requires `autodiff` feature")
886 }
887
888 fn inner(_tensor: DispatchTensor) -> DispatchTensor {
889 unimplemented!("Requires `autodiff` feature")
890 }
891
892 fn int_inner(_tensor: DispatchTensor) -> DispatchTensor {
893 unimplemented!("Requires `autodiff` feature")
894 }
895
896 fn bool_inner(_tensor: DispatchTensor) -> DispatchTensor {
897 unimplemented!("Requires `autodiff` feature")
898 }
899
900 fn q_inner(_tensor: DispatchTensor) -> DispatchTensor {
901 unimplemented!("Requires `autodiff` feature")
902 }
903
904 fn from_inner(_tensor: DispatchTensor) -> DispatchTensor {
905 unimplemented!("Requires `autodiff` feature")
906 }
907
908 fn int_from_inner(_tensor: DispatchTensor) -> DispatchTensor {
909 unimplemented!("Requires `autodiff` feature")
910 }
911
912 fn bool_from_inner(_tensor: DispatchTensor) -> DispatchTensor {
913 unimplemented!("Requires `autodiff` feature")
914 }
915
916 fn q_from_inner(_tensor: DispatchTensor) -> DispatchTensor {
917 unimplemented!("Requires `autodiff` feature")
918 }
919}
920
921impl Dispatch {
922 pub fn enumerate(type_id: DispatchDeviceId) -> Vec<DispatchDevice> {
924 match type_id {
926 #[cfg(feature = "cpu")]
927 DispatchDeviceId::Cpu => vec![CpuDevice.into()],
928 #[cfg(feature = "cuda")]
929 DispatchDeviceId::Cuda => (0..Cuda::device_count(0))
930 .map(|i| CudaDevice::new(i).into())
931 .collect(),
932 #[cfg(feature = "metal")]
933 DispatchDeviceId::Metal => (0..Metal::device_count(0))
934 .map(|i| DispatchDevice::Metal(WgpuDevice::DiscreteGpu(i)))
935 .collect(),
936 #[cfg(feature = "rocm")]
937 DispatchDeviceId::Rocm => (0..Rocm::device_count(0))
938 .map(|i| RocmDevice::new(i).into())
939 .collect(),
940 #[cfg(feature = "vulkan")]
941 DispatchDeviceId::Vulkan => (0..Vulkan::device_count(0))
942 .map(|i| DispatchDevice::Vulkan(WgpuDevice::DiscreteGpu(i)))
943 .collect(),
944 #[cfg(feature = "wgpu")]
945 DispatchDeviceId::Wgpu => (0..Wgpu::device_count(0))
946 .map(|i| DispatchDevice::Wgpu(WgpuDevice::DiscreteGpu(i)))
947 .collect(),
948 #[cfg(feature = "webgpu")]
949 DispatchDeviceId::WebGpu => (0..WebGpu::device_count(0))
950 .map(|i| DispatchDevice::WebGpu(WgpuDevice::DiscreteGpu(i)))
951 .collect(),
952 #[cfg(any(feature = "flex", default_backend))]
953 DispatchDeviceId::Flex => vec![FlexDevice.into()],
954 #[cfg(feature = "ndarray")]
955 DispatchDeviceId::NdArray => vec![NdArrayDevice::Cpu.into()],
956 #[cfg(feature = "tch")]
957 DispatchDeviceId::LibTorch => (0..LibTorch::device_count(0))
958 .map(|i| LibTorchDevice::Cuda(i).into())
959 .collect(),
960 #[cfg(feature = "remote")]
961 DispatchDeviceId::Remote => Vec::new(),
965 #[cfg(feature = "capture")]
966 DispatchDeviceId::Capture => Vec::new(),
969 _ => unreachable!("No backend feature enabled."),
970 }
971 }
972
973 #[cfg(feature = "remote-websocket")]
982 pub fn enumerate_remote_websocket(address: &str) -> Vec<DispatchDevice> {
983 RemoteDevice::enumerate_websocket(address)
984 .into_iter()
985 .map(DispatchDevice::Remote)
986 .collect()
987 }
988}