1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//! Compile-time einops-style tensor transformations for Candle.
//!
//! The [`einops!`] macro combines rearrange, reduce, repeat, composition, and
//! decomposition operations. [`einsum!`] provides explicit-output,
//! arbitrary-arity Einstein summation. Backend failures are returned as Candle
//! errors.
//!
//! Einsum equations require exactly one `->`, use whitespace-delimited named
//! axes, and have one comma-separated input list per operand. Axes omitted from
//! the output are summed, `..` captures right-aligned runtime axes, and repeated
//! labels select diagonals. See the repository's `docs/einsum-contract.md` for
//! the complete supported contract.
//!
//! ```
//! use candle_core::{Device, Result, Tensor};
//! use candle_einops::einops;
//!
//! # fn main() -> Result<()> {
//! let input = Tensor::arange(0f32, 6f32, &Device::Cpu)?.reshape((2, 3))?;
//! let output = einops!("rows columns -> columns rows", &input)?;
//! assert_eq!(output.dims(), &[3, 2]);
//! # Ok(())
//! # }
//! ```
//!
//! A `..` captures zero or more axes. Captures from multiple operands align
//! from the right and broadcast, while omitting `..` from the output reduces
//! those axes:
//!
//! ```
//! use candle_core::{Device, Result, Tensor};
//! use candle_einops::einsum;
//!
//! # fn main() -> Result<()> {
//! let input = Tensor::arange(0f32, 12f32, &Device::Cpu)?.reshape((2, 2, 3))?;
//! let reduced = einsum!(".. feature -> feature", &input)?;
//! assert_eq!(reduced.to_vec1::<f32>()?, [18., 22., 26.]);
//! # Ok(())
//! # }
//! ```
//!
//! Contractions lower through Candle matrix multiplication, including batch
//! broadcasting. Equations with more than two operands use deterministic,
//! shape-aware greedy pair selection:
//!
//! ```
//! use candle_core::{Device, Result, Tensor};
//! use candle_einops::einsum;
//!
//! # fn main() -> Result<()> {
//! let left = Tensor::new(&[[1f32, 2., 3.], [4., 5., 6.]], &Device::Cpu)?;
//! let right = Tensor::new(&[[1f32, 2.], [3., 4.], [5., 6.]], &Device::Cpu)?;
//! let output = einsum!("row inner, inner column -> row column", &left, &right)?;
//! assert_eq!(output.to_vec2::<f32>()?, [[22., 28.], [49., 64.]]);
//! let weights = Tensor::new(&[1f32, 1.], &Device::Cpu)?;
//! let projected = einsum!(
//! "row inner, inner column, column -> row",
//! &left,
//! &right,
//! &weights,
//! )?;
//! assert_eq!(projected.to_vec1::<f32>()?, [50., 113.]);
//! # Ok(())
//! # }
//! ```
//!
//! Repeating a label within one operand extracts its diagonal. Omitting that
//! label from the output computes a trace:
//!
//! ```
//! use candle_core::{Device, Result, Tensor};
//! use candle_einops::einsum;
//!
//! # fn main() -> Result<()> {
//! let matrix = Tensor::arange(0f32, 9f32, &Device::Cpu)?.reshape((3, 3))?;
//! let diagonal = einsum!("index index -> index", &matrix)?;
//! assert_eq!(diagonal.to_vec1::<f32>()?, [0., 4., 8.]);
//! let trace = einsum!("index index ->", &matrix)?;
//! assert_eq!(trace.to_scalar::<f32>()?, 12.);
//! # Ok(())
//! # }
//! ```
//!
//! ## Dtypes, devices, and gradients
//!
//! `einsum!` never casts operands or transfers them between devices. Every
//! operand in a multi-operand equation must have the same dtype and reside on
//! the same device; mismatches return a contextual [`candle_core::Error`].
//! Unary permutations preserve every dtype supported by the corresponding
//! Candle operation. Binary equations without contracted labels use Candle
//! multiplication, including its integer and BF16 support. True contractions
//! lower through Candle matrix multiplication and therefore inherit its dtype
//! and device support: unsupported combinations return an error rather than
//! being silently converted.
//!
//! Einsum execution is assembled from tracked public Candle operations, so
//! floating-point inputs participate in Candle autograd. Accelerator execution
//! likewise follows the features and devices made available by Candle; results
//! remain on the operands' original device.
//!
//! Unary einsum equations use whitespace-delimited named axes. Axes omitted
//! from the explicit output are summed:
//!
//! ```
//! use candle_core::{Device, Result, Tensor};
//! use candle_einops::einsum;
//!
//! # fn main() -> Result<()> {
//! let input = Tensor::arange(0f32, 6f32, &Device::Cpu)?.reshape((2, 3))?;
//! let columns = einsum!("rows columns -> columns", &input)?;
//! assert_eq!(columns.to_vec1::<f32>()?, [3., 5., 7.]);
//! # Ok(())
//! # }
//! ```
extern crate self as candle_einops;
/// The result type returned by [`einops!`] and [`Backend`] transformations.
pub use Result;
pub use ;
pub use Backend;
pub use PreparedDiagonalPlan;
/// Implementation details used by macros generated for this crate.
///
/// This module is not a stable public API.
/// Specifies the operation used to reduce an axis