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}