1use crate::backend::BackendStorage;
2use crate::custom_op::{all_distinct, InplaceOpN, Src};
3use crate::op::{self, CmpOp, ReduceOp};
4use crate::scalar::Scalar;
5use crate::{CpuStorage, CudaStorage, DType, Device, Error, Layout, MetalStorage, Result, Shape};
6use crate::{CustomOp1, CustomOp2, CustomOp3};
7use parking_lot::{RwLockReadGuard, RwLockWriteGuard};
8
9#[derive(Debug)]
12pub enum Storage {
13 Cpu(CpuStorage),
14 Cuda(CudaStorage),
15 Metal(MetalStorage),
16}
17
18pub type StorageRef<'a> = RwLockReadGuard<'a, Storage>;
19pub type StorageMutRef<'a> = RwLockWriteGuard<'a, Storage>;
20
21impl Storage {
22 pub fn try_clone(&self, layout: &Layout) -> Result<Self> {
23 match self {
24 Self::Cpu(storage) => Ok(Self::Cpu(storage.clone())),
25 Self::Cuda(storage) => {
26 let storage = storage.try_clone(layout)?;
27 Ok(Self::Cuda(storage))
28 }
29 Self::Metal(storage) => {
30 let storage = storage.try_clone(layout)?;
31 Ok(Self::Metal(storage))
32 }
33 }
34 }
35
36 pub fn device(&self) -> Device {
37 match self {
38 Self::Cpu(_) => Device::Cpu,
39 Self::Cuda(storage) => Device::Cuda(storage.device().clone()),
40 Self::Metal(storage) => Device::Metal(storage.device().clone()),
41 }
42 }
43
44 pub fn dtype(&self) -> DType {
45 match self {
46 Self::Cpu(storage) => storage.dtype(),
47 Self::Cuda(storage) => storage.dtype(),
48 Self::Metal(storage) => storage.dtype(),
49 }
50 }
51
52 pub(crate) fn same_device(&self, rhs: &Self, op: &'static str) -> Result<()> {
53 let lhs_device = self.device();
54 let rhs_device = rhs.device();
55 let lhs = lhs_device.location();
56 let rhs = rhs_device.location();
57 let same_device = if self.device().is_metal() {
58 lhs_device.same_device(&rhs_device)
62 } else {
63 lhs == rhs
64 };
65 if !same_device {
66 Err(Error::DeviceMismatchBinaryOp { lhs, rhs, op }.bt())
67 } else {
68 Ok(())
69 }
70 }
71
72 pub(crate) fn same_dtype(&self, rhs: &Self, op: &'static str) -> Result<()> {
73 let lhs = self.dtype();
74 let rhs = rhs.dtype();
75 if lhs != rhs {
76 Err(Error::DTypeMismatchBinaryOp { lhs, rhs, op }.bt())
77 } else {
78 Ok(())
79 }
80 }
81
82 pub(crate) fn const_set(&mut self, v: Scalar, l: &Layout) -> Result<()> {
83 match self {
84 Storage::Cpu(storage) => storage.const_set(v, l),
85 Storage::Cuda(storage) => storage.const_set(v, l),
86 Storage::Metal(storage) => storage.const_set(v, l),
87 }
88 }
89
90 pub(crate) fn affine(&self, layout: &Layout, mul: f64, add: f64) -> Result<Self> {
91 match self {
92 Storage::Cpu(storage) => {
93 let storage = storage.affine(layout, mul, add)?;
94 Ok(Self::Cpu(storage))
95 }
96 Self::Cuda(storage) => {
97 let storage = storage.affine(layout, mul, add)?;
98 Ok(Self::Cuda(storage))
99 }
100 Self::Metal(storage) => {
101 let storage = storage.affine(layout, mul, add)?;
102 Ok(Self::Metal(storage))
103 }
104 }
105 }
106
107 pub(crate) fn powf(&self, layout: &Layout, alpha: f64) -> Result<Self> {
108 match self {
109 Storage::Cpu(storage) => {
110 let storage = storage.powf(layout, alpha)?;
111 Ok(Self::Cpu(storage))
112 }
113 Self::Cuda(storage) => {
114 let storage = storage.powf(layout, alpha)?;
115 Ok(Self::Cuda(storage))
116 }
117 Self::Metal(storage) => {
118 let storage = storage.powf(layout, alpha)?;
119 Ok(Self::Metal(storage))
120 }
121 }
122 }
123
124 pub(crate) fn elu(&self, layout: &Layout, alpha: f64) -> Result<Self> {
125 match self {
126 Storage::Cpu(storage) => {
127 let storage = storage.elu(layout, alpha)?;
128 Ok(Self::Cpu(storage))
129 }
130 Self::Cuda(storage) => {
131 let storage = storage.elu(layout, alpha)?;
132 Ok(Self::Cuda(storage))
133 }
134 Self::Metal(storage) => {
135 let storage = storage.elu(layout, alpha)?;
136 Ok(Self::Metal(storage))
137 }
138 }
139 }
140
141 pub(crate) fn cmp(
142 &self,
143 op: CmpOp,
144 rhs: &Self,
145 lhs_layout: &Layout,
146 rhs_layout: &Layout,
147 ) -> Result<Self> {
148 self.same_device(rhs, "cmp")?;
149 self.same_dtype(rhs, "cmp")?;
150 match (self, rhs) {
151 (Storage::Cpu(lhs), Storage::Cpu(rhs)) => {
152 let storage = lhs.cmp(op, rhs, lhs_layout, rhs_layout)?;
153 Ok(Self::Cpu(storage))
154 }
155 (Self::Cuda(lhs), Self::Cuda(rhs)) => {
156 let storage = lhs.cmp(op, rhs, lhs_layout, rhs_layout)?;
157 Ok(Self::Cuda(storage))
158 }
159 (Self::Metal(lhs), Self::Metal(rhs)) => {
160 let storage = lhs.cmp(op, rhs, lhs_layout, rhs_layout)?;
161 Ok(Self::Metal(storage))
162 }
163 (lhs, rhs) => {
164 Err(Error::DeviceMismatchBinaryOp {
167 lhs: lhs.device().location(),
168 rhs: rhs.device().location(),
169 op: "cmp",
170 }
171 .bt())
172 }
173 }
174 }
175
176 pub(crate) fn reduce_op(&self, op: ReduceOp, layout: &Layout, s: &[usize]) -> Result<Self> {
177 match self {
178 Storage::Cpu(storage) => {
179 let storage = storage.reduce_op(op, layout, s)?;
180 Ok(Self::Cpu(storage))
181 }
182 Self::Cuda(storage) => {
183 let storage = storage.reduce_op(op, layout, s)?;
184 Ok(Self::Cuda(storage))
185 }
186 Self::Metal(storage) => {
187 let storage = storage.reduce_op(op, layout, s)?;
188 Ok(Self::Metal(storage))
189 }
190 }
191 }
192
193 pub(crate) fn to_dtype(&self, layout: &Layout, dtype: DType) -> Result<Self> {
194 match self {
195 Storage::Cpu(storage) => {
196 let storage = storage.to_dtype(layout, dtype)?;
197 Ok(Self::Cpu(storage))
198 }
199 Self::Cuda(storage) => {
200 let storage = storage.to_dtype(layout, dtype)?;
201 Ok(Self::Cuda(storage))
202 }
203 Self::Metal(storage) => {
204 let storage = storage.to_dtype(layout, dtype)?;
205 Ok(Self::Metal(storage))
206 }
207 }
208 }
209
210 pub(crate) fn apply_op1(&self, l: &Layout, c: &dyn CustomOp1) -> Result<(Self, Shape)> {
211 match self {
212 Self::Cpu(storage) => {
213 let (storage, shape) = c.cpu_fwd(storage, l)?;
214 Ok((Self::Cpu(storage), shape))
215 }
216 Self::Cuda(storage) => {
217 let (storage, shape) = c.cuda_fwd(storage, l)?;
218 Ok((Self::Cuda(storage), shape))
219 }
220 Self::Metal(storage) => {
221 let (storage, shape) = c.metal_fwd(storage, l)?;
222 Ok((Self::Metal(storage), shape))
223 }
224 }
225 }
226
227 pub(crate) fn apply_op2(
228 &self,
229 l1: &Layout,
230 t2: &Self,
231 l2: &Layout,
232 c: &dyn CustomOp2,
233 ) -> Result<(Self, Shape)> {
234 self.same_device(t2, c.name())?;
235 match (self, t2) {
236 (Self::Cpu(s1), Self::Cpu(s2)) => {
237 let (s, shape) = c.cpu_fwd(s1, l1, s2, l2)?;
238 Ok((Self::Cpu(s), shape))
239 }
240 (Self::Cuda(s1), Self::Cuda(s2)) => {
241 let (s, shape) = c.cuda_fwd(s1, l1, s2, l2)?;
242 Ok((Self::Cuda(s), shape))
243 }
244 (Self::Metal(s1), Self::Metal(s2)) => {
245 let (s, shape) = c.metal_fwd(s1, l1, s2, l2)?;
246 Ok((Self::Metal(s), shape))
247 }
248 _ => unreachable!(),
249 }
250 }
251
252 pub(crate) fn apply_op3(
253 &self,
254 l1: &Layout,
255 t2: &Self,
256 l2: &Layout,
257 t3: &Self,
258 l3: &Layout,
259 c: &dyn CustomOp3,
260 ) -> Result<(Self, Shape)> {
261 self.same_device(t2, c.name())?;
262 self.same_device(t3, c.name())?;
263 match (self, t2, t3) {
264 (Self::Cpu(s1), Self::Cpu(s2), Self::Cpu(s3)) => {
265 let (s, shape) = c.cpu_fwd(s1, l1, s2, l2, s3, l3)?;
266 Ok((Self::Cpu(s), shape))
267 }
268 (Self::Cuda(s1), Self::Cuda(s2), Self::Cuda(s3)) => {
269 let (s, shape) = c.cuda_fwd(s1, l1, s2, l2, s3, l3)?;
270 Ok((Self::Cuda(s), shape))
271 }
272 (Self::Metal(s1), Self::Metal(s2), Self::Metal(s3)) => {
273 let (s, shape) = c.metal_fwd(s1, l1, s2, l2, s3, l3)?;
274 Ok((Self::Metal(s), shape))
275 }
276 _ => unreachable!(),
277 }
278 }
279
280 pub(crate) fn inplace_op<const N: usize, C: InplaceOpN<N>>(
286 &mut self,
287 dst_l: &Layout,
288 srcs: [(Src<'_, Storage>, &Layout); N],
289 c: &C,
290 ) -> Result<()> {
291 for (s, _) in srcs.iter() {
293 if let Src::Distinct(s) = s {
294 self.same_device(s, c.name())?;
295 }
296 }
297 macro_rules! inplace_dispatch {
298 ($dst:expr, $variant:ident, $fwd:ident, $fwd_aliased:ident) => {{
299 let operands: [(Src<'_, _>, &Layout); N] = std::array::from_fn(|i| {
301 let (s, l) = srcs[i];
302 let s = match s {
303 Src::Distinct(Storage::$variant(s)) => Src::Distinct(s),
304 Src::Aliased(rel) => Src::Aliased(rel),
305 Src::Distinct(_) => {
306 unreachable!("same_device above rejects mismatched backends")
307 }
308 };
309 (s, l)
310 });
311
312 match all_distinct(&operands) {
313 Some(distinct) => c.$fwd($dst, dst_l, distinct),
314 None => c.$fwd_aliased($dst, dst_l, operands),
315 }
316 }};
317 }
318
319 match self {
320 Storage::Cpu(dst) => inplace_dispatch!(dst, Cpu, cpu_fwd, cpu_fwd_aliased),
321 Storage::Cuda(dst) => inplace_dispatch!(dst, Cuda, cuda_fwd, cuda_fwd_aliased),
322 Storage::Metal(dst) => inplace_dispatch!(dst, Metal, metal_fwd, metal_fwd_aliased),
323 }
324 }
325
326 pub(crate) fn unary_impl<B: op::UnaryOpT>(&self, layout: &Layout) -> Result<Self> {
327 match self {
328 Storage::Cpu(storage) => {
329 let storage = storage.unary_impl::<B>(layout)?;
330 Ok(Self::Cpu(storage))
331 }
332 Self::Cuda(storage) => {
333 let storage = storage.unary_impl::<B>(layout)?;
334 Ok(Self::Cuda(storage))
335 }
336 Self::Metal(storage) => {
337 let storage = storage.unary_impl::<B>(layout)?;
338 Ok(Self::Metal(storage))
339 }
340 }
341 }
342
343 pub(crate) fn binary_impl<B: op::BinaryOpT>(
344 &self,
345 rhs: &Self,
346 lhs_layout: &Layout,
347 rhs_layout: &Layout,
348 ) -> Result<Self> {
349 self.same_device(rhs, B::NAME)?;
350 self.same_dtype(rhs, B::NAME)?;
351 match (self, rhs) {
352 (Storage::Cpu(lhs), Storage::Cpu(rhs)) => {
353 let storage = lhs.binary_impl::<B>(rhs, lhs_layout, rhs_layout)?;
354 Ok(Self::Cpu(storage))
355 }
356 (Self::Cuda(lhs), Self::Cuda(rhs)) => {
357 let storage = lhs.binary_impl::<B>(rhs, lhs_layout, rhs_layout)?;
358 Ok(Self::Cuda(storage))
359 }
360 (Self::Metal(lhs), Self::Metal(rhs)) => {
361 let storage = lhs.binary_impl::<B>(rhs, lhs_layout, rhs_layout)?;
362 Ok(Self::Metal(storage))
363 }
364 (lhs, rhs) => {
365 Err(Error::DeviceMismatchBinaryOp {
368 lhs: lhs.device().location(),
369 rhs: rhs.device().location(),
370 op: B::NAME,
371 }
372 .bt())
373 }
374 }
375 }
376
377 pub(crate) fn conv1d(
378 &self,
379 l: &Layout,
380 kernel: &Self,
381 kernel_l: &Layout,
382 params: &crate::conv::ParamsConv1D,
383 ) -> Result<Self> {
384 self.same_device(kernel, "conv1d")?;
385 self.same_dtype(kernel, "conv1d")?;
386 match (self, &kernel) {
387 (Storage::Cpu(inp), Storage::Cpu(kernel)) => {
388 let s = inp.conv1d(l, kernel, kernel_l, params)?;
389 Ok(Self::Cpu(s))
390 }
391 (Storage::Cuda(inp), Storage::Cuda(kernel)) => {
392 let s = inp.conv1d(l, kernel, kernel_l, params)?;
393 Ok(Self::Cuda(s))
394 }
395 (Storage::Metal(inp), Storage::Metal(kernel)) => {
396 let s = inp.conv1d(l, kernel, kernel_l, params)?;
397 Ok(Self::Metal(s))
398 }
399 (lhs, rhs) => Err(Error::DeviceMismatchBinaryOp {
400 lhs: lhs.device().location(),
401 rhs: rhs.device().location(),
402 op: "conv1d",
403 }
404 .bt()),
405 }
406 }
407
408 pub(crate) fn conv_transpose1d(
409 &self,
410 l: &Layout,
411 kernel: &Self,
412 kernel_l: &Layout,
413 params: &crate::conv::ParamsConvTranspose1D,
414 ) -> Result<Self> {
415 self.same_device(kernel, "conv-transpose1d")?;
416 self.same_dtype(kernel, "conv-transpose1d")?;
417 match (self, &kernel) {
418 (Storage::Cpu(inp), Storage::Cpu(kernel)) => {
419 let s = inp.conv_transpose1d(l, kernel, kernel_l, params)?;
420 Ok(Self::Cpu(s))
421 }
422 (Storage::Cuda(inp), Storage::Cuda(kernel)) => {
423 let s = inp.conv_transpose1d(l, kernel, kernel_l, params)?;
424 Ok(Self::Cuda(s))
425 }
426 (Storage::Metal(inp), Storage::Metal(kernel)) => {
427 let s = inp.conv_transpose1d(l, kernel, kernel_l, params)?;
428 Ok(Self::Metal(s))
429 }
430 (lhs, rhs) => Err(Error::DeviceMismatchBinaryOp {
431 lhs: lhs.device().location(),
432 rhs: rhs.device().location(),
433 op: "conv-transpose1d",
434 }
435 .bt()),
436 }
437 }
438
439 pub(crate) fn conv2d(
440 &self,
441 l: &Layout,
442 kernel: &Self,
443 kernel_l: &Layout,
444 params: &crate::conv::ParamsConv2D,
445 ) -> Result<Self> {
446 self.same_device(kernel, "conv2d")?;
447 self.same_dtype(kernel, "conv2d")?;
448 match (self, &kernel) {
449 (Storage::Cpu(inp), Storage::Cpu(kernel)) => {
450 let s = inp.conv2d(l, kernel, kernel_l, params)?;
451 Ok(Self::Cpu(s))
452 }
453 (Storage::Cuda(inp), Storage::Cuda(kernel)) => {
454 let s = inp.conv2d(l, kernel, kernel_l, params)?;
455 Ok(Self::Cuda(s))
456 }
457 (Storage::Metal(inp), Storage::Metal(kernel)) => {
458 let s = inp.conv2d(l, kernel, kernel_l, params)?;
459 Ok(Self::Metal(s))
460 }
461 (lhs, rhs) => Err(Error::DeviceMismatchBinaryOp {
462 lhs: lhs.device().location(),
463 rhs: rhs.device().location(),
464 op: "conv2d",
465 }
466 .bt()),
467 }
468 }
469
470 pub(crate) fn conv_transpose2d(
471 &self,
472 l: &Layout,
473 kernel: &Self,
474 kernel_l: &Layout,
475 params: &crate::conv::ParamsConvTranspose2D,
476 ) -> Result<Self> {
477 self.same_device(kernel, "conv_transpose2d")?;
478 self.same_dtype(kernel, "conv_transpose2d")?;
479 match (self, &kernel) {
480 (Storage::Cpu(inp), Storage::Cpu(kernel)) => {
481 let s = inp.conv_transpose2d(l, kernel, kernel_l, params)?;
482 Ok(Self::Cpu(s))
483 }
484 (Storage::Cuda(inp), Storage::Cuda(kernel)) => {
485 let s = inp.conv_transpose2d(l, kernel, kernel_l, params)?;
486 Ok(Self::Cuda(s))
487 }
488 (Storage::Metal(inp), Storage::Metal(kernel)) => {
489 let s = inp.conv_transpose2d(l, kernel, kernel_l, params)?;
490 Ok(Self::Metal(s))
491 }
492 (lhs, rhs) => Err(Error::DeviceMismatchBinaryOp {
493 lhs: lhs.device().location(),
494 rhs: rhs.device().location(),
495 op: "conv_transpose2d",
496 }
497 .bt()),
498 }
499 }
500
501 pub(crate) fn avg_pool2d(
502 &self,
503 layout: &Layout,
504 kernel_size: (usize, usize),
505 stride: (usize, usize),
506 ) -> Result<Self> {
507 match self {
508 Storage::Cpu(storage) => {
509 let storage = storage.avg_pool2d(layout, kernel_size, stride)?;
510 Ok(Self::Cpu(storage))
511 }
512 Self::Cuda(storage) => {
513 let storage = storage.avg_pool2d(layout, kernel_size, stride)?;
514 Ok(Self::Cuda(storage))
515 }
516 Self::Metal(storage) => {
517 let storage = storage.avg_pool2d(layout, kernel_size, stride)?;
518 Ok(Self::Metal(storage))
519 }
520 }
521 }
522
523 pub(crate) fn max_pool2d(
524 &self,
525 layout: &Layout,
526 kernel_size: (usize, usize),
527 stride: (usize, usize),
528 ) -> Result<Self> {
529 match self {
530 Storage::Cpu(storage) => {
531 let storage = storage.max_pool2d(layout, kernel_size, stride)?;
532 Ok(Self::Cpu(storage))
533 }
534 Self::Cuda(storage) => {
535 let storage = storage.max_pool2d(layout, kernel_size, stride)?;
536 Ok(Self::Cuda(storage))
537 }
538 Self::Metal(storage) => {
539 let storage = storage.max_pool2d(layout, kernel_size, stride)?;
540 Ok(Self::Metal(storage))
541 }
542 }
543 }
544
545 pub(crate) fn upsample_nearest1d(&self, layout: &Layout, sz: usize) -> Result<Self> {
546 match self {
547 Storage::Cpu(storage) => {
548 let storage = storage.upsample_nearest1d(layout, sz)?;
549 Ok(Self::Cpu(storage))
550 }
551 Self::Cuda(storage) => {
552 let storage = storage.upsample_nearest1d(layout, sz)?;
553 Ok(Self::Cuda(storage))
554 }
555 Self::Metal(storage) => {
556 let storage = storage.upsample_nearest1d(layout, sz)?;
557 Ok(Self::Metal(storage))
558 }
559 }
560 }
561
562 pub(crate) fn upsample_nearest2d(&self, layout: &Layout, h: usize, w: usize) -> Result<Self> {
563 match self {
564 Storage::Cpu(storage) => {
565 let storage = storage.upsample_nearest2d(layout, h, w)?;
566 Ok(Self::Cpu(storage))
567 }
568 Self::Cuda(storage) => {
569 let storage = storage.upsample_nearest2d(layout, h, w)?;
570 Ok(Self::Cuda(storage))
571 }
572 Self::Metal(storage) => {
573 let storage = storage.upsample_nearest2d(layout, h, w)?;
574 Ok(Self::Metal(storage))
575 }
576 }
577 }
578
579 pub(crate) fn upsample_bilinear2d(
580 &self,
581 layout: &Layout,
582 h: usize,
583 w: usize,
584 align_corners: bool,
585 scale_h: Option<f64>,
586 scale_w: Option<f64>,
587 ) -> Result<Self> {
588 match self {
589 Storage::Cpu(storage) => {
590 let storage =
591 storage.upsample_bilinear2d(layout, h, w, align_corners, scale_h, scale_w)?;
592 Ok(Self::Cpu(storage))
593 }
594 Self::Cuda(storage) => {
595 let storage =
596 storage.upsample_bilinear2d(layout, h, w, align_corners, scale_h, scale_w)?;
597 Ok(Self::Cuda(storage))
598 }
599 Self::Metal(storage) => {
600 let storage =
601 storage.upsample_bilinear2d(layout, h, w, align_corners, scale_h, scale_w)?;
602 Ok(Self::Metal(storage))
603 }
604 }
605 }
606
607 pub(crate) fn where_cond(
608 &self,
609 layout: &Layout,
610 t: &Self,
611 layout_t: &Layout,
612 f: &Self,
613 layout_f: &Layout,
614 ) -> Result<Self> {
615 self.same_device(t, "where")?;
616 self.same_device(f, "where")?;
617 t.same_dtype(f, "where")?;
618 match (self, t, f) {
619 (Storage::Cpu(cond), Storage::Cpu(t), Storage::Cpu(f)) => {
620 let storage = cond.where_cond(layout, t, layout_t, f, layout_f)?;
621 Ok(Self::Cpu(storage))
622 }
623 (Self::Cuda(cond), Self::Cuda(t), Self::Cuda(f)) => {
624 let storage = cond.where_cond(layout, t, layout_t, f, layout_f)?;
625 Ok(Self::Cuda(storage))
626 }
627 (Self::Metal(cond), Self::Metal(t), Self::Metal(f)) => {
628 let storage = cond.where_cond(layout, t, layout_t, f, layout_f)?;
629 Ok(Self::Metal(storage))
630 }
631 (_, lhs, rhs) => Err(Error::DeviceMismatchBinaryOp {
632 lhs: lhs.device().location(),
633 rhs: rhs.device().location(),
634 op: "where",
635 }
636 .bt()),
637 }
638 }
639
640 pub(crate) fn gather(
641 &self,
642 l: &Layout,
643 indexes: &Self,
644 indexes_l: &Layout,
645 d: usize,
646 ) -> Result<Self> {
647 self.same_device(indexes, "index-add")?;
648 match (self, indexes) {
649 (Self::Cpu(s), Self::Cpu(indexes)) => {
650 let storage = s.gather(l, indexes, indexes_l, d)?;
651 Ok(Self::Cpu(storage))
652 }
653 (Self::Cuda(s), Self::Cuda(indexes)) => {
654 let storage = s.gather(l, indexes, indexes_l, d)?;
655 Ok(Self::Cuda(storage))
656 }
657 (Self::Metal(s), Self::Metal(indexes)) => {
658 let storage = s.gather(l, indexes, indexes_l, d)?;
659 Ok(Self::Metal(storage))
660 }
661 _ => unreachable!(),
662 }
663 }
664
665 pub(crate) fn scatter_set(
666 &mut self,
667 l: &Layout,
668 indexes: &Self,
669 indexes_l: &Layout,
670 source: &Self,
671 source_l: &Layout,
672 d: usize,
673 ) -> Result<()> {
674 self.same_device(indexes, "scatter-set")?;
675 self.same_device(source, "scatter-set")?;
676 match (self, indexes, source) {
677 (Self::Cpu(s), Self::Cpu(indexes), Self::Cpu(source)) => {
678 s.scatter_set(l, indexes, indexes_l, source, source_l, d)?;
679 }
680 (Self::Cuda(s), Self::Cuda(indexes), Self::Cuda(source)) => {
681 s.scatter_set(l, indexes, indexes_l, source, source_l, d)?;
682 }
683 (Self::Metal(s), Self::Metal(indexes), Self::Metal(source)) => {
684 s.scatter_set(l, indexes, indexes_l, source, source_l, d)?;
685 }
686 _ => unreachable!(),
687 }
688 Ok(())
689 }
690
691 pub(crate) fn scatter_add(
692 &mut self,
693 l: &Layout,
694 indexes: &Self,
695 indexes_l: &Layout,
696 source: &Self,
697 source_l: &Layout,
698 d: usize,
699 ) -> Result<()> {
700 self.same_device(indexes, "scatter-add")?;
701 self.same_device(source, "scatter-add")?;
702 match (self, indexes, source) {
703 (Self::Cpu(s), Self::Cpu(indexes), Self::Cpu(source)) => {
704 s.scatter_add_set(l, indexes, indexes_l, source, source_l, d)?;
705 }
706 (Self::Cuda(s), Self::Cuda(indexes), Self::Cuda(source)) => {
707 s.scatter_add_set(l, indexes, indexes_l, source, source_l, d)?;
708 }
709 (Self::Metal(s), Self::Metal(indexes), Self::Metal(source)) => {
710 s.scatter_add_set(l, indexes, indexes_l, source, source_l, d)?;
711 }
712 _ => unreachable!(),
713 }
714 Ok(())
715 }
716
717 pub(crate) fn index_add(
718 &self,
719 l: &Layout,
720 indexes: &Self,
721 indexes_l: &Layout,
722 source: &Self,
723 source_l: &Layout,
724 d: usize,
725 ) -> Result<Self> {
726 self.same_device(indexes, "index-add")?;
727 self.same_device(source, "index-add")?;
728 match (self, indexes, source) {
729 (Self::Cpu(s), Self::Cpu(indexes), Self::Cpu(source)) => {
730 let storage = s.index_add(l, indexes, indexes_l, source, source_l, d)?;
731 Ok(Self::Cpu(storage))
732 }
733 (Self::Cuda(s), Self::Cuda(indexes), Self::Cuda(source)) => {
734 let storage = s.index_add(l, indexes, indexes_l, source, source_l, d)?;
735 Ok(Self::Cuda(storage))
736 }
737 (Self::Metal(s), Self::Metal(indexes), Self::Metal(source)) => {
738 let storage = s.index_add(l, indexes, indexes_l, source, source_l, d)?;
739 Ok(Self::Metal(storage))
740 }
741 _ => unreachable!(),
742 }
743 }
744
745 pub(crate) fn index_select(
746 &self,
747 rhs: &Self,
748 lhs_l: &Layout,
749 rhs_l: &Layout,
750 d: usize,
751 ) -> Result<Self> {
752 self.same_device(rhs, "index-select")?;
753 match (self, rhs) {
754 (Self::Cpu(lhs), Self::Cpu(rhs)) => {
755 let storage = lhs.index_select(rhs, lhs_l, rhs_l, d)?;
756 Ok(Self::Cpu(storage))
757 }
758 (Self::Cuda(lhs), Self::Cuda(rhs)) => {
759 let storage = lhs.index_select(rhs, lhs_l, rhs_l, d)?;
760 Ok(Self::Cuda(storage))
761 }
762 (Self::Metal(lhs), Self::Metal(rhs)) => {
763 let storage = lhs.index_select(rhs, lhs_l, rhs_l, d)?;
764 Ok(Self::Metal(storage))
765 }
766 (lhs, rhs) => Err(Error::DeviceMismatchBinaryOp {
767 lhs: lhs.device().location(),
768 rhs: rhs.device().location(),
769 op: "index-select",
770 }
771 .bt()),
772 }
773 }
774
775 pub(crate) fn matmul(
776 &self,
777 rhs: &Self,
778 bmnk: (usize, usize, usize, usize),
779 lhs_layout: &Layout,
780 rhs_layout: &Layout,
781 ) -> Result<Self> {
782 self.same_device(rhs, "matmul")?;
783 self.same_dtype(rhs, "matmul")?;
784 match (self, rhs) {
785 (Self::Cpu(lhs), Self::Cpu(rhs)) => {
786 let storage = lhs.matmul(rhs, bmnk, lhs_layout, rhs_layout)?;
787 Ok(Self::Cpu(storage))
788 }
789 (Self::Cuda(lhs), Self::Cuda(rhs)) => {
790 let storage = lhs.matmul(rhs, bmnk, lhs_layout, rhs_layout)?;
791 Ok(Self::Cuda(storage))
792 }
793 (Self::Metal(lhs), Self::Metal(rhs)) => {
794 let storage = lhs.matmul(rhs, bmnk, lhs_layout, rhs_layout)?;
795 Ok(Self::Metal(storage))
796 }
797 (lhs, rhs) => Err(Error::DeviceMismatchBinaryOp {
798 lhs: lhs.device().location(),
799 rhs: rhs.device().location(),
800 op: "matmul",
801 }
802 .bt()),
803 }
804 }
805
806 pub(crate) fn copy_strided_src(
808 &self,
809 dst: &mut Self,
810 dst_offset: usize,
811 src_l: &Layout,
812 ) -> Result<()> {
813 match (self, dst) {
814 (Self::Cpu(src), Self::Cpu(dst)) => src.copy_strided_src(dst, dst_offset, src_l),
815 (Self::Cuda(src), Self::Cuda(dst)) => Ok(src.copy_strided_src(dst, dst_offset, src_l)?),
816 (Self::Metal(src), Self::Metal(dst)) => {
817 Ok(src.copy_strided_src(dst, dst_offset, src_l)?)
818 }
819 (lhs, rhs) => Err(Error::DeviceMismatchBinaryOp {
820 lhs: lhs.device().location(),
821 rhs: rhs.device().location(),
822 op: "copy",
823 }
824 .bt()),
825 }
826 }
827
828 #[allow(clippy::too_many_arguments)]
829 pub(crate) fn copy2d(
830 &self,
831 dst: &mut Self,
832 d1: usize,
833 d2: usize,
834 src_s: usize,
835 dst_s: usize,
836 src_o: usize,
837 dst_o: usize,
838 ) -> Result<()> {
839 match (self, dst) {
840 (Self::Cpu(src), Self::Cpu(dst)) => src.copy2d(dst, d1, d2, src_s, dst_s, src_o, dst_o),
841 (Self::Cuda(src), Self::Cuda(dst)) => {
842 Ok(src.copy2d(dst, d1, d2, src_s, dst_s, src_o, dst_o)?)
843 }
844 (Self::Metal(src), Self::Metal(dst)) => {
845 Ok(src.copy2d(dst, d1, d2, src_s, dst_s, src_o, dst_o)?)
846 }
847 (lhs, rhs) => Err(Error::DeviceMismatchBinaryOp {
848 lhs: lhs.device().location(),
849 rhs: rhs.device().location(),
850 op: "copy2d",
851 }
852 .bt()),
853 }
854 }
855}