1use crate::{Tensor, TensorElement};
7use torsh_core::error::Result;
8
9pub trait TensorConvenience<T: TensorElement> {
11 #[allow(non_snake_case)]
20 fn T(&self) -> Result<Tensor<T>>;
21
22 #[allow(non_snake_case)]
24 fn mT(&self) -> Result<Tensor<T>>;
25
26 #[allow(non_snake_case)]
28 fn H(&self) -> Result<Tensor<T>>;
29
30 fn t(&self) -> Result<Tensor<T>>;
32
33 fn m_t(&self) -> Result<Tensor<T>>;
35
36 fn h(&self) -> Result<Tensor<T>>;
38
39 fn detach(&self) -> Tensor<T>;
41
42 fn clone_tensor(&self) -> Result<Tensor<T>>;
44
45 fn is_contiguous(&self) -> bool;
47
48 fn contiguous(&self) -> Result<Tensor<T>>;
50
51 fn numel(&self) -> usize;
53
54 fn size(&self) -> Vec<usize>;
56
57 fn is_empty(&self) -> bool;
59
60 fn is_scalar(&self) -> bool;
62
63 fn item(&self) -> T;
65
66 fn to_scalar(&self) -> Result<T>;
68}
69
70impl<T: TensorElement + Copy + torsh_core::FloatElement> TensorConvenience<T> for Tensor<T> {
71 #[allow(non_snake_case)]
72 fn T(&self) -> Result<Tensor<T>> {
73 if self.shape().dims().len() == 2 {
75 self.transpose(0, 1)
76 } else if self.shape().dims().len() == 1 {
77 Ok(self.clone())
79 } else {
80 let ndim = self.shape().dims().len();
82 if ndim >= 2 {
83 self.transpose((ndim - 2) as i32, (ndim - 1) as i32)
84 } else {
85 Ok(self.clone())
86 }
87 }
88 }
89
90 #[allow(non_snake_case)]
91 fn mT(&self) -> Result<Tensor<T>> {
92 self.T()
93 }
94
95 #[allow(non_snake_case)]
96 fn H(&self) -> Result<Tensor<T>> {
97 let transposed = self.T()?;
100
101 Ok(transposed)
104 }
105
106 fn t(&self) -> Result<Tensor<T>> {
107 self.T()
108 }
109
110 fn m_t(&self) -> Result<Tensor<T>> {
111 self.T()
112 }
113
114 fn h(&self) -> Result<Tensor<T>> {
115 self.H()
116 }
117
118 fn detach(&self) -> Tensor<T> {
119 self.clone()
122 }
123
124 fn clone_tensor(&self) -> Result<Tensor<T>> {
125 Ok(self.detach())
126 }
127
128 fn is_contiguous(&self) -> bool {
129 let shape_ref = self.shape();
130 if shape_ref.dims().is_empty() {
131 return true; }
133 match &self.strides {
135 None => true,
136 Some(strides) => {
137 let expected = self.compute_default_strides();
138 strides == &expected
139 }
140 }
141 }
142
143 fn contiguous(&self) -> Result<Tensor<T>> {
144 if self.is_contiguous() {
145 Ok(self.clone())
146 } else {
147 self.clone_tensor()
149 }
150 }
151
152 fn numel(&self) -> usize {
153 self.shape().dims().iter().product()
154 }
155
156 fn size(&self) -> Vec<usize> {
157 self.shape().dims().to_vec()
158 }
159
160 fn is_empty(&self) -> bool {
161 self.numel() == 0
162 }
163
164 fn is_scalar(&self) -> bool {
165 self.shape().dims().is_empty()
166 }
167
168 fn item(&self) -> T {
169 if self.numel() != 1 {
171 panic!("Can only call item() on tensors with one element");
172 }
173 let data = self
174 .to_vec()
175 .expect("tensor to vec conversion should succeed");
176 data[0]
177 }
178
179 fn to_scalar(&self) -> Result<T> {
180 let squeezed = self.squeeze_all()?;
182 squeezed.item()
183 }
184}
185
186pub trait TensorShapeConvenience<T: TensorElement> {
188 fn unsqueeze_at(&self, dim: i32) -> Result<Tensor<T>>;
190
191 fn squeeze_all(&self) -> Result<Tensor<T>>;
193
194 fn flatten(&self) -> Result<Tensor<T>>;
196
197 fn flatten_from(&self, start_dim: i32) -> Result<Tensor<T>>;
199
200 fn unflatten(&self, dim: i32, sizes: &[usize]) -> Result<Tensor<T>>;
202}
203
204impl<T: TensorElement + Copy> TensorShapeConvenience<T> for Tensor<T> {
205 fn unsqueeze_at(&self, dim: i32) -> Result<Tensor<T>> {
206 self.unsqueeze(dim)
207 }
208
209 fn squeeze_all(&self) -> Result<Tensor<T>> {
210 let mut result = self.clone();
211 let shape_ref = self.shape();
212 let dims = shape_ref.dims();
213
214 for (i, &size) in dims.iter().enumerate().rev() {
216 if size == 1 {
217 result = result.squeeze(i as i32)?;
218 }
219 }
220
221 Ok(result)
222 }
223
224 fn flatten(&self) -> Result<Tensor<T>> {
225 let total_elements = self.numel();
226 self.reshape(&[total_elements as i32])
227 }
228
229 fn flatten_from(&self, start_dim: i32) -> Result<Tensor<T>> {
230 let shape_ref = self.shape();
231 let shape = shape_ref.dims();
232 let ndim = shape.len() as i32;
233 let start_dim = if start_dim < 0 {
234 ndim + start_dim
235 } else {
236 start_dim
237 };
238
239 if start_dim < 0 || start_dim >= ndim {
240 return Err(torsh_core::error::TorshError::InvalidArgument(format!(
241 "Invalid start_dim {start_dim} for tensor with {ndim} dimensions"
242 )));
243 }
244
245 let mut new_shape = Vec::new();
246
247 for &dim in shape.iter().take(start_dim as usize) {
249 new_shape.push(dim);
250 }
251
252 let flattened_size: usize = shape[start_dim as usize..].iter().product();
254 new_shape.push(flattened_size);
255
256 let new_shape_i32: Vec<i32> = new_shape.iter().map(|&x| x as i32).collect();
257 self.reshape(&new_shape_i32)
258 }
259
260 fn unflatten(&self, dim: i32, sizes: &[usize]) -> Result<Tensor<T>> {
261 let shape_ref = self.shape();
262 let shape = shape_ref.dims();
263 let ndim = shape.len() as i32;
264 let dim = if dim < 0 { ndim + dim } else { dim };
265
266 if dim < 0 || dim >= ndim {
267 return Err(torsh_core::error::TorshError::InvalidArgument(format!(
268 "Invalid dim {dim} for tensor with {ndim} dimensions"
269 )));
270 }
271
272 let expected_size = shape[dim as usize];
274 let actual_size: usize = sizes.iter().product();
275
276 if expected_size != actual_size {
277 return Err(torsh_core::error::TorshError::InvalidArgument(format!(
278 "Sizes {actual_size} don't multiply to dimension size {expected_size}"
279 )));
280 }
281
282 let mut new_shape = Vec::new();
284
285 for &dim_size in shape.iter().take(dim as usize) {
287 new_shape.push(dim_size);
288 }
289
290 new_shape.extend_from_slice(sizes);
292
293 for &dim_size in shape.iter().skip(dim as usize + 1) {
295 new_shape.push(dim_size);
296 }
297
298 let new_shape_i32: Vec<i32> = new_shape.iter().map(|&x| x as i32).collect();
299 self.reshape(&new_shape_i32)
300 }
301}
302
303#[cfg(test)]
304mod tests {
305 use super::*;
306
307 #[test]
308 fn test_transpose_shortcuts() {
309 let tensor = crate::creation::tensor_2d_arrays(&[[1.0f32, 2.0], [3.0, 4.0]])
310 .expect("tensor creation failed");
311
312 let transposed = tensor.T().expect("T() failed");
314 assert_eq!(transposed.shape().dims(), &[2, 2]);
315
316 let mt_transposed = tensor.mT().expect("mT() failed");
318 assert_eq!(mt_transposed.shape().dims(), &[2, 2]);
319
320 let hermitian = tensor.H().expect("H() failed");
322 assert_eq!(hermitian.shape().dims(), &[2, 2]);
323 }
324
325 #[test]
326 fn test_tensor_properties() {
327 let tensor = crate::creation::tensor_2d_arrays(&[[1.0f32, 2.0], [3.0, 4.0]])
328 .expect("tensor creation failed");
329
330 assert_eq!(tensor.numel(), 4);
331 assert_eq!(tensor.shape().dims(), &[2, 2]);
332 assert!(!tensor.is_empty());
333 assert!(!tensor.is_scalar());
334 assert!(tensor.is_contiguous());
335
336 let scalar = crate::creation::tensor_scalar(42.0f32).expect("scalar creation failed");
338 assert!(scalar.is_scalar());
339 assert_eq!(scalar.item().expect("item retrieval failed"), 42.0);
340 }
341
342 #[test]
343 fn test_shape_convenience() {
344 let tensor = crate::creation::zeros::<f32>(&[4])
346 .expect("zeros creation failed")
347 .reshape(&[2, 1, 2])
348 .expect("reshape failed");
349
350 let squeezed = tensor.squeeze_all().expect("squeeze_all failed");
352 assert_eq!(squeezed.shape().dims(), &[2, 2]);
353
354 let flattened = tensor.flatten().expect("flatten failed");
356 assert_eq!(flattened.shape().dims(), &[4]);
357
358 let flat_from_1 = tensor.flatten_from(1).expect("flatten_from failed");
360 assert_eq!(flat_from_1.shape().dims(), &[2, 2]);
361 }
362
363 #[test]
364 fn test_detach() {
365 let tensor =
366 crate::creation::tensor_1d(&[1.0f32, 2.0, 3.0]).expect("tensor creation failed");
367 let detached = tensor.detach();
368
369 assert_eq!(tensor.shape().dims(), detached.shape().dims());
371 assert_eq!(
372 tensor.data().expect("data retrieval failed"),
373 detached.data().expect("detached data retrieval failed")
374 );
375 }
376
377 #[test]
378 fn test_fluent_api() {
379 use crate::TensorFluentExt;
380 let tensor =
381 crate::creation::tensor_1d(&[1.0f32, 2.0, 3.0, 4.0]).expect("tensor creation failed");
382
383 let result = tensor
385 .fluent()
386 .add_scalar(1.0) .mul_scalar(2.0) .sub_scalar(1.0) .unwrap()
390 .unwrap();
391
392 let expected = vec![3.0, 5.0, 7.0, 9.0];
393 let actual = result.to_vec().expect("to_vec failed");
394
395 for (exp, act) in expected.iter().zip(actual.iter()) {
396 assert!((exp - act).abs() < f32::EPSILON);
397 }
398 }
399
400 #[test]
401 fn test_fluent_api_operations() {
402 use crate::TensorFluentExt;
403 let tensor1 =
404 crate::creation::tensor_1d(&[1.0f32, 2.0, 3.0, 4.0]).expect("tensor1 creation failed");
405 let tensor2 =
406 crate::creation::tensor_1d(&[2.0f32, 2.0, 2.0, 2.0]).expect("tensor2 creation failed");
407
408 let result = tensor1
410 .fluent()
411 .add(&tensor2) .mul_scalar(0.5) .sum() .unwrap()
415 .unwrap();
416
417 let actual = result.to_vec().expect("to_vec failed");
418 assert!((actual[0] - 9.0).abs() < f32::EPSILON);
419 }
420
421 #[test]
422 fn test_fluent_api_mathematical_operations() {
423 use crate::TensorFluentExt;
424 let tensor =
425 crate::creation::tensor_1d(&[1.0f32, 2.0, 3.0, 4.0]).expect("tensor creation failed");
426
427 let result = tensor
429 .fluent()
430 .relu() .pow(2.0) .sigmoid() .unwrap()
434 .unwrap();
435
436 let actual = result.to_vec().expect("to_vec failed");
437 for val in actual.iter() {
439 assert!(*val > 0.0 && *val < 1.0);
440 }
441 }
442}
443
444pub trait TensorFluentExt<T: TensorElement> {
464 fn fluent(self) -> FluentTensor<T>;
466}
467
468pub struct FluentTensor<T: TensorElement> {
470 tensor: Tensor<T>,
471}
472
473impl<T: TensorElement> TensorFluentExt<T> for Tensor<T> {
474 fn fluent(self) -> FluentTensor<T> {
475 FluentTensor { tensor: self }
476 }
477}
478
479impl<
480 T: TensorElement
481 + Copy
482 + std::ops::Add<Output = T>
483 + std::ops::Sub<Output = T>
484 + std::ops::Mul<Output = T>
485 + std::ops::Div<Output = T>
486 + num_traits::Zero,
487 > FluentTensor<T>
488{
489 pub fn tensor(self) -> Tensor<T> {
491 self.tensor
492 }
493
494 pub fn unwrap(self) -> Result<Tensor<T>> {
496 Ok(self.tensor)
497 }
498
499 pub fn add_scalar(mut self, scalar: T) -> Self {
501 if let Ok(result) = self.tensor.add_scalar(scalar) {
502 self.tensor = result;
503 }
504 self
505 }
506
507 pub fn mul_scalar(mut self, scalar: T) -> Self {
509 if let Ok(result) = self.tensor.mul_scalar(scalar) {
510 self.tensor = result;
511 }
512 self
513 }
514
515 pub fn sub_scalar(mut self, scalar: T) -> Self {
517 if let Ok(result) = self.tensor.sub_scalar(scalar) {
518 self.tensor = result;
519 }
520 self
521 }
522
523 pub fn div_scalar(mut self, scalar: T) -> Self {
525 if let Ok(result) = self.tensor.div_scalar(scalar) {
526 self.tensor = result;
527 }
528 self
529 }
530
531 pub fn add(mut self, other: &Tensor<T>) -> Self {
533 if let Ok(result) = self.tensor.add_op(other) {
534 self.tensor = result;
535 }
536 self
537 }
538
539 pub fn mul(mut self, other: &Tensor<T>) -> Self {
541 if let Ok(result) = self.tensor.mul_op(other) {
542 self.tensor = result;
543 }
544 self
545 }
546
547 pub fn sub(mut self, other: &Tensor<T>) -> Self {
549 if let Ok(result) = self.tensor.sub(other) {
550 self.tensor = result;
551 }
552 self
553 }
554
555 pub fn div(mut self, other: &Tensor<T>) -> Self {
557 if let Ok(result) = self.tensor.div(other) {
558 self.tensor = result;
559 }
560 self
561 }
562
563 pub fn reshape(mut self, shape: &[i32]) -> Self {
565 if let Ok(result) = self.tensor.reshape(shape) {
566 self.tensor = result;
567 }
568 self
569 }
570
571 pub fn transpose(mut self, dim0: i32, dim1: i32) -> Self {
573 if let Ok(result) = self.tensor.transpose(dim0, dim1) {
574 self.tensor = result;
575 }
576 self
577 }
578
579 pub fn t(mut self) -> Self {
581 if let Ok(result) = self.tensor.t() {
582 self.tensor = result;
583 }
584 self
585 }
586
587 pub fn sum(mut self) -> Self {
589 if let Ok(result) = self.tensor.sum() {
590 self.tensor = result;
591 }
592 self
593 }
594
595 pub fn sum_dim(mut self, dims: &[i32], keepdim: bool) -> Self {
597 if let Ok(result) = self.tensor.sum_dim(dims, keepdim) {
598 self.tensor = result;
599 }
600 self
601 }
602
603 pub fn squeeze(mut self, dim: i32) -> Self {
605 if let Ok(result) = self.tensor.squeeze(dim) {
606 self.tensor = result;
607 }
608 self
609 }
610
611 pub fn unsqueeze(mut self, dim: i32) -> Self {
613 if let Ok(result) = self.tensor.unsqueeze(dim) {
614 self.tensor = result;
615 }
616 self
617 }
618}
619
620impl<T: TensorElement + Copy + num_traits::Float> FluentTensor<T> {
622 pub fn relu(mut self) -> Self {
624 if let Ok(result) = self.tensor.relu() {
625 self.tensor = result;
626 }
627 self
628 }
629
630 pub fn sigmoid(mut self) -> Self
632 where
633 T: torsh_core::dtype::FloatElement,
634 {
635 if let Ok(result) = self.tensor.sigmoid() {
636 self.tensor = result;
637 }
638 self
639 }
640
641 pub fn tanh(mut self) -> Self
643 where
644 T: torsh_core::dtype::FloatElement,
645 {
646 if let Ok(result) = self.tensor.tanh() {
647 self.tensor = result;
648 }
649 self
650 }
651
652 pub fn exp(mut self) -> Self
654 where
655 T: torsh_core::dtype::FloatElement,
656 {
657 if let Ok(result) = self.tensor.exp() {
658 self.tensor = result;
659 }
660 self
661 }
662
663 pub fn log(mut self) -> Self
665 where
666 T: torsh_core::dtype::FloatElement,
667 {
668 if let Ok(result) = self.tensor.log() {
669 self.tensor = result;
670 }
671 self
672 }
673
674 pub fn pow(mut self, exponent: T) -> Self
676 where
677 T: torsh_core::dtype::FloatElement + Into<f32>,
678 {
679 if let Ok(result) = self.tensor.pow(exponent) {
680 self.tensor = result;
681 }
682 self
683 }
684
685 }
688
689impl<T: TensorElement + Copy> FluentTensor<T>
691where
692 T: num_traits::Float + std::iter::Sum,
693{
694 pub fn matmul(mut self, other: &Tensor<T>) -> Self {
696 if let Ok(result) = self.tensor.matmul(other) {
697 self.tensor = result;
698 }
699 self
700 }
701}
702
703impl<
705 T: TensorElement
706 + Copy
707 + num_traits::FromPrimitive
708 + std::ops::Div<Output = T>
709 + num_traits::Zero
710 + num_traits::One,
711 > FluentTensor<T>
712{
713 pub fn mean(mut self, dims: Option<&[usize]>, keepdim: bool) -> Self {
715 if let Ok(result) = self.tensor.mean(dims, keepdim) {
716 self.tensor = result;
717 }
718 self
719 }
720}