Skip to main content

tenferro_runtime/
typed_session_ext.rs

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