1use tenferro_tensor::{
4 DType, DotGeneralAccumulation, Tensor, TensorBackend, TensorRead, TensorScalar, TensorWrite,
5 TypedTensor, TypedTensorView, TypedTensorWrite,
6};
7
8use crate::eager::{
9 eager_einsum_exec, eager_einsum_exec_read, eager_einsum_exec_read_into,
10 eager_einsum_exec_read_into_accum, eager_einsum_read_subscripts, eager_einsum_subscripts,
11 plan_subscripts,
12};
13use crate::TensorDotAxes;
14use crate::{ContractionTree, EinsumSubscripts, Error, Result, Subscripts};
15
16const TENSOR_EINSUM_OP: &str = "TensorEinsumExt::einsum";
17const TENSOR_EINSUM_INTO_OP: &str = "TensorEinsumIntoExt::einsum_into";
18const TENSOR_READ_EINSUM_OP: &str = "TensorReadEinsumExt::einsum_read";
19const TENSOR_READ_EINSUM_INTO_OP: &str = "TensorReadEinsumIntoExt::einsum_read_into";
20const TYPED_TENSOR_EINSUM_OP: &str = "TypedTensorEinsumExt::einsum";
21const TYPED_TENSOR_EINSUM_INTO_OP: &str = "TypedTensorEinsumIntoExt::einsum_into";
22const TYPED_TENSOR_READ_EINSUM_OP: &str = "TypedTensorReadEinsumExt::einsum_read";
23const TYPED_TENSOR_READ_EINSUM_INTO_OP: &str = "TypedTensorReadEinsumIntoExt::einsum_read_into";
24const PLAN_PREPARE_OP: &str = "ConcreteEinsumPlan::prepare";
25const PLAN_EXECUTE_OP: &str = "ConcreteEinsumPlan::execute";
26const TYPED_TENSOR_TENSORDOT_OP: &str = "TypedTensorTensordotExt::tensordot";
27
28pub trait TensorTensordotExt {
30 fn tensordot<B: TensorBackend>(
39 &self,
40 rhs: &Tensor,
41 axes: TensorDotAxes<'_>,
42 backend: &mut B,
43 ) -> Result<Tensor>;
44}
45
46impl TensorTensordotExt for Tensor {
47 fn tensordot<B: TensorBackend>(
48 &self,
49 rhs: &Tensor,
50 axes: TensorDotAxes<'_>,
51 backend: &mut B,
52 ) -> Result<Tensor> {
53 let config =
54 crate::tensordot::dot_general_config(axes, self.shape().len(), rhs.shape().len())?;
55 crate::tensordot::validate_concrete_contract_dims(self.shape(), rhs.shape(), &config)?;
56 backend.dot_general(self, rhs, &config).map_err(Error::from)
57 }
58}
59
60pub trait TypedTensorTensordotExt<T: TensorScalar> {
62 fn tensordot<B: TensorBackend>(
71 &self,
72 rhs: &TypedTensor<T>,
73 axes: TensorDotAxes<'_>,
74 backend: &mut B,
75 ) -> Result<TypedTensor<T>>;
76}
77
78impl<T: TensorScalar> TypedTensorTensordotExt<T> for TypedTensor<T> {
79 fn tensordot<B: TensorBackend>(
80 &self,
81 rhs: &TypedTensor<T>,
82 axes: TensorDotAxes<'_>,
83 backend: &mut B,
84 ) -> Result<TypedTensor<T>> {
85 let config =
86 crate::tensordot::dot_general_config(axes, self.shape().len(), rhs.shape().len())?;
87 crate::tensordot::validate_concrete_contract_dims(self.shape(), rhs.shape(), &config)?;
88 let result = backend
89 .dot_general_read(T::tensor_read(self), T::tensor_read(rhs), &config)
90 .map_err(Error::from)?;
91 into_typed_result(result, TYPED_TENSOR_TENSORDOT_OP)
92 }
93}
94
95pub trait TensorEinsumExt {
117 fn einsum<B: TensorBackend>(&self, subscripts: &str, backend: &mut B) -> Result<Tensor>;
125
126 fn einsum_subscripts<B: TensorBackend>(
133 &self,
134 subscripts: &EinsumSubscripts,
135 backend: &mut B,
136 ) -> Result<Tensor>;
137}
138
139impl TensorEinsumExt for [&Tensor] {
140 fn einsum<B: TensorBackend>(&self, subscripts: &str, backend: &mut B) -> Result<Tensor> {
141 let subscripts = parse_subscripts(subscripts, TENSOR_EINSUM_OP)?;
142 eager_einsum_subscripts(backend, self, &subscripts).map_err(Error::from)
143 }
144
145 fn einsum_subscripts<B: TensorBackend>(
146 &self,
147 subscripts: &EinsumSubscripts,
148 backend: &mut B,
149 ) -> Result<Tensor> {
150 let subscripts = Subscripts::from(subscripts);
151 eager_einsum_subscripts(backend, self, &subscripts).map_err(Error::from)
152 }
153}
154
155impl<const N: usize> TensorEinsumExt for [&Tensor; N] {
156 fn einsum<B: TensorBackend>(&self, subscripts: &str, backend: &mut B) -> Result<Tensor> {
157 self.as_slice().einsum(subscripts, backend)
158 }
159
160 fn einsum_subscripts<B: TensorBackend>(
161 &self,
162 subscripts: &EinsumSubscripts,
163 backend: &mut B,
164 ) -> Result<Tensor> {
165 self.as_slice().einsum_subscripts(subscripts, backend)
166 }
167}
168
169pub trait TensorEinsumIntoExt {
171 fn einsum_into<B: TensorBackend>(
179 &self,
180 subscripts: &str,
181 backend: &mut B,
182 out: TensorWrite<'_>,
183 ) -> Result<()>;
184
185 fn einsum_into_subscripts<B: TensorBackend>(
193 &self,
194 subscripts: &EinsumSubscripts,
195 backend: &mut B,
196 out: TensorWrite<'_>,
197 ) -> Result<()>;
198}
199
200impl TensorEinsumIntoExt for [&Tensor] {
201 fn einsum_into<B: TensorBackend>(
202 &self,
203 subscripts: &str,
204 backend: &mut B,
205 out: TensorWrite<'_>,
206 ) -> Result<()> {
207 let subscripts = parse_subscripts(subscripts, TENSOR_EINSUM_INTO_OP)?;
208 tensor_einsum_into_subscripts(backend, self, &subscripts, out, TENSOR_EINSUM_INTO_OP)
209 }
210
211 fn einsum_into_subscripts<B: TensorBackend>(
212 &self,
213 subscripts: &EinsumSubscripts,
214 backend: &mut B,
215 out: TensorWrite<'_>,
216 ) -> Result<()> {
217 let subscripts = Subscripts::from(subscripts);
218 tensor_einsum_into_subscripts(backend, self, &subscripts, out, TENSOR_EINSUM_INTO_OP)
219 }
220}
221
222impl<const N: usize> TensorEinsumIntoExt for [&Tensor; N] {
223 fn einsum_into<B: TensorBackend>(
224 &self,
225 subscripts: &str,
226 backend: &mut B,
227 out: TensorWrite<'_>,
228 ) -> Result<()> {
229 self.as_slice().einsum_into(subscripts, backend, out)
230 }
231
232 fn einsum_into_subscripts<B: TensorBackend>(
233 &self,
234 subscripts: &EinsumSubscripts,
235 backend: &mut B,
236 out: TensorWrite<'_>,
237 ) -> Result<()> {
238 self.as_slice()
239 .einsum_into_subscripts(subscripts, backend, out)
240 }
241}
242
243pub trait TypedTensorEinsumExt<T: TensorScalar> {
264 fn einsum<B: TensorBackend>(&self, subscripts: &str, backend: &mut B)
272 -> Result<TypedTensor<T>>;
273
274 fn einsum_subscripts<B: TensorBackend>(
281 &self,
282 subscripts: &EinsumSubscripts,
283 backend: &mut B,
284 ) -> Result<TypedTensor<T>>;
285}
286
287impl<T: TensorScalar> TypedTensorEinsumExt<T> for [&TypedTensor<T>] {
288 fn einsum<B: TensorBackend>(
289 &self,
290 subscripts: &str,
291 backend: &mut B,
292 ) -> Result<TypedTensor<T>> {
293 let subscripts = parse_subscripts(subscripts, TYPED_TENSOR_EINSUM_OP)?;
294 typed_einsum_subscripts(backend, self, &subscripts, TYPED_TENSOR_EINSUM_OP)
295 }
296
297 fn einsum_subscripts<B: TensorBackend>(
298 &self,
299 subscripts: &EinsumSubscripts,
300 backend: &mut B,
301 ) -> Result<TypedTensor<T>> {
302 let subscripts = Subscripts::from(subscripts);
303 typed_einsum_subscripts(backend, self, &subscripts, TYPED_TENSOR_EINSUM_OP)
304 }
305}
306
307impl<T: TensorScalar, const N: usize> TypedTensorEinsumExt<T> for [&TypedTensor<T>; N] {
308 fn einsum<B: TensorBackend>(
309 &self,
310 subscripts: &str,
311 backend: &mut B,
312 ) -> Result<TypedTensor<T>> {
313 self.as_slice().einsum(subscripts, backend)
314 }
315
316 fn einsum_subscripts<B: TensorBackend>(
317 &self,
318 subscripts: &EinsumSubscripts,
319 backend: &mut B,
320 ) -> Result<TypedTensor<T>> {
321 self.as_slice().einsum_subscripts(subscripts, backend)
322 }
323}
324
325pub trait TypedTensorReadEinsumExt<T: TensorScalar> {
346 fn einsum_read<B: TensorBackend>(
368 &self,
369 subscripts: &str,
370 backend: &mut B,
371 ) -> Result<TypedTensor<T>>;
372
373 fn einsum_read_subscripts<B: TensorBackend>(
396 &self,
397 subscripts: &EinsumSubscripts,
398 backend: &mut B,
399 ) -> Result<TypedTensor<T>>;
400}
401
402impl<'a, T: TensorScalar> TypedTensorReadEinsumExt<T> for [TypedTensorView<'a, T>] {
403 fn einsum_read<B: TensorBackend>(
404 &self,
405 subscripts: &str,
406 backend: &mut B,
407 ) -> Result<TypedTensor<T>> {
408 let subscripts = parse_subscripts(subscripts, TYPED_TENSOR_READ_EINSUM_OP)?;
409 typed_view_einsum_subscripts(backend, self, &subscripts, TYPED_TENSOR_READ_EINSUM_OP)
410 }
411
412 fn einsum_read_subscripts<B: TensorBackend>(
413 &self,
414 subscripts: &EinsumSubscripts,
415 backend: &mut B,
416 ) -> Result<TypedTensor<T>> {
417 let subscripts = Subscripts::from(subscripts);
418 typed_view_einsum_subscripts(backend, self, &subscripts, TYPED_TENSOR_READ_EINSUM_OP)
419 }
420}
421
422impl<'a, T: TensorScalar, const N: usize> TypedTensorReadEinsumExt<T>
423 for [TypedTensorView<'a, T>; N]
424{
425 fn einsum_read<B: TensorBackend>(
426 &self,
427 subscripts: &str,
428 backend: &mut B,
429 ) -> Result<TypedTensor<T>> {
430 self.as_slice().einsum_read(subscripts, backend)
431 }
432
433 fn einsum_read_subscripts<B: TensorBackend>(
434 &self,
435 subscripts: &EinsumSubscripts,
436 backend: &mut B,
437 ) -> Result<TypedTensor<T>> {
438 self.as_slice().einsum_read_subscripts(subscripts, backend)
439 }
440}
441
442pub trait TypedTensorEinsumIntoExt<T: TensorScalar> {
444 fn einsum_into<'out, B, O>(&self, subscripts: &str, backend: &mut B, out: O) -> Result<()>
452 where
453 B: TensorBackend,
454 O: Into<TypedTensorWrite<'out, T>>;
455
456 fn einsum_into_subscripts<'out, B, O>(
464 &self,
465 subscripts: &EinsumSubscripts,
466 backend: &mut B,
467 out: O,
468 ) -> Result<()>
469 where
470 B: TensorBackend,
471 O: Into<TypedTensorWrite<'out, T>>;
472}
473
474impl<T: TensorScalar> TypedTensorEinsumIntoExt<T> for [&TypedTensor<T>] {
475 fn einsum_into<'out, B, O>(&self, subscripts: &str, backend: &mut B, out: O) -> Result<()>
476 where
477 B: TensorBackend,
478 O: Into<TypedTensorWrite<'out, T>>,
479 {
480 let subscripts = parse_subscripts(subscripts, TYPED_TENSOR_EINSUM_INTO_OP)?;
481 typed_einsum_into_subscripts(
482 backend,
483 self,
484 &subscripts,
485 out.into(),
486 TYPED_TENSOR_EINSUM_INTO_OP,
487 )
488 }
489
490 fn einsum_into_subscripts<'out, B, O>(
491 &self,
492 subscripts: &EinsumSubscripts,
493 backend: &mut B,
494 out: O,
495 ) -> Result<()>
496 where
497 B: TensorBackend,
498 O: Into<TypedTensorWrite<'out, T>>,
499 {
500 let subscripts = Subscripts::from(subscripts);
501 typed_einsum_into_subscripts(
502 backend,
503 self,
504 &subscripts,
505 out.into(),
506 TYPED_TENSOR_EINSUM_INTO_OP,
507 )
508 }
509}
510
511impl<T: TensorScalar, const N: usize> TypedTensorEinsumIntoExt<T> for [&TypedTensor<T>; N] {
512 fn einsum_into<'out, B, O>(&self, subscripts: &str, backend: &mut B, out: O) -> Result<()>
513 where
514 B: TensorBackend,
515 O: Into<TypedTensorWrite<'out, T>>,
516 {
517 self.as_slice().einsum_into(subscripts, backend, out)
518 }
519
520 fn einsum_into_subscripts<'out, B, O>(
521 &self,
522 subscripts: &EinsumSubscripts,
523 backend: &mut B,
524 out: O,
525 ) -> Result<()>
526 where
527 B: TensorBackend,
528 O: Into<TypedTensorWrite<'out, T>>,
529 {
530 self.as_slice()
531 .einsum_into_subscripts(subscripts, backend, out)
532 }
533}
534
535pub trait TypedTensorReadEinsumIntoExt<T: TensorScalar> {
553 fn einsum_read_into<'out, B, O>(&self, subscripts: &str, backend: &mut B, out: O) -> Result<()>
577 where
578 B: TensorBackend,
579 O: Into<TypedTensorWrite<'out, T>>;
580
581 fn einsum_read_into_subscripts<'out, B, O>(
606 &self,
607 subscripts: &EinsumSubscripts,
608 backend: &mut B,
609 out: O,
610 ) -> Result<()>
611 where
612 B: TensorBackend,
613 O: Into<TypedTensorWrite<'out, T>>;
614}
615
616impl<'a, T: TensorScalar> TypedTensorReadEinsumIntoExt<T> for [TypedTensorView<'a, T>] {
617 fn einsum_read_into<'out, B, O>(&self, subscripts: &str, backend: &mut B, out: O) -> Result<()>
618 where
619 B: TensorBackend,
620 O: Into<TypedTensorWrite<'out, T>>,
621 {
622 let subscripts = parse_subscripts(subscripts, TYPED_TENSOR_READ_EINSUM_INTO_OP)?;
623 typed_view_einsum_into_subscripts(
624 backend,
625 self,
626 &subscripts,
627 out.into(),
628 TYPED_TENSOR_READ_EINSUM_INTO_OP,
629 )
630 }
631
632 fn einsum_read_into_subscripts<'out, B, O>(
633 &self,
634 subscripts: &EinsumSubscripts,
635 backend: &mut B,
636 out: O,
637 ) -> Result<()>
638 where
639 B: TensorBackend,
640 O: Into<TypedTensorWrite<'out, T>>,
641 {
642 let subscripts = Subscripts::from(subscripts);
643 typed_view_einsum_into_subscripts(
644 backend,
645 self,
646 &subscripts,
647 out.into(),
648 TYPED_TENSOR_READ_EINSUM_INTO_OP,
649 )
650 }
651}
652
653impl<'a, T: TensorScalar, const N: usize> TypedTensorReadEinsumIntoExt<T>
654 for [TypedTensorView<'a, T>; N]
655{
656 fn einsum_read_into<'out, B, O>(&self, subscripts: &str, backend: &mut B, out: O) -> Result<()>
657 where
658 B: TensorBackend,
659 O: Into<TypedTensorWrite<'out, T>>,
660 {
661 self.as_slice().einsum_read_into(subscripts, backend, out)
662 }
663
664 fn einsum_read_into_subscripts<'out, B, O>(
665 &self,
666 subscripts: &EinsumSubscripts,
667 backend: &mut B,
668 out: O,
669 ) -> Result<()>
670 where
671 B: TensorBackend,
672 O: Into<TypedTensorWrite<'out, T>>,
673 {
674 self.as_slice()
675 .einsum_read_into_subscripts(subscripts, backend, out)
676 }
677}
678
679pub trait TensorReadEinsumExt {
706 fn einsum_read<B: TensorBackend>(&self, subscripts: &str, backend: &mut B) -> Result<Tensor>;
714
715 fn einsum_read_subscripts<B: TensorBackend>(
723 &self,
724 subscripts: &EinsumSubscripts,
725 backend: &mut B,
726 ) -> Result<Tensor>;
727}
728
729impl<'a> TensorReadEinsumExt for [TensorRead<'a>] {
730 fn einsum_read<B: TensorBackend>(&self, subscripts: &str, backend: &mut B) -> Result<Tensor> {
731 let subscripts = parse_subscripts(subscripts, TENSOR_READ_EINSUM_OP)?;
732 eager_einsum_read_subscripts(backend, self, &subscripts).map_err(Error::from)
733 }
734
735 fn einsum_read_subscripts<B: TensorBackend>(
736 &self,
737 subscripts: &EinsumSubscripts,
738 backend: &mut B,
739 ) -> Result<Tensor> {
740 let subscripts = Subscripts::from(subscripts);
741 eager_einsum_read_subscripts(backend, self, &subscripts).map_err(Error::from)
742 }
743}
744
745impl<'a, const N: usize> TensorReadEinsumExt for [TensorRead<'a>; N] {
746 fn einsum_read<B: TensorBackend>(&self, subscripts: &str, backend: &mut B) -> Result<Tensor> {
747 self.as_slice().einsum_read(subscripts, backend)
748 }
749
750 fn einsum_read_subscripts<B: TensorBackend>(
751 &self,
752 subscripts: &EinsumSubscripts,
753 backend: &mut B,
754 ) -> Result<Tensor> {
755 self.as_slice().einsum_read_subscripts(subscripts, backend)
756 }
757}
758
759pub trait TensorReadEinsumIntoExt {
761 fn einsum_read_into<B: TensorBackend>(
769 &self,
770 subscripts: &str,
771 backend: &mut B,
772 out: TensorWrite<'_>,
773 ) -> Result<()>;
774
775 fn einsum_read_into_subscripts<B: TensorBackend>(
783 &self,
784 subscripts: &EinsumSubscripts,
785 backend: &mut B,
786 out: TensorWrite<'_>,
787 ) -> Result<()>;
788}
789
790impl<'a> TensorReadEinsumIntoExt for [TensorRead<'a>] {
791 fn einsum_read_into<B: TensorBackend>(
792 &self,
793 subscripts: &str,
794 backend: &mut B,
795 out: TensorWrite<'_>,
796 ) -> Result<()> {
797 let subscripts = parse_subscripts(subscripts, TENSOR_READ_EINSUM_INTO_OP)?;
798 tensor_read_einsum_into_subscripts(
799 backend,
800 self,
801 &subscripts,
802 out,
803 TENSOR_READ_EINSUM_INTO_OP,
804 )
805 }
806
807 fn einsum_read_into_subscripts<B: TensorBackend>(
808 &self,
809 subscripts: &EinsumSubscripts,
810 backend: &mut B,
811 out: TensorWrite<'_>,
812 ) -> Result<()> {
813 let subscripts = Subscripts::from(subscripts);
814 tensor_read_einsum_into_subscripts(
815 backend,
816 self,
817 &subscripts,
818 out,
819 TENSOR_READ_EINSUM_INTO_OP,
820 )
821 }
822}
823
824impl<'a, const N: usize> TensorReadEinsumIntoExt for [TensorRead<'a>; N] {
825 fn einsum_read_into<B: TensorBackend>(
826 &self,
827 subscripts: &str,
828 backend: &mut B,
829 out: TensorWrite<'_>,
830 ) -> Result<()> {
831 self.as_slice().einsum_read_into(subscripts, backend, out)
832 }
833
834 fn einsum_read_into_subscripts<B: TensorBackend>(
835 &self,
836 subscripts: &EinsumSubscripts,
837 backend: &mut B,
838 out: TensorWrite<'_>,
839 ) -> Result<()> {
840 self.as_slice()
841 .einsum_read_into_subscripts(subscripts, backend, out)
842 }
843}
844
845#[derive(Debug)]
869pub struct ConcreteEinsumPlan {
870 tree: ContractionTree,
871 inputs: Vec<ConcreteEinsumInputSpec>,
872}
873
874impl ConcreteEinsumPlan {
875 pub fn prepare<'a, I>(inputs: I, subscripts: &str) -> Result<Self>
884 where
885 I: AsRef<[&'a Tensor]>,
886 {
887 let subscripts = parse_subscripts(subscripts, PLAN_PREPARE_OP)?;
888 Self::prepare_subscripts_internal(input_specs(inputs.as_ref()), &subscripts)
889 }
890
891 pub fn prepare_subscripts<'a, I>(inputs: I, subscripts: &EinsumSubscripts) -> Result<Self>
900 where
901 I: AsRef<[&'a Tensor]>,
902 {
903 let subscripts = Subscripts::from(subscripts);
904 Self::prepare_subscripts_internal(input_specs(inputs.as_ref()), &subscripts)
905 }
906
907 pub fn prepare_typed<'a, T, I>(inputs: I, subscripts: &str) -> Result<Self>
915 where
916 T: TensorScalar,
917 I: AsRef<[&'a TypedTensor<T>]>,
918 {
919 let subscripts = parse_subscripts(subscripts, PLAN_PREPARE_OP)?;
920 Self::prepare_subscripts_internal(typed_input_specs(inputs.as_ref()), &subscripts)
921 }
922
923 pub fn prepare_typed_subscripts<'a, T, I>(
931 inputs: I,
932 subscripts: &EinsumSubscripts,
933 ) -> Result<Self>
934 where
935 T: TensorScalar,
936 I: AsRef<[&'a TypedTensor<T>]>,
937 {
938 let subscripts = Subscripts::from(subscripts);
939 Self::prepare_subscripts_internal(typed_input_specs(inputs.as_ref()), &subscripts)
940 }
941
942 pub fn prepare_read<'a, I>(inputs: I, subscripts: &str) -> Result<Self>
950 where
951 I: AsRef<[TensorRead<'a>]>,
952 {
953 let subscripts = parse_subscripts(subscripts, PLAN_PREPARE_OP)?;
954 Self::prepare_subscripts_internal(read_input_specs(inputs.as_ref()), &subscripts)
955 }
956
957 pub fn prepare_read_subscripts<'a, I>(inputs: I, subscripts: &EinsumSubscripts) -> Result<Self>
966 where
967 I: AsRef<[TensorRead<'a>]>,
968 {
969 let subscripts = Subscripts::from(subscripts);
970 Self::prepare_subscripts_internal(read_input_specs(inputs.as_ref()), &subscripts)
971 }
972
973 pub fn execute<'a, I, B>(&self, inputs: I, backend: &mut B) -> Result<Tensor>
981 where
982 I: AsRef<[&'a Tensor]>,
983 B: TensorBackend,
984 {
985 let inputs = inputs.as_ref();
986 self.validate_inputs(&input_specs(inputs), PLAN_EXECUTE_OP)?;
987 backend
988 .with_backend_session(|exec| eager_einsum_exec(exec, inputs, &self.tree))
989 .map_err(Error::from)
990 }
991
992 pub fn execute_typed<'a, T, I, B>(&self, inputs: I, backend: &mut B) -> Result<TypedTensor<T>>
999 where
1000 T: TensorScalar,
1001 I: AsRef<[&'a TypedTensor<T>]>,
1002 B: TensorBackend,
1003 {
1004 let inputs = inputs.as_ref();
1005 self.validate_inputs(&typed_input_specs(inputs), PLAN_EXECUTE_OP)?;
1006 let reads: Vec<_> = inputs.iter().map(|tensor| T::tensor_read(tensor)).collect();
1007 let result = backend
1008 .with_backend_session(|exec| eager_einsum_exec_read(exec, &reads, &self.tree))?;
1009 into_typed_result(result, PLAN_EXECUTE_OP)
1010 }
1011
1012 pub fn execute_read<'a, I, B>(&self, inputs: I, backend: &mut B) -> Result<Tensor>
1020 where
1021 I: AsRef<[TensorRead<'a>]>,
1022 B: TensorBackend,
1023 {
1024 let inputs = inputs.as_ref();
1025 self.validate_inputs(&read_input_specs(inputs), PLAN_EXECUTE_OP)?;
1026 backend
1027 .with_backend_session(|exec| eager_einsum_exec_read(exec, inputs, &self.tree))
1028 .map_err(Error::from)
1029 }
1030
1031 pub fn execute_into<'a, I, B>(
1038 &self,
1039 inputs: I,
1040 backend: &mut B,
1041 out: TensorWrite<'_>,
1042 ) -> Result<()>
1043 where
1044 I: AsRef<[&'a Tensor]>,
1045 B: TensorBackend,
1046 {
1047 let inputs = inputs.as_ref();
1048 let specs = input_specs(inputs);
1049 self.validate_inputs(&specs, PLAN_EXECUTE_OP)?;
1050 validate_output(&self.inputs, &self.tree, &out, PLAN_EXECUTE_OP)?;
1051 let reads: Vec<_> = inputs
1052 .iter()
1053 .map(|tensor| TensorRead::from_tensor(tensor))
1054 .collect();
1055 backend
1056 .with_backend_session(|exec| eager_einsum_exec_read_into(exec, &reads, &self.tree, out))
1057 .map_err(Error::from)
1058 }
1059
1060 pub fn execute_typed_into<'a, 'out, T, I, B, O>(
1067 &self,
1068 inputs: I,
1069 backend: &mut B,
1070 out: O,
1071 ) -> Result<()>
1072 where
1073 T: TensorScalar,
1074 I: AsRef<[&'a TypedTensor<T>]>,
1075 B: TensorBackend,
1076 O: Into<TypedTensorWrite<'out, T>>,
1077 {
1078 let inputs = inputs.as_ref();
1079 let specs = typed_input_specs(inputs);
1080 self.validate_inputs(&specs, PLAN_EXECUTE_OP)?;
1081 let out = out.into().into_tensor_write();
1082 validate_output(&self.inputs, &self.tree, &out, PLAN_EXECUTE_OP)?;
1083 let reads: Vec<_> = inputs.iter().map(|tensor| T::tensor_read(tensor)).collect();
1084 backend
1085 .with_backend_session(|exec| eager_einsum_exec_read_into(exec, &reads, &self.tree, out))
1086 .map_err(Error::from)
1087 }
1088
1089 pub fn execute_read_into<'a, I, B>(
1096 &self,
1097 inputs: I,
1098 backend: &mut B,
1099 out: TensorWrite<'_>,
1100 ) -> Result<()>
1101 where
1102 I: AsRef<[TensorRead<'a>]>,
1103 B: TensorBackend,
1104 {
1105 let inputs = inputs.as_ref();
1106 let specs = read_input_specs(inputs);
1107 self.validate_inputs(&specs, PLAN_EXECUTE_OP)?;
1108 validate_output(&self.inputs, &self.tree, &out, PLAN_EXECUTE_OP)?;
1109 backend
1110 .with_backend_session(|exec| eager_einsum_exec_read_into(exec, inputs, &self.tree, out))
1111 .map_err(Error::from)
1112 }
1113
1114 pub fn execute_read_into_accum<'a, I, B>(
1149 &self,
1150 inputs: I,
1151 backend: &mut B,
1152 accumulation: DotGeneralAccumulation,
1153 out: TensorWrite<'_>,
1154 ) -> Result<()>
1155 where
1156 I: AsRef<[TensorRead<'a>]>,
1157 B: TensorBackend,
1158 {
1159 let inputs = inputs.as_ref();
1160 let specs = read_input_specs(inputs);
1161 self.validate_inputs(&specs, PLAN_EXECUTE_OP)?;
1162 validate_output(&self.inputs, &self.tree, &out, PLAN_EXECUTE_OP)?;
1163 backend
1164 .with_backend_session(|exec| {
1165 eager_einsum_exec_read_into_accum(exec, inputs, &self.tree, accumulation, out)
1166 })
1167 .map_err(Error::from)
1168 }
1169
1170 fn prepare_subscripts_internal(
1171 inputs: Vec<ConcreteEinsumInputSpec>,
1172 subscripts: &Subscripts,
1173 ) -> Result<Self> {
1174 let shapes: Vec<&[usize]> = inputs.iter().map(|input| input.shape.as_slice()).collect();
1175 let tree = plan_subscripts(subscripts, &shapes)?;
1176 Ok(Self { tree, inputs })
1177 }
1178
1179 fn validate_inputs(&self, actual: &[ConcreteEinsumInputSpec], op: &'static str) -> Result<()> {
1180 if actual.len() != self.inputs.len() {
1181 return Err(Error::invalid_argument(
1182 op,
1183 "inputs",
1184 format!(
1185 "prepared einsum expects {} inputs, got {}",
1186 self.inputs.len(),
1187 actual.len()
1188 ),
1189 ));
1190 }
1191
1192 for (expected, actual) in self.inputs.iter().zip(actual.iter()) {
1193 if expected.dtype != actual.dtype {
1194 return Err(Error::dtype_mismatch(op, expected.dtype, actual.dtype));
1195 }
1196 if expected.shape != actual.shape {
1197 return Err(Error::shape_mismatch(
1198 op,
1199 expected.shape.clone(),
1200 actual.shape.clone(),
1201 ));
1202 }
1203 }
1204
1205 Ok(())
1206 }
1207}
1208
1209#[derive(Clone, Debug)]
1210struct ConcreteEinsumInputSpec {
1211 dtype: DType,
1212 shape: Vec<usize>,
1213}
1214
1215fn parse_subscripts(subscripts: &str, _op: &'static str) -> Result<Subscripts> {
1216 Subscripts::parse(subscripts)
1217}
1218
1219fn input_specs(inputs: &[&Tensor]) -> Vec<ConcreteEinsumInputSpec> {
1220 inputs
1221 .iter()
1222 .map(|tensor| ConcreteEinsumInputSpec {
1223 dtype: tensor.dtype(),
1224 shape: tensor.shape().to_vec(),
1225 })
1226 .collect()
1227}
1228
1229fn typed_input_specs<T: TensorScalar>(inputs: &[&TypedTensor<T>]) -> Vec<ConcreteEinsumInputSpec> {
1230 inputs
1231 .iter()
1232 .map(|tensor| ConcreteEinsumInputSpec {
1233 dtype: T::dtype(),
1234 shape: tensor.shape().to_vec(),
1235 })
1236 .collect()
1237}
1238
1239fn typed_view_input_specs<T: TensorScalar>(
1240 inputs: &[TypedTensorView<'_, T>],
1241) -> Vec<ConcreteEinsumInputSpec> {
1242 inputs
1243 .iter()
1244 .map(|tensor| ConcreteEinsumInputSpec {
1245 dtype: T::dtype(),
1246 shape: tensor.shape().to_vec(),
1247 })
1248 .collect()
1249}
1250
1251fn read_input_specs(inputs: &[TensorRead<'_>]) -> Vec<ConcreteEinsumInputSpec> {
1252 inputs
1253 .iter()
1254 .map(|tensor| ConcreteEinsumInputSpec {
1255 dtype: tensor.dtype(),
1256 shape: tensor.shape().to_vec(),
1257 })
1258 .collect()
1259}
1260
1261fn typed_view_einsum_subscripts<T: TensorScalar>(
1262 backend: &mut impl TensorBackend,
1263 inputs: &[TypedTensorView<'_, T>],
1264 subscripts: &Subscripts,
1265 op: &'static str,
1266) -> Result<TypedTensor<T>> {
1267 let reads: Vec<_> = inputs
1268 .iter()
1269 .cloned()
1270 .map(|view| TensorRead::from_view(T::tensor_view(view)))
1271 .collect();
1272 let result = eager_einsum_read_subscripts(backend, &reads, subscripts)?;
1273 into_typed_result(result, op)
1274}
1275
1276fn tensor_einsum_into_subscripts(
1277 backend: &mut impl TensorBackend,
1278 inputs: &[&Tensor],
1279 subscripts: &Subscripts,
1280 out: TensorWrite<'_>,
1281 op: &'static str,
1282) -> Result<()> {
1283 let specs = input_specs(inputs);
1284 let plan = ConcreteEinsumPlan::prepare_subscripts_internal(specs.clone(), subscripts)?;
1285 validate_output(&specs, &plan.tree, &out, op)?;
1286 let reads: Vec<_> = inputs
1287 .iter()
1288 .map(|tensor| TensorRead::from_tensor(tensor))
1289 .collect();
1290 backend
1291 .with_backend_session(|exec| eager_einsum_exec_read_into(exec, &reads, &plan.tree, out))
1292 .map_err(Error::from)
1293}
1294
1295fn typed_view_einsum_into_subscripts<T: TensorScalar>(
1296 backend: &mut impl TensorBackend,
1297 inputs: &[TypedTensorView<'_, T>],
1298 subscripts: &Subscripts,
1299 out: TypedTensorWrite<'_, T>,
1300 op: &'static str,
1301) -> Result<()> {
1302 let specs = typed_view_input_specs(inputs);
1303 let plan = ConcreteEinsumPlan::prepare_subscripts_internal(specs.clone(), subscripts)?;
1304 let out = out.into_tensor_write();
1305 validate_output(&specs, &plan.tree, &out, op)?;
1306 let reads: Vec<_> = inputs
1307 .iter()
1308 .cloned()
1309 .map(|view| TensorRead::from_view(T::tensor_view(view)))
1310 .collect();
1311 backend
1312 .with_backend_session(|exec| eager_einsum_exec_read_into(exec, &reads, &plan.tree, out))
1313 .map_err(Error::from)
1314}
1315
1316fn typed_einsum_into_subscripts<T: TensorScalar>(
1317 backend: &mut impl TensorBackend,
1318 inputs: &[&TypedTensor<T>],
1319 subscripts: &Subscripts,
1320 out: TypedTensorWrite<'_, T>,
1321 op: &'static str,
1322) -> Result<()> {
1323 let specs = typed_input_specs(inputs);
1324 let plan = ConcreteEinsumPlan::prepare_subscripts_internal(specs.clone(), subscripts)?;
1325 let out = out.into_tensor_write();
1326 validate_output(&specs, &plan.tree, &out, op)?;
1327 let reads: Vec<_> = inputs.iter().map(|tensor| T::tensor_read(tensor)).collect();
1328 backend
1329 .with_backend_session(|exec| eager_einsum_exec_read_into(exec, &reads, &plan.tree, out))
1330 .map_err(Error::from)
1331}
1332
1333fn tensor_read_einsum_into_subscripts(
1334 backend: &mut impl TensorBackend,
1335 inputs: &[TensorRead<'_>],
1336 subscripts: &Subscripts,
1337 out: TensorWrite<'_>,
1338 op: &'static str,
1339) -> Result<()> {
1340 let specs = read_input_specs(inputs);
1341 let plan = ConcreteEinsumPlan::prepare_subscripts_internal(specs.clone(), subscripts)?;
1342 validate_output(&specs, &plan.tree, &out, op)?;
1343 backend
1344 .with_backend_session(|exec| eager_einsum_exec_read_into(exec, inputs, &plan.tree, out))
1345 .map_err(Error::from)
1346}
1347
1348fn validate_output(
1349 inputs: &[ConcreteEinsumInputSpec],
1350 tree: &ContractionTree,
1351 out: &TensorWrite<'_>,
1352 op: &'static str,
1353) -> Result<()> {
1354 let expected = output_spec(inputs, tree, op)?;
1355 if out.dtype() != expected.dtype {
1356 return Err(Error::dtype_mismatch(op, expected.dtype, out.dtype()));
1357 }
1358 if out.shape() != expected.shape.as_slice() {
1359 return Err(Error::shape_mismatch(
1360 op,
1361 out.shape().to_vec(),
1362 expected.shape.clone(),
1363 ));
1364 }
1365 Ok(())
1366}
1367
1368fn output_spec(
1369 inputs: &[ConcreteEinsumInputSpec],
1370 tree: &ContractionTree,
1371 op: &'static str,
1372) -> Result<ConcreteEinsumInputSpec> {
1373 let dtype = inputs
1374 .first()
1375 .ok_or_else(|| {
1376 Error::invalid_argument(op, "inputs", "einsum requires at least one input tensor")
1377 })?
1378 .dtype;
1379 for input in inputs {
1380 if input.dtype != dtype {
1381 return Err(Error::dtype_mismatch(op, dtype, input.dtype));
1382 }
1383 }
1384
1385 let mut output_shape = Vec::with_capacity(tree.subscripts.output.len());
1386 for &label in &tree.subscripts.output {
1387 let mut found = None;
1388 for (input, labels) in inputs.iter().zip(tree.subscripts.inputs.iter()) {
1389 if labels.len() != input.shape.len() {
1390 return Err(Error::rank_mismatch(op, labels.len(), input.shape.len()));
1391 }
1392 if let Some(axis) = labels.iter().position(|candidate| *candidate == label) {
1393 found = Some(input.shape[axis]);
1394 break;
1395 }
1396 }
1397 let Some(extent) = found else {
1398 return Err(Error::invalid_argument(
1399 op,
1400 "output labels",
1401 format!("output label {label} is missing from inputs"),
1402 ));
1403 };
1404 output_shape.push(extent);
1405 }
1406 Ok(ConcreteEinsumInputSpec {
1407 dtype,
1408 shape: output_shape,
1409 })
1410}
1411
1412fn typed_einsum_subscripts<T: TensorScalar>(
1413 backend: &mut impl TensorBackend,
1414 inputs: &[&TypedTensor<T>],
1415 subscripts: &Subscripts,
1416 op: &'static str,
1417) -> Result<TypedTensor<T>> {
1418 let reads: Vec<_> = inputs.iter().map(|tensor| T::tensor_read(tensor)).collect();
1419 let result = eager_einsum_read_subscripts(backend, &reads, subscripts)?;
1420 into_typed_result(result, op)
1421}
1422
1423pub(crate) fn into_typed_result<T: TensorScalar>(
1424 result: Tensor,
1425 op: &'static str,
1426) -> Result<TypedTensor<T>> {
1427 let actual = result.dtype();
1428 T::into_typed(result).map_err(|_| Error::dtype_mismatch(op, T::dtype(), actual))
1429}