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 { kind, .. } = tensor;
472 let DispatchTensor { kind: grad, .. } = grad;
473
474 match &kind {
475 DispatchTensorKind::Autodiff(inner_kind) => match (&**inner_kind, grad) {
476 #[cfg(feature = "cpu")]
477 (DispatchTensorKind::Cpu(tensor), DispatchTensorKind::Cpu(grad)) => {
478 tensor.as_autodiff().grad_replace(grads, grad.float())
479 }
480 #[cfg(feature = "cuda")]
481 (DispatchTensorKind::Cuda(tensor), DispatchTensorKind::Cuda(grad)) => {
482 tensor.as_autodiff().grad_replace(grads, grad.float())
483 }
484 #[cfg(feature = "metal")]
485 (DispatchTensorKind::Metal(tensor), DispatchTensorKind::Metal(grad)) => {
486 tensor.as_autodiff().grad_replace(grads, grad.float())
487 }
488 #[cfg(feature = "rocm")]
489 (DispatchTensorKind::Rocm(tensor), DispatchTensorKind::Rocm(grad)) => {
490 tensor.as_autodiff().grad_replace(grads, grad.float())
491 }
492 #[cfg(feature = "vulkan")]
493 (DispatchTensorKind::Vulkan(tensor), DispatchTensorKind::Vulkan(grad)) => {
494 tensor.as_autodiff().grad_replace(grads, grad.float())
495 }
496 #[cfg(feature = "wgpu")]
497 (DispatchTensorKind::Wgpu(tensor), DispatchTensorKind::Wgpu(grad)) => {
498 tensor.as_autodiff().grad_replace(grads, grad.float())
499 }
500 #[cfg(feature = "webgpu")]
501 (DispatchTensorKind::WebGpu(tensor), DispatchTensorKind::WebGpu(grad)) => {
502 tensor.as_autodiff().grad_replace(grads, grad.float())
503 }
504 #[cfg(any(feature = "flex", default_backend))]
505 (DispatchTensorKind::Flex(tensor), DispatchTensorKind::Flex(grad)) => {
506 tensor.as_autodiff().grad_replace(grads, grad.float())
507 }
508 #[cfg(feature = "ndarray")]
509 (DispatchTensorKind::NdArray(tensor), DispatchTensorKind::NdArray(grad)) => {
510 tensor.as_autodiff().grad_replace(grads, grad.float())
511 }
512 #[cfg(feature = "remote")]
513 (DispatchTensorKind::Remote(tensor), DispatchTensorKind::Remote(grad)) => {
514 tensor.as_autodiff().grad_replace(grads, grad.float())
515 }
516 (DispatchTensorKind::Autodiff(_), _) => {
517 panic!("Autodiff should not wrap an autodiff tensor.")
518 }
519 (t, g) => panic!(
521 "The provided tensors are not on the same backend. Got backends {t:?} and {g:?}."
522 ),
523 },
524 _ => panic!("Requires autodiff tensor."),
525 }
526 }
527
528 fn inner(tensor: DispatchTensor) -> DispatchTensor {
529 let DispatchTensor {
530 kind,
531 checkpointing: _,
532 } = tensor;
533
534 let kind = match kind {
535 DispatchTensorKind::Autodiff(inner_kind) => match *inner_kind {
536 #[cfg(feature = "cpu")]
537 DispatchTensorKind::Cpu(tensor) => DispatchTensorKind::Cpu(
538 crate::BackendTensor::Float(tensor.autodiff().primitive),
539 ),
540 #[cfg(feature = "cuda")]
541 DispatchTensorKind::Cuda(tensor) => DispatchTensorKind::Cuda(
542 crate::BackendTensor::Float(tensor.autodiff().primitive),
543 ),
544 #[cfg(feature = "metal")]
545 DispatchTensorKind::Metal(tensor) => DispatchTensorKind::Metal(
546 crate::BackendTensor::Float(tensor.autodiff().primitive),
547 ),
548 #[cfg(feature = "rocm")]
549 DispatchTensorKind::Rocm(tensor) => DispatchTensorKind::Rocm(
550 crate::BackendTensor::Float(tensor.autodiff().primitive),
551 ),
552 #[cfg(feature = "vulkan")]
553 DispatchTensorKind::Vulkan(tensor) => DispatchTensorKind::Vulkan(
554 crate::BackendTensor::Float(tensor.autodiff().primitive),
555 ),
556 #[cfg(feature = "wgpu")]
557 DispatchTensorKind::Wgpu(tensor) => DispatchTensorKind::Wgpu(
558 crate::BackendTensor::Float(tensor.autodiff().primitive),
559 ),
560 #[cfg(feature = "webgpu")]
561 DispatchTensorKind::WebGpu(tensor) => DispatchTensorKind::WebGpu(
562 crate::BackendTensor::Float(tensor.autodiff().primitive),
563 ),
564 #[cfg(any(feature = "flex", default_backend))]
565 DispatchTensorKind::Flex(tensor) => DispatchTensorKind::Flex(
566 crate::BackendTensor::Float(tensor.autodiff().primitive),
567 ),
568 #[cfg(feature = "ndarray")]
569 DispatchTensorKind::NdArray(tensor) => DispatchTensorKind::NdArray(
570 crate::BackendTensor::Float(tensor.autodiff().primitive),
571 ),
572 #[cfg(feature = "tch")]
573 DispatchTensorKind::LibTorch(tensor) => DispatchTensorKind::LibTorch(
574 crate::BackendTensor::Float(tensor.autodiff().primitive),
575 ),
576 #[cfg(feature = "remote")]
577 DispatchTensorKind::Remote(tensor) => DispatchTensorKind::Remote(
578 crate::BackendTensor::Float(tensor.autodiff().primitive),
579 ),
580 DispatchTensorKind::Autodiff(_) => {
581 panic!("Autodiff should not wrap an autodiff tensor.")
582 }
583 },
584 _ => panic!("Requires autodiff tensor."),
585 };
586 DispatchTensor {
587 kind,
588 checkpointing: None,
589 }
590 }
591
592 fn int_inner(tensor: DispatchTensor) -> DispatchTensor {
593 tensor
594 }
595
596 fn bool_inner(tensor: DispatchTensor) -> DispatchTensor {
597 tensor
598 }
599
600 fn q_inner(tensor: DispatchTensor) -> DispatchTensor {
601 tensor
602 }
603
604 fn from_inner(tensor: DispatchTensor) -> DispatchTensor {
605 let DispatchTensor {
606 kind,
607 checkpointing,
608 } = tensor;
609
610 let kind = match kind {
611 #[cfg(feature = "cpu")]
612 DispatchTensorKind::Cpu(tensor) => {
613 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::Cpu(
614 crate::BackendTensor::Autodiff(Autodiff::<Cpu>::from_inner(tensor.float())),
615 )))
616 }
617 #[cfg(feature = "cuda")]
618 DispatchTensorKind::Cuda(tensor) => {
619 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::Cuda(
620 crate::BackendTensor::Autodiff(Autodiff::<Cuda>::from_inner(tensor.float())),
621 )))
622 }
623 #[cfg(feature = "metal")]
624 DispatchTensorKind::Metal(tensor) => {
625 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::Metal(
626 crate::BackendTensor::Autodiff(Autodiff::<Metal>::from_inner(tensor.float())),
627 )))
628 }
629 #[cfg(feature = "rocm")]
630 DispatchTensorKind::Rocm(tensor) => {
631 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::Rocm(
632 crate::BackendTensor::Autodiff(Autodiff::<Rocm>::from_inner(tensor.float())),
633 )))
634 }
635 #[cfg(feature = "vulkan")]
636 DispatchTensorKind::Vulkan(tensor) => {
637 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::Vulkan(
638 crate::BackendTensor::Autodiff(Autodiff::<Vulkan>::from_inner(tensor.float())),
639 )))
640 }
641 #[cfg(feature = "wgpu")]
642 DispatchTensorKind::Wgpu(tensor) => {
643 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::Wgpu(
644 crate::BackendTensor::Autodiff(Autodiff::<Wgpu>::from_inner(tensor.float())),
645 )))
646 }
647 #[cfg(feature = "webgpu")]
648 DispatchTensorKind::WebGpu(tensor) => {
649 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::WebGpu(
650 crate::BackendTensor::Autodiff(Autodiff::<WebGpu>::from_inner(tensor.float())),
651 )))
652 }
653 #[cfg(any(feature = "flex", default_backend))]
654 DispatchTensorKind::Flex(tensor) => {
655 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::Flex(
656 crate::BackendTensor::Autodiff(Autodiff::<Flex>::from_inner(tensor.float())),
657 )))
658 }
659 #[cfg(feature = "ndarray")]
660 DispatchTensorKind::NdArray(tensor) => {
661 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::NdArray(
662 crate::BackendTensor::Autodiff(Autodiff::<NdArray>::from_inner(tensor.float())),
663 )))
664 }
665 #[cfg(feature = "tch")]
666 DispatchTensorKind::LibTorch(tensor) => DispatchTensorKind::Autodiff(Box::new(
667 DispatchTensorKind::LibTorch(crate::BackendTensor::Autodiff(
668 Autodiff::<LibTorch>::from_inner(tensor.float()),
669 )),
670 )),
671 #[cfg(feature = "remote")]
672 DispatchTensorKind::Remote(tensor) => {
673 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::Remote(
674 crate::BackendTensor::Autodiff(Autodiff::<Remote>::from_inner(tensor.float())),
675 )))
676 }
677 DispatchTensorKind::Autodiff(_) => {
678 panic!("Autodiff should not wrap an autodiff tensor.")
679 }
680 };
681
682 let checkpointing = if let Some(strategy) = checkpointing {
684 Some(strategy)
685 } else {
686 Some(crate::CheckpointingStrategy::None)
687 };
688 DispatchTensor {
689 kind,
690 checkpointing,
691 }
692 }
693
694 fn int_from_inner(tensor: DispatchTensor) -> DispatchTensor {
695 tensor
696 }
697
698 fn bool_from_inner(tensor: DispatchTensor) -> DispatchTensor {
699 tensor
700 }
701
702 fn q_from_inner(tensor: DispatchTensor) -> DispatchTensor {
703 tensor
704 }
705
706 #[allow(unused_variables, unreachable_code)]
709 fn set_distributed_params(
710 tensor: DispatchTensor,
711 param_id: DistributedParamId,
712 ) -> DispatchTensor {
713 let DispatchTensor {
714 kind,
715 checkpointing,
716 } = tensor;
717
718 let kind = match kind {
719 DispatchTensorKind::Autodiff(inner_kind) => match *inner_kind {
720 #[cfg(feature = "cuda")]
721 DispatchTensorKind::Cuda(tensor) => {
722 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::Cuda(
723 crate::BackendTensor::Autodiff(Autodiff::<Cuda>::set_distributed_params(
724 tensor.as_autodiff().clone(),
725 param_id,
726 )),
727 )))
728 }
729 #[cfg(feature = "remote")]
730 DispatchTensorKind::Remote(tensor) => {
731 DispatchTensorKind::Autodiff(Box::new(DispatchTensorKind::Remote(
732 crate::BackendTensor::Autodiff(Autodiff::<Remote>::set_distributed_params(
733 tensor.as_autodiff().clone(),
734 param_id,
735 )),
736 )))
737 }
738 DispatchTensorKind::Autodiff(_) => {
739 panic!("Autodiff should not wrap an autodiff tensor.")
740 }
741 other => {
742 panic!("Distributed operations are not supported for tensor kind {other:?}")
743 }
744 },
745 _ => panic!("Requires autodiff tensor."),
746 };
747
748 let checkpointing = if let Some(strategy) = checkpointing {
749 Some(strategy)
750 } else {
751 Some(crate::CheckpointingStrategy::None)
752 };
753 DispatchTensor {
754 kind,
755 checkpointing,
756 }
757 }
758
759 #[allow(unused_variables)]
760 fn distributed_params(tensor: &DispatchTensor) -> Option<DistributedParams> {
761 let DispatchTensor {
762 kind,
763 checkpointing: _,
764 } = tensor;
765
766 match &kind {
767 DispatchTensorKind::Autodiff(inner_kind) => match &**inner_kind {
768 #[cfg(feature = "cuda")]
769 DispatchTensorKind::Cuda(tensor) => {
770 tensor.as_autodiff().node.distributed_params.clone()
771 }
772 #[cfg(feature = "remote")]
773 DispatchTensorKind::Remote(tensor) => {
774 tensor.as_autodiff().node.distributed_params.clone()
775 }
776
777 DispatchTensorKind::Autodiff(_) => {
778 panic!("Autodiff should not wrap an autodiff tensor.")
779 }
780 _ => None,
782 },
783 _ => panic!("Requires autodiff tensor."),
784 }
785 }
786
787 #[allow(unused_variables)]
788 fn is_distributed(tensor: &DispatchTensor) -> bool {
789 let DispatchTensor {
790 kind,
791 checkpointing: _,
792 } = tensor;
793
794 match &kind {
795 DispatchTensorKind::Autodiff(inner_kind) => match &**inner_kind {
796 #[cfg(feature = "cuda")]
797 DispatchTensorKind::Cuda(tensor) => {
798 tensor.as_autodiff().node.distributed_params.is_some()
799 }
800 #[cfg(feature = "remote")]
801 DispatchTensorKind::Remote(tensor) => {
802 tensor.as_autodiff().node.distributed_params.is_some()
803 }
804
805 DispatchTensorKind::Autodiff(_) => {
806 panic!("Autodiff should not wrap an autodiff tensor.")
807 }
808 _ => false,
810 },
811 _ => panic!("Requires autodiff tensor."),
812 }
813 }
814}
815
816#[cfg(not(feature = "autodiff"))]
818impl AutodiffBackend for Dispatch {
819 type InnerBackend = Dispatch;
820
821 type Gradients = bool;
822
823 fn backward(_tensor: DispatchTensor) -> Self::Gradients {
824 unimplemented!("Requires `autodiff` feature")
825 }
826
827 fn grad(_tensor: &DispatchTensor, _grads: &Self::Gradients) -> Option<DispatchTensor> {
828 unimplemented!("Requires `autodiff` feature")
829 }
830
831 fn grad_remove(
832 _tensor: &DispatchTensor,
833 _grads: &mut Self::Gradients,
834 ) -> Option<DispatchTensor> {
835 unimplemented!("Requires `autodiff` feature")
836 }
837
838 fn grad_replace(_tensor: &DispatchTensor, _grads: &mut Self::Gradients, _grad: DispatchTensor) {
839 unimplemented!("Requires `autodiff` feature")
840 }
841
842 fn inner(_tensor: DispatchTensor) -> DispatchTensor {
843 unimplemented!("Requires `autodiff` feature")
844 }
845
846 fn int_inner(_tensor: DispatchTensor) -> DispatchTensor {
847 unimplemented!("Requires `autodiff` feature")
848 }
849
850 fn bool_inner(_tensor: DispatchTensor) -> DispatchTensor {
851 unimplemented!("Requires `autodiff` feature")
852 }
853
854 fn q_inner(_tensor: DispatchTensor) -> DispatchTensor {
855 unimplemented!("Requires `autodiff` feature")
856 }
857
858 fn from_inner(_tensor: DispatchTensor) -> DispatchTensor {
859 unimplemented!("Requires `autodiff` feature")
860 }
861
862 fn int_from_inner(_tensor: DispatchTensor) -> DispatchTensor {
863 unimplemented!("Requires `autodiff` feature")
864 }
865
866 fn bool_from_inner(_tensor: DispatchTensor) -> DispatchTensor {
867 unimplemented!("Requires `autodiff` feature")
868 }
869
870 fn q_from_inner(_tensor: DispatchTensor) -> DispatchTensor {
871 unimplemented!("Requires `autodiff` feature")
872 }
873}
874
875impl Dispatch {
876 pub fn enumerate(type_id: DispatchDeviceId) -> Vec<DispatchDevice> {
878 match type_id {
880 #[cfg(feature = "cpu")]
881 DispatchDeviceId::Cpu => vec![CpuDevice.into()],
882 #[cfg(feature = "cuda")]
883 DispatchDeviceId::Cuda => (0..Cuda::device_count(0))
884 .map(|i| CudaDevice::new(i).into())
885 .collect(),
886 #[cfg(feature = "metal")]
887 DispatchDeviceId::Metal => (0..Metal::device_count(0))
888 .map(|i| DispatchDevice::Metal(WgpuDevice::DiscreteGpu(i)))
889 .collect(),
890 #[cfg(feature = "rocm")]
891 DispatchDeviceId::Rocm => (0..Rocm::device_count(0))
892 .map(|i| RocmDevice::new(i).into())
893 .collect(),
894 #[cfg(feature = "vulkan")]
895 DispatchDeviceId::Vulkan => (0..Vulkan::device_count(0))
896 .map(|i| DispatchDevice::Vulkan(WgpuDevice::DiscreteGpu(i)))
897 .collect(),
898 #[cfg(feature = "wgpu")]
899 DispatchDeviceId::Wgpu => (0..Wgpu::device_count(0))
900 .map(|i| DispatchDevice::Wgpu(WgpuDevice::DiscreteGpu(i)))
901 .collect(),
902 #[cfg(feature = "webgpu")]
903 DispatchDeviceId::WebGpu => (0..WebGpu::device_count(0))
904 .map(|i| DispatchDevice::WebGpu(WgpuDevice::DiscreteGpu(i)))
905 .collect(),
906 #[cfg(any(feature = "flex", default_backend))]
907 DispatchDeviceId::Flex => vec![FlexDevice.into()],
908 #[cfg(feature = "ndarray")]
909 DispatchDeviceId::NdArray => vec![NdArrayDevice::Cpu.into()],
910 #[cfg(feature = "tch")]
911 DispatchDeviceId::LibTorch => (0..LibTorch::device_count(0))
912 .map(|i| LibTorchDevice::Cuda(i).into())
913 .collect(),
914 #[cfg(feature = "remote")]
915 DispatchDeviceId::Remote => Vec::new(),
919 _ => unreachable!("No backend feature enabled."),
920 }
921 }
922
923 #[cfg(feature = "remote-websocket")]
932 pub fn enumerate_remote_websocket(address: &str) -> Vec<DispatchDevice> {
933 RemoteDevice::enumerate_websocket(address)
934 .into_iter()
935 .map(DispatchDevice::Remote)
936 .collect()
937 }
938}