1use tenferro_ops::broadcast::{broadcast_input_plan, broadcast_shape, broadcast_shapes};
7use tenferro_tensor::validate::matmul_config_for_shapes;
8use tenferro_tensor::{CompareDir, DType, Error, Result, TensorBackend};
9
10use crate::TensorOpsExt;
11use tenferro_tensor::Tensor;
12
13impl TensorOpsExt for Tensor {
14 fn convert<B: TensorBackend>(&self, to: DType, backend: &mut B) -> Result<Tensor> {
15 convert(self, to, backend)
16 }
17
18 fn cast<B: TensorBackend>(&self, to: DType, backend: &mut B) -> Result<Tensor> {
19 cast(self, to, backend)
20 }
21
22 fn add<B: TensorBackend>(&self, rhs: &Tensor, backend: &mut B) -> Result<Tensor> {
23 add(self, rhs, backend)
24 }
25
26 fn sub<B: TensorBackend>(&self, rhs: &Tensor, backend: &mut B) -> Result<Tensor> {
27 sub(self, rhs, backend)
28 }
29
30 fn mul<B: TensorBackend>(&self, rhs: &Tensor, backend: &mut B) -> Result<Tensor> {
31 mul(self, rhs, backend)
32 }
33
34 fn div<B: TensorBackend>(&self, rhs: &Tensor, backend: &mut B) -> Result<Tensor> {
35 div(self, rhs, backend)
36 }
37
38 fn pow<B: TensorBackend>(&self, rhs: &Tensor, backend: &mut B) -> Result<Tensor> {
39 pow(self, rhs, backend)
40 }
41
42 fn maximum<B: TensorBackend>(&self, rhs: &Tensor, backend: &mut B) -> Result<Tensor> {
43 maximum(self, rhs, backend)
44 }
45
46 fn minimum<B: TensorBackend>(&self, rhs: &Tensor, backend: &mut B) -> Result<Tensor> {
47 minimum(self, rhs, backend)
48 }
49
50 fn neg<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
51 neg(self, backend)
52 }
53
54 fn abs<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
55 abs(self, backend)
56 }
57
58 fn sign<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
59 sign(self, backend)
60 }
61
62 fn conj<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
63 conj(self, backend)
64 }
65
66 fn exp<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
67 exp(self, backend)
68 }
69
70 fn log<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
71 log(self, backend)
72 }
73
74 fn sin<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
75 sin(self, backend)
76 }
77
78 fn cos<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
79 cos(self, backend)
80 }
81
82 fn tanh<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
83 tanh(self, backend)
84 }
85
86 fn sqrt<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
87 sqrt(self, backend)
88 }
89
90 fn rsqrt<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
91 rsqrt(self, backend)
92 }
93
94 fn expm1<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
95 expm1(self, backend)
96 }
97
98 fn log1p<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
99 log1p(self, backend)
100 }
101
102 fn compare<B: TensorBackend>(
103 &self,
104 rhs: &Tensor,
105 dir: CompareDir,
106 backend: &mut B,
107 ) -> Result<Tensor> {
108 compare(self, rhs, dir, backend)
109 }
110
111 fn where_select<B: TensorBackend>(
112 &self,
113 on_true: &Tensor,
114 on_false: &Tensor,
115 backend: &mut B,
116 ) -> Result<Tensor> {
117 where_select(self, on_true, on_false, backend)
118 }
119
120 fn clamp<B: TensorBackend>(
121 &self,
122 lower: &Tensor,
123 upper: &Tensor,
124 backend: &mut B,
125 ) -> Result<Tensor> {
126 clamp(self, lower, upper, backend)
127 }
128
129 fn matmul<B: TensorBackend>(&self, rhs: &Tensor, backend: &mut B) -> Result<Tensor> {
130 matmul(self, rhs, backend)
131 }
132
133 fn reshape<B: TensorBackend>(&self, shape: &[usize], backend: &mut B) -> Result<Tensor> {
134 reshape(self, shape, backend)
135 }
136
137 fn transpose<B: TensorBackend>(&self, perm: &[usize], backend: &mut B) -> Result<Tensor> {
138 transpose(self, perm, backend)
139 }
140
141 fn reduce_sum<B: TensorBackend>(&self, axes: &[usize], backend: &mut B) -> Result<Tensor> {
142 reduce_sum(self, axes, backend)
143 }
144}
145
146fn convert(input: &Tensor, to: DType, backend: &mut impl TensorBackend) -> Result<Tensor> {
167 backend.with_backend_session(|exec| exec.convert(input, to))
168}
169
170fn cast(input: &Tensor, to: DType, backend: &mut impl TensorBackend) -> Result<Tensor> {
192 backend.with_backend_session(|exec| exec.cast(input, to))
193}
194
195fn add(lhs: &Tensor, rhs: &Tensor, backend: &mut impl TensorBackend) -> Result<Tensor> {
208 let (lhs, rhs) = broadcast_binary(lhs, rhs, backend)?;
209 backend.with_backend_session(|exec| exec.add(&lhs, &rhs))
210}
211
212macro_rules! unary_fn {
213 ($name:ident, $method:ident, $summary:literal) => {
214 #[doc = $summary]
215 #[doc = concat!("let y = x.", stringify!($name), "(&mut backend).unwrap();")]
224 fn $name(input: &Tensor, backend: &mut impl TensorBackend) -> Result<Tensor> {
226 backend.with_backend_session(|exec| exec.$method(input))
227 }
228 };
229}
230
231macro_rules! binary_fn {
232 ($name:ident, $method:ident, $summary:literal) => {
233 #[doc = $summary]
234 #[doc = concat!("let z = x.", stringify!($name), "(&y, &mut backend).unwrap();")]
244 fn $name(lhs: &Tensor, rhs: &Tensor, backend: &mut impl TensorBackend) -> Result<Tensor> {
246 let (lhs, rhs) = broadcast_binary(lhs, rhs, backend)?;
247 backend.with_backend_session(|exec| exec.$method(&lhs, &rhs))
248 }
249 };
250}
251
252binary_fn!(
253 mul,
254 mul,
255 "Elementwise multiplication with NumPy-style broadcasting."
256);
257binary_fn!(
258 div,
259 div,
260 "Elementwise division with NumPy-style broadcasting."
261);
262binary_fn!(pow, pow, "Elementwise power with NumPy-style broadcasting.");
263binary_fn!(
264 maximum,
265 maximum,
266 "Elementwise maximum with NumPy-style broadcasting."
267);
268binary_fn!(
269 minimum,
270 minimum,
271 "Elementwise minimum with NumPy-style broadcasting."
272);
273
274unary_fn!(neg, neg, "Elementwise negation.");
275unary_fn!(abs, abs, "Elementwise absolute value.");
276unary_fn!(sign, sign, "Elementwise sign.");
277unary_fn!(conj, conj, "Elementwise complex conjugate.");
278unary_fn!(exp, exp, "Elementwise exponential.");
279unary_fn!(log, log, "Elementwise natural logarithm.");
280unary_fn!(sin, sin, "Elementwise sine.");
281unary_fn!(cos, cos, "Elementwise cosine.");
282unary_fn!(tanh, tanh, "Elementwise hyperbolic tangent.");
283unary_fn!(sqrt, sqrt, "Elementwise square root.");
284unary_fn!(rsqrt, rsqrt, "Elementwise reciprocal square root.");
285unary_fn!(expm1, expm1, "Elementwise `exp(x) - 1`.");
286unary_fn!(log1p, log1p, "Elementwise `log(1 + x)`.");
287
288fn sub(lhs: &Tensor, rhs: &Tensor, backend: &mut impl TensorBackend) -> Result<Tensor> {
301 let (lhs, rhs) = broadcast_binary(lhs, rhs, backend)?;
302 let neg_rhs = backend.with_backend_session(|exec| exec.neg(&rhs))?;
303 backend.with_backend_session(|exec| exec.add(&lhs, &neg_rhs))
304}
305
306fn compare(
322 lhs: &Tensor,
323 rhs: &Tensor,
324 dir: CompareDir,
325 backend: &mut impl TensorBackend,
326) -> Result<Tensor> {
327 let (lhs, rhs) = broadcast_binary(lhs, rhs, backend)?;
328 backend.with_backend_session(|exec| exec.compare(&lhs, &rhs, &dir))
329}
330
331fn where_select(
347 condition: &Tensor,
348 on_true: &Tensor,
349 on_false: &Tensor,
350 backend: &mut impl TensorBackend,
351) -> Result<Tensor> {
352 let (condition, on_true, on_false) = broadcast_ternary(condition, on_true, on_false, backend)?;
353 backend.with_backend_session(|exec| exec.select(&condition, &on_true, &on_false))
354}
355
356fn clamp(
370 input: &Tensor,
371 lower: &Tensor,
372 upper: &Tensor,
373 backend: &mut impl TensorBackend,
374) -> Result<Tensor> {
375 let (input, lower, upper) = broadcast_ternary(input, lower, upper, backend)?;
376 backend.with_backend_session(|exec| exec.clamp(&input, &lower, &upper))
377}
378
379fn matmul(a: &Tensor, b: &Tensor, backend: &mut impl TensorBackend) -> Result<Tensor> {
394 let config = matmul_config_for_shapes("matmul", a.shape(), b.shape())?;
395 backend.with_backend_session(|exec| exec.dot_general(a, b, &config))
396}
397
398fn reshape(input: &Tensor, shape: &[usize], backend: &mut impl TensorBackend) -> Result<Tensor> {
411 backend.with_backend_session(|exec| exec.reshape(input, shape))
412}
413
414fn transpose(input: &Tensor, perm: &[usize], backend: &mut impl TensorBackend) -> Result<Tensor> {
427 backend.with_backend_session(|exec| exec.transpose(input, perm))
428}
429
430fn reduce_sum(input: &Tensor, axes: &[usize], backend: &mut impl TensorBackend) -> Result<Tensor> {
443 backend.with_backend_session(|exec| exec.reduce_sum(input, axes))
444}
445
446fn broadcast_binary(
447 lhs: &Tensor,
448 rhs: &Tensor,
449 backend: &mut impl TensorBackend,
450) -> Result<(Tensor, Tensor)> {
451 let shape = broadcast_shape(lhs.shape(), rhs.shape()).map_err(broadcast_error)?;
452 Ok((
453 broadcast_to(lhs, &shape, backend)?,
454 broadcast_to(rhs, &shape, backend)?,
455 ))
456}
457
458fn broadcast_ternary(
459 first: &Tensor,
460 second: &Tensor,
461 third: &Tensor,
462 backend: &mut impl TensorBackend,
463) -> Result<(Tensor, Tensor, Tensor)> {
464 let shape = broadcast_shapes([first.shape(), second.shape(), third.shape()])
465 .map_err(broadcast_error)?;
466 Ok((
467 broadcast_to(first, &shape, backend)?,
468 broadcast_to(second, &shape, backend)?,
469 broadcast_to(third, &shape, backend)?,
470 ))
471}
472
473fn broadcast_to(
474 input: &Tensor,
475 target_shape: &[usize],
476 backend: &mut impl TensorBackend,
477) -> Result<Tensor> {
478 let input_shape = input.shape();
479 if input_shape == target_shape {
480 return Ok(input.clone());
481 }
482
483 let plan = broadcast_input_plan(input_shape, target_shape).map_err(broadcast_error)?;
484 let source = if plan.source_shape == input_shape {
485 input.clone()
486 } else {
487 backend.with_backend_session(|exec| exec.reshape(input, &plan.source_shape))?
488 };
489 backend.with_backend_session(|exec| exec.broadcast_in_dim(&source, target_shape, &plan.dims))
490}
491
492fn broadcast_error(err: impl std::fmt::Display) -> Error {
493 Error::backend_failure("broadcast", err.to_string())
494}