Skip to main content

tenferro_runtime/
session_ext.rs

1//! Engine-explicit operations on [`Tensor`].
2
3use num_complex::Complex64;
4
5use crate::{
6    CompareDir, DType, DotGeneralConfig, Engine, GatherConfig, PadConfig, ScatterConfig,
7    SliceConfig, Tensor,
8};
9
10/// AD-free tensor operations on [`Tensor`], run on a borrowed engine.
11///
12/// Every method takes the receiver first and the engine last, with the
13/// arguments and config types of the eager `EagerSession` method of the same
14/// name, so AD-free and eager code differ only in where the engine comes
15/// from: the caller holds one `&mut dyn Engine` and borrows it for each call.
16/// Nothing is entered and nothing is locked, so there is no entry error and a
17/// single call needs one `?`. Borrow one engine for a whole sequence instead of
18/// building one per operation.
19///
20/// # Examples
21///
22/// ```rust
23/// use tenferro_cpu::CpuEngine;
24/// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
25///
26/// let mut engine = CpuEngine::serial();
27/// let x = Tensor::from_vec_col_major(vec![2, 2], vec![1.0_f64, 2.0, 3.0, 4.0])?;
28/// let y = {
29///     let lower = x.tril(0, &mut engine)?;
30///     lower.reduce_sum(None, &mut engine)
31/// }?;
32/// assert_eq!(y.as_slice::<f64>()?, &[7.0]);
33/// # Ok::<(), Box<dyn std::error::Error>>(())
34/// ```
35pub trait TensorSessionOpsExt {
36    /// Elementwise addition with NumPy-style broadcasting on an engine.
37    ///
38    /// The broadcast (reshape + `broadcast_in_dim`, or a copy when shapes
39    /// already match) and the add itself all run in the caller's engine;
40    /// this op never opens an engine of its own.
41    ///
42    /// # Examples
43    ///
44    /// ```rust
45    /// use tenferro_cpu::CpuEngine;
46    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
47    ///
48    ///
49    /// let mut engine = CpuEngine::serial();
50    /// let a = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0]).unwrap();
51    /// let b = Tensor::from_vec_col_major(vec![2], vec![3.0_f64, 4.0]).unwrap();
52    /// let sum = a.add(&b, &mut engine)?;
53    /// assert_eq!(sum.as_slice::<f64>().unwrap(), &[4.0, 6.0]);
54    /// # Ok::<(), Box<dyn std::error::Error>>(())
55    /// ```
56    ///
57    /// # Errors
58    ///
59    /// Returns [`tenferro_tensor::Error::Validation`] with a
60    /// [`ShapeMismatch`](tenferro_tensor::ValidationError::ShapeMismatch) or
61    /// `DTypeMismatch` payload when operands are incompatible, or
62    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
63    fn add(&self, rhs: &Tensor, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
64    /// Elementwise multiplication with NumPy-style broadcasting on an engine.
65    ///
66    /// Like [`Self::add`], broadcast and multiply run on the one engine.
67    ///
68    /// # Examples
69    ///
70    /// ```rust
71    /// use tenferro_cpu::CpuEngine;
72    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
73    ///
74    ///
75    /// let mut engine = CpuEngine::serial();
76    /// let a = Tensor::from_vec_col_major(vec![1], vec![2.0_f64]).unwrap();
77    /// let b = Tensor::from_vec_col_major(vec![4], vec![3.0_f64; 4]).unwrap();
78    /// let product = a.mul(&b, &mut engine)?;
79    /// assert_eq!(product.as_slice::<f64>().unwrap(), &[6.0; 4]);
80    /// # Ok::<(), Box<dyn std::error::Error>>(())
81    /// ```
82    ///
83    /// # Errors
84    ///
85    /// Returns [`tenferro_tensor::Error::Validation`] with `ShapeMismatch` or
86    /// `DTypeMismatch` for incompatible operands, or
87    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
88    fn mul(&self, rhs: &Tensor, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
89    /// Elementwise exponential on an engine.
90    ///
91    /// # Examples
92    ///
93    /// ```rust
94    /// use tenferro_cpu::CpuEngine;
95    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
96    ///
97    ///
98    /// let mut engine = CpuEngine::serial();
99    /// let x = Tensor::from_vec_col_major(vec![2], vec![0.0_f64, 1.0]).unwrap();
100    /// let y = x.exp(&mut engine)?;
101    /// let y = y.as_slice::<f64>().unwrap();
102    /// assert!((y[0] - 1.0).abs() < 1.0e-12);
103    /// assert!((y[1] - std::f64::consts::E).abs() < 1.0e-12);
104    /// # Ok::<(), Box<dyn std::error::Error>>(())
105    /// ```
106    ///
107    /// # Errors
108    ///
109    /// Returns [`tenferro_tensor::Error::Unsupported`] for an unsupported
110    /// dtype or [`tenferro_tensor::Error::BackendSource`] for a typed backend
111    /// failure.
112    fn exp(&self, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
113    /// Sum over the selected axes on an engine. `None` reduces every
114    /// axis and `Some(&[])` keeps the input shape, as in the eager and traced
115    /// reduction family.
116    ///
117    /// # Examples
118    ///
119    /// ```rust
120    /// use tenferro_cpu::CpuEngine;
121    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
122    ///
123    ///
124    /// let mut engine = CpuEngine::serial();
125    /// let x = Tensor::from_vec_col_major(vec![2, 3], vec![1.0_f64; 6]).unwrap();
126    /// let sums = x.reduce_sum(Some(&[1]), &mut engine)?;
127    /// assert_eq!(sums.as_slice::<f64>().unwrap(), &[3.0, 3.0]);
128    /// let total = x.reduce_sum(None, &mut engine)?;
129    /// assert_eq!(total.as_slice::<f64>().unwrap(), &[6.0]);
130    /// # Ok::<(), Box<dyn std::error::Error>>(())
131    /// ```
132    ///
133    /// # Errors
134    ///
135    /// Returns [`tenferro_tensor::Error::Validation`] with `AxisOutOfBounds`
136    /// or `DuplicateAxis` for invalid reductions, or
137    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
138    fn reduce_sum(
139        &self,
140        axes: Option<&[usize]>,
141        session: &mut dyn Engine,
142    ) -> tenferro_tensor::Result<Tensor>;
143    /// Convert to a different dtype using the checked conversion lattice on an engine.
144    ///
145    /// # Examples
146    ///
147    /// ```rust
148    /// use tenferro_cpu::CpuEngine;
149    /// use tenferro_runtime::{DType, Tensor, TensorSessionOpsExt};
150    ///
151    ///
152    /// let mut engine = CpuEngine::serial();
153    /// let x = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0]).unwrap();
154    /// let y = x.convert(DType::C64, &mut engine)?;
155    /// assert_eq!(y.dtype(), DType::C64);
156    /// # Ok::<(), Box<dyn std::error::Error>>(())
157    /// ```
158    ///
159    /// # Errors
160    ///
161    /// Returns [`tenferro_tensor::Error::UnsupportedDTypeConversion`] when the
162    /// conversion is outside the checked lattice,
163    /// [`tenferro_tensor::Error::Validation`] with `DTypeMismatch` or
164    /// `InvalidArgument` for invalid tensor metadata, or
165    /// [`tenferro_tensor::Error::BackendSource`] when the backend reports a
166    /// typed failure.
167    fn convert(&self, to: DType, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
168    /// Cast to a different dtype using explicit lossy projection on an engine.
169    ///
170    /// # Examples
171    ///
172    /// ```rust
173    /// use tenferro_cpu::CpuEngine;
174    /// use tenferro_runtime::{DType, Tensor, TensorSessionOpsExt};
175    ///
176    ///
177    /// let mut engine = CpuEngine::serial();
178    /// let x = Tensor::from_vec_col_major(vec![2], vec![1.2_f64, -2.8]).unwrap();
179    /// let y = x.cast(DType::I32, &mut engine)?;
180    /// assert_eq!(y.as_slice::<i32>().unwrap(), &[1, -2]);
181    /// # Ok::<(), Box<dyn std::error::Error>>(())
182    /// ```
183    ///
184    /// # Errors
185    ///
186    /// Returns [`tenferro_tensor::Error::UnsupportedDTypeConversion`] when the
187    /// requested cast is unsupported, [`tenferro_tensor::Error::Validation`]
188    /// with `DTypeMismatch` or `InvalidArgument` for invalid tensor metadata,
189    /// or [`tenferro_tensor::Error::BackendSource`] for a typed backend
190    /// failure.
191    fn cast(&self, to: DType, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
192    /// Elementwise subtraction with NumPy-style broadcasting on an engine.
193    ///
194    /// Like [`Self::add`], the broadcast and the subtraction run on the one
195    /// engine.
196    ///
197    /// # Examples
198    ///
199    /// ```rust
200    /// use tenferro_cpu::CpuEngine;
201    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
202    ///
203    ///
204    /// let mut engine = CpuEngine::serial();
205    /// let a = Tensor::from_vec_col_major(vec![2], vec![2.0_f64, 4.0]).unwrap();
206    /// let b = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 8.0]).unwrap();
207    /// let y = a.sub(&b, &mut engine)?;
208    /// assert_eq!(y.as_slice::<f64>().unwrap(), &[1.0, -4.0]);
209    /// # Ok::<(), Box<dyn std::error::Error>>(())
210    /// ```
211    ///
212    /// # Errors
213    ///
214    /// Returns [`tenferro_tensor::Error::Validation`] with `ShapeMismatch` or
215    /// `DTypeMismatch` for incompatible operands, or
216    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
217    fn sub(&self, rhs: &Tensor, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
218    /// Elementwise division with NumPy-style broadcasting on an engine.
219    ///
220    /// # Examples
221    ///
222    /// ```rust
223    /// use tenferro_cpu::CpuEngine;
224    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
225    ///
226    ///
227    /// let mut engine = CpuEngine::serial();
228    /// let a = Tensor::from_vec_col_major(vec![2], vec![4.0_f64, 8.0]).unwrap();
229    /// let b = Tensor::from_vec_col_major(vec![2], vec![2.0_f64, 4.0]).unwrap();
230    /// let y = a.div(&b, &mut engine)?;
231    /// assert_eq!(y.as_slice::<f64>().unwrap(), &[2.0, 2.0]);
232    /// # Ok::<(), Box<dyn std::error::Error>>(())
233    /// ```
234    ///
235    /// # Errors
236    ///
237    /// Returns [`tenferro_tensor::Error::Validation`] with `ShapeMismatch` or
238    /// `DTypeMismatch` for shape/dtype incompatibility,
239    /// [`tenferro_tensor::Error::Extension`] with a numerical classification
240    /// for a detected zero divisor, or
241    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
242    fn div(&self, rhs: &Tensor, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
243    /// Elementwise remainder with NumPy-style broadcasting on an engine.
244    ///
245    /// # Examples
246    ///
247    /// ```rust
248    /// use tenferro_cpu::CpuEngine;
249    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
250    ///
251    ///
252    /// let mut engine = CpuEngine::serial();
253    /// let a = Tensor::from_vec_col_major(vec![2], vec![5.0_f64, 7.0]).unwrap();
254    /// let b = Tensor::from_vec_col_major(vec![2], vec![2.0_f64, 4.0]).unwrap();
255    /// let y = a.rem(&b, &mut engine)?;
256    /// assert_eq!(y.as_slice::<f64>().unwrap(), &[1.0, 3.0]);
257    /// # Ok::<(), Box<dyn std::error::Error>>(())
258    /// ```
259    ///
260    /// # Errors
261    ///
262    /// Returns [`tenferro_tensor::Error::Validation`] with `ShapeMismatch` or
263    /// `DTypeMismatch` for shape/dtype incompatibility, a numerical
264    /// [`tenferro_tensor::Error::Extension`] for a detected zero divisor, or
265    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
266    fn rem(&self, rhs: &Tensor, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
267    /// Elementwise power with NumPy-style broadcasting on an engine.
268    ///
269    /// # Examples
270    ///
271    /// ```rust
272    /// use tenferro_cpu::CpuEngine;
273    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
274    ///
275    ///
276    /// let mut engine = CpuEngine::serial();
277    /// let a = Tensor::from_vec_col_major(vec![2], vec![2.0_f64, 3.0]).unwrap();
278    /// let b = Tensor::from_vec_col_major(vec![2], vec![3.0_f64, 2.0]).unwrap();
279    /// let y = a.pow(&b, &mut engine)?;
280    /// assert_eq!(y.as_slice::<f64>().unwrap(), &[8.0, 9.0]);
281    /// # Ok::<(), Box<dyn std::error::Error>>(())
282    /// ```
283    ///
284    /// # Errors
285    ///
286    /// Returns [`tenferro_tensor::Error::Validation`] with `ShapeMismatch` or
287    /// `DTypeMismatch` for incompatible metadata, a numerical
288    /// [`tenferro_tensor::Error::Extension`] for a detected negative integer
289    /// exponent, or [`tenferro_tensor::Error::BackendSource`] for a typed
290    /// backend failure.
291    fn pow(&self, rhs: &Tensor, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
292    /// Elementwise maximum with NumPy-style broadcasting on an engine.
293    ///
294    /// # Examples
295    ///
296    /// ```rust
297    /// use tenferro_cpu::CpuEngine;
298    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
299    ///
300    ///
301    /// let mut engine = CpuEngine::serial();
302    /// let a = Tensor::from_vec_col_major(vec![2], vec![2.0_f64, 4.0]).unwrap();
303    /// let b = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 8.0]).unwrap();
304    /// let y = a.maximum(&b, &mut engine)?;
305    /// assert_eq!(y.as_slice::<f64>().unwrap(), &[2.0, 8.0]);
306    /// # Ok::<(), Box<dyn std::error::Error>>(())
307    /// ```
308    ///
309    /// # Errors
310    ///
311    /// Returns [`tenferro_tensor::Error::Validation`] with `ShapeMismatch` or
312    /// `DTypeMismatch` for incompatible operands, or
313    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
314    fn maximum(&self, rhs: &Tensor, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
315    /// Elementwise minimum with NumPy-style broadcasting on an engine.
316    ///
317    /// # Examples
318    ///
319    /// ```rust
320    /// use tenferro_cpu::CpuEngine;
321    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
322    ///
323    ///
324    /// let mut engine = CpuEngine::serial();
325    /// let a = Tensor::from_vec_col_major(vec![2], vec![2.0_f64, 4.0]).unwrap();
326    /// let b = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 8.0]).unwrap();
327    /// let y = a.minimum(&b, &mut engine)?;
328    /// assert_eq!(y.as_slice::<f64>().unwrap(), &[1.0, 4.0]);
329    /// # Ok::<(), Box<dyn std::error::Error>>(())
330    /// ```
331    ///
332    /// # Errors
333    ///
334    /// Returns [`tenferro_tensor::Error::Validation`] with `ShapeMismatch` or
335    /// `DTypeMismatch` for incompatible operands, or
336    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
337    fn minimum(&self, rhs: &Tensor, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
338    /// Elementwise negation on an engine.
339    ///
340    /// # Examples
341    ///
342    /// ```rust
343    /// use tenferro_cpu::CpuEngine;
344    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
345    ///
346    ///
347    /// let mut engine = CpuEngine::serial();
348    /// let x = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, -2.0]).unwrap();
349    /// let y = x.neg(&mut engine)?;
350    /// assert_eq!(y.as_slice::<f64>().unwrap(), &[-1.0, 2.0]);
351    /// # Ok::<(), Box<dyn std::error::Error>>(())
352    /// ```
353    ///
354    /// # Errors
355    ///
356    /// Returns [`tenferro_tensor::Error::Unsupported`] when the dtype is not
357    /// supported by the operation, or [`tenferro_tensor::Error::BackendSource`]
358    /// for a typed backend failure.
359    fn neg(&self, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
360    /// Elementwise absolute value on an engine.
361    ///
362    /// # Examples
363    ///
364    /// ```rust
365    /// use tenferro_cpu::CpuEngine;
366    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
367    ///
368    ///
369    /// let mut engine = CpuEngine::serial();
370    /// let x = Tensor::from_vec_col_major(vec![2], vec![-1.0_f64, 2.0]).unwrap();
371    /// let y = x.abs(&mut engine)?;
372    /// assert_eq!(y.as_slice::<f64>().unwrap(), &[1.0, 2.0]);
373    /// # Ok::<(), Box<dyn std::error::Error>>(())
374    /// ```
375    ///
376    /// # Errors
377    ///
378    /// Returns [`tenferro_tensor::Error::Unsupported`] for an unsupported
379    /// dtype or [`tenferro_tensor::Error::BackendSource`] for a typed backend
380    /// failure.
381    fn abs(&self, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
382    /// Elementwise sign on an engine.
383    ///
384    /// # Examples
385    ///
386    /// ```rust
387    /// use tenferro_cpu::CpuEngine;
388    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
389    ///
390    ///
391    /// let mut engine = CpuEngine::serial();
392    /// let x = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, -2.0]).unwrap();
393    /// let y = x.sign(&mut engine)?;
394    /// assert_eq!(y.as_slice::<f64>().unwrap(), &[1.0, -1.0]);
395    /// # Ok::<(), Box<dyn std::error::Error>>(())
396    /// ```
397    ///
398    /// # Errors
399    ///
400    /// Returns [`tenferro_tensor::Error::Unsupported`] for an unsupported
401    /// dtype or [`tenferro_tensor::Error::BackendSource`] for a typed backend
402    /// failure.
403    fn sign(&self, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
404    /// Elementwise complex conjugate on an engine.
405    ///
406    /// For real dtypes the conjugate is the identity.
407    ///
408    /// # Examples
409    ///
410    /// ```rust
411    /// use tenferro_cpu::CpuEngine;
412    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
413    ///
414    ///
415    /// let mut engine = CpuEngine::serial();
416    /// let x = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, -2.0]).unwrap();
417    /// let y = x.conj(&mut engine)?;
418    /// assert_eq!(y.as_slice::<f64>().unwrap(), &[1.0, -2.0]);
419    /// # Ok::<(), Box<dyn std::error::Error>>(())
420    /// ```
421    ///
422    /// # Errors
423    ///
424    /// Returns [`tenferro_tensor::Error::Unsupported`] for an unsupported
425    /// dtype or [`tenferro_tensor::Error::BackendSource`] for a typed backend
426    /// failure.
427    fn conj(&self, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
428    /// Elementwise natural logarithm on an engine.
429    ///
430    /// # Examples
431    ///
432    /// ```rust
433    /// use tenferro_cpu::CpuEngine;
434    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
435    ///
436    ///
437    /// let mut engine = CpuEngine::serial();
438    /// let x = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, std::f64::consts::E]).unwrap();
439    /// let y = x.log(&mut engine)?;
440    /// let y = y.as_slice::<f64>().unwrap();
441    /// assert!(y[0].abs() < 1.0e-12);
442    /// assert!((y[1] - 1.0).abs() < 1.0e-12);
443    /// # Ok::<(), Box<dyn std::error::Error>>(())
444    /// ```
445    ///
446    /// # Errors
447    ///
448    /// Returns [`tenferro_tensor::Error::Unsupported`] for an unsupported
449    /// dtype or [`tenferro_tensor::Error::BackendSource`] for a typed backend
450    /// failure.
451    fn log(&self, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
452    /// Elementwise `exp(x) - 1` on an engine.
453    ///
454    /// # Examples
455    ///
456    /// ```rust
457    /// use tenferro_cpu::CpuEngine;
458    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
459    ///
460    ///
461    /// let mut engine = CpuEngine::serial();
462    /// let x = Tensor::from_vec_col_major(vec![2], vec![0.0_f64, 1.0]).unwrap();
463    /// let y = x.expm1(&mut engine)?;
464    /// let y = y.as_slice::<f64>().unwrap();
465    /// assert!(y[0].abs() < 1.0e-12);
466    /// assert!((y[1] - (std::f64::consts::E - 1.0)).abs() < 1.0e-12);
467    /// # Ok::<(), Box<dyn std::error::Error>>(())
468    /// ```
469    ///
470    /// # Errors
471    ///
472    /// Returns [`tenferro_tensor::Error::Unsupported`] for an unsupported
473    /// dtype or [`tenferro_tensor::Error::BackendSource`] for a typed backend
474    /// failure.
475    fn expm1(&self, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
476    /// Elementwise `log(1 + x)` on an engine.
477    ///
478    /// # Examples
479    ///
480    /// ```rust
481    /// use tenferro_cpu::CpuEngine;
482    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
483    ///
484    ///
485    /// let mut engine = CpuEngine::serial();
486    /// let x = Tensor::from_vec_col_major(vec![2], vec![0.0_f64, std::f64::consts::E - 1.0]).unwrap();
487    /// let y = x.log1p(&mut engine)?;
488    /// let y = y.as_slice::<f64>().unwrap();
489    /// assert!(y[0].abs() < 1.0e-12);
490    /// assert!((y[1] - 1.0).abs() < 1.0e-12);
491    /// # Ok::<(), Box<dyn std::error::Error>>(())
492    /// ```
493    ///
494    /// # Errors
495    ///
496    /// Returns [`tenferro_tensor::Error::Unsupported`] for an unsupported
497    /// dtype or [`tenferro_tensor::Error::BackendSource`] for a typed backend
498    /// failure.
499    fn log1p(&self, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
500    /// Elementwise error function `erf(x)` on the engine, for real `F32`/`F64`.
501    ///
502    /// # Examples
503    ///
504    /// ```rust
505    /// use tenferro_cpu::CpuEngine;
506    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
507    ///
508    ///
509    /// let mut engine = CpuEngine::serial();
510    /// let x = Tensor::from_vec_col_major(vec![2], vec![0.0_f64, 1.0]).unwrap();
511    /// let y = x.erf(&mut engine)?;
512    /// let y = y.as_slice::<f64>().unwrap();
513    /// assert_eq!(y[0], 0.0);
514    /// assert!((y[1] - 0.842_700_792_949_714_9).abs() < 1.0e-15);
515    /// # Ok::<(), Box<dyn std::error::Error>>(())
516    /// ```
517    ///
518    /// # Errors
519    ///
520    /// Returns [`tenferro_tensor::Error::UnsupportedDType`] for complex,
521    /// integer, or `Bool` input, or [`tenferro_tensor::Error::BackendSource`]
522    /// for a typed backend failure.
523    fn erf(&self, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
524    /// Elementwise sine on an engine.
525    ///
526    /// # Examples
527    ///
528    /// ```rust
529    /// use tenferro_cpu::CpuEngine;
530    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
531    ///
532    ///
533    /// let mut engine = CpuEngine::serial();
534    /// let x = Tensor::from_vec_col_major(vec![2], vec![0.0_f64, std::f64::consts::FRAC_PI_2]).unwrap();
535    /// let y = x.sin(&mut engine)?;
536    /// let y = y.as_slice::<f64>().unwrap();
537    /// assert!(y[0].abs() < 1.0e-12);
538    /// assert!((y[1] - 1.0).abs() < 1.0e-12);
539    /// # Ok::<(), Box<dyn std::error::Error>>(())
540    /// ```
541    ///
542    /// # Errors
543    ///
544    /// Returns [`tenferro_tensor::Error::Unsupported`] for an unsupported
545    /// dtype or [`tenferro_tensor::Error::BackendSource`] for a typed backend
546    /// failure.
547    fn sin(&self, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
548    /// Elementwise cosine on an engine.
549    ///
550    /// # Examples
551    ///
552    /// ```rust
553    /// use tenferro_cpu::CpuEngine;
554    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
555    ///
556    ///
557    /// let mut engine = CpuEngine::serial();
558    /// let x = Tensor::from_vec_col_major(vec![2], vec![0.0_f64, std::f64::consts::PI]).unwrap();
559    /// let y = x.cos(&mut engine)?;
560    /// let y = y.as_slice::<f64>().unwrap();
561    /// assert!((y[0] - 1.0).abs() < 1.0e-12);
562    /// assert!((y[1] + 1.0).abs() < 1.0e-12);
563    /// # Ok::<(), Box<dyn std::error::Error>>(())
564    /// ```
565    ///
566    /// # Errors
567    ///
568    /// Returns [`tenferro_tensor::Error::Unsupported`] for an unsupported
569    /// dtype or [`tenferro_tensor::Error::BackendSource`] for a typed backend
570    /// failure.
571    fn cos(&self, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
572    /// Elementwise hyperbolic tangent on an engine.
573    ///
574    /// # Examples
575    ///
576    /// ```rust
577    /// use tenferro_cpu::CpuEngine;
578    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
579    ///
580    ///
581    /// let mut engine = CpuEngine::serial();
582    /// let x = Tensor::from_vec_col_major(vec![2], vec![0.0_f64, 1.0]).unwrap();
583    /// let y = x.tanh(&mut engine)?;
584    /// let y = y.as_slice::<f64>().unwrap();
585    /// assert!(y[0].abs() < 1.0e-12);
586    /// assert!((y[1] - 0.7615941559557649).abs() < 1.0e-12);
587    /// # Ok::<(), Box<dyn std::error::Error>>(())
588    /// ```
589    ///
590    /// # Errors
591    ///
592    /// Returns [`tenferro_tensor::Error::Unsupported`] for an unsupported
593    /// dtype or [`tenferro_tensor::Error::BackendSource`] for a typed backend
594    /// failure.
595    fn tanh(&self, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
596    /// Elementwise square root on an engine.
597    ///
598    /// # Examples
599    ///
600    /// ```rust
601    /// use tenferro_cpu::CpuEngine;
602    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
603    ///
604    ///
605    /// let mut engine = CpuEngine::serial();
606    /// let x = Tensor::from_vec_col_major(vec![2], vec![4.0_f64, 9.0]).unwrap();
607    /// let y = x.sqrt(&mut engine)?;
608    /// assert_eq!(y.as_slice::<f64>().unwrap(), &[2.0, 3.0]);
609    /// # Ok::<(), Box<dyn std::error::Error>>(())
610    /// ```
611    ///
612    /// # Errors
613    ///
614    /// Returns [`tenferro_tensor::Error::Unsupported`] for an unsupported
615    /// dtype or [`tenferro_tensor::Error::BackendSource`] for a typed backend
616    /// failure.
617    fn sqrt(&self, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
618    /// Elementwise reciprocal square root on an engine.
619    ///
620    /// # Examples
621    ///
622    /// ```rust
623    /// use tenferro_cpu::CpuEngine;
624    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
625    ///
626    ///
627    /// let mut engine = CpuEngine::serial();
628    /// let x = Tensor::from_vec_col_major(vec![2], vec![4.0_f64, 1.0]).unwrap();
629    /// let y = x.rsqrt(&mut engine)?;
630    /// let y = y.as_slice::<f64>().unwrap();
631    /// assert!((y[0] - 0.5).abs() < 1.0e-12);
632    /// assert!((y[1] - 1.0).abs() < 1.0e-12);
633    /// # Ok::<(), Box<dyn std::error::Error>>(())
634    /// ```
635    ///
636    /// # Errors
637    ///
638    /// Returns [`tenferro_tensor::Error::Unsupported`] for an unsupported
639    /// dtype or [`tenferro_tensor::Error::BackendSource`] for a typed backend
640    /// failure.
641    fn rsqrt(&self, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
642    /// Elementwise comparison with NumPy-style broadcasting on an engine.
643    ///
644    /// The result is a bool tensor.
645    ///
646    /// # Examples
647    ///
648    /// ```rust
649    /// use tenferro_cpu::CpuEngine;
650    /// use tenferro_runtime::{CompareDir, Tensor, TensorSessionOpsExt};
651    ///
652    ///
653    /// let mut engine = CpuEngine::serial();
654    /// let a = Tensor::from_vec_col_major(vec![2], vec![2.0_f64, 4.0]).unwrap();
655    /// let b = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 8.0]).unwrap();
656    /// let y = a.compare(&b, CompareDir::Gt, &mut engine)?;
657    /// assert_eq!(y.as_slice::<bool>().unwrap(), &[true, false]);
658    /// # Ok::<(), Box<dyn std::error::Error>>(())
659    /// ```
660    ///
661    /// # Errors
662    ///
663    /// Returns [`tenferro_tensor::Error::Validation`] with `ShapeMismatch` or
664    /// `DTypeMismatch` for incompatible shape/dtype metadata, or
665    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
666    fn compare(
667        &self,
668        rhs: &Tensor,
669        dir: CompareDir,
670        session: &mut dyn Engine,
671    ) -> tenferro_tensor::Result<Tensor>;
672    /// Select values from `on_true` or `on_false` using this tensor as condition on an engine.
673    ///
674    /// # Examples
675    ///
676    /// ```rust
677    /// use tenferro_cpu::CpuEngine;
678    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
679    ///
680    ///
681    /// let mut engine = CpuEngine::serial();
682    /// let condition = Tensor::from_vec_col_major(vec![2], vec![true, false]).unwrap();
683    /// let on_true = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0]).unwrap();
684    /// let on_false = Tensor::from_vec_col_major(vec![2], vec![3.0_f64, 4.0]).unwrap();
685    /// let y = condition.where_select(&on_true, &on_false, &mut engine)?;
686    /// assert_eq!(y.as_slice::<f64>().unwrap(), &[1.0, 4.0]);
687    /// # Ok::<(), Box<dyn std::error::Error>>(())
688    /// ```
689    ///
690    /// # Errors
691    ///
692    /// Returns [`tenferro_tensor::Error::Validation`] with `ShapeMismatch` or
693    /// `DTypeMismatch` when the condition and branches are incompatible, or
694    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
695    fn where_select(
696        &self,
697        on_true: &Tensor,
698        on_false: &Tensor,
699        session: &mut dyn Engine,
700    ) -> tenferro_tensor::Result<Tensor>;
701    /// Clamp values elementwise between lower and upper bounds on an engine.
702    ///
703    /// # Examples
704    ///
705    /// ```rust
706    /// use tenferro_cpu::CpuEngine;
707    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
708    ///
709    ///
710    /// let mut engine = CpuEngine::serial();
711    /// let x = Tensor::from_vec_col_major(vec![2], vec![-2.0_f64, 4.0]).unwrap();
712    /// let lower = Tensor::from_vec_col_major(vec![], vec![0.0_f64]).unwrap();
713    /// let upper = Tensor::from_vec_col_major(vec![], vec![3.0_f64]).unwrap();
714    /// let y = x.clamp(&lower, &upper, &mut engine)?;
715    /// assert_eq!(y.as_slice::<f64>().unwrap(), &[0.0, 3.0]);
716    /// # Ok::<(), Box<dyn std::error::Error>>(())
717    /// ```
718    ///
719    /// # Errors
720    ///
721    /// Returns [`tenferro_tensor::Error::Validation`] with `ShapeMismatch` or
722    /// `DTypeMismatch` when bounds are incompatible with the input, or
723    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
724    fn clamp(
725        &self,
726        lower: &Tensor,
727        upper: &Tensor,
728        session: &mut dyn Engine,
729    ) -> tenferro_tensor::Result<Tensor>;
730    /// Rank-2 matrix multiplication on an engine.
731    ///
732    /// # Examples
733    ///
734    /// ```rust
735    /// use tenferro_cpu::CpuEngine;
736    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
737    ///
738    ///
739    /// let mut engine = CpuEngine::serial();
740    /// let a = Tensor::from_vec_col_major(vec![2, 3], vec![1.0_f64; 6]).unwrap();
741    /// let b = Tensor::from_vec_col_major(vec![3, 2], vec![1.0_f64; 6]).unwrap();
742    /// let c = a.matmul(&b, &mut engine)?;
743    /// assert_eq!(c.shape(), &[2, 2]);
744    /// # Ok::<(), Box<dyn std::error::Error>>(())
745    /// ```
746    ///
747    /// # Errors
748    ///
749    /// Returns [`tenferro_tensor::Error::Validation`] with `RankMismatch`,
750    /// `ShapeMismatch`, or `DTypeMismatch` for incompatible matrices, or
751    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
752    fn matmul(&self, rhs: &Tensor, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
753    /// Reshape without changing element order on an engine.
754    ///
755    /// # Examples
756    ///
757    /// ```rust
758    /// use tenferro_cpu::CpuEngine;
759    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
760    ///
761    ///
762    /// let mut engine = CpuEngine::serial();
763    /// let x = Tensor::from_vec_col_major(vec![2, 2], vec![1.0_f64, 2.0, 3.0, 4.0]).unwrap();
764    /// let y = x.reshape(&[4], &mut engine)?;
765    /// assert_eq!(y.shape(), &[4]);
766    /// # Ok::<(), Box<dyn std::error::Error>>(())
767    /// ```
768    ///
769    /// # Errors
770    ///
771    /// Returns [`tenferro_tensor::Error::Validation`] with
772    /// `ShapeMismatch`, `RankMismatch`, or `InvalidArgument` when element
773    /// counts or ranks are invalid, or
774    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
775    fn reshape(&self, shape: &[usize], session: &mut dyn Engine)
776        -> tenferro_tensor::Result<Tensor>;
777    /// Permute axes on an engine.
778    ///
779    /// # Examples
780    ///
781    /// ```rust
782    /// use tenferro_cpu::CpuEngine;
783    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
784    ///
785    ///
786    /// let mut engine = CpuEngine::serial();
787    /// let x = Tensor::from_vec_col_major(vec![2, 3], vec![1.0_f64; 6]).unwrap();
788    /// let y = x.transpose(&[1, 0], &mut engine)?;
789    /// assert_eq!(y.shape(), &[3, 2]);
790    /// # Ok::<(), Box<dyn std::error::Error>>(())
791    /// ```
792    ///
793    /// # Errors
794    ///
795    /// Returns [`tenferro_tensor::Error::Validation`] with
796    /// `InvalidPermutationLength`, `AxisOutOfBounds`, or `DuplicateAxis` for
797    /// an invalid permutation, or
798    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
799    fn transpose(
800        &self,
801        perm: &[usize],
802        session: &mut dyn Engine,
803    ) -> tenferro_tensor::Result<Tensor>;
804    /// Gather slices of this tensor at `indices` on the engine (StableHLO `gather`).
805    ///
806    /// # Examples
807    ///
808    /// ```rust
809    /// use tenferro_cpu::CpuEngine;
810    /// use tenferro_runtime::{GatherConfig, Tensor, TensorSessionOpsExt};
811    ///
812    ///
813    /// let mut engine = CpuEngine::serial();
814    /// let x = Tensor::from_vec_col_major(vec![3], vec![10.0_f64, 20.0, 30.0])?;
815    /// let indices = Tensor::from_vec_col_major(vec![2, 1], vec![2_i64, 0])?;
816    /// let config = GatherConfig {
817    ///     offset_dims: vec![],
818    ///     collapsed_slice_dims: vec![0],
819    ///     start_index_map: vec![0],
820    ///     index_vector_dim: 1,
821    ///     slice_sizes: vec![1],
822    /// };
823    /// let y = x.gather(&indices, config, &mut engine)?;
824    /// assert_eq!(y.as_slice::<f64>()?, &[30.0, 10.0]);
825    /// # Ok::<(), Box<dyn std::error::Error>>(())
826    /// ```
827    ///
828    /// # Errors
829    ///
830    /// Returns [`tenferro_tensor::Error::Validation`] for an invalid configuration, index dtype or out-of-range index, or
831    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
832    fn gather(
833        &self,
834        indices: &Tensor,
835        config: GatherConfig,
836        session: &mut dyn Engine,
837    ) -> tenferro_tensor::Result<Tensor>;
838    /// Scatter `updates` into a copy of this tensor at `indices` on the engine (StableHLO `scatter`).
839    ///
840    /// # Examples
841    ///
842    /// ```rust
843    /// use tenferro_cpu::CpuEngine;
844    /// use tenferro_runtime::{ScatterConfig, Tensor, TensorSessionOpsExt};
845    ///
846    ///
847    /// let mut engine = CpuEngine::serial();
848    /// let x = Tensor::from_vec_col_major(vec![4], vec![0.0_f64; 4])?;
849    /// let indices = Tensor::from_vec_col_major(vec![2, 1], vec![1_i64, 3])?;
850    /// let updates = Tensor::from_vec_col_major(vec![2], vec![5.0_f64, 7.0])?;
851    /// let config = ScatterConfig {
852    ///     update_window_dims: vec![],
853    ///     inserted_window_dims: vec![0],
854    ///     scatter_dims_to_operand_dims: vec![0],
855    ///     index_vector_dim: 1,
856    /// };
857    /// let y = x.scatter(&indices, &updates, config, &mut engine)?;
858    /// assert_eq!(y.as_slice::<f64>()?, &[0.0, 5.0, 0.0, 7.0]);
859    /// # Ok::<(), Box<dyn std::error::Error>>(())
860    /// ```
861    ///
862    /// # Errors
863    ///
864    /// Returns [`tenferro_tensor::Error::Validation`] for an invalid configuration, index dtype or update shape, or
865    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
866    fn scatter(
867        &self,
868        indices: &Tensor,
869        updates: &Tensor,
870        config: ScatterConfig,
871        session: &mut dyn Engine,
872    ) -> tenferro_tensor::Result<Tensor>;
873    /// Slice this tensor with explicit start, limit and stride per axis on an engine.
874    ///
875    /// # Examples
876    ///
877    /// ```rust
878    /// use tenferro_cpu::CpuEngine;
879    /// use tenferro_runtime::{SliceConfig, Tensor, TensorSessionOpsExt};
880    ///
881    ///
882    /// let mut engine = CpuEngine::serial();
883    /// let x = Tensor::from_vec_col_major(vec![4], vec![1.0_f64, 2.0, 3.0, 4.0])?;
884    /// let config = SliceConfig { starts: vec![1], limits: vec![3], strides: vec![1] };
885    /// let y = x.slice(config, &mut engine)?;
886    /// assert_eq!(y.as_slice::<f64>()?, &[2.0, 3.0]);
887    /// # Ok::<(), Box<dyn std::error::Error>>(())
888    /// ```
889    ///
890    /// # Errors
891    ///
892    /// Returns [`tenferro_tensor::Error::Validation`] for bounds or strides that do not fit the input, or
893    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
894    fn slice(
895        &self,
896        config: SliceConfig,
897        session: &mut dyn Engine,
898    ) -> tenferro_tensor::Result<Tensor>;
899    /// Slice this tensor at runtime `starts` (an integer tensor) with static `sizes` on an engine.
900    ///
901    /// # Examples
902    ///
903    /// ```rust
904    /// use tenferro_cpu::CpuEngine;
905    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
906    ///
907    ///
908    /// let mut engine = CpuEngine::serial();
909    /// let x = Tensor::from_vec_col_major(vec![4], vec![1.0_f64, 2.0, 3.0, 4.0])?;
910    /// let starts = Tensor::from_vec_col_major(vec![1], vec![1_i64])?;
911    /// let y = x.dynamic_slice(&starts, &[2], &mut engine)?;
912    /// assert_eq!(y.as_slice::<f64>()?, &[2.0, 3.0]);
913    /// # Ok::<(), Box<dyn std::error::Error>>(())
914    /// ```
915    ///
916    /// # Errors
917    ///
918    /// Returns [`tenferro_tensor::Error::Validation`] for a start-index dtype or rank mismatch or sizes larger than the input, or
919    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
920    fn dynamic_slice(
921        &self,
922        starts: &Tensor,
923        sizes: &[usize],
924        session: &mut dyn Engine,
925    ) -> tenferro_tensor::Result<Tensor>;
926    /// Return this tensor with `update` written at runtime `starts` on an engine.
927    ///
928    /// `starts` is a rank-1 integer tensor with one entry per axis. Starts are
929    /// clamped so the whole update window fits, matching `dynamic_slice`.
930    ///
931    /// # Examples
932    ///
933    /// ```rust
934    /// use tenferro_cpu::CpuEngine;
935    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
936    ///
937    /// let mut engine = CpuEngine::serial();
938    /// let x = Tensor::from_vec_col_major(vec![5], vec![0.0_f64; 5])?;
939    /// let update = Tensor::from_vec_col_major(vec![2], vec![3.0_f64, 4.0])?;
940    /// // A start of 4 is clamped to 3 so the window of 2 fits.
941    /// let starts = Tensor::from_vec_col_major(vec![1], vec![4_i64])?;
942    /// let y = x.dynamic_update_slice(&update, &starts, &mut engine)?;
943    /// assert_eq!(y.as_slice::<f64>()?, &[0.0, 0.0, 0.0, 3.0, 4.0]);
944    /// # Ok::<(), Box<dyn std::error::Error>>(())
945    /// ```
946    ///
947    /// # Errors
948    ///
949    /// Returns [`tenferro_tensor::Error::Validation`] for a rank or dtype mismatch, a
950    /// start-index tensor that is not rank 1 with one entry per axis, or an update
951    /// larger than this tensor, or [`tenferro_tensor::Error::BackendSource`] for a
952    /// typed backend failure.
953    fn dynamic_update_slice(
954        &self,
955        update: &Tensor,
956        starts: &Tensor,
957        session: &mut dyn Engine,
958    ) -> tenferro_tensor::Result<Tensor>;
959    /// Pad this tensor with zeros (edge and interior padding per axis) on an engine.
960    ///
961    /// # Examples
962    ///
963    /// ```rust
964    /// use tenferro_cpu::CpuEngine;
965    /// use tenferro_runtime::{PadConfig, Tensor, TensorSessionOpsExt};
966    ///
967    ///
968    /// let mut engine = CpuEngine::serial();
969    /// let x = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0])?;
970    /// let config = PadConfig {
971    ///     edge_padding_low: vec![1],
972    ///     edge_padding_high: vec![1],
973    ///     interior_padding: vec![1],
974    /// };
975    /// let y = x.pad(config, &mut engine)?;
976    /// assert_eq!(y.as_slice::<f64>()?, &[0.0, 1.0, 0.0, 2.0, 0.0]);
977    /// # Ok::<(), Box<dyn std::error::Error>>(())
978    /// ```
979    ///
980    /// # Errors
981    ///
982    /// Returns [`tenferro_tensor::Error::Validation`] for a padding configuration whose length does not match the input rank, or
983    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
984    fn pad(&self, config: PadConfig, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
985    /// Concatenate tensors along `axis` on an engine.
986    ///
987    /// This has no receiver, like the eager `EagerSession::concatenate`: call it as
988    /// `Tensor::concatenate(&[&a, &b], axis, session)`.
989    ///
990    /// # Examples
991    ///
992    /// ```rust
993    /// use tenferro_cpu::CpuEngine;
994    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
995    ///
996    ///
997    /// let mut engine = CpuEngine::serial();
998    /// let a = Tensor::from_vec_col_major(vec![1], vec![1.0_f64])?;
999    /// let b = Tensor::from_vec_col_major(vec![1], vec![2.0_f64])?;
1000    /// let y = Tensor::concatenate(&[&a, &b], 0, &mut engine)?;
1001    /// assert_eq!(y.as_slice::<f64>()?, &[1.0, 2.0]);
1002    /// # Ok::<(), Box<dyn std::error::Error>>(())
1003    /// ```
1004    ///
1005    /// # Errors
1006    ///
1007    /// Returns [`tenferro_tensor::Error::Validation`] for an empty input list, an axis out of range, or mismatched shapes or dtypes, or
1008    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
1009    fn concatenate(
1010        inputs: &[&Tensor],
1011        axis: usize,
1012        session: &mut dyn Engine,
1013    ) -> tenferro_tensor::Result<Tensor>
1014    where
1015        Self: Sized;
1016    /// Reverse the elements along `axes` on an engine.
1017    ///
1018    /// # Examples
1019    ///
1020    /// ```rust
1021    /// use tenferro_cpu::CpuEngine;
1022    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1023    ///
1024    ///
1025    /// let mut engine = CpuEngine::serial();
1026    /// let x = Tensor::from_vec_col_major(vec![3], vec![1.0_f64, 2.0, 3.0])?;
1027    /// let y = x.reverse(&[0], &mut engine)?;
1028    /// assert_eq!(y.as_slice::<f64>()?, &[3.0, 2.0, 1.0]);
1029    /// # Ok::<(), Box<dyn std::error::Error>>(())
1030    /// ```
1031    ///
1032    /// # Errors
1033    ///
1034    /// Returns [`tenferro_tensor::Error::Validation`] for an out-of-range or repeated axis, or
1035    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
1036    fn reverse(&self, axes: &[usize], session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
1037    /// Take the maximum over the selected axes on an engine. `None` reduces every axis and `Some(&[])` keeps the input shape.
1038    ///
1039    /// # Examples
1040    ///
1041    /// ```rust
1042    /// use tenferro_cpu::CpuEngine;
1043    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1044    ///
1045    ///
1046    /// let mut engine = CpuEngine::serial();
1047    /// let x = Tensor::from_vec_col_major(vec![2, 2], vec![1.0_f64, 5.0, 3.0, 2.0])?;
1048    /// let y = x.reduce_max(Some(&[0]), &mut engine)?;
1049    /// assert_eq!(y.as_slice::<f64>()?, &[5.0, 3.0]);
1050    /// # Ok::<(), Box<dyn std::error::Error>>(())
1051    /// ```
1052    ///
1053    /// # Errors
1054    ///
1055    /// Returns [`tenferro_tensor::Error::Validation`] for an out-of-range or repeated axis, or
1056    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
1057    /// An unsupported dtype returns [`tenferro_tensor::Error::Unsupported`].
1058    fn reduce_max(
1059        &self,
1060        axes: Option<&[usize]>,
1061        session: &mut dyn Engine,
1062    ) -> tenferro_tensor::Result<Tensor>;
1063    /// Take the minimum over the selected axes on an engine. `None` reduces every axis and `Some(&[])` keeps the input shape.
1064    ///
1065    /// # Examples
1066    ///
1067    /// ```rust
1068    /// use tenferro_cpu::CpuEngine;
1069    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1070    ///
1071    ///
1072    /// let mut engine = CpuEngine::serial();
1073    /// let x = Tensor::from_vec_col_major(vec![2, 2], vec![1.0_f64, 5.0, 3.0, 2.0])?;
1074    /// let y = x.reduce_min(Some(&[0]), &mut engine)?;
1075    /// assert_eq!(y.as_slice::<f64>()?, &[1.0, 2.0]);
1076    /// # Ok::<(), Box<dyn std::error::Error>>(())
1077    /// ```
1078    ///
1079    /// # Errors
1080    ///
1081    /// Returns [`tenferro_tensor::Error::Validation`] for an out-of-range or repeated axis, or
1082    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
1083    /// An unsupported dtype returns [`tenferro_tensor::Error::Unsupported`].
1084    fn reduce_min(
1085        &self,
1086        axes: Option<&[usize]>,
1087        session: &mut dyn Engine,
1088    ) -> tenferro_tensor::Result<Tensor>;
1089    /// Multiply over the selected axes on an engine. `None` reduces every axis and `Some(&[])` keeps the input shape.
1090    ///
1091    /// # Examples
1092    ///
1093    /// ```rust
1094    /// use tenferro_cpu::CpuEngine;
1095    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1096    ///
1097    ///
1098    /// let mut engine = CpuEngine::serial();
1099    /// let x = Tensor::from_vec_col_major(vec![2, 2], vec![1.0_f64, 5.0, 3.0, 2.0])?;
1100    /// let y = x.reduce_prod(Some(&[0]), &mut engine)?;
1101    /// assert_eq!(y.as_slice::<f64>()?, &[5.0, 6.0]);
1102    /// # Ok::<(), Box<dyn std::error::Error>>(())
1103    /// ```
1104    ///
1105    /// # Errors
1106    ///
1107    /// Returns [`tenferro_tensor::Error::Validation`] for an out-of-range or repeated axis, or
1108    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
1109    /// An unsupported dtype returns [`tenferro_tensor::Error::Unsupported`].
1110    fn reduce_prod(
1111        &self,
1112        axes: Option<&[usize]>,
1113        session: &mut dyn Engine,
1114    ) -> tenferro_tensor::Result<Tensor>;
1115    /// Sum elementwise squares over the selected axes on the engine (`f32`/`f64`). `None` reduces every axis and `Some(&[])` keeps the input shape.
1116    ///
1117    /// # Examples
1118    ///
1119    /// ```rust
1120    /// use tenferro_cpu::CpuEngine;
1121    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1122    ///
1123    ///
1124    /// let mut engine = CpuEngine::serial();
1125    /// let x = Tensor::from_vec_col_major(vec![2, 2], vec![1.0_f64, 5.0, 3.0, 2.0])?;
1126    /// let y = x.reduce_sum_squares(Some(&[0]), &mut engine)?;
1127    /// assert_eq!(y.as_slice::<f64>()?, &[26.0, 13.0]);
1128    /// # Ok::<(), Box<dyn std::error::Error>>(())
1129    /// ```
1130    ///
1131    /// # Errors
1132    ///
1133    /// Returns [`tenferro_tensor::Error::Validation`] for an out-of-range or repeated axis, or
1134    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
1135    /// An unsupported dtype returns [`tenferro_tensor::Error::Unsupported`].
1136    fn reduce_sum_squares(
1137        &self,
1138        axes: Option<&[usize]>,
1139        session: &mut dyn Engine,
1140    ) -> tenferro_tensor::Result<Tensor>;
1141    /// Broadcast this tensor into `shape`, mapping input axis `i` to output axis `dims[i]`, on an engine.
1142    ///
1143    /// # Examples
1144    ///
1145    /// ```rust
1146    /// use tenferro_cpu::CpuEngine;
1147    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1148    ///
1149    ///
1150    /// let mut engine = CpuEngine::serial();
1151    /// let x = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0])?;
1152    /// let y = x.broadcast_in_dim(&[2, 2], &[0], &mut engine)?;
1153    /// assert_eq!(y.as_slice::<f64>()?, &[1.0, 2.0, 1.0, 2.0]);
1154    /// # Ok::<(), Box<dyn std::error::Error>>(())
1155    /// ```
1156    ///
1157    /// # Errors
1158    ///
1159    /// Returns [`tenferro_tensor::Error::Validation`] for a dimension mapping that does not fit the input or target shape, or
1160    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
1161    fn broadcast_in_dim(
1162        &self,
1163        shape: &[usize],
1164        dims: &[usize],
1165        session: &mut dyn Engine,
1166    ) -> tenferro_tensor::Result<Tensor>;
1167    /// Keep the lower triangle (on and below diagonal `k`) of the trailing matrix axes on an engine.
1168    ///
1169    /// # Examples
1170    ///
1171    /// ```rust
1172    /// use tenferro_cpu::CpuEngine;
1173    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1174    ///
1175    ///
1176    /// let mut engine = CpuEngine::serial();
1177    /// let x = Tensor::from_vec_col_major(vec![2, 2], vec![1.0_f64, 2.0, 3.0, 4.0])?;
1178    /// let y = x.tril(0, &mut engine)?;
1179    /// assert_eq!(y.as_slice::<f64>()?, &[1.0, 2.0, 0.0, 4.0]);
1180    /// # Ok::<(), Box<dyn std::error::Error>>(())
1181    /// ```
1182    ///
1183    /// # Errors
1184    ///
1185    /// Returns [`tenferro_tensor::Error::Validation`] for an input of rank below 2, or
1186    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
1187    fn tril(&self, k: i64, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
1188    /// Keep the upper triangle (on and above diagonal `k`) of the trailing matrix axes on an engine.
1189    ///
1190    /// # Examples
1191    ///
1192    /// ```rust
1193    /// use tenferro_cpu::CpuEngine;
1194    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1195    ///
1196    ///
1197    /// let mut engine = CpuEngine::serial();
1198    /// let x = Tensor::from_vec_col_major(vec![2, 2], vec![1.0_f64, 2.0, 3.0, 4.0])?;
1199    /// let y = x.triu(0, &mut engine)?;
1200    /// assert_eq!(y.as_slice::<f64>()?, &[1.0, 0.0, 3.0, 4.0]);
1201    /// # Ok::<(), Box<dyn std::error::Error>>(())
1202    /// ```
1203    ///
1204    /// # Errors
1205    ///
1206    /// Returns [`tenferro_tensor::Error::Validation`] for an input of rank below 2, or
1207    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
1208    fn triu(&self, k: i64, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
1209    /// Extract the diagonal along `axis_a` and `axis_b` on an engine.
1210    ///
1211    /// # Examples
1212    ///
1213    /// ```rust
1214    /// use tenferro_cpu::CpuEngine;
1215    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1216    ///
1217    ///
1218    /// let mut engine = CpuEngine::serial();
1219    /// let x = Tensor::from_vec_col_major(vec![2, 2], vec![1.0_f64, 2.0, 3.0, 4.0])?;
1220    /// let y = x.extract_diag(0, 1, &mut engine)?;
1221    /// assert_eq!(y.as_slice::<f64>()?, &[1.0, 4.0]);
1222    /// # Ok::<(), Box<dyn std::error::Error>>(())
1223    /// ```
1224    ///
1225    /// # Errors
1226    ///
1227    /// Returns [`tenferro_tensor::Error::Validation`] for equal or out-of-range axes, or axes of different extent, or
1228    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
1229    fn extract_diag(
1230        &self,
1231        axis_a: usize,
1232        axis_b: usize,
1233        session: &mut dyn Engine,
1234    ) -> tenferro_tensor::Result<Tensor>;
1235    /// Embed this tensor along the diagonal of `axis_a` and `axis_b` on an engine.
1236    ///
1237    /// # Examples
1238    ///
1239    /// ```rust
1240    /// use tenferro_cpu::CpuEngine;
1241    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1242    ///
1243    ///
1244    /// let mut engine = CpuEngine::serial();
1245    /// let x = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0])?;
1246    /// let y = x.embed_diag(0, 1, &mut engine)?;
1247    /// assert_eq!(y.as_slice::<f64>()?, &[1.0, 0.0, 0.0, 2.0]);
1248    /// # Ok::<(), Box<dyn std::error::Error>>(())
1249    /// ```
1250    ///
1251    /// # Errors
1252    ///
1253    /// Returns [`tenferro_tensor::Error::Validation`] for invalid diagonal axes, or
1254    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
1255    fn embed_diag(
1256        &self,
1257        axis_a: usize,
1258        axis_b: usize,
1259        session: &mut dyn Engine,
1260    ) -> tenferro_tensor::Result<Tensor>;
1261    /// Contract this tensor with `rhs` on the engine (StableHLO `dot_general`).
1262    ///
1263    /// The output layout is `[lhs free..., rhs free..., batch...]` (batch axes trail).
1264    ///
1265    /// # Examples
1266    ///
1267    /// ```rust
1268    /// use tenferro_cpu::CpuEngine;
1269    /// use tenferro_runtime::{DotGeneralConfig, Tensor, TensorSessionOpsExt};
1270    ///
1271    ///
1272    /// let mut engine = CpuEngine::serial();
1273    /// let lhs = Tensor::from_vec_col_major(vec![1, 2], vec![2.0_f64, 3.0])?;
1274    /// let rhs = Tensor::from_vec_col_major(vec![2, 1], vec![4.0_f64, 5.0])?;
1275    /// let config = DotGeneralConfig {
1276    ///     lhs_contracting_dims: [1].as_slice().into(),
1277    ///     rhs_contracting_dims: [0].as_slice().into(),
1278    ///     lhs_batch_dims: [].as_slice().into(),
1279    ///     rhs_batch_dims: [].as_slice().into(),
1280    /// };
1281    /// let y = lhs.dot_general(&rhs, config, &mut engine)?;
1282    /// assert_eq!(y.as_slice::<f64>()?, &[23.0]);
1283    /// # Ok::<(), Box<dyn std::error::Error>>(())
1284    /// ```
1285    ///
1286    /// # Errors
1287    ///
1288    /// Returns [`tenferro_tensor::Error::Validation`] for incompatible contraction or batch dimensions or dtypes, or
1289    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
1290    fn dot_general(
1291        &self,
1292        rhs: &Tensor,
1293        config: DotGeneralConfig,
1294        session: &mut dyn Engine,
1295    ) -> tenferro_tensor::Result<Tensor>;
1296    /// Contract with optional conjugation of either operand on an engine.
1297    ///
1298    /// # Examples
1299    ///
1300    /// ```rust
1301    /// use tenferro_cpu::CpuEngine;
1302    /// use tenferro_runtime::{DotGeneralConfig, Tensor, TensorSessionOpsExt};
1303    ///
1304    ///
1305    /// let mut engine = CpuEngine::serial();
1306    /// use num_complex::Complex64;
1307    /// let lhs = Tensor::from_vec_col_major(vec![1, 1], vec![Complex64::new(0.0, 1.0)])?;
1308    /// let rhs = Tensor::from_vec_col_major(vec![1, 1], vec![Complex64::new(0.0, 1.0)])?;
1309    /// let config = DotGeneralConfig {
1310    ///     lhs_contracting_dims: [1].as_slice().into(),
1311    ///     rhs_contracting_dims: [0].as_slice().into(),
1312    ///     lhs_batch_dims: [].as_slice().into(),
1313    ///     rhs_batch_dims: [].as_slice().into(),
1314    /// };
1315    /// let y = lhs.dot_general_with_conj(&rhs, config, true, false, &mut engine)?;
1316    /// assert_eq!(y.as_slice::<Complex64>()?, &[Complex64::new(1.0, 0.0)]);
1317    /// # Ok::<(), Box<dyn std::error::Error>>(())
1318    /// ```
1319    ///
1320    /// # Errors
1321    ///
1322    /// Returns [`tenferro_tensor::Error::Validation`] for incompatible contraction or batch dimensions or dtypes, or
1323    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
1324    fn dot_general_with_conj(
1325        &self,
1326        rhs: &Tensor,
1327        config: DotGeneralConfig,
1328        lhs_conj: bool,
1329        rhs_conj: bool,
1330        session: &mut dyn Engine,
1331    ) -> tenferro_tensor::Result<Tensor>;
1332    /// Multiply by a real scalar on the engine, with the eager `scale_real` dtype rules.
1333    ///
1334    /// Integer dtypes round the factor.
1335    ///
1336    /// # Examples
1337    ///
1338    /// ```rust
1339    /// use tenferro_cpu::CpuEngine;
1340    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1341    ///
1342    ///
1343    /// let mut engine = CpuEngine::serial();
1344    /// let x = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0])?;
1345    /// let y = x.scale_real(2.0, &mut engine)?;
1346    /// assert_eq!(y.as_slice::<f64>()?, &[2.0, 4.0]);
1347    /// # Ok::<(), Box<dyn std::error::Error>>(())
1348    /// ```
1349    ///
1350    /// # Errors
1351    ///
1352    /// Returns [`tenferro_tensor::Error::Validation`] with `InvalidArgument` for a
1353    /// non-finite factor, an integer factor out of range, or an external dtype,
1354    /// or [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
1355    fn scale_real(&self, factor: f64, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
1356    /// Multiply a complex tensor by a complex scalar on an engine.
1357    ///
1358    /// # Examples
1359    ///
1360    /// ```rust
1361    /// use tenferro_cpu::CpuEngine;
1362    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1363    ///
1364    ///
1365    /// let mut engine = CpuEngine::serial();
1366    /// use num_complex::Complex64;
1367    /// let x = Tensor::from_vec_col_major(vec![1], vec![Complex64::new(1.0, 2.0)])?;
1368    /// let y = x.scale_complex(Complex64::new(0.0, 1.0), &mut engine)?;
1369    /// assert_eq!(y.as_slice::<Complex64>()?, &[Complex64::new(-2.0, 1.0)]);
1370    /// # Ok::<(), Box<dyn std::error::Error>>(())
1371    /// ```
1372    ///
1373    /// # Errors
1374    ///
1375    /// Returns [`tenferro_tensor::Error::Validation`] with `InvalidArgument` when
1376    /// the input dtype is not complex, or [`tenferro_tensor::Error::BackendSource`]
1377    /// for a typed backend failure.
1378    fn scale_complex(
1379        &self,
1380        factor: Complex64,
1381        session: &mut dyn Engine,
1382    ) -> tenferro_tensor::Result<Tensor>;
1383    /// Logistic sigmoid `1 / (1 + exp(-x))` on the engine, overflow-free.
1384    ///
1385    /// Evaluated as `1 / (1 + e)` for `x > 0` and `e / (1 + e)` otherwise, with
1386    /// `e = exp(-|x|)`. Real `F32`/`F64` only.
1387    ///
1388    /// # Examples
1389    ///
1390    /// ```rust
1391    /// use tenferro_cpu::CpuEngine;
1392    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1393    ///
1394    ///
1395    /// let mut engine = CpuEngine::serial();
1396    /// let x = Tensor::from_vec_col_major(vec![3], vec![-700.0_f64, 0.0, 1000.0])?;
1397    /// let y = x.sigmoid(&mut engine)?;
1398    /// let y = y.as_slice::<f64>()?;
1399    /// assert_eq!(y[1], 0.5);
1400    /// assert!(y[0] > 0.0 && y[0] < 1e-300);
1401    /// assert_eq!(y[2], 1.0);
1402    /// # Ok::<(), Box<dyn std::error::Error>>(())
1403    /// ```
1404    ///
1405    /// # Errors
1406    ///
1407    /// Returns [`tenferro_tensor::Error::UnsupportedDType`] for complex,
1408    /// integer, or `Bool` input, or [`tenferro_tensor::Error::BackendSource`]
1409    /// for a typed backend failure.
1410    fn sigmoid(&self, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
1411    /// SiLU (swish) `x * sigmoid(x)` on an engine.
1412    ///
1413    /// Real `F32`/`F64` only.
1414    ///
1415    /// # Examples
1416    ///
1417    /// ```rust
1418    /// use tenferro_cpu::CpuEngine;
1419    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1420    ///
1421    ///
1422    /// let mut engine = CpuEngine::serial();
1423    /// let x = Tensor::from_vec_col_major(vec![3], vec![-1.0_f64, 0.0, 1.0])?;
1424    /// let y = x.silu(&mut engine)?;
1425    /// let y = y.as_slice::<f64>()?;
1426    /// assert_eq!(y[1], 0.0);
1427    /// assert!((y[2] - 1.0 / (1.0 + (-1.0_f64).exp())).abs() < 1e-15);
1428    /// # Ok::<(), Box<dyn std::error::Error>>(())
1429    /// ```
1430    ///
1431    /// # Errors
1432    ///
1433    /// Returns [`tenferro_tensor::Error::UnsupportedDType`] for complex,
1434    /// integer, or `Bool` input, or [`tenferro_tensor::Error::BackendSource`]
1435    /// for a typed backend failure.
1436    fn silu(&self, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
1437    /// Softplus `log(1 + exp(x))` on the engine, in the stable form `max(x, 0) + log1p(exp(-|x|))`.
1438    ///
1439    /// Real `F32`/`F64` only; never overflows.
1440    ///
1441    /// # Examples
1442    ///
1443    /// ```rust
1444    /// use tenferro_cpu::CpuEngine;
1445    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1446    ///
1447    ///
1448    /// let mut engine = CpuEngine::serial();
1449    /// let x = Tensor::from_vec_col_major(vec![3], vec![-1000.0_f64, 0.0, 1000.0])?;
1450    /// let y = x.softplus(&mut engine)?;
1451    /// let y = y.as_slice::<f64>()?;
1452    /// assert_eq!(y[0], 0.0);
1453    /// assert!((y[1] - 2.0_f64.ln()).abs() < 1e-15);
1454    /// assert_eq!(y[2], 1000.0);
1455    /// # Ok::<(), Box<dyn std::error::Error>>(())
1456    /// ```
1457    ///
1458    /// # Errors
1459    ///
1460    /// Returns [`tenferro_tensor::Error::UnsupportedDType`] for complex,
1461    /// integer, or `Bool` input, or [`tenferro_tensor::Error::BackendSource`]
1462    /// for a typed backend failure.
1463    fn softplus(&self, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
1464    /// Exact GELU `x/2 * (1 + erf(x / sqrt(2)))` on an engine.
1465    ///
1466    /// Real `F32`/`F64` only (PyTorch `approximate="none"`).
1467    ///
1468    /// # Examples
1469    ///
1470    /// ```rust
1471    /// use tenferro_cpu::CpuEngine;
1472    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1473    ///
1474    ///
1475    /// let mut engine = CpuEngine::serial();
1476    /// let x = Tensor::from_vec_col_major(vec![3], vec![-1.0_f64, 0.0, 1.0])?;
1477    /// let y = x.gelu(&mut engine)?;
1478    /// let y = y.as_slice::<f64>()?;
1479    /// assert_eq!(y[1], 0.0);
1480    /// assert!((y[2] - 0.841_344_746_068_542_9).abs() < 1e-15);
1481    /// # Ok::<(), Box<dyn std::error::Error>>(())
1482    /// ```
1483    ///
1484    /// # Errors
1485    ///
1486    /// Returns [`tenferro_tensor::Error::UnsupportedDType`] for complex,
1487    /// integer, or `Bool` input, or [`tenferro_tensor::Error::BackendSource`]
1488    /// for a typed backend failure.
1489    fn gelu(&self, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
1490    /// GELU tanh approximation on the engine (PyTorch `approximate="tanh"`).
1491    ///
1492    /// `x/2 * (1 + tanh(sqrt(2/pi) * (x + 0.044715 x^3)))`; real `F32`/`F64` only.
1493    ///
1494    /// # Examples
1495    ///
1496    /// ```rust
1497    /// use tenferro_cpu::CpuEngine;
1498    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1499    ///
1500    ///
1501    /// let mut engine = CpuEngine::serial();
1502    /// let x = Tensor::from_vec_col_major(vec![3], vec![-1.0_f64, 0.0, 1.0])?;
1503    /// let y = x.gelu_tanh(&mut engine)?;
1504    /// let y = y.as_slice::<f64>()?;
1505    /// assert_eq!(y[1], 0.0);
1506    /// assert!((y[2] - 0.841_191_990_608_276_8).abs() < 1e-12);
1507    /// # Ok::<(), Box<dyn std::error::Error>>(())
1508    /// ```
1509    ///
1510    /// # Errors
1511    ///
1512    /// Returns [`tenferro_tensor::Error::UnsupportedDType`] for complex,
1513    /// integer, or `Bool` input, or [`tenferro_tensor::Error::BackendSource`]
1514    /// for a typed backend failure.
1515    fn gelu_tanh(&self, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
1516    /// Arithmetic mean over `axes` on the engine (`None` reduces every axis).
1517    ///
1518    /// Float and complex dtypes. The sum is divided by the element count; a mean
1519    /// over zero elements is `NaN`, and `Some(&[])` is the identity.
1520    ///
1521    /// # Examples
1522    ///
1523    /// ```rust
1524    /// use tenferro_cpu::CpuEngine;
1525    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1526    ///
1527    ///
1528    /// let mut engine = CpuEngine::serial();
1529    /// let x = Tensor::from_vec_col_major(vec![2, 2], vec![1.0_f64, 2.0, 3.0, 4.0])?;
1530    /// let y = x.reduce_mean(Some(&[1]), &mut engine)?;
1531    /// assert_eq!(y.as_slice::<f64>()?, &[2.0, 3.0]);
1532    /// # Ok::<(), Box<dyn std::error::Error>>(())
1533    /// ```
1534    ///
1535    /// # Errors
1536    ///
1537    /// Returns [`tenferro_tensor::Error::UnsupportedDType`] for integer or
1538    /// `Bool` input, [`tenferro_tensor::Error::Validation`] with
1539    /// `AxisOutOfBounds` or `DuplicateAxis` for invalid axes, or
1540    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
1541    fn reduce_mean(
1542        &self,
1543        axes: Option<&[usize]>,
1544        session: &mut dyn Engine,
1545    ) -> tenferro_tensor::Result<Tensor>;
1546    /// Max-subtracted softmax along `axis` on an engine.
1547    ///
1548    /// Real `F32`/`F64` only. A slice that is entirely `-inf` returns zeros
1549    /// instead of `NaN`; a `NaN` or `+inf` entry makes its slice `NaN`.
1550    ///
1551    /// # Examples
1552    ///
1553    /// ```rust
1554    /// use tenferro_cpu::CpuEngine;
1555    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1556    ///
1557    ///
1558    /// let mut engine = CpuEngine::serial();
1559    /// let x = Tensor::from_vec_col_major(vec![2], vec![0.0_f64, f64::NEG_INFINITY])?;
1560    /// let y = x.softmax(0, &mut engine)?;
1561    /// assert_eq!(y.as_slice::<f64>()?, &[1.0, 0.0]);
1562    /// # Ok::<(), Box<dyn std::error::Error>>(())
1563    /// ```
1564    ///
1565    /// # Errors
1566    ///
1567    /// Returns [`tenferro_tensor::Error::UnsupportedDType`] for complex,
1568    /// integer, or `Bool` input, [`tenferro_tensor::Error::Validation`] with
1569    /// `AxisOutOfBounds` for an invalid axis, or
1570    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
1571    fn softmax(&self, axis: usize, session: &mut dyn Engine) -> tenferro_tensor::Result<Tensor>;
1572    /// Max-subtracted log-softmax along `axis` on an engine.
1573    ///
1574    /// Real `F32`/`F64` only. A slice that is entirely `-inf` returns `-inf`
1575    /// instead of `NaN`.
1576    ///
1577    /// # Examples
1578    ///
1579    /// ```rust
1580    /// use tenferro_cpu::CpuEngine;
1581    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1582    ///
1583    ///
1584    /// let mut engine = CpuEngine::serial();
1585    /// let x = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 1.0])?;
1586    /// let y = x.log_softmax(0, &mut engine)?;
1587    /// assert_eq!(y.as_slice::<f64>()?, &[-std::f64::consts::LN_2; 2]);
1588    /// # Ok::<(), Box<dyn std::error::Error>>(())
1589    /// ```
1590    ///
1591    /// # Errors
1592    ///
1593    /// Returns [`tenferro_tensor::Error::UnsupportedDType`] for complex,
1594    /// integer, or `Bool` input, [`tenferro_tensor::Error::Validation`] with
1595    /// `AxisOutOfBounds` for an invalid axis, or
1596    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
1597    fn log_softmax(&self, axis: usize, session: &mut dyn Engine)
1598        -> tenferro_tensor::Result<Tensor>;
1599    /// Softmax along `axis` over the entries where the `Bool` `mask` is true.
1600    ///
1601    /// `mask` broadcasts to the input shape. Masked-out entries are `0` whatever
1602    /// their value; a slice with no unmasked entry is all zeros.
1603    ///
1604    /// # Examples
1605    ///
1606    /// ```rust
1607    /// use tenferro_cpu::CpuEngine;
1608    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1609    ///
1610    ///
1611    /// let mut engine = CpuEngine::serial();
1612    /// let x = Tensor::from_vec_col_major(vec![3], vec![1.0_f64, 1.0, f64::NAN])?;
1613    /// let mask = Tensor::from_vec_col_major(vec![3], vec![true, true, false])?;
1614    /// let y = x.masked_softmax(&mask, 0, &mut engine)?;
1615    /// assert_eq!(y.as_slice::<f64>()?, &[0.5, 0.5, 0.0]);
1616    /// # Ok::<(), Box<dyn std::error::Error>>(())
1617    /// ```
1618    ///
1619    /// # Errors
1620    ///
1621    /// Returns [`tenferro_tensor::Error::UnsupportedDType`] for complex,
1622    /// integer, or `Bool` input, [`tenferro_tensor::Error::Validation`] with
1623    /// `DTypeMismatch` for a non-`Bool` mask, `ShapeMismatch` for a mask that
1624    /// does not broadcast to the input, or `AxisOutOfBounds` for an invalid
1625    /// axis, or [`tenferro_tensor::Error::BackendSource`] for a typed backend
1626    /// failure.
1627    fn masked_softmax(
1628        &self,
1629        mask: &Tensor,
1630        axis: usize,
1631        session: &mut dyn Engine,
1632    ) -> tenferro_tensor::Result<Tensor>;
1633    /// Log-softmax along `axis` over the entries where the `Bool` `mask` is true.
1634    ///
1635    /// Masked-out entries are `-inf`; a slice with no unmasked entry is all `-inf`.
1636    ///
1637    /// # Examples
1638    ///
1639    /// ```rust
1640    /// use tenferro_cpu::CpuEngine;
1641    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1642    ///
1643    ///
1644    /// let mut engine = CpuEngine::serial();
1645    /// let x = Tensor::from_vec_col_major(vec![3], vec![1.0_f64, 1.0, 5.0])?;
1646    /// let mask = Tensor::from_vec_col_major(vec![3], vec![true, true, false])?;
1647    /// let y = x.masked_log_softmax(&mask, 0, &mut engine)?;
1648    /// let ln_half = -std::f64::consts::LN_2;
1649    /// assert_eq!(y.as_slice::<f64>()?, &[ln_half, ln_half, f64::NEG_INFINITY]);
1650    /// # Ok::<(), Box<dyn std::error::Error>>(())
1651    /// ```
1652    ///
1653    /// # Errors
1654    ///
1655    /// Returns [`tenferro_tensor::Error::UnsupportedDType`] for complex,
1656    /// integer, or `Bool` input, [`tenferro_tensor::Error::Validation`] with
1657    /// `DTypeMismatch` for a non-`Bool` mask, `ShapeMismatch` for a mask that
1658    /// does not broadcast to the input, or `AxisOutOfBounds` for an invalid
1659    /// axis, or [`tenferro_tensor::Error::BackendSource`] for a typed backend
1660    /// failure.
1661    fn masked_log_softmax(
1662        &self,
1663        mask: &Tensor,
1664        axis: usize,
1665        session: &mut dyn Engine,
1666    ) -> tenferro_tensor::Result<Tensor>;
1667    /// Layer normalization along `axis` with optional affine `weight` / `bias`, on an engine.
1668    ///
1669    /// `(x - mean) / sqrt(var + eps) * weight + bias` with the biased variance;
1670    /// `weight` and `bias` are rank-1 of length `shape[axis]`. Real `F32`/`F64` only.
1671    ///
1672    /// # Examples
1673    ///
1674    /// ```rust
1675    /// use tenferro_cpu::CpuEngine;
1676    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1677    ///
1678    ///
1679    /// let mut engine = CpuEngine::serial();
1680    /// let x = Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 3.0])?;
1681    /// let bias = Tensor::from_vec_col_major(vec![2], vec![10.0_f64, 10.0])?;
1682    /// let y = x.layer_norm(0, None, Some(&bias), 0.0, &mut engine)?;
1683    /// assert_eq!(y.as_slice::<f64>()?, &[9.0, 11.0]);
1684    /// # Ok::<(), Box<dyn std::error::Error>>(())
1685    /// ```
1686    ///
1687    /// # Errors
1688    ///
1689    /// Returns [`tenferro_tensor::Error::UnsupportedDType`] for complex,
1690    /// integer, or `Bool` input, [`tenferro_tensor::Error::Validation`] with
1691    /// `AxisOutOfBounds` for an invalid axis, `InvalidArgument` for a negative
1692    /// or non-finite `eps`, or `DTypeMismatch` / `ShapeMismatch` for a weight or
1693    /// bias that is not a same-dtype vector of the axis length, or
1694    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
1695    fn layer_norm(
1696        &self,
1697        axis: usize,
1698        weight: Option<&Tensor>,
1699        bias: Option<&Tensor>,
1700        eps: f64,
1701        session: &mut dyn Engine,
1702    ) -> tenferro_tensor::Result<Tensor>;
1703    /// RMS normalization along `axis` with optional affine `weight` / `bias`, on an engine.
1704    ///
1705    /// `x / sqrt(mean(x^2) + eps) * weight + bias`; `weight` and `bias` are rank-1
1706    /// of length `shape[axis]`. Real `F32`/`F64` only.
1707    ///
1708    /// # Examples
1709    ///
1710    /// ```rust
1711    /// use tenferro_cpu::CpuEngine;
1712    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1713    ///
1714    ///
1715    /// let mut engine = CpuEngine::serial();
1716    /// let x = Tensor::from_vec_col_major(vec![2], vec![3.0_f64, 4.0])?;
1717    /// let weight = Tensor::from_vec_col_major(vec![2], vec![2.0_f64, 1.0])?;
1718    /// let y = x.rms_norm(0, Some(&weight), None, 0.0, &mut engine)?;
1719    /// let y = y.as_slice::<f64>()?;
1720    /// let rms = 12.5_f64.sqrt();
1721    /// assert!((y[0] - 6.0 / rms).abs() < 1e-15 && (y[1] - 4.0 / rms).abs() < 1e-15);
1722    /// # Ok::<(), Box<dyn std::error::Error>>(())
1723    /// ```
1724    ///
1725    /// # Errors
1726    ///
1727    /// Returns [`tenferro_tensor::Error::UnsupportedDType`] for complex,
1728    /// integer, or `Bool` input, [`tenferro_tensor::Error::Validation`] with
1729    /// `AxisOutOfBounds` for an invalid axis, `InvalidArgument` for a negative
1730    /// or non-finite `eps`, or `DTypeMismatch` / `ShapeMismatch` for a weight or
1731    /// bias that is not a same-dtype vector of the axis length, or
1732    /// [`tenferro_tensor::Error::BackendSource`] for a typed backend failure.
1733    fn rms_norm(
1734        &self,
1735        axis: usize,
1736        weight: Option<&Tensor>,
1737        bias: Option<&Tensor>,
1738        eps: f64,
1739        session: &mut dyn Engine,
1740    ) -> tenferro_tensor::Result<Tensor>;
1741    /// NumPy-style `take_along_axis` over `gather`, on an engine.
1742    ///
1743    /// `out[.., i, ..] = self[.., indices[.., i, ..], ..]` along `axis`. `indices`
1744    /// (I32/I64) has the input's rank; every other dimension is either the
1745    /// input's extent (batch-varying indices) or `1` (the whole extent is taken).
1746    /// Indices must be in bounds.
1747    ///
1748    /// # Examples
1749    ///
1750    /// ```rust
1751    /// use tenferro_cpu::CpuEngine;
1752    /// use tenferro_runtime::{Tensor, TensorSessionOpsExt};
1753    ///
1754    ///
1755    /// let mut engine = CpuEngine::serial();
1756    /// // Per-batch row gather: out[i, j, b] = x[idx[i, b], j, b].
1757    /// let x = Tensor::from_vec_col_major(vec![2, 2, 2], (0..8).map(f64::from).collect::<Vec<_>>())?;
1758    /// let idx = Tensor::from_vec_col_major(vec![2, 1, 2], vec![1_i64, 0, 0, 0])?;
1759    /// let y = x.take_along_axis(&idx, 0, &mut engine)?;
1760    /// assert_eq!(y.as_slice::<f64>()?, &[1.0, 0.0, 3.0, 2.0, 4.0, 4.0, 6.0, 6.0]);
1761    /// # Ok::<(), Box<dyn std::error::Error>>(())
1762    /// ```
1763    ///
1764    /// # Errors
1765    ///
1766    /// Returns [`tenferro_tensor::Error::Validation`] with `RankMismatch` or
1767    /// `ShapeMismatch` for incompatible index shapes, `AxisOutOfBounds` for an
1768    /// invalid axis, or `InvalidArgument` when taking from a zero-length axis;
1769    /// [`tenferro_tensor::Error::UnsupportedDType`] for a non-integer index
1770    /// dtype; or [`tenferro_tensor::Error::BackendSource`] for a typed backend
1771    /// failure.
1772    fn take_along_axis(
1773        &self,
1774        indices: &Tensor,
1775        axis: usize,
1776        session: &mut dyn Engine,
1777    ) -> tenferro_tensor::Result<Tensor>;
1778}