1use burn_backend::{DeviceId, DeviceOps, DeviceSettings};
2
3use crate::backends::*;
4
5#[cfg(feature = "autodiff")]
6use alloc::boxed::Box;
7
8#[cfg(feature = "cubecl")]
11use alloc::vec::Vec;
12#[cfg(feature = "cubecl")]
13use burn_backend::cubecl::{ThroughputKey, ThroughputValue};
14
15#[derive(Clone, Eq)]
31pub enum DispatchDevice {
32 #[cfg(feature = "cpu")]
34 Cpu(CpuDevice),
35
36 #[cfg(feature = "cuda")]
38 Cuda(CudaDevice),
39
40 #[cfg(feature = "metal")]
42 Metal(WgpuDevice),
43
44 #[cfg(feature = "rocm")]
46 Rocm(RocmDevice),
47
48 #[cfg(feature = "vulkan")]
50 Vulkan(WgpuDevice),
51
52 #[cfg(feature = "wgpu")]
54 Wgpu(WgpuDevice),
55
56 #[cfg(feature = "webgpu")]
58 WebGpu(WgpuDevice),
59
60 #[cfg(any(feature = "flex", default_backend))]
62 Flex(FlexDevice),
63
64 #[cfg(feature = "ndarray")]
66 NdArray(NdArrayDevice),
67
68 #[cfg(feature = "tch")]
70 LibTorch(LibTorchDevice),
71
72 #[cfg(feature = "remote")]
74 Remote(RemoteDevice),
75
76 #[cfg(feature = "autodiff")]
78 Autodiff(AutodiffDevice),
79}
80
81#[cfg(feature = "cubecl")]
82impl DispatchDevice {
83 pub fn performance_stats(&self, keys: &[ThroughputKey]) -> Vec<ThroughputValue> {
91 match self {
94 #[cfg(feature = "cpu")]
95 DispatchDevice::Cpu(device) => burn_cpu::device_throughput(device, keys),
96 #[cfg(feature = "cuda")]
97 DispatchDevice::Cuda(device) => burn_cuda::device_throughput(device, keys),
98 #[cfg(feature = "rocm")]
99 DispatchDevice::Rocm(device) => burn_rocm::device_throughput(device, keys),
100 #[cfg(feature = "wgpu")]
101 DispatchDevice::Wgpu(device) => burn_wgpu::device_throughput(device, keys),
102 #[cfg(feature = "vulkan")]
103 DispatchDevice::Vulkan(device) => burn_wgpu::device_throughput(device, keys),
104 #[cfg(feature = "metal")]
105 DispatchDevice::Metal(device) => burn_wgpu::device_throughput(device, keys),
106 #[cfg(feature = "webgpu")]
107 DispatchDevice::WebGpu(device) => burn_wgpu::device_throughput(device, keys),
108 #[cfg(feature = "autodiff")]
110 DispatchDevice::Autodiff(device) => device.performance_stats(keys),
111
112 #[cfg(any(feature = "flex", default_backend))]
114 DispatchDevice::Flex(_) => Vec::new(),
115 #[cfg(feature = "ndarray")]
116 DispatchDevice::NdArray(_) => Vec::new(),
117 #[cfg(feature = "tch")]
118 DispatchDevice::LibTorch(_) => Vec::new(),
119
120 #[cfg(feature = "remote")]
122 DispatchDevice::Remote(_) => Vec::new(),
123 }
124 }
125}
126
127#[cfg(feature = "autodiff")]
128#[derive(Debug, Clone, PartialEq, Eq)]
133pub struct AutodiffDevice {
134 pub(crate) inner: Box<DispatchDevice>,
135 pub(crate) checkpointing: CheckpointingStrategy,
136}
137
138#[cfg(feature = "autodiff")]
139impl AutodiffDevice {
140 pub(crate) fn new(device: DispatchDevice, checkpointing: CheckpointingStrategy) -> Self {
141 Self {
142 inner: Box::new(device),
143 checkpointing,
144 }
145 }
146
147 pub fn inner(self) -> DispatchDevice {
149 *self.inner
150 }
151}
152
153#[cfg(feature = "autodiff")]
154impl core::ops::Deref for AutodiffDevice {
156 type Target = DispatchDevice;
157
158 fn deref(&self) -> &Self::Target {
159 &self.inner
160 }
161}
162
163#[allow(missing_docs)]
164#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
165#[repr(u8)]
167pub enum CheckpointingStrategy {
168 Balanced,
169 #[default]
170 None,
171}
172
173#[cfg(feature = "autodiff")]
174pub(crate) fn validate_checkpointing(
175 lhs: Option<crate::CheckpointingStrategy>,
176 rhs: Option<crate::CheckpointingStrategy>,
177) -> Option<crate::CheckpointingStrategy> {
178 match (lhs, rhs) {
179 (Some(lhs), Some(rhs)) => {
180 assert_eq!(
181 lhs, rhs,
182 "Autodiff strategy mismatch: {lhs:?} vs {rhs:?}. Tensors in the same operation must share a strategy."
183 );
184 Some(lhs)
185 }
186 (None, None) => None,
187 (None, rhs) => rhs,
191 (lhs, None) => lhs,
192 }
193}
194
195impl core::fmt::Debug for DispatchDevice {
196 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
197 match self {
198 #[cfg(feature = "cpu")]
199 Self::Cpu(device) => f.debug_tuple("Cpu").field(device).finish(),
200 #[cfg(feature = "cuda")]
201 Self::Cuda(device) => f.debug_tuple("Cuda").field(device).finish(),
202 #[cfg(feature = "metal")]
203 Self::Metal(device) => f.debug_tuple("Metal").field(device).finish(),
204 #[cfg(feature = "rocm")]
205 Self::Rocm(device) => f.debug_tuple("Rocm").field(device).finish(),
206 #[cfg(feature = "vulkan")]
207 Self::Vulkan(device) => f.debug_tuple("Vulkan").field(device).finish(),
208 #[cfg(feature = "wgpu")]
209 Self::Wgpu(device) => f.debug_tuple("Wgpu").field(device).finish(),
210 #[cfg(feature = "webgpu")]
211 Self::WebGpu(device) => f.debug_tuple("WebGpu").field(device).finish(),
212 #[cfg(any(feature = "flex", default_backend))]
213 Self::Flex(device) => f.debug_tuple("Flex").field(device).finish(),
214 #[cfg(feature = "ndarray")]
215 Self::NdArray(device) => f.debug_tuple("NdArray").field(device).finish(),
216 #[cfg(feature = "tch")]
217 Self::LibTorch(device) => f.debug_tuple("LibTorch").field(device).finish(),
218 #[cfg(feature = "remote")]
219 Self::Remote(device) => f.debug_tuple("Remote").field(device).finish(),
220 #[cfg(feature = "autodiff")]
221 Self::Autodiff(device) => f.debug_tuple("Autodiff").field(&device.inner).finish(),
223 }
224 }
225}
226
227impl Default for DispatchDevice {
228 #[allow(unreachable_code)]
229 fn default() -> Self {
230 #[cfg(feature = "std")]
237 {
238 if let Ok(device_str) = std::env::var("BURN_DEVICE") {
239 match device_str.to_lowercase().as_str() {
240 "cuda" => {
241 #[cfg(feature = "cuda")]
242 return Self::Cuda(CudaDevice::default());
243 panic!(
244 "BURN_DEVICE=cuda requested, but the 'cuda' feature is not enabled."
245 );
246 }
247 "metal" => {
248 #[cfg(feature = "metal")]
249 return Self::Metal(burn_wgpu::WgpuDevice::default());
250 panic!(
251 "BURN_DEVICE=metal requested, but the 'metal' feature is not enabled."
252 );
253 }
254 "rocm" => {
255 #[cfg(feature = "rocm")]
256 return Self::Rocm(RocmDevice::default());
257 panic!(
258 "BURN_DEVICE=rocm requested, but the 'rocm' feature is not enabled."
259 );
260 }
261 "vulkan" => {
262 #[cfg(feature = "vulkan")]
263 return Self::Vulkan(burn_wgpu::WgpuDevice::default());
264 panic!(
265 "BURN_DEVICE=vulkan requested, but the 'vulkan' feature is not enabled."
266 );
267 }
268 "webgpu" => {
269 #[cfg(feature = "webgpu")]
270 return Self::WebGpu(burn_wgpu::WgpuDevice::default());
271 panic!(
272 "BURN_DEVICE=webgpu requested, but the 'webgpu' feature is not enabled."
273 );
274 }
275 "wgpu" => {
276 #[cfg(feature = "wgpu")]
277 return Self::Wgpu(burn_wgpu::WgpuDevice::default());
278 panic!(
279 "BURN_DEVICE=wgpu requested, but the 'wgpu' feature is not enabled."
280 );
281 }
282 "cpu" => {
283 #[cfg(feature = "cpu")]
284 return Self::Cpu(CpuDevice);
285 panic!("BURN_DEVICE=cpu requested, but the 'cpu' feature is not enabled.");
286 }
287 "tch" => {
288 #[cfg(feature = "tch")]
289 return Self::LibTorch(LibTorchDevice::default());
290 panic!("BURN_DEVICE=tch requested, but the 'tch' feature is not enabled.");
291 }
292 "remote" => {
293 #[cfg(feature = "remote")]
294 return Self::Remote(RemoteDevice::default());
295 panic!(
296 "BURN_DEVICE=remote requested, but the 'remote' feature is not enabled."
297 );
298 }
299 "flex" => {
300 #[cfg(any(feature = "flex", default_backend))]
301 return Self::Flex(FlexDevice);
302 panic!(
303 "BURN_DEVICE=flex requested, but the 'flex' feature is not enabled."
304 );
305 }
306 "ndarray" => {
307 #[cfg(feature = "ndarray")]
308 return Self::NdArray(NdArrayDevice::default());
309 panic!(
310 "BURN_DEVICE=ndarray requested, but the 'ndarray' feature is not enabled."
311 );
312 }
313 _ => panic!("Unknown BURN_DEVICE override: '{}'.", device_str),
314 }
315 }
316 }
317
318 #[cfg(feature = "cuda")]
319 return Self::Cuda(CudaDevice::default());
320
321 #[cfg(feature = "metal")]
322 return Self::Metal(burn_wgpu::WgpuDevice::default());
323
324 #[cfg(feature = "rocm")]
325 return Self::Rocm(RocmDevice::default());
326
327 #[cfg(feature = "vulkan")]
328 return Self::Vulkan(burn_wgpu::WgpuDevice::default());
329
330 #[cfg(feature = "webgpu")]
331 return Self::WebGpu(burn_wgpu::WgpuDevice::default());
332
333 #[cfg(feature = "wgpu")]
334 return Self::Wgpu(burn_wgpu::WgpuDevice::default());
335
336 #[cfg(feature = "cpu")]
337 return Self::Cpu(CpuDevice);
338
339 #[cfg(feature = "tch")]
340 return Self::LibTorch(LibTorchDevice::default());
341
342 #[cfg(any(feature = "flex", default_backend))]
345 return Self::Flex(FlexDevice);
346
347 #[cfg(feature = "remote")]
348 return Self::Remote(RemoteDevice::default());
349
350 #[cfg(feature = "ndarray")]
351 return Self::NdArray(NdArrayDevice::default());
352 }
353}
354
355impl PartialEq for DispatchDevice {
356 fn eq(&self, other: &Self) -> bool {
361 match (self, other) {
362 #[cfg(feature = "autodiff")]
364 (DispatchDevice::Autodiff(a), DispatchDevice::Autodiff(b)) => a == b,
365 #[cfg(feature = "autodiff")]
367 (DispatchDevice::Autodiff(a), b) => a.inner.as_ref() == b,
368 #[cfg(feature = "autodiff")]
369 (a, DispatchDevice::Autodiff(b)) => a == b.inner.as_ref(),
370 #[cfg(feature = "cpu")]
371 (Self::Cpu(a), Self::Cpu(b)) => a == b,
372 #[cfg(feature = "cuda")]
373 (Self::Cuda(a), Self::Cuda(b)) => a == b,
374 #[cfg(feature = "metal")]
375 (Self::Metal(a), Self::Metal(b)) => a == b,
376 #[cfg(feature = "rocm")]
377 (Self::Rocm(a), Self::Rocm(b)) => a == b,
378 #[cfg(feature = "vulkan")]
379 (Self::Vulkan(a), Self::Vulkan(b)) => a == b,
380 #[cfg(feature = "wgpu")]
381 (Self::Wgpu(a), Self::Wgpu(b)) => a == b,
382 #[cfg(feature = "webgpu")]
383 (Self::WebGpu(a), Self::WebGpu(b)) => a == b,
384 #[cfg(any(feature = "flex", default_backend))]
385 (Self::Flex(a), Self::Flex(b)) => a == b,
386 #[cfg(feature = "ndarray")]
387 (Self::NdArray(a), Self::NdArray(b)) => a == b,
388 #[cfg(feature = "tch")]
389 (Self::LibTorch(a), Self::LibTorch(b)) => a == b,
390 #[cfg(feature = "remote")]
391 (Self::Remote(a), Self::Remote(b)) => a == b,
392 #[allow(unreachable_patterns)]
393 (_, _) => false,
394 }
395 }
396}
397
398const INTERNAL_ID_MASK: u16 = 0x00FF;
399const BACKEND_SHIFT: u32 = 8;
400
401impl DispatchDevice {
402 #[cfg(feature = "autodiff")]
403 pub fn autodiff(device: impl Into<DispatchDevice>) -> DispatchDevice {
405 Self::autodiff_checkpointed(device, CheckpointingStrategy::None)
406 }
407 #[cfg(feature = "autodiff")]
408 pub fn autodiff_checkpointed(
410 device: impl Into<DispatchDevice>,
411 checkpointing: CheckpointingStrategy,
412 ) -> DispatchDevice {
413 let device = device.into();
414 DispatchDevice::Autodiff(AutodiffDevice::new(device, checkpointing))
415 }
416
417 pub fn inner(self) -> Self {
419 #[cfg(feature = "autodiff")]
420 if let DispatchDevice::Autodiff(device) = self {
421 return *device.inner;
422 }
423
424 self
425 }
426
427 fn backend_id(&self) -> DispatchDeviceId {
429 match self {
430 #[cfg(feature = "cpu")]
431 Self::Cpu(_) => DispatchDeviceId::Cpu,
432 #[cfg(feature = "cuda")]
433 Self::Cuda(_) => DispatchDeviceId::Cuda,
434 #[cfg(feature = "metal")]
435 Self::Metal(_) => DispatchDeviceId::Metal,
436 #[cfg(feature = "rocm")]
437 Self::Rocm(_) => DispatchDeviceId::Rocm,
438 #[cfg(feature = "vulkan")]
439 Self::Vulkan(_) => DispatchDeviceId::Vulkan,
440 #[cfg(feature = "wgpu")]
441 Self::Wgpu(_) => DispatchDeviceId::Wgpu,
442 #[cfg(feature = "webgpu")]
443 Self::WebGpu(_) => DispatchDeviceId::WebGpu,
444 #[cfg(any(feature = "flex", default_backend))]
445 Self::Flex(_) => DispatchDeviceId::Flex,
446 #[cfg(feature = "ndarray")]
447 Self::NdArray(_) => DispatchDeviceId::NdArray,
448 #[cfg(feature = "tch")]
449 Self::LibTorch(_) => DispatchDeviceId::LibTorch,
450 #[cfg(feature = "remote")]
451 Self::Remote(_) => DispatchDeviceId::Remote,
452 #[cfg(feature = "autodiff")]
453 Self::Autodiff(device) => device.inner.backend_id(),
454 }
455 }
456
457 fn encode_type_id(&self, backend_type_id: u16) -> u16 {
459 let internal_type_id = backend_type_id & INTERNAL_ID_MASK;
461 let backend = u16::from(self.backend_id()) << BACKEND_SHIFT;
463 backend | internal_type_id
464 }
465
466 pub(crate) fn decode_type_id(type_id: u16) -> (DispatchDeviceId, u16) {
468 let backend_raw = type_id >> BACKEND_SHIFT;
469 let internal_type_id = type_id & INTERNAL_ID_MASK;
470
471 let backend = DispatchDeviceId::try_from(backend_raw).expect("Unknown DispatchDevice ID");
472
473 (backend, internal_type_id)
474 }
475}
476
477#[allow(missing_docs)]
478#[derive(Debug, Clone, Copy, PartialEq, Eq)]
479#[repr(u16)]
480pub enum DispatchDeviceId {
481 Cpu = 0,
482 Cuda = 1,
483 Wgpu = 2,
484 Rocm = 3,
485 Flex = 4,
486 LibTorch = 5,
487 NdArray = 6,
488 Metal = 7,
489 Vulkan = 8,
490 WebGpu = 9,
491 Remote = 10,
492}
493
494impl From<DispatchDeviceId> for u16 {
495 fn from(variant: DispatchDeviceId) -> Self {
496 variant as u16
497 }
498}
499
500impl TryFrom<u16> for DispatchDeviceId {
501 type Error = ();
502
503 fn try_from(value: u16) -> Result<Self, Self::Error> {
504 match value {
505 #[cfg(feature = "cpu")]
506 0 => Ok(Self::Cpu),
507 #[cfg(feature = "cuda")]
508 1 => Ok(Self::Cuda),
509 #[cfg(feature = "wgpu")]
510 2 => Ok(Self::Wgpu),
511 #[cfg(feature = "rocm")]
512 3 => Ok(Self::Rocm),
513 #[cfg(any(feature = "flex", default_backend))]
514 4 => Ok(Self::Flex),
515 #[cfg(feature = "tch")]
516 5 => Ok(Self::LibTorch),
517 #[cfg(feature = "ndarray")]
518 6 => Ok(Self::NdArray),
519 #[cfg(feature = "metal")]
520 7 => Ok(Self::Metal),
521 #[cfg(feature = "vulkan")]
522 8 => Ok(Self::Vulkan),
523 #[cfg(feature = "webgpu")]
524 9 => Ok(Self::WebGpu),
525 #[cfg(feature = "remote")]
526 10 => Ok(Self::Remote),
527 _ => Err(()),
528 }
529 }
530}
531
532impl DeviceOps for DispatchDevice {
533 fn defaults(&self) -> DeviceSettings {
534 match self {
535 #[cfg(feature = "cpu")]
536 Self::Cpu(device) => device.defaults(),
537 #[cfg(feature = "cuda")]
538 Self::Cuda(device) => device.defaults(),
539 #[cfg(feature = "metal")]
540 Self::Metal(device) => device.defaults(),
541 #[cfg(feature = "rocm")]
542 Self::Rocm(device) => device.defaults(),
543 #[cfg(feature = "vulkan")]
544 Self::Vulkan(device) => device.defaults(),
545 #[cfg(feature = "wgpu")]
546 Self::Wgpu(device) => device.defaults(),
547 #[cfg(feature = "webgpu")]
548 Self::WebGpu(device) => device.defaults(),
549 #[cfg(any(feature = "flex", default_backend))]
550 Self::Flex(device) => device.defaults(),
551 #[cfg(feature = "ndarray")]
552 Self::NdArray(device) => device.defaults(),
553 #[cfg(feature = "tch")]
554 Self::LibTorch(device) => device.defaults(),
555 #[cfg(feature = "remote")]
556 Self::Remote(device) => device.defaults(),
557 #[cfg(feature = "autodiff")]
558 Self::Autodiff(device) => device.inner.defaults(),
559 }
560 }
561}
562
563impl burn_backend::Device for DispatchDevice {
564 fn from_id(mut device_id: DeviceId) -> Self {
565 let (dispatch_id, backend_type_id) = Self::decode_type_id(device_id.type_id);
566 device_id.type_id = backend_type_id;
567
568 match dispatch_id {
569 #[cfg(feature = "cpu")]
570 DispatchDeviceId::Cpu => Self::Cpu(CpuDevice::from_id(device_id)),
571 #[cfg(feature = "cuda")]
572 DispatchDeviceId::Cuda => Self::Cuda(CudaDevice::from_id(device_id)),
573 #[cfg(feature = "metal")]
574 DispatchDeviceId::Metal => Self::Metal(WgpuDevice::from_id(device_id)),
575 #[cfg(feature = "rocm")]
576 DispatchDeviceId::Rocm => Self::Rocm(RocmDevice::from_id(device_id)),
577 #[cfg(feature = "vulkan")]
578 DispatchDeviceId::Vulkan => Self::Vulkan(WgpuDevice::from_id(device_id)),
579 #[cfg(feature = "wgpu")]
580 DispatchDeviceId::Wgpu => Self::Wgpu(WgpuDevice::from_id(device_id)),
581 #[cfg(feature = "webgpu")]
582 DispatchDeviceId::WebGpu => Self::WebGpu(WgpuDevice::from_id(device_id)),
583 #[cfg(any(feature = "flex", default_backend))]
584 DispatchDeviceId::Flex => Self::Flex(FlexDevice::from_id(device_id)),
585 #[cfg(feature = "ndarray")]
586 DispatchDeviceId::NdArray => Self::NdArray(NdArrayDevice::from_id(device_id)),
587 #[cfg(feature = "tch")]
588 DispatchDeviceId::LibTorch => Self::LibTorch(LibTorchDevice::from_id(device_id)),
589 #[cfg(feature = "remote")]
590 DispatchDeviceId::Remote => Self::Remote(RemoteDevice::from_id(device_id)),
591 _ => unreachable!("No backend feature enabled."),
592 }
593 }
594
595 fn to_id(&self) -> DeviceId {
596 let mut device_id = match self {
597 #[cfg(feature = "cpu")]
598 Self::Cpu(device) => device.to_id(),
599 #[cfg(feature = "cuda")]
600 Self::Cuda(device) => device.to_id(),
601 #[cfg(feature = "metal")]
602 Self::Metal(device) => device.to_id(),
603 #[cfg(feature = "rocm")]
604 Self::Rocm(device) => device.to_id(),
605 #[cfg(feature = "vulkan")]
606 Self::Vulkan(device) => device.to_id(),
607 #[cfg(feature = "wgpu")]
608 Self::Wgpu(device) => device.to_id(),
609 #[cfg(feature = "webgpu")]
610 Self::WebGpu(device) => device.to_id(),
611 #[cfg(any(feature = "flex", default_backend))]
612 Self::Flex(device) => device.to_id(),
613 #[cfg(feature = "ndarray")]
614 Self::NdArray(device) => device.to_id(),
615 #[cfg(feature = "tch")]
616 Self::LibTorch(device) => device.to_id(),
617 #[cfg(feature = "remote")]
618 Self::Remote(device) => device.to_id(),
619 #[cfg(feature = "autodiff")]
620 Self::Autodiff(device) => device.inner.to_id(),
621 };
622 device_id.type_id = self.encode_type_id(device_id.type_id);
623 device_id
624 }
625}
626
627#[cfg(feature = "cpu")]
628impl From<CpuDevice> for DispatchDevice {
629 fn from(device: CpuDevice) -> Self {
630 DispatchDevice::Cpu(device)
631 }
632}
633
634#[cfg(feature = "cuda")]
635impl From<CudaDevice> for DispatchDevice {
636 fn from(device: CudaDevice) -> Self {
637 DispatchDevice::Cuda(device)
638 }
639}
640
641#[cfg(feature = "rocm")]
642impl From<RocmDevice> for DispatchDevice {
643 fn from(device: RocmDevice) -> Self {
644 DispatchDevice::Rocm(device)
645 }
646}
647
648#[cfg(all(
655 feature = "wgpu",
656 not(any(feature = "metal", feature = "vulkan", feature = "webgpu"))
657))]
658impl From<WgpuDevice> for DispatchDevice {
659 fn from(device: WgpuDevice) -> Self {
660 DispatchDevice::Wgpu(device)
661 }
662}
663
664#[cfg(feature = "metal")]
665impl From<WgpuDevice> for DispatchDevice {
666 fn from(device: WgpuDevice) -> Self {
667 DispatchDevice::Metal(device)
668 }
669}
670
671#[cfg(all(feature = "vulkan", not(feature = "metal")))]
672impl From<WgpuDevice> for DispatchDevice {
673 fn from(device: WgpuDevice) -> Self {
674 DispatchDevice::Vulkan(device)
675 }
676}
677
678#[cfg(all(feature = "webgpu", not(any(feature = "metal", feature = "vulkan"))))]
679impl From<WgpuDevice> for DispatchDevice {
680 fn from(device: WgpuDevice) -> Self {
681 DispatchDevice::WebGpu(device)
682 }
683}
684
685#[cfg(any(feature = "flex", default_backend))]
686impl From<FlexDevice> for DispatchDevice {
687 fn from(device: FlexDevice) -> Self {
688 DispatchDevice::Flex(device)
689 }
690}
691
692#[cfg(feature = "ndarray")]
693impl From<NdArrayDevice> for DispatchDevice {
694 fn from(device: NdArrayDevice) -> Self {
695 DispatchDevice::NdArray(device)
696 }
697}
698
699#[cfg(feature = "tch")]
700impl From<LibTorchDevice> for DispatchDevice {
701 fn from(device: LibTorchDevice) -> Self {
702 DispatchDevice::LibTorch(device)
703 }
704}
705
706#[cfg(feature = "remote")]
707impl From<RemoteDevice> for DispatchDevice {
708 fn from(device: RemoteDevice) -> Self {
709 DispatchDevice::Remote(device)
710 }
711}