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