Skip to main content

lumen_core/ops/
arith.rs

1use crate::{BinaryOp, AutogradMetaT, CmpOp, Error, FloatDType, NumDType, Shape, Storage, StorageIndices, TensorOrScalar, UnaryOp, WithDType};
2use crate::Tensor;
3use paste::paste;
4
5//////////////////////////////////////////////////////////////////////////////
6///        Binary(Assign) Op with Tensor and Tensor / scalar
7//////////////////////////////////////////////////////////////////////////////
8
9impl<T: WithDType> Tensor<T> {
10    pub(crate) fn same_shape_binary_op(&self, rhs: &Self, op: &'static str) -> crate::Result<&Shape> {
11        let lhs = self.shape();
12        let rhs = rhs.shape();
13        if lhs != rhs {
14            Err(Error::ShapeMismatchBinaryOp {
15                lhs: lhs.clone(),
16                rhs: rhs.clone(),
17                op,
18            })?
19        } else {
20            Ok(lhs)
21        }
22    }
23}
24
25impl<T: WithDType> Tensor<T> {
26    fn compute_binary_scalar_rhs_op<U, F>(lhs: &Tensor<T>, rhs: T, f: F, _op_name: &'static str) -> crate::Result<(Storage<U>, Shape)>
27        where 
28            U: WithDType, 
29            F: Fn(T, T) -> U 
30    {
31        let shape = lhs.shape();
32        let lhs_storage = lhs.storage_read()?;
33        let lhs_layout = lhs.layout();
34        let lhs = lhs_storage.data();
35
36        let output: Vec<_> = match lhs_layout.storage_indices() {
37            StorageIndices::Contiguous(index) => {
38                let lhs = &lhs[index.begin_index..index.end_index];
39                lhs.iter()
40                    .map(|&v| f(v, rhs))
41                    .collect()
42            }
43            StorageIndices::Uncontiguous(index) => {
44                index
45                    .map(|lhs_index| f(lhs[lhs_index], rhs))
46                    .collect()
47            }
48        };
49        
50        let storage = Storage::<U>::new(output);
51        Ok((storage, shape.clone()))
52    }
53
54    fn compute_binary_scalar_lhs_op<U, F>(lhs: T, rhs: &Tensor<T>, f: F, _op_name: &'static str) -> crate::Result<(Storage<U>, Shape)>
55        where 
56            U: WithDType, 
57            F: Fn(T, T) -> U 
58    {
59        let shape = rhs.shape();
60        let rhs_storage = rhs.storage_read()?;
61        let rhs_layout = rhs.layout();
62        let rhs = rhs_storage.data();
63
64        let output: Vec<_> = match rhs_layout.storage_indices() {
65            StorageIndices::Contiguous(index) => {
66                let rhs = &rhs[index.begin_index..index.end_index];
67                rhs.iter()
68                    .map(|&v| f(lhs, v))
69                    .collect()
70            }
71            StorageIndices::Uncontiguous(index) => {
72                index
73                    .map(|index| f(lhs, rhs[index]))
74                    .collect()
75            }
76        };
77        
78        let storage = Storage::<U>::new(output);
79        Ok((storage, shape.clone()))
80    }
81
82    fn compute_binary_op<U, F>(lhs: &Tensor<T>, rhs: &Tensor<T>, f: F, op_name: &'static str) -> crate::Result<(Storage<U>, Shape)>
83        where 
84            U: WithDType, 
85            F: Fn(T, T) -> U + Send + Sync 
86    {
87        let shape = Tensor::<T>::same_shape_binary_op(lhs, rhs, op_name)?;
88        let lhs_storage = lhs.storage_read()?;
89        let rhs_storage = rhs.storage_read()?;
90        let lhs_layout = lhs.layout();
91        let rhs_layout = rhs.layout();
92
93        assert_eq!(lhs_layout.dims(), rhs_layout.dims(), "lhs dims != rhs dim2");
94
95        let lhs = lhs_storage.data();
96        let rhs = rhs_storage.data();
97        
98        let output: Vec<_> = match (lhs_layout.storage_indices(), rhs_layout.storage_indices()) {
99            (StorageIndices::Contiguous(lhs_index), StorageIndices::Contiguous(rhs_index)) => {
100                let lhs = &lhs[lhs_index.begin_index..lhs_index.end_index];
101                let rhs = &rhs[rhs_index.begin_index..rhs_index.end_index];
102                lhs.iter().zip(rhs.iter())
103                    .map(|(&l, &r)| f(l, r))
104                    .collect()
105            }
106            (StorageIndices::Contiguous(lhs_index), StorageIndices::Uncontiguous(rhs_index)) => {
107                let lhs = &lhs[lhs_index.begin_index..lhs_index.end_index];
108                lhs.iter().zip(rhs_index)
109                    .map(|(&l, r_index)| f(l, rhs[r_index]))
110                    .collect()
111            }
112            (StorageIndices::Uncontiguous(lhs_index), StorageIndices::Contiguous(rhs_index)) => {
113                let rhs = &rhs[rhs_index.begin_index..rhs_index.end_index];
114                lhs_index.zip(rhs.iter())
115                    .map(|(l_index, &r)| f(lhs[l_index], r))
116                    .collect()
117            }
118            (StorageIndices::Uncontiguous(lhs_index), StorageIndices::Uncontiguous(rhs_index)) => {
119                lhs_index.zip(rhs_index)
120                    .map(|(lhs_index, rhs_index)| f(lhs[lhs_index], rhs[rhs_index]))
121                    .collect()
122            }
123        };
124
125        let storage = Storage::<U>::new(output);
126        Ok((storage, shape.clone()))
127    }
128
129    fn binary_op<U, F>(lhs: &Tensor<T>, rhs: &Tensor<T>, f: F, meta: U::AutogradMeta, op_name: &'static str) -> crate::Result<Tensor<U>>
130    where 
131        U: WithDType, 
132        F: Fn(T, T) -> U + Sync + Send
133    {
134        let (storage, shape) = Self::compute_binary_op(lhs, rhs, f, op_name)?;
135        Ok(Tensor::<U>::from_storage(storage, shape, meta))
136    }
137
138    fn binary_scalar_rhs_op<U, F>(lhs: &Tensor<T>, rhs: T, f: F, meta: U::AutogradMeta, op_name: &'static str) -> crate::Result<Tensor<U>>
139    where 
140        U: WithDType, 
141        F: Fn(T, T) -> U 
142    {
143        let (storage, shape) = Self::compute_binary_scalar_rhs_op(lhs, rhs, f, op_name)?;
144        Ok(Tensor::<U>::from_storage(storage, shape, meta))
145    }
146
147    fn binary_scalar_lhs_op<U, F>(lhs: T, rhs: &Tensor<T>, f: F, meta: U::AutogradMeta, op_name: &'static str) -> crate::Result<Tensor<U>>
148    where 
149        U: WithDType, 
150        F: Fn(T, T) -> U 
151    {
152        let (storage, shape) = Self::compute_binary_scalar_lhs_op(lhs, rhs, f, op_name)?;
153        Ok(Tensor::<U>::from_storage(storage, shape, meta))
154    }
155}
156
157macro_rules! binary_op_impl {
158    ($fn_name:ident) => {
159        paste! {
160            pub fn [< $fn_name _tensor >](&self, rhs: &Self) -> crate::Result<Self> {
161                let meta = T::AutogradMeta::on_binary_op(self, rhs, BinaryOp::  [< $fn_name:camel >]);
162                Self::binary_op(self, rhs, T::$fn_name, meta, stringify!([< $fn_name _tensor >]))
163            }
164        
165            pub fn [< $fn_name _scalar >](&self, rhs: T) -> crate::Result<Self> {
166                let meta = T::AutogradMeta::on_binary_scalar_rhs_op(self, rhs, BinaryOp::  [< $fn_name:camel >]);
167                Self::binary_scalar_rhs_op(self, rhs, T::$fn_name, meta, stringify!([< $fn_name _scalar >]))
168            } 
169        
170            pub fn [< scalar_ $fn_name >](lhs: T, rhs: &Tensor<T>) -> crate::Result<Tensor<T>> {
171                let meta = T::AutogradMeta::on_binary_scalar_lhs_op(lhs, rhs, BinaryOp::  [< $fn_name:camel >]);
172                Self::binary_scalar_lhs_op(lhs, rhs, T::$fn_name, meta, stringify!([< scalar_ $fn_name >]))
173            }
174
175            pub fn $fn_name(&self, rhs: impl Into<TensorOrScalar<T>>) -> crate::Result<Self> {
176                match rhs.into() {
177                    TensorOrScalar::Tensor(t) => self.[< $fn_name _tensor >](&t),
178                    TensorOrScalar::Scalar(s) => self.[< $fn_name _scalar >](s)
179                }
180            } 
181        }
182    };
183}
184
185impl<T: NumDType> Tensor<T> {
186    binary_op_impl!(add);
187    binary_op_impl!(mul);
188    binary_op_impl!(sub);
189    binary_op_impl!(div);
190    binary_op_impl!(minimum);
191    binary_op_impl!(maximum);
192
193    pub fn clamp(&self, min: T, max: T) -> crate::Result<Self> {
194        self.maximum(min)?.minimum(max)
195    }
196}
197
198impl<T: NumDType> Tensor<T> {
199    fn binary_inplace_op<F>(lhs: &Tensor<T>, rhs: &Tensor<T>, f: F, op_name: &'static str) -> crate::Result<()> 
200    where 
201        F: Fn(T, T) -> T
202    {
203        let _ = Tensor::<T>::same_shape_binary_op(lhs, rhs, op_name)?;
204
205        let mut lhs_storage = lhs.storage_write()?;
206        let rhs_storage = rhs.storage_read()?;
207        let lhs_layout = lhs.layout();
208        let rhs_layout = rhs.layout();
209
210        assert_eq!(lhs_layout.dims(), rhs_layout.dims(), "lhs dims != rhs dim2");
211
212        let lhs = lhs_storage.data_mut();
213        let rhs = rhs_storage.data();
214        
215        match (lhs_layout.storage_indices(), rhs_layout.storage_indices()) {
216            (StorageIndices::Contiguous(lhs_index), StorageIndices::Contiguous(rhs_index)) => {
217                let lhs = &mut lhs[lhs_index.begin_index..lhs_index.end_index];
218                let rhs = &rhs[rhs_index.begin_index..rhs_index.end_index];
219                lhs.iter_mut().zip(rhs.iter())
220                    .for_each(|(l, &r)| *l = f(*l, r));
221            }
222            (StorageIndices::Contiguous(lhs_index), StorageIndices::Uncontiguous(rhs_index)) => {
223                let lhs = &mut lhs[lhs_index.begin_index..lhs_index.end_index];
224                lhs.iter_mut().zip(rhs_index)
225                    .for_each(|(l, rhs_index)| *l = f(*l, rhs[rhs_index]));
226            }
227            (StorageIndices::Uncontiguous(lhs_index), StorageIndices::Contiguous(rhs_index)) => {
228                let rhs = &rhs[rhs_index.begin_index..rhs_index.end_index];
229                lhs_index.zip(rhs.iter())
230                    .for_each(|(lhs_index, &r)| lhs[lhs_index] = f(lhs[lhs_index], r));
231            }
232            (StorageIndices::Uncontiguous(lhs_index), StorageIndices::Uncontiguous(rhs_index)) => {
233                lhs_index.zip(rhs_index)
234                    .for_each(|(lhs_index, rhs_index)| lhs[lhs_index] = f(lhs[lhs_index], rhs[rhs_index]));
235            }
236        }
237        
238        Ok(())
239    }
240
241    fn binary_op_scalar_inplace<F>(lhs: &Tensor<T>, rhs: T, f: F, _op_name: &'static str) -> crate::Result<()> 
242    where 
243        F: Fn(T, T) -> T
244    {
245        let mut lhs_storage = lhs.storage_write()?;
246        let lhs_layout = lhs.layout();
247        let lhs = lhs_storage.data_mut();
248    
249        match lhs_layout.storage_indices() {
250            StorageIndices::Contiguous(index) => {
251                let lhs = &mut lhs[index.begin_index..index.end_index];
252                lhs.iter_mut().for_each(|l| *l = f(*l, rhs));
253            }
254            StorageIndices::Uncontiguous(index) => {
255                index.for_each(|lhs_index| lhs[lhs_index] = f(lhs[lhs_index], rhs));
256            }
257        }
258        
259        Ok(())
260    }
261}
262
263macro_rules! binary_inplace_op_impl {
264    ($fn_name:ident) => {
265        paste! {
266            /// WARNING: This is an unsafe operation that bypasses the autograd engine. 
267            /// It does NOT track computation history and cannot compute gradients. 
268            /// Attempting to call this method on a tensor with `requires_grad=True` 
269            /// will raise a Error.
270            pub fn [< $fn_name _ >](&self, rhs: impl Into<TensorOrScalar<T>>) -> crate::Result<Self> {
271                self.check_implace_op()?;
272                self.[<impl_ $fn_name _ >](rhs)
273            }
274
275            pub(crate) fn [<impl_ $fn_name _ >](&self, rhs: impl Into<TensorOrScalar<T>>) -> crate::Result<Self> {
276                let rhs = rhs.into();
277                match rhs {
278                    TensorOrScalar::Scalar(rhs) => Self::binary_op_scalar_inplace(self, rhs, T::$fn_name, stringify!([< $fn_name _scalar_ >]))?,
279                    TensorOrScalar::Tensor(rhs) => Self::binary_inplace_op(self, &rhs, T::$fn_name, stringify!([< $fn_name _scalar >]))?,
280                }
281                Ok(self.clone())
282            }
283        }
284    };
285}
286
287#[allow(unused)]
288impl<T: NumDType> Tensor<T> {
289    binary_inplace_op_impl!(add);
290    binary_inplace_op_impl!(sub);
291    binary_inplace_op_impl!(mul);
292    binary_inplace_op_impl!(div);
293    binary_inplace_op_impl!(minimum);
294    binary_inplace_op_impl!(maximum);
295
296    /// WARNING: This is an unsafe operation that bypasses the autograd engine. 
297    /// It does NOT track computation history and cannot compute gradients. 
298    /// Attempting to call this method on a tensor with `requires_grad=True` 
299    /// will raise a Error.
300    pub fn clamp_(&self, min: T, max: T) -> crate::Result<()> {
301        self.maximum_(min)?;
302        self.minimum_(max)?;
303        Ok(())
304    }
305}
306
307impl<T: NumDType> Tensor<T> {
308    pub fn eq(&self, rhs: impl Into<TensorOrScalar<T>>) -> crate::Result<Tensor<bool>> {
309        self.cmp(rhs, CmpOp::Eq)
310    }
311
312    pub fn ne(&self, rhs: impl Into<TensorOrScalar<T>>) -> crate::Result<Tensor<bool>> {
313        self.cmp(rhs, CmpOp::Ne)
314    }
315
316    pub fn le(&self, rhs: impl Into<TensorOrScalar<T>>) -> crate::Result<Tensor<bool>> {
317        self.cmp(rhs, CmpOp::Le)
318    }
319
320    pub fn ge(&self, rhs: impl Into<TensorOrScalar<T>>) -> crate::Result<Tensor<bool>> {
321        self.cmp(rhs, CmpOp::Ge)
322    }
323
324    pub fn lt(&self, rhs: impl Into<TensorOrScalar<T>>) -> crate::Result<Tensor<bool>> {
325        self.cmp(rhs, CmpOp::Lt)
326    }
327
328    pub fn gt(&self, rhs: impl Into<TensorOrScalar<T>>) -> crate::Result<Tensor<bool>> {
329        self.cmp(rhs, CmpOp::Gt)
330    }
331
332    pub fn cmp(&self, rhs: impl Into<TensorOrScalar<T>>, op: CmpOp) -> crate::Result<Tensor<bool>> {
333        match rhs.into() {
334            TensorOrScalar::Tensor(rhs) => {
335                match op {
336                    CmpOp::Eq => Self::binary_op(self, &rhs, |a, b| a == b, Default::default(), "eq"),
337                    CmpOp::Ne => Self::binary_op(self, &rhs, |a, b| a != b, Default::default(), "nq"),
338                    CmpOp::Le => Self::binary_op(self, &rhs, |a, b| a <= b, Default::default(), "le"),
339                    CmpOp::Ge => Self::binary_op(self, &rhs, |a, b| a >= b, Default::default(), "ge"),
340                    CmpOp::Lt => Self::binary_op(self, &rhs, |a, b| a <  b, Default::default(), "lt"),
341                    CmpOp::Gt => Self::binary_op(self, &rhs, |a, b| a >  b, Default::default(), "gt"),
342                }
343            }
344            TensorOrScalar::Scalar(rhs) => {
345                match op {
346                    CmpOp::Eq => Self::binary_scalar_rhs_op(self, rhs, |a, b| a == b, Default::default(), "eq"),
347                    CmpOp::Ne => Self::binary_scalar_rhs_op(self, rhs, |a, b| a != b, Default::default(), "nq"),
348                    CmpOp::Le => Self::binary_scalar_rhs_op(self, rhs, |a, b| a <= b, Default::default(), "le"),
349                    CmpOp::Ge => Self::binary_scalar_rhs_op(self, rhs, |a, b| a >= b, Default::default(), "ge"),
350                    CmpOp::Lt => Self::binary_scalar_rhs_op(self, rhs, |a, b| a <  b, Default::default(), "lt"),
351                    CmpOp::Gt => Self::binary_scalar_rhs_op(self, rhs, |a, b| a >  b, Default::default(), "gt"),
352                }
353            }
354        }
355    } 
356}
357
358impl Tensor<bool> {
359    pub fn and(&self, rhs: impl Into<TensorOrScalar<bool>>) -> crate::Result<Tensor<bool>> {
360        match rhs.into() {
361            TensorOrScalar::Tensor(rhs) => Self::binary_op(self, &rhs, |a, b| a & b, Default::default(), "and"),
362            TensorOrScalar::Scalar(rhs) => Self::binary_scalar_rhs_op(self, rhs, |a, b| a & b, Default::default(), "and"),
363        }
364    }
365
366    pub fn or(&self, rhs: impl Into<TensorOrScalar<bool>>) -> crate::Result<Tensor<bool>> {
367        match rhs.into() {
368            TensorOrScalar::Tensor(rhs) => Self::binary_op(self, &rhs, |a, b| a | b, Default::default(), "or"),
369            TensorOrScalar::Scalar(rhs) => Self::binary_scalar_rhs_op(self, rhs, |a, b| a | b, Default::default(), "or"),
370        }
371    }
372
373    pub fn xor(&self, rhs: impl Into<TensorOrScalar<bool>>) -> crate::Result<Tensor<bool>> {
374        match rhs.into() {
375            TensorOrScalar::Tensor(rhs) => Self::binary_op(self, &rhs, |a, b| a ^ b, Default::default(), "xor"),
376            TensorOrScalar::Scalar(rhs) => Self::binary_scalar_rhs_op(self, rhs, |a, b| a ^ b, Default::default(), "xor"),
377        }
378    }
379
380    pub fn not(&self) -> crate::Result<Tensor<bool>> {
381        if self.element_count() == 0 {
382            return Ok(self.clone());
383        }
384        let storage = self.compute_unary_op(|v| !v)?;
385        Ok(Self::from_storage(storage, self.shape(), Default::default()))
386    }
387}
388
389//////////////////////////////////////////////////////////////////////////////
390///        Unary Op / Unary Assign Op  for Tensor
391//////////////////////////////////////////////////////////////////////////////
392
393impl<T: WithDType> Tensor<T> {
394    fn compute_unary_op<U, F>(&self, f: F) -> crate::Result<Storage<U>> 
395    where
396        U: WithDType,
397        F: Fn(T) -> U
398    {
399        let storage = self.storage_read()?;
400        let vec = storage.data();
401
402        let output: Vec<_> = match self.storage_indices() {
403            StorageIndices::Contiguous(index) => {
404                let data = &vec[index.begin_index..index.end_index];
405                data.iter().map(|&v| f(v)).collect()
406            }
407            StorageIndices::Uncontiguous(index) => {
408                index.map(|i| f(vec[i])).collect()
409            }
410        };
411        
412        Ok(Storage::new(output))
413    }
414
415    fn unary_op<U, F>(&self, f: F, meta: U::AutogradMeta) -> crate::Result<Tensor<U>> 
416    where
417        U: WithDType,
418        F: Fn(T) -> U
419    {
420        let storage = self.compute_unary_op(f)?;
421        Ok(Tensor::from_storage(storage, self.shape(), meta))
422    }
423
424    fn unary_assign_op<F>(&self, f: F) -> crate::Result<()>
425    where
426        F: Fn(T) -> T
427    {
428        self.check_implace_op()?;
429
430        let mut storage = self.storage_write()?;
431        let vec = storage.data_mut();
432        match self.layout().storage_indices() {
433            StorageIndices::Contiguous(index) => {
434                let vec = &mut vec[index.begin_index..index.end_index];
435                vec.iter_mut().for_each(|v| *v = f(*v));
436            }
437            StorageIndices::Uncontiguous(index) => {
438                for index in index {
439                    vec[index] = f(vec[index]);
440                }
441            }
442        }
443 
444        Ok(())
445    }
446}
447
448macro_rules! float_unary_op_impl {
449    ($fn_name:ident) => {
450        paste! {
451            pub fn $fn_name(&self) -> crate::Result<Self> {
452                let meta = F::AutogradMeta::on_unray_op(self, UnaryOp:: [< $fn_name:camel >]);
453                self.unary_op(F::$fn_name, meta)
454            }
455
456            /// WARNING: This is an unsafe operation that bypasses the autograd engine. 
457            /// It does NOT track computation history and cannot compute gradients. 
458            /// Attempting to call this method on a tensor with `requires_grad=True` 
459            /// will raise a Error.
460            #[inline]
461            pub fn [<$fn_name _>](&self) -> crate::Result<()> {
462                self.unary_assign_op(F::$fn_name)
463            }
464        }
465    };
466}
467
468impl<F: NumDType> Tensor<F> {
469    pub fn sign(&self) -> crate::Result<Self> {
470        let meta = F::AutogradMeta::on_unray_op(self, UnaryOp::Sign);
471        self.unary_op(F::sign, meta)
472    }
473
474    /// WARNING: This is an unsafe operation that bypasses the autograd engine. 
475    /// It does NOT track computation history and cannot compute gradients. 
476    /// Attempting to call this method on a tensor with `requires_grad=True` 
477    /// will raise a Error.
478    #[inline]
479    pub fn sign_(&self) -> crate::Result<()> {
480        self.unary_assign_op(F::sign)
481    }
482}
483
484impl<F: FloatDType> Tensor<F> {
485    float_unary_op_impl!(floor);
486    float_unary_op_impl!(ceil);
487    float_unary_op_impl!(round);
488    float_unary_op_impl!(exp);
489    float_unary_op_impl!(ln);
490    float_unary_op_impl!(sin);
491    float_unary_op_impl!(cos);
492    float_unary_op_impl!(tanh);
493    float_unary_op_impl!(sqrt);
494    float_unary_op_impl!(sqr);
495    float_unary_op_impl!(abs);
496    float_unary_op_impl!(recip);
497    float_unary_op_impl!(gelu);
498    float_unary_op_impl!(gelu_erf);
499    float_unary_op_impl!(erf);
500    float_unary_op_impl!(relu);
501    float_unary_op_impl!(silu);
502    float_unary_op_impl!(sigmoid);
503
504    pub fn leaky_relu(&self, negative_slope: F) -> crate::Result<Self> {
505        let meta = F::AutogradMeta::on_unray_op(self, UnaryOp::LeakyRelu(negative_slope));
506        self.unary_op(|v: F| F::leaky_relu(v, negative_slope), meta)
507    }
508
509    #[inline]
510    pub fn leaky_relu_(&self, negative_slope: F) -> crate::Result<()> {
511        self.unary_assign_op(|v: F| F::leaky_relu(v, negative_slope))
512    }
513}
514
515impl<F: FloatDType> Tensor<F> {
516    pub fn pow(&self, e: F) -> crate::Result<Self> {
517        let meta = F::AutogradMeta::on_unray_op(self, UnaryOp::Pow(e));
518        self.unary_op(|v: F| v.powf(e), meta)
519    }
520
521    #[inline]
522    pub fn pow_(&self, e: F) -> crate::Result<()> {
523        self.unary_assign_op(|v: F| v.powf(e))
524    }
525}
526
527impl<T: WithDType> Tensor<T> {
528    #[inline]
529    pub fn map<F, O>(&self, f: F) -> crate::Result<Tensor<O>>
530    where 
531        O: WithDType,
532        F: Fn(T) -> O,
533    {
534        self.unary_op(f, Default::default())
535    }
536
537    #[inline]
538    pub fn map_<F>(&self, f: F) -> crate::Result<()>
539    where 
540        F: Fn(T) -> T,
541    {
542        self.unary_assign_op(f)
543    }
544}
545
546impl<T: NumDType + Neg<Output = T>> Tensor<T> {
547    pub fn neg(&self) -> crate::Result<Self> {
548        let meta = T::AutogradMeta::on_unray_op(self, UnaryOp::Neg);
549        self.unary_op(T::neg, meta)
550    }
551
552    #[inline]
553    pub fn neg_(&self) -> crate::Result<()> {
554        self.unary_assign_op(T::neg)
555    }
556}
557
558impl<T: NumDType> Tensor<T> {
559    /// y = mul * a + add
560    pub fn affine(&self, mul: T, add: T) -> crate::Result<Self> {
561        let meta = T::AutogradMeta::on_unray_op(self, UnaryOp::Affine{ mul, add });
562        self.unary_op(|v| v * mul + add, meta)
563    }
564
565    #[inline]
566    pub fn affine_(&self, mul: T, add: T) -> crate::Result<()> {
567        self.unary_assign_op(|v| v * mul + add)
568    }
569}
570
571use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Sub};
572
573//////////////////////////////////////////////////////////////////////////////
574///        Add
575//////////////////////////////////////////////////////////////////////////////
576
577impl<'a, T: NumDType, R: Into<TensorOrScalar<T>>> Add<R> for &Tensor<T> {
578    type Output = Tensor<T>;
579    fn add(self, rhs: R) -> Self::Output {
580        Tensor::add(self, rhs).expect("Tensor::add failed")
581    }
582}
583
584impl<'a, T: NumDType, R> Add<R> for Tensor<T> 
585where R: Into<TensorOrScalar<T>> 
586{
587    type Output = Tensor<T>;
588    fn add(self, rhs: R) -> Self::Output {
589        Tensor::add(&self, rhs).expect("Tensor::add failed")
590    }
591}
592
593//////////////////////////////////////////////////////////////////////////////
594///        Sub
595//////////////////////////////////////////////////////////////////////////////
596
597impl<'a, T: NumDType, R: Into<TensorOrScalar<T>>> Sub<R> for &Tensor<T> {
598    type Output = Tensor<T>;
599    fn sub(self, rhs: R) -> Self::Output {
600        Tensor::sub(self, rhs).expect("Tensor::sub failed")
601    }
602}
603
604impl<'a, T: NumDType, R: Into<TensorOrScalar<T>>> Sub<R> for Tensor<T> {
605    type Output = Tensor<T>;
606    fn sub(self, rhs: R) -> Self::Output {
607        Tensor::sub(&self, rhs).expect("Tensor::sub failed")
608    }
609}
610
611//////////////////////////////////////////////////////////////////////////////
612///        Mul
613//////////////////////////////////////////////////////////////////////////////
614
615impl<'a, T: NumDType, R: Into<TensorOrScalar<T>>> Mul<R> for &Tensor<T> {
616    type Output = Tensor<T>;
617    fn mul(self, rhs: R) -> Self::Output {
618        Tensor::mul(self, rhs).expect("Tensor::mul failed")
619    }
620}
621
622impl<'a, T: NumDType, R: Into<TensorOrScalar<T>>> Mul<R> for Tensor<T> {
623    type Output = Tensor<T>;
624    fn mul(self, rhs: R) -> Self::Output {
625        Tensor::mul(&self, rhs).expect("Tensor::mul failed")
626    }
627}
628
629//////////////////////////////////////////////////////////////////////////////
630///        Div
631//////////////////////////////////////////////////////////////////////////////
632
633impl<'a, T: NumDType, R: Into<TensorOrScalar<T>>> Div<R> for &Tensor<T> {
634    type Output = Tensor<T>;
635    fn div(self, rhs: R) -> Self::Output {
636        Tensor::div(self, rhs).expect("Tensor::div failed")
637    }
638}
639
640impl<'a, T: NumDType, R: Into<TensorOrScalar<T>>> Div<R> for Tensor<T> {
641    type Output = Tensor<T>;
642    fn div(self, rhs: R) -> Self::Output {
643        Tensor::div(&self, rhs).expect("Tensor::div failed")
644    }
645}
646
647//////////////////////////////////////////////////////////////////////////////
648///        Bool
649//////////////////////////////////////////////////////////////////////////////
650
651impl<'a, R: Into<TensorOrScalar<bool>>> BitAnd<R> for &Tensor<bool> {
652    type Output = Tensor<bool>;
653    fn bitand(self, rhs: R) -> Self::Output {
654        self.and(rhs).expect("Tensor::and failed")
655    }
656}
657
658impl<'a, R: Into<TensorOrScalar<bool>>> BitAnd<R> for Tensor<bool> {
659    type Output = Tensor<bool>;
660    fn bitand(self, rhs: R) -> Self::Output {
661        self.and(rhs).expect("Tensor::and failed")
662    }
663}
664
665impl<'a, R: Into<TensorOrScalar<bool>>> BitOr<R> for &Tensor<bool> {
666    type Output = Tensor<bool>;
667    fn bitor(self, rhs: R) -> Self::Output {
668        self.or(rhs).expect("Tensor::or failed")
669    }
670}
671
672impl<'a, R: Into<TensorOrScalar<bool>>> BitOr<R> for Tensor<bool> {
673    type Output = Tensor<bool>;
674    fn bitor(self, rhs: R) -> Self::Output {
675        self.or(rhs).expect("Tensor::or failed")
676    }
677}
678
679impl<'a, R: Into<TensorOrScalar<bool>>> BitXor<R> for &Tensor<bool> {
680    type Output = Tensor<bool>;
681    fn bitxor(self, rhs: R) -> Self::Output {
682        self.xor(rhs).expect("Tensor::xor failed")
683    }
684}
685
686impl<'a, R: Into<TensorOrScalar<bool>>> BitXor<R> for Tensor<bool> {
687    type Output = Tensor<bool>;
688    fn bitxor(self, rhs: R) -> Self::Output {
689        self.xor(rhs).expect("Tensor::xor failed")
690    }
691}
692
693//////////////////////////////////////////////////////////////////////////////
694///        Scalar & op
695//////////////////////////////////////////////////////////////////////////////
696
697macro_rules! impl_scalar_tensor_binary {
698    ($($t:ty),*) => {
699        $(
700            impl Add<Tensor<$t>> for $t {
701                type Output = Tensor<$t>;
702
703                fn add(self, rhs: Tensor<$t>) -> Self::Output {
704                    Tensor::add(&rhs, self).expect("Tensor::add failed")
705                }
706            }
707
708            impl Add<&Tensor<$t>> for $t {
709                type Output = Tensor<$t>;
710
711                fn add(self, rhs: &Tensor<$t>) -> Self::Output {
712                    Tensor::add(rhs, self).expect("Tensor::add failed")
713                }
714            }
715
716            impl Mul<Tensor<$t>> for $t {
717                type Output = Tensor<$t>;
718
719                fn mul(self, rhs: Tensor<$t>) -> Self::Output {
720                    Tensor::mul(&rhs, self).expect("Tensor::mul failed")
721                }
722            }
723
724            impl Mul<&Tensor<$t>> for $t {
725                type Output = Tensor<$t>;
726
727                fn mul(self, rhs: &Tensor<$t>) -> Self::Output {
728                    Tensor::mul(rhs, self).expect("Tensor::mul failed")
729                }
730            }
731
732            impl Sub<&Tensor<$t>> for $t {
733                type Output = Tensor<$t>;
734
735                fn sub(self, rhs: &Tensor<$t>) -> Self::Output {
736                    Tensor::scalar_sub(self, rhs).expect("Tensor::scalar_sub failed")
737                }
738            }
739
740            impl Sub<Tensor<$t>> for $t {
741                type Output = Tensor<$t>;
742
743                fn sub(self, rhs: Tensor<$t>) -> Self::Output {
744                    Tensor::scalar_sub(self, &rhs).expect("Tensor::scalar_sub failed")
745                }
746            }
747
748            impl Div<&Tensor<$t>> for $t {
749                type Output = Tensor<$t>;
750
751                fn div(self, rhs: &Tensor<$t>) -> Self::Output {
752                    Tensor::scalar_div(self, rhs).expect("Tensor::scalar_div failed")
753                }
754            }
755
756            impl Div<Tensor<$t>> for $t {
757                type Output = Tensor<$t>;
758
759                fn div(self, rhs: Tensor<$t>) -> Self::Output {
760                    Tensor::scalar_div(self, &rhs).expect("Tensor::scalar_div failed")
761                }
762            }
763        )*
764    };
765}
766
767impl_scalar_tensor_binary!(half::bf16, f32, f64, u8, i32, u32);
768
769#[cfg(test)]
770mod tests {
771    use super::*;
772
773    #[test]
774    fn test_inplace_safe_ops() -> crate::Result<()> {
775        let data = vec![-1.5f32, -0.5, 0.0, 0.5, 1.5];
776    
777        macro_rules! test_op {
778            ($op:ident, $op_inplace:ident) => {{
779                let a = Tensor::new(data.clone())?;
780                let b = Tensor::new(data.clone())?;
781    
782                let out = a.$op()?;
783                b.$op_inplace()?;
784    
785                assert!(b.allclose(&out, 1e-5, 1e-6)?);
786            }};
787        }
788    
789        test_op!(floor, floor_);
790        test_op!(ceil, ceil_);
791        test_op!(round, round_);
792        test_op!(sin, sin_);
793        test_op!(cos, cos_);
794        test_op!(tanh, tanh_);
795        test_op!(abs, abs_);
796        test_op!(relu, relu_);
797        test_op!(silu, silu_);
798        test_op!(sigmoid, sigmoid_);
799    
800        Ok(())
801    }
802    
803    #[test]
804    fn test_inplace_positive_domain_ops() -> crate::Result<()> {
805        let data = vec![0.1f32, 0.5, 1.0, 2.0];
806    
807        macro_rules! test_op {
808            ($op:ident, $op_inplace:ident) => {{
809                let a = Tensor::new(data.clone())?;
810                let b = Tensor::new(data.clone())?;
811    
812                let out = a.$op()?;
813                b.$op_inplace()?;
814    
815                assert!(b.allclose(&out, 1e-5, 1e-6)?);
816            }};
817        }
818    
819        test_op!(ln, ln_);
820        test_op!(sqrt, sqrt_);
821        test_op!(recip, recip_);
822        test_op!(exp, exp_);
823        test_op!(gelu, gelu_);
824        test_op!(gelu_erf, gelu_erf_);
825        test_op!(erf, erf_);
826    
827        Ok(())
828    }
829
830    #[test]
831    fn test_exp_log() -> crate::Result<()> {
832        let a = Tensor::new(&[0.0f32, 1.0, 2.0])?;
833        let exp_a = a.exp()?;
834        let log_a = exp_a.ln()?;
835        assert!(a.allclose(&log_a, 1e-5, 1e-8)?);
836        Ok(())
837    }
838
839    #[test]
840    fn test_trig() -> crate::Result<()> {
841        let a = Tensor::new(&[0.0f32, std::f32::consts::FRAC_PI_2])?;
842        let sin_a = a.sin()?;
843        let cos_a = a.cos()?;
844
845        let expected_sin = Tensor::new(&[0.0f32, 1.0])?;
846        let expected_cos = Tensor::new(&[1.0f32, 0.0])?;
847
848        println!("{:?}", cos_a.iter()?.collect::<Vec<_>>());
849
850        assert!(sin_a.allclose(&expected_sin, 1e-5, 1e-8)?);
851        assert!(cos_a.allclose(&expected_cos, 1e-5, 8e-8)?);
852
853        Ok(())
854    }
855
856    #[test]
857    fn test_abs_neg() -> crate::Result<()> {
858        let a = Tensor::new(&[-1.0f32, 0.0, 2.0])?;
859        let abs_a = a.abs()?;
860        let neg_a = a.neg()?;
861
862        let expected_abs = Tensor::new(&[1.0f32, 0.0, 2.0])?;
863        let expected_neg = Tensor::new(&[1.0f32, 0.0, -2.0])?;
864
865        assert!(abs_a.allclose(&expected_abs, 1e-6, 1e-6)?);
866        assert!(neg_a.allclose(&expected_neg, 1e-6, 1e-6)?);
867
868        Ok(())
869    }
870
871    #[test]
872    fn test_floor_ceil_round() -> crate::Result<()> {
873        let a = Tensor::new(&[1.2f32, 2.7, -1.3])?;
874        let floor_a = a.floor()?;
875        let ceil_a = a.ceil()?;
876        let round_a = a.round()?;
877
878        let expected_floor = Tensor::new(&[1.0f32, 2.0, -2.0])?;
879        let expected_ceil = Tensor::new(&[2.0f32, 3.0, -1.0])?;
880        let expected_round = Tensor::new(&[1.0f32, 3.0, -1.0])?;
881
882        assert!(floor_a.allclose(&expected_floor, 1e-6, 1e-6)?);
883        assert!(ceil_a.allclose(&expected_ceil, 1e-6, 1e-6)?);
884        assert!(round_a.allclose(&expected_round, 1e-6, 1e-6)?);
885
886        Ok(())
887    }
888
889    #[test]
890    fn test_floor_recip() -> crate::Result<()> {
891        let a = Tensor::new(&[1.2f32, 2.7, -1.3])?;
892        let recip_a = a.recip()?;
893        let expected = Tensor::new(&[1.2f32.recip(), 2.7f32.recip(), -1.3f32.recip(),])?;
894
895        assert!(recip_a.allclose(&expected, 1e-6, 1e-6)?);
896
897        Ok(())
898    }
899
900    #[test]
901    fn test_add_basic() -> crate::Result<()> {
902        let a = Tensor::new(&[1.0f32, 2.0, 3.0])?;
903        let b = Tensor::new(&[4.0f32, 5.0, 6.0])?;
904        let c = Tensor::add(&a, &b)?;
905        let expected = Tensor::new(&[5.0f32, 7.0, 9.0])?;
906        assert!(c.allclose(&expected, 1e-6, 1e-6)?);
907
908        Ok(())
909    }
910
911    #[test]
912    fn test_add_basic_variable() -> crate::Result<()> {
913        let a = Tensor::new_var(&[1.0f32, 2.0, 3.0])?;
914        let b = Tensor::new_var(&[4.0f32, 5.0, 6.0])?;
915        let c = Tensor::add(&a, &b)?;
916        let expected = Tensor::new(&[5.0f32, 7.0, 9.0])?;
917        assert!(c.allclose(&expected, 1e-6, 1e-6)?);
918
919        Ok(())
920    }
921
922    #[test]
923    fn test_sub_basic() -> crate::Result<()> {
924        let a = Tensor::new(&[10.0f32, 20.0, 30.0])?;
925        let b = Tensor::new(&[1.0f32, 2.0, 3.0])?;
926        let c = Tensor::sub(&a, &b)?;
927        let expected = Tensor::new(&[9.0f32, 18.0, 27.0])?;
928        assert!(c.allclose(&expected, 1e-6, 1e-6)?);
929
930        Ok(())
931    }
932
933    #[test]
934    fn test_mul_basic() -> crate::Result<()> {
935        let a = Tensor::new(&[1.0f32, 2.0, 3.0])?;
936        let b = Tensor::new(&[2.0f32, 3.0, 4.0])?;
937        let c = Tensor::mul(&a, &b)?;
938        let expected = Tensor::new(&[2.0f32, 6.0, 12.0])?;
939        assert!(c.allclose(&expected, 1e-6, 1e-6)?);
940
941        Ok(())
942    }
943
944    #[test]
945    fn test_div_basic() -> crate::Result<()> {
946        let a = Tensor::new(&[4.0f32, 9.0, 16.0])?;
947        let b = Tensor::new(&[2.0f32, 3.0, 4.0])?;
948        let c = Tensor::div(&a, &b)?;
949        let expected = Tensor::new(&[2.0f32, 3.0, 4.0])?;
950        assert!(c.allclose(&expected, 1e-6, 1e-6)?);
951
952        Ok(())
953    }
954
955    #[test]
956    fn test_min_max_basic() -> crate::Result<()> {
957        let a = Tensor::new(&[1.0f32, 5.0, 3.0])?;
958        let b = Tensor::new(&[2.0f32, 4.0, 6.0])?;
959        let min_res = Tensor::minimum(&a, &b)?;
960        let max_res = Tensor::maximum(&a, &b)?;
961        let expected_min = Tensor::new(&[1.0f32, 4.0, 3.0])?;
962        let expected_max = Tensor::new(&[2.0f32, 5.0, 6.0])?;
963        assert!(min_res.allclose(&expected_min, 1e-6, 1e-6)?);
964        assert!(max_res.allclose(&expected_max, 1e-6, 1e-6)?);
965
966        Ok(())
967    }
968
969    #[test]
970    fn test_comparisons() -> crate::Result<()> {
971        let a = Tensor::new(&[1, 2, 3])?;
972        let b = Tensor::new(&[1, 0, 3])?;
973
974        assert_eq!(a.eq(&b).unwrap().to_vec()?, [true, false, true]);
975        assert_eq!(a.ne(&b).unwrap().to_vec()?, [false, true, false]);
976        assert_eq!(a.lt(&b).unwrap().to_vec()?, [false, false, false]);
977        assert_eq!(a.le(&b).unwrap().to_vec()?, [true, false, true]);
978        assert_eq!(a.gt(&b).unwrap().to_vec()?, [false, true, false]);
979        assert_eq!(a.ge(&b).unwrap().to_vec()?, [true, true, true]);
980
981        Ok(())
982    }
983
984    #[test]
985    fn test_add_mul_2d_3d() -> crate::Result<()> {
986        let a = Tensor::new(&[[1.0f32, 2.0], [3.0, 4.0]])?;
987        let b = Tensor::new(&[[5.0f32, 6.0], [7.0, 8.0]])?;
988        let c = Tensor::add(&a, &b)?;
989        let expected = Tensor::new(&[[6., 8.], [10., 12.]])?;
990        assert!(c.allclose(&expected, 1e-6, 1e-6)?);
991
992        let a3 = Tensor::new(&[
993            [[1., 2.], [3., 4.]],
994            [[5., 6.], [7., 8.]],
995        ])?;
996        let b3 = Tensor::new(&[
997            [[2., 0.5], [1., 2.]],
998            [[0.5, 2.], [1.5, 1.]],
999        ])?;
1000        let c3 = Tensor::mul(&a3, &b3)?;
1001        let expected3 = Tensor::new(&[
1002            [[2., 1.], [3., 8.]],
1003            [[2.5, 12.], [10.5, 8.]],
1004        ])?;
1005        assert!(c3.allclose(&expected3, 1e-6, 1e-6)?);
1006
1007        Ok(())
1008    }
1009
1010    #[test]
1011    fn test_div_high_dim() -> crate::Result<()> {
1012        let a = Tensor::full((2, 2, 2, 2), 8.0f32)?;
1013        let b = Tensor::full((2, 2, 2, 2), 2.0f32)?;
1014        let c = Tensor::div(&a, &b)?;
1015        let expected = Tensor::full((2, 2, 2, 2), 4.0f32)?;
1016        assert!(c.allclose(&expected, 1e-6, 1e-6)?);
1017    
1018        Ok(())
1019    }
1020
1021    #[test]
1022    fn test_affine_and_affine_() -> crate::Result<()> {
1023        let a = Tensor::<f64>::ones((3, 3))?;
1024        let b = a.affine(3., 2.)?;
1025        let expected = Tensor::new(&[[5., 5., 5.],[5.,5.,5.],[5.,5.,5.]])?;
1026        assert!(b.allclose(&expected, 1e-6, 1e-6)?);
1027
1028        let a2 = Tensor::<f64>::ones((3, 3))?;
1029        a2.affine_(3., 2.)?;
1030        assert!(a2.allclose(&expected, 1e-6, 1e-6)?);
1031        Ok(())
1032    }
1033
1034    #[test]
1035    fn test_add_scalar() -> crate::Result<()> {
1036        let a = Tensor::new(&[1.0f32, 2.0, 3.0])?;
1037        let b = 10.0f32;
1038        let c = Tensor::add(&a, b)?;
1039        let expected = Tensor::new(&[11.0f32, 12.0, 13.0])?;
1040        assert!(c.allclose(&expected, 1e-6, 1e-6)?);
1041        Ok(())
1042    }
1043
1044    #[test]
1045    fn test_sub_scalar() -> crate::Result<()> {
1046        let a = Tensor::new(&[10.0f32, 20.0, 30.0])?;
1047        let b = 5.0f32;
1048        let c = Tensor::sub(&a, b)?;
1049        let expected = Tensor::new(&[5.0f32, 15.0, 25.0])?;
1050        assert!(c.allclose(&expected, 1e-6, 1e-6)?);
1051        Ok(())
1052    }
1053
1054    #[test]
1055    fn test_mul_scalar() -> crate::Result<()> {
1056        let a = Tensor::new(&[1.0f32, 2.0, 3.0])?;
1057        let b = 2.0f32;
1058        let c = Tensor::mul(&a, b)?;
1059        let expected = Tensor::new(&[2.0f32, 4.0, 6.0])?;
1060        assert!(c.allclose(&expected, 1e-6, 1e-6)?);
1061        Ok(())
1062    }
1063
1064    #[test]
1065    fn test_div_scalar() -> crate::Result<()> {
1066        let a = Tensor::new(&[4.0f32, 9.0, 16.0])?;
1067        let b = 2.0f32;
1068        let c = Tensor::div(&a, b)?;
1069        let expected = Tensor::new(&[2.0f32, 4.5, 8.0])?;
1070        assert!(c.allclose(&expected, 1e-6, 1e-6)?);
1071        Ok(())
1072    }
1073
1074    #[test]
1075    fn test_minimum_scalar() -> crate::Result<()> {
1076        let a = Tensor::new(&[1.0f32, 5.0, 3.0])?;
1077        let b = 4.0f32;
1078        let c = Tensor::minimum(&a, b)?;
1079        let expected = Tensor::new(&[1.0f32, 4.0, 3.0])?;
1080        assert!(c.allclose(&expected, 1e-6, 1e-6)?);
1081        Ok(())
1082    }
1083
1084    #[test]
1085    fn test_maximum_scalar() -> crate::Result<()> {
1086        let a = Tensor::new(&[1.0f32, 5.0, 3.0])?;
1087        let b = 4.0f32;
1088        let c = Tensor::maximum(&a, b)?;
1089        let expected = Tensor::new(&[4.0f32, 5.0, 4.0])?;
1090        assert!(c.allclose(&expected, 1e-6, 1e-6)?);
1091        Ok(())
1092    }
1093
1094    #[test]
1095    fn test_eq_ne_scalar() -> crate::Result<()> {
1096        let a = Tensor::new(&[1, 2, 3])?;
1097        let b = 2;
1098
1099        // Tensor vs scalar
1100        let eq_res = a.eq(b)?;
1101        let expected_eq = Tensor::new(&[false, true, false])?;
1102        assert_eq!(eq_res.to_vec()?, expected_eq.to_vec()?);
1103
1104        let ne_res = a.ne(b)?;
1105        let expected_ne = Tensor::new(&[true, false, true])?;
1106        assert_eq!(ne_res.to_vec()?, expected_ne.to_vec()?);
1107        Ok(())
1108    }
1109
1110    #[test]
1111    fn test_lt_le_gt_ge_scalar() -> crate::Result<()> {
1112        let a = Tensor::new(&[1, 2, 3])?;
1113        let b = 2;
1114
1115        let lt_res = a.lt(b)?;
1116        assert_eq!(lt_res.to_vec()?, [true, false, false]);
1117
1118        let le_res = a.le(b)?;
1119        assert_eq!(le_res.to_vec()?, [true, true, false]);
1120
1121        let gt_res = a.gt(b)?;
1122        assert_eq!(gt_res.to_vec()?, [false, false, true]);
1123
1124        let ge_res = a.ge(b)?;
1125        assert_eq!(ge_res.to_vec()?, [false, true, true]);
1126
1127        Ok(())
1128    }
1129
1130    #[test]
1131    fn test_eq_ne_tensor() -> crate::Result<()> {
1132        let a = Tensor::new(&[1, 2, 3])?;
1133        let b = Tensor::new(&[1, 0, 3])?;
1134
1135        let eq_res = a.eq(&b)?;
1136        assert_eq!(eq_res.to_vec()?, [true, false, true]);
1137
1138        let ne_res = a.ne(&b)?;
1139        assert_eq!(ne_res.to_vec()?, [false, true, false]);
1140
1141        Ok(())
1142    }
1143
1144    #[test]
1145    fn test_lt_le_gt_ge_tensor() -> crate::Result<()> {
1146        let a = Tensor::new(&[1, 2, 3])?;
1147        let b = Tensor::new(&[2, 2, 1])?;
1148
1149        let lt_res = a.lt(&b)?;
1150        assert_eq!(lt_res.to_vec()?, [true, false, false]);
1151
1152        let le_res = a.le(&b)?;
1153        assert_eq!(le_res.to_vec()?, [true, true, false]);
1154
1155        let gt_res = a.gt(&b)?;
1156        assert_eq!(gt_res.to_vec()?, [false, false, true]);
1157
1158        let ge_res = a.ge(&b)?;
1159        assert_eq!(ge_res.to_vec()?, [false, true, true]);
1160
1161        Ok(())
1162    }
1163
1164    #[test]
1165    fn test_comparison_2d() -> crate::Result<()> {
1166        let a = Tensor::new(&[[1, 2], [3, 4]])?;
1167        let b = Tensor::new(&[[2, 2], [1, 5]])?;
1168
1169        let eq_res = a.eq(&b)?;
1170        let expected_eq = Tensor::new(&[[false, true], [false, false]])?;
1171        assert_eq!(eq_res.to_vec()?, expected_eq.to_vec()?);
1172
1173        let gt_res = a.gt(&b)?;
1174        let expected_gt = Tensor::new(&[[false, false], [true, false]])?;
1175        assert_eq!(gt_res.to_vec()?, expected_gt.to_vec()?);
1176
1177        // Tensor vs scalar
1178        let le_res = a.le(3)?;
1179        let expected_le = Tensor::new(&[[true, true], [true, false]])?;
1180        assert_eq!(le_res.to_vec()?, expected_le.to_vec()?);
1181
1182        Ok(())
1183    }
1184
1185    #[test]
1186    fn test_std_ops() -> crate::Result<()> {
1187        let a = Tensor::new(&[[1., 2.], [3., 4.]])?;
1188        let b = Tensor::new(&[[2., 2.], [1., 5.]])?;
1189        let _ = a + b;
1190
1191        let a = Tensor::new(&[[1., 2.], [3., 4.]])?;
1192        let b = Tensor::new(&[[2., 2.], [1., 5.]])?;
1193        let _ = &a + &b;
1194
1195        let a = Tensor::new(&[[1., 2.], [3., 4.]])?;
1196        let b = Tensor::new(&[[2., 2.], [1., 5.]])?;
1197        let _ = a + b;
1198
1199        let a = Tensor::new(&[[1., 2.], [3., 4.]])?;
1200        let b = Tensor::new(&[[2., 2.], [1., 5.]])?;
1201        let _ = a + b;
1202
1203        Ok(())
1204    }
1205
1206    #[test]
1207    fn test_large_ops() -> crate::Result<()> {
1208        let a = Tensor::randn(0.0, 1.0, (5000, 1000))?;
1209        let b = Tensor::randn(0.0, 1.0, (5000, 1000))?;
1210        let now = std::time::Instant::now();
1211        let _ = a + b;
1212        println!("{:?}", std::time::Instant::now() - now);
1213        Ok(())
1214    }
1215}