Skip to main content

tenferro_ad/
eager_ops_elementwise.rs

1use tenferro_ops::std_tensor_op::StdTensorOp;
2
3use crate::eager::EagerTensor;
4use crate::eager_ops::{broadcast_binary, broadcast_ternary};
5use crate::error::Result;
6use crate::CompareDir;
7
8impl EagerTensor {
9    /// Elementwise absolute value.
10    ///
11    /// # Examples
12    ///
13    /// ```
14    /// use tenferro_cpu::CpuBackend;
15    /// use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};
16    ///
17    /// let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
18    /// let x = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![2], vec![-1.0_f64, 2.0]).unwrap(), ctx.clone()).unwrap();
19    /// let y = x.abs().unwrap();
20    ///
21    /// assert_eq!(y.materialized().unwrap().as_slice::<f64>().unwrap(), &[1.0, 2.0]);
22    /// # Ok::<(), tenferro_ad::Error>(())
23    /// ```
24    /// # Errors
25    ///
26    /// Returns [`tenferro_tensor::Error::Unsupported`] when the dtype has no
27    /// absolute-value implementation, or a typed backend/runtime-state error.
28    pub fn abs(&self) -> Result<Self> {
29        self.unary_op(StdTensorOp::Abs)
30    }
31
32    /// Elementwise complex conjugate.
33    ///
34    /// # Examples
35    ///
36    /// ```
37    /// use tenferro_cpu::CpuBackend;
38    /// use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};
39    ///
40    /// let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
41    /// let x = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![2], vec![1.0_f64, -2.0]).unwrap(), ctx.clone()).unwrap();
42    /// let y = x.conj().unwrap();
43    ///
44    /// assert_eq!(y.materialized().unwrap().as_slice::<f64>().unwrap(), &[1.0, -2.0]);
45    /// # Ok::<(), tenferro_ad::Error>(())
46    /// ```
47    /// # Errors
48    ///
49    /// Returns [`tenferro_tensor::Error::Unsupported`] when conjugation is not
50    /// defined for the dtype, or a typed backend/runtime-state error.
51    pub fn conj(&self) -> Result<Self> {
52        self.unary_op(StdTensorOp::Conj)
53    }
54
55    /// Elementwise sign.
56    ///
57    /// # Examples
58    ///
59    /// ```
60    /// use tenferro_cpu::CpuBackend;
61    /// use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};
62    ///
63    /// let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
64    /// let x = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![2], vec![-2.0_f64, 3.0]).unwrap(), ctx.clone()).unwrap();
65    /// let y = x.sign().unwrap();
66    ///
67    /// assert_eq!(y.materialized().unwrap().as_slice::<f64>().unwrap(), &[-1.0, 1.0]);
68    /// # Ok::<(), tenferro_ad::Error>(())
69    /// ```
70    /// # Errors
71    ///
72    /// Returns [`tenferro_tensor::Error::Unsupported`] when sign is not
73    /// defined for the dtype, or a typed backend/runtime-state error.
74    pub fn sign(&self) -> Result<Self> {
75        self.unary_op(StdTensorOp::Sign)
76    }
77
78    /// Elementwise natural logarithm.
79    ///
80    /// # Examples
81    ///
82    /// ```
83    /// use tenferro_cpu::CpuBackend;
84    /// use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};
85    ///
86    /// let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
87    /// let x = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![1], vec![1.0_f64]).unwrap(), ctx.clone()).unwrap();
88    /// let y = x.log().unwrap();
89    ///
90    /// assert_eq!(y.materialized().unwrap().as_slice::<f64>().unwrap(), &[0.0]);
91    /// # Ok::<(), tenferro_ad::Error>(())
92    /// ```
93    /// # Errors
94    ///
95    /// Returns [`tenferro_tensor::Error::Unsupported`] for an unsupported
96    /// dtype, or a typed backend/runtime-state error during execution.
97    pub fn log(&self) -> Result<Self> {
98        self.unary_op(StdTensorOp::Log)
99    }
100
101    /// Elementwise square root.
102    ///
103    /// # Examples
104    ///
105    /// ```
106    /// use tenferro_cpu::CpuBackend;
107    /// use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};
108    ///
109    /// let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
110    /// let x = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![1], vec![4.0_f64]).unwrap(), ctx.clone()).unwrap();
111    /// let y = x.sqrt().unwrap();
112    ///
113    /// assert_eq!(y.materialized().unwrap().as_slice::<f64>().unwrap(), &[2.0]);
114    /// # Ok::<(), tenferro_ad::Error>(())
115    /// ```
116    /// # Errors
117    ///
118    /// Returns [`tenferro_tensor::Error::Unsupported`] for an unsupported
119    /// dtype, or a typed backend/runtime-state error during execution.
120    pub fn sqrt(&self) -> Result<Self> {
121        self.unary_op(StdTensorOp::Sqrt)
122    }
123
124    /// Elementwise reciprocal square root.
125    ///
126    /// # Examples
127    ///
128    /// ```
129    /// use tenferro_cpu::CpuBackend;
130    /// use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};
131    ///
132    /// let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
133    /// let x = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![1], vec![4.0_f64]).unwrap(), ctx.clone()).unwrap();
134    /// let y = x.rsqrt().unwrap();
135    ///
136    /// assert_eq!(y.materialized().unwrap().as_slice::<f64>().unwrap(), &[0.5]);
137    /// # Ok::<(), tenferro_ad::Error>(())
138    /// ```
139    /// # Errors
140    ///
141    /// Returns [`tenferro_tensor::Error::Unsupported`] for an unsupported
142    /// dtype, or a typed backend/runtime-state error during execution.
143    pub fn rsqrt(&self) -> Result<Self> {
144        self.unary_op(StdTensorOp::Rsqrt)
145    }
146
147    /// Elementwise sine.
148    ///
149    /// # Examples
150    ///
151    /// ```
152    /// use tenferro_cpu::CpuBackend;
153    /// use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};
154    ///
155    /// let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
156    /// let x = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![1], vec![0.0_f64]).unwrap(), ctx.clone()).unwrap();
157    /// let y = x.sin().unwrap();
158    ///
159    /// assert_eq!(y.materialized().unwrap().as_slice::<f64>().unwrap(), &[0.0]);
160    /// # Ok::<(), tenferro_ad::Error>(())
161    /// ```
162    /// # Errors
163    ///
164    /// Returns [`tenferro_tensor::Error::Unsupported`] for an unsupported
165    /// dtype, or a typed backend/runtime-state error during execution.
166    pub fn sin(&self) -> Result<Self> {
167        self.unary_op(StdTensorOp::Sin)
168    }
169
170    /// Elementwise cosine.
171    ///
172    /// # Examples
173    ///
174    /// ```
175    /// use tenferro_cpu::CpuBackend;
176    /// use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};
177    ///
178    /// let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
179    /// let x = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![1], vec![0.0_f64]).unwrap(), ctx.clone()).unwrap();
180    /// let y = x.cos().unwrap();
181    ///
182    /// assert_eq!(y.materialized().unwrap().as_slice::<f64>().unwrap(), &[1.0]);
183    /// # Ok::<(), tenferro_ad::Error>(())
184    /// ```
185    /// # Errors
186    ///
187    /// Returns [`tenferro_tensor::Error::Unsupported`] for an unsupported
188    /// dtype, or a typed backend/runtime-state error during execution.
189    pub fn cos(&self) -> Result<Self> {
190        self.unary_op(StdTensorOp::Cos)
191    }
192
193    /// Elementwise hyperbolic tangent.
194    ///
195    /// # Examples
196    ///
197    /// ```
198    /// use tenferro_cpu::CpuBackend;
199    /// use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};
200    ///
201    /// let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
202    /// let x = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![1], vec![0.0_f64]).unwrap(), ctx.clone()).unwrap();
203    /// let y = x.tanh().unwrap();
204    ///
205    /// assert_eq!(y.materialized().unwrap().as_slice::<f64>().unwrap(), &[0.0]);
206    /// # Ok::<(), tenferro_ad::Error>(())
207    /// ```
208    /// # Errors
209    ///
210    /// Returns [`tenferro_tensor::Error::Unsupported`] for an unsupported
211    /// dtype, or a typed backend/runtime-state error during execution.
212    pub fn tanh(&self) -> Result<Self> {
213        self.unary_op(StdTensorOp::Tanh)
214    }
215
216    /// Elementwise `exp(x) - 1`.
217    ///
218    /// # Examples
219    ///
220    /// ```
221    /// use tenferro_cpu::CpuBackend;
222    /// use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};
223    ///
224    /// let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
225    /// let x = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![1], vec![0.0_f64]).unwrap(), ctx.clone()).unwrap();
226    /// let y = x.expm1().unwrap();
227    ///
228    /// assert_eq!(y.materialized().unwrap().as_slice::<f64>().unwrap(), &[0.0]);
229    /// # Ok::<(), tenferro_ad::Error>(())
230    /// ```
231    /// # Errors
232    ///
233    /// Returns [`tenferro_tensor::Error::Unsupported`] for an unsupported
234    /// dtype, or a typed backend/runtime-state error during execution.
235    pub fn expm1(&self) -> Result<Self> {
236        self.unary_op(StdTensorOp::Expm1)
237    }
238
239    /// Elementwise `log(1 + x)`.
240    ///
241    /// # Examples
242    ///
243    /// ```
244    /// use tenferro_cpu::CpuBackend;
245    /// use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};
246    ///
247    /// let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
248    /// let x = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![1], vec![0.0_f64]).unwrap(), ctx.clone()).unwrap();
249    /// let y = x.log1p().unwrap();
250    ///
251    /// assert_eq!(y.materialized().unwrap().as_slice::<f64>().unwrap(), &[0.0]);
252    /// # Ok::<(), tenferro_ad::Error>(())
253    /// ```
254    /// # Errors
255    ///
256    /// Returns [`tenferro_tensor::Error::Unsupported`] for an unsupported
257    /// dtype, or a typed backend/runtime-state error during execution.
258    pub fn log1p(&self) -> Result<Self> {
259        self.unary_op(StdTensorOp::Log1p)
260    }
261
262    /// Elementwise division.
263    ///
264    /// # Examples
265    ///
266    /// ```
267    /// use tenferro_cpu::CpuBackend;
268    /// use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};
269    ///
270    /// let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
271    /// let x = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![3], vec![8.0_f64, -6.0, 9.0]).unwrap(), ctx.clone()).unwrap();
272    /// let y = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![3], vec![2.0_f64, 3.0, 3.0]).unwrap(), ctx.clone()).unwrap();
273    /// let z = x.div(&y).unwrap();
274    ///
275    /// assert_eq!(z.materialized().unwrap().as_slice::<f64>().unwrap(), &[4.0, -2.0, 3.0]);
276    /// # Ok::<(), tenferro_ad::Error>(())
277    /// ```
278    /// # Errors
279    ///
280    /// Returns [`crate::error::Error::ContextMismatch`] for different eager runtimes,
281    /// [`tenferro_tensor::ValidationError::ShapeMismatch`] or
282    /// `ValidationError::DTypeMismatch` for
283    /// incompatible operands, or a typed backend/runtime-state error. Addition
284    /// does not have a zero-divisor failure; numerical zero-divisor errors are
285    /// specific to division and remainder.
286    pub fn div(&self, other: &Self) -> Result<Self> {
287        let (lhs, rhs) = broadcast_binary("div", self, other)?;
288        lhs.binary_op(&rhs, StdTensorOp::Div)
289    }
290
291    /// Elementwise remainder.
292    /// # Errors
293    ///
294    /// Returns [`crate::error::Error::ContextMismatch`] for different eager runtimes,
295    /// [`tenferro_tensor::ValidationError::ShapeMismatch`] or
296    /// `ValidationError::DTypeMismatch` for
297    /// incompatible operands, or a typed backend/runtime-state error.
298    /// Subtraction does not have a zero-divisor failure; numerical
299    /// zero-divisor errors are specific to division and remainder.
300    pub fn rem(&self, other: &Self) -> Result<Self> {
301        let (lhs, rhs) = broadcast_binary("rem", self, other)?;
302        lhs.binary_op(&rhs, StdTensorOp::Rem)
303    }
304
305    /// Elementwise power.
306    ///
307    /// # Examples
308    ///
309    /// ```
310    /// use tenferro_cpu::CpuBackend;
311    /// use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};
312    ///
313    /// let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
314    /// let base = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![2], vec![2.0_f64, 3.0]).unwrap(), ctx.clone()).unwrap();
315    /// let exp = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![2], vec![3.0_f64, 2.0]).unwrap(), ctx.clone()).unwrap();
316    /// let y = base.pow(&exp).unwrap();
317    ///
318    /// assert_eq!(y.materialized().unwrap().as_slice::<f64>().unwrap(), &[8.0, 9.0]);
319    /// # Ok::<(), tenferro_ad::Error>(())
320    /// ```
321    /// # Errors
322    ///
323    /// Returns [`crate::error::Error::ContextMismatch`] for different eager runtimes,
324    /// [`tenferro_tensor::ValidationError::ShapeMismatch`] or
325    /// `ValidationError::DTypeMismatch` for
326    /// incompatible operands, `NumericalFailure` for a checked invalid power,
327    /// or a typed backend/runtime-state error.
328    pub fn pow(&self, other: &Self) -> Result<Self> {
329        let (lhs, rhs) = broadcast_binary("pow", self, other)?;
330        lhs.binary_op(&rhs, StdTensorOp::Pow)
331    }
332
333    /// Elementwise maximum.
334    ///
335    /// # Examples
336    ///
337    /// ```
338    /// use tenferro_cpu::CpuBackend;
339    /// use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};
340    ///
341    /// let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
342    /// let x = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 5.0]).unwrap(), ctx.clone()).unwrap();
343    /// let y = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![2], vec![3.0_f64, 4.0]).unwrap(), ctx.clone()).unwrap();
344    /// let z = x.maximum(&y).unwrap();
345    ///
346    /// assert_eq!(z.materialized().unwrap().as_slice::<f64>().unwrap(), &[3.0, 5.0]);
347    /// # Ok::<(), tenferro_ad::Error>(())
348    /// ```
349    /// # Errors
350    ///
351    /// Returns [`crate::error::Error::ContextMismatch`] for different eager runtimes,
352    /// [`tenferro_tensor::ValidationError::ShapeMismatch`] or
353    /// `ValidationError::DTypeMismatch` for
354    /// incompatible operands, or a typed unsupported/backend/runtime-state
355    /// error.
356    pub fn maximum(&self, other: &Self) -> Result<Self> {
357        let (lhs, rhs) = broadcast_binary("maximum", self, other)?;
358        lhs.binary_op(&rhs, StdTensorOp::Maximum)
359    }
360
361    /// Elementwise minimum.
362    ///
363    /// # Examples
364    ///
365    /// ```
366    /// use tenferro_cpu::CpuBackend;
367    /// use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};
368    ///
369    /// let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
370    /// let x = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 5.0]).unwrap(), ctx.clone()).unwrap();
371    /// let y = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![2], vec![3.0_f64, 4.0]).unwrap(), ctx.clone()).unwrap();
372    /// let z = x.minimum(&y).unwrap();
373    ///
374    /// assert_eq!(z.materialized().unwrap().as_slice::<f64>().unwrap(), &[1.0, 4.0]);
375    /// # Ok::<(), tenferro_ad::Error>(())
376    /// ```
377    /// # Errors
378    ///
379    /// Returns [`crate::error::Error::ContextMismatch`] for different eager runtimes,
380    /// [`tenferro_tensor::ValidationError::ShapeMismatch`] or
381    /// `ValidationError::DTypeMismatch` for
382    /// incompatible operands, or a typed unsupported/backend/runtime-state
383    /// error.
384    pub fn minimum(&self, other: &Self) -> Result<Self> {
385        let (lhs, rhs) = broadcast_binary("minimum", self, other)?;
386        lhs.binary_op(&rhs, StdTensorOp::Minimum)
387    }
388
389    /// Elementwise comparison.
390    /// # Errors
391    ///
392    /// Returns [`crate::error::Error::ContextMismatch`] for different eager runtimes,
393    /// [`tenferro_tensor::ValidationError::ShapeMismatch`] or
394    /// `ValidationError::DTypeMismatch` for
395    /// incompatible operands, or a typed unsupported/backend/runtime-state
396    /// error.
397    pub fn compare(&self, other: &Self, dir: CompareDir) -> Result<Self> {
398        let (lhs, rhs) = broadcast_binary("compare", self, other)?;
399        lhs.binary_op(&rhs, StdTensorOp::Compare(dir))
400    }
401
402    /// Select values from `on_true` or `on_false` using `condition`.
403    ///
404    /// # Examples
405    ///
406    /// ```
407    /// use tenferro_cpu::CpuBackend;
408    /// use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};
409    ///
410    /// let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
411    /// let condition = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![2], vec![false, true]).unwrap(), ctx.clone()).unwrap();
412    /// let on_true = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![2], vec![10.0_f64, 20.0]).unwrap(), ctx.clone()).unwrap();
413    /// let on_false = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![2], vec![1.0_f64, 2.0]).unwrap(), ctx.clone()).unwrap();
414    /// let y = EagerTensor::select(&condition, &on_true, &on_false).unwrap();
415    ///
416    /// assert_eq!(y.materialized().unwrap().as_slice::<f64>().unwrap(), &[1.0, 20.0]);
417    /// # Ok::<(), tenferro_ad::Error>(())
418    /// ```
419    /// # Errors
420    ///
421    /// Returns [`crate::error::Error::ContextMismatch`] for different eager runtimes,
422    /// [`tenferro_tensor::ValidationError::ShapeMismatch`] when the three operands do not
423    /// broadcast, `DTypeMismatch` for incompatible value dtypes, or a typed
424    /// backend/runtime-state error.
425    pub fn select(condition: &Self, on_true: &Self, on_false: &Self) -> Result<Self> {
426        Self::where_select(condition, on_true, on_false)
427    }
428
429    /// Select values from `on_true` or `on_false` using `condition`.
430    /// # Errors
431    ///
432    /// Returns [`crate::error::Error::ContextMismatch`] for different eager runtimes,
433    /// [`tenferro_tensor::ValidationError::ShapeMismatch`] when the three operands do not
434    /// broadcast, `DTypeMismatch` for incompatible value dtypes, or a typed
435    /// backend/runtime-state error.
436    pub fn where_select(condition: &Self, on_true: &Self, on_false: &Self) -> Result<Self> {
437        let (condition, on_true, on_false) =
438            broadcast_ternary("where_select", condition, on_true, on_false)?;
439        condition.ternary_op(&on_true, &on_false, StdTensorOp::Select)
440    }
441
442    /// Clamp values elementwise between lower and upper bounds.
443    ///
444    /// # Examples
445    ///
446    /// ```
447    /// use tenferro_cpu::CpuBackend;
448    /// use tenferro_ad::{EagerRuntime, EagerTensor, Tensor};
449    ///
450    /// let ctx = EagerRuntime::with_cpu_backend(CpuBackend::new())?;
451    /// let x = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![3], vec![-2.0_f64, 0.5, 5.0]).unwrap(), ctx.clone()).unwrap();
452    /// let lower = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![3], vec![-1.0_f64, 0.0, 1.0]).unwrap(), ctx.clone()).unwrap();
453    /// let upper = EagerTensor::from_tensor_in(Tensor::from_vec_col_major(vec![3], vec![1.0_f64, 2.0, 4.0]).unwrap(), ctx.clone()).unwrap();
454    /// let y = x.clamp(&lower, &upper).unwrap();
455    ///
456    /// assert_eq!(y.materialized().unwrap().as_slice::<f64>().unwrap(), &[-1.0, 0.5, 4.0]);
457    /// # Ok::<(), tenferro_ad::Error>(())
458    /// ```
459    /// # Errors
460    ///
461    /// Returns [`crate::error::Error::ContextMismatch`] for different eager runtimes,
462    /// [`tenferro_tensor::ValidationError::ShapeMismatch`] when the three operands do not
463    /// broadcast, `DTypeMismatch` for incompatible bounds, or a typed
464    /// unsupported/backend/runtime-state error.
465    pub fn clamp(&self, lower: &Self, upper: &Self) -> Result<Self> {
466        let (input, lower, upper) = broadcast_ternary("clamp", self, lower, upper)?;
467        input.ternary_op(&lower, &upper, StdTensorOp::Clamp)
468    }
469}