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}