1use tenferro_ops::broadcast::{
7 broadcast_error_to_validation, broadcast_input_plan, broadcast_shape, broadcast_shapes,
8};
9use tenferro_tensor::validate::matmul_config_for_shapes;
10use tenferro_tensor::{CompareDir, DType, Error, Result, TensorBackend};
11
12use crate::TensorOpsExt;
13use tenferro_tensor::Tensor;
14
15impl TensorOpsExt for Tensor {
16 fn convert<B: TensorBackend>(&self, to: DType, backend: &mut B) -> Result<Tensor> {
17 convert(self, to, backend)
18 }
19
20 fn cast<B: TensorBackend>(&self, to: DType, backend: &mut B) -> Result<Tensor> {
21 cast(self, to, backend)
22 }
23
24 fn add<B: TensorBackend>(&self, rhs: &Tensor, backend: &mut B) -> Result<Tensor> {
25 add(self, rhs, backend)
26 }
27
28 fn sub<B: TensorBackend>(&self, rhs: &Tensor, backend: &mut B) -> Result<Tensor> {
29 sub(self, rhs, backend)
30 }
31
32 fn mul<B: TensorBackend>(&self, rhs: &Tensor, backend: &mut B) -> Result<Tensor> {
33 mul(self, rhs, backend)
34 }
35
36 fn div<B: TensorBackend>(&self, rhs: &Tensor, backend: &mut B) -> Result<Tensor> {
37 div(self, rhs, backend)
38 }
39
40 fn rem<B: TensorBackend>(&self, rhs: &Tensor, backend: &mut B) -> Result<Tensor> {
41 rem(self, rhs, backend)
42 }
43
44 fn pow<B: TensorBackend>(&self, rhs: &Tensor, backend: &mut B) -> Result<Tensor> {
45 pow(self, rhs, backend)
46 }
47
48 fn maximum<B: TensorBackend>(&self, rhs: &Tensor, backend: &mut B) -> Result<Tensor> {
49 maximum(self, rhs, backend)
50 }
51
52 fn minimum<B: TensorBackend>(&self, rhs: &Tensor, backend: &mut B) -> Result<Tensor> {
53 minimum(self, rhs, backend)
54 }
55
56 fn neg<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
57 neg(self, backend)
58 }
59
60 fn abs<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
61 abs(self, backend)
62 }
63
64 fn sign<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
65 sign(self, backend)
66 }
67
68 fn conj<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
69 conj(self, backend)
70 }
71
72 fn exp<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
73 exp(self, backend)
74 }
75
76 fn log<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
77 log(self, backend)
78 }
79
80 fn sin<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
81 sin(self, backend)
82 }
83
84 fn cos<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
85 cos(self, backend)
86 }
87
88 fn tanh<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
89 tanh(self, backend)
90 }
91
92 fn sqrt<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
93 sqrt(self, backend)
94 }
95
96 fn rsqrt<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
97 rsqrt(self, backend)
98 }
99
100 fn expm1<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
101 expm1(self, backend)
102 }
103
104 fn log1p<B: TensorBackend>(&self, backend: &mut B) -> Result<Tensor> {
105 log1p(self, backend)
106 }
107
108 fn compare<B: TensorBackend>(
109 &self,
110 rhs: &Tensor,
111 dir: CompareDir,
112 backend: &mut B,
113 ) -> Result<Tensor> {
114 compare(self, rhs, dir, backend)
115 }
116
117 fn where_select<B: TensorBackend>(
118 &self,
119 on_true: &Tensor,
120 on_false: &Tensor,
121 backend: &mut B,
122 ) -> Result<Tensor> {
123 where_select(self, on_true, on_false, backend)
124 }
125
126 fn clamp<B: TensorBackend>(
127 &self,
128 lower: &Tensor,
129 upper: &Tensor,
130 backend: &mut B,
131 ) -> Result<Tensor> {
132 clamp(self, lower, upper, backend)
133 }
134
135 fn matmul<B: TensorBackend>(&self, rhs: &Tensor, backend: &mut B) -> Result<Tensor> {
136 matmul(self, rhs, backend)
137 }
138
139 fn reshape<B: TensorBackend>(&self, shape: &[usize], backend: &mut B) -> Result<Tensor> {
140 reshape(self, shape, backend)
141 }
142
143 fn transpose<B: TensorBackend>(&self, perm: &[usize], backend: &mut B) -> Result<Tensor> {
144 transpose(self, perm, backend)
145 }
146
147 fn reduce_sum<B: TensorBackend>(&self, axes: &[usize], backend: &mut B) -> Result<Tensor> {
148 reduce_sum(self, axes, backend)
149 }
150}
151
152fn convert(input: &Tensor, to: DType, backend: &mut impl TensorBackend) -> Result<Tensor> {
173 backend.with_backend_session(|exec| exec.convert(input, to))
174}
175
176fn cast(input: &Tensor, to: DType, backend: &mut impl TensorBackend) -> Result<Tensor> {
198 backend.with_backend_session(|exec| exec.cast(input, to))
199}
200
201fn add(lhs: &Tensor, rhs: &Tensor, backend: &mut impl TensorBackend) -> Result<Tensor> {
214 let (lhs, rhs) = broadcast_binary(lhs, rhs, backend)?;
215 backend.with_backend_session(|exec| exec.add(&lhs, &rhs))
216}
217
218macro_rules! unary_fn {
219 ($name:ident, $method:ident, $summary:literal) => {
220 #[doc = $summary]
221 #[doc = concat!("let y = x.", stringify!($name), "(&mut backend).unwrap();")]
230 fn $name(input: &Tensor, backend: &mut impl TensorBackend) -> Result<Tensor> {
232 backend.with_backend_session(|exec| exec.$method(input))
233 }
234 };
235}
236
237macro_rules! binary_fn {
238 ($name:ident, $method:ident, $summary:literal) => {
239 #[doc = $summary]
240 #[doc = concat!("let z = x.", stringify!($name), "(&y, &mut backend).unwrap();")]
250 fn $name(lhs: &Tensor, rhs: &Tensor, backend: &mut impl TensorBackend) -> Result<Tensor> {
252 let (lhs, rhs) = broadcast_binary(lhs, rhs, backend)?;
253 backend.with_backend_session(|exec| exec.$method(&lhs, &rhs))
254 }
255 };
256}
257
258binary_fn!(
259 mul,
260 mul,
261 "Elementwise multiplication with NumPy-style broadcasting."
262);
263binary_fn!(
264 div,
265 div,
266 "Elementwise division with NumPy-style broadcasting."
267);
268binary_fn!(
269 rem,
270 rem,
271 "Elementwise remainder with NumPy-style broadcasting."
272);
273binary_fn!(pow, pow, "Elementwise power with NumPy-style broadcasting.");
274binary_fn!(
275 maximum,
276 maximum,
277 "Elementwise maximum with NumPy-style broadcasting."
278);
279binary_fn!(
280 minimum,
281 minimum,
282 "Elementwise minimum with NumPy-style broadcasting."
283);
284
285unary_fn!(neg, neg, "Elementwise negation.");
286unary_fn!(abs, abs, "Elementwise absolute value.");
287unary_fn!(sign, sign, "Elementwise sign.");
288unary_fn!(conj, conj, "Elementwise complex conjugate.");
289unary_fn!(exp, exp, "Elementwise exponential.");
290unary_fn!(log, log, "Elementwise natural logarithm.");
291unary_fn!(sin, sin, "Elementwise sine.");
292unary_fn!(cos, cos, "Elementwise cosine.");
293unary_fn!(tanh, tanh, "Elementwise hyperbolic tangent.");
294unary_fn!(sqrt, sqrt, "Elementwise square root.");
295unary_fn!(rsqrt, rsqrt, "Elementwise reciprocal square root.");
296unary_fn!(expm1, expm1, "Elementwise `exp(x) - 1`.");
297unary_fn!(log1p, log1p, "Elementwise `log(1 + x)`.");
298
299fn sub(lhs: &Tensor, rhs: &Tensor, backend: &mut impl TensorBackend) -> Result<Tensor> {
312 let (lhs, rhs) = broadcast_binary(lhs, rhs, backend)?;
313 backend.with_backend_session(|exec| exec.sub(&lhs, &rhs))
314}
315
316fn compare(
332 lhs: &Tensor,
333 rhs: &Tensor,
334 dir: CompareDir,
335 backend: &mut impl TensorBackend,
336) -> Result<Tensor> {
337 let (lhs, rhs) = broadcast_binary(lhs, rhs, backend)?;
338 backend.with_backend_session(|exec| exec.compare(&lhs, &rhs, &dir))
339}
340
341fn where_select(
357 condition: &Tensor,
358 on_true: &Tensor,
359 on_false: &Tensor,
360 backend: &mut impl TensorBackend,
361) -> Result<Tensor> {
362 let (condition, on_true, on_false) = broadcast_ternary(condition, on_true, on_false, backend)?;
363 backend.with_backend_session(|exec| exec.select(&condition, &on_true, &on_false))
364}
365
366fn clamp(
380 input: &Tensor,
381 lower: &Tensor,
382 upper: &Tensor,
383 backend: &mut impl TensorBackend,
384) -> Result<Tensor> {
385 let (input, lower, upper) = broadcast_ternary(input, lower, upper, backend)?;
386 backend.with_backend_session(|exec| exec.clamp(&input, &lower, &upper))
387}
388
389fn matmul(a: &Tensor, b: &Tensor, backend: &mut impl TensorBackend) -> Result<Tensor> {
404 let config = matmul_config_for_shapes("matmul", a.shape(), b.shape())?;
405 backend.with_backend_session(|exec| exec.dot_general(a, b, &config))
406}
407
408fn reshape(input: &Tensor, shape: &[usize], backend: &mut impl TensorBackend) -> Result<Tensor> {
421 backend.with_backend_session(|exec| exec.reshape(input, shape))
422}
423
424fn transpose(input: &Tensor, perm: &[usize], backend: &mut impl TensorBackend) -> Result<Tensor> {
437 backend.with_backend_session(|exec| exec.transpose(input, perm))
438}
439
440fn reduce_sum(input: &Tensor, axes: &[usize], backend: &mut impl TensorBackend) -> Result<Tensor> {
453 backend.with_backend_session(|exec| exec.reduce_sum(input, axes))
454}
455
456fn broadcast_binary(
457 lhs: &Tensor,
458 rhs: &Tensor,
459 backend: &mut impl TensorBackend,
460) -> Result<(Tensor, Tensor)> {
461 let shape = broadcast_shape(lhs.shape(), rhs.shape()).map_err(broadcast_error)?;
462 Ok((
463 broadcast_to(lhs, &shape, backend)?,
464 broadcast_to(rhs, &shape, backend)?,
465 ))
466}
467
468fn broadcast_ternary(
469 first: &Tensor,
470 second: &Tensor,
471 third: &Tensor,
472 backend: &mut impl TensorBackend,
473) -> Result<(Tensor, Tensor, Tensor)> {
474 let shape = broadcast_shapes([first.shape(), second.shape(), third.shape()])
475 .map_err(broadcast_error)?;
476 Ok((
477 broadcast_to(first, &shape, backend)?,
478 broadcast_to(second, &shape, backend)?,
479 broadcast_to(third, &shape, backend)?,
480 ))
481}
482
483fn broadcast_to(
484 input: &Tensor,
485 target_shape: &[usize],
486 backend: &mut impl TensorBackend,
487) -> Result<Tensor> {
488 let input_shape = input.shape();
489 if input_shape == target_shape {
490 return Ok(input.clone());
491 }
492
493 let plan = broadcast_input_plan(input_shape, target_shape).map_err(broadcast_error)?;
494 let source = if plan.source_shape == input_shape {
495 input.clone()
496 } else {
497 backend.with_backend_session(|exec| exec.reshape(input, &plan.source_shape))?
498 };
499 backend.with_backend_session(|exec| exec.broadcast_in_dim(&source, target_shape, &plan.dims))
500}
501
502fn broadcast_error(err: tenferro_ops::broadcast::BroadcastError) -> Error {
503 Error::validation("broadcast", broadcast_error_to_validation(err))
504}