use crate::{NumDType, StorageIndices, WithDType};
use crate::Tensor;
use paste::paste;
macro_rules! fused_2arg_inplace_op_impl {
($name:ident, $f_inplace:expr, $f:expr) => {
paste! {
#[doc = crate::inplace_warning_doc!()]
pub fn [< $name _ >](&self, a: &Self, b: &Self) -> crate::Result<()> {
self.[< impl_ $name _ >](a, b)?;
self.impl_add_mul_(a, b)
}
#[doc = crate::inplace_warning_doc!()]
pub(crate) fn [< impl_ $name _ >](&self, a: &Self, b: &Self) -> crate::Result<()> {
self.fused_2arg_inplace_op(a, b, $f_inplace, $f, stringify!($name))
}
}
};
}
impl<T: NumDType> Tensor<T> {
fused_2arg_inplace_op_impl!(add_mul, T::add, T::mul);
fused_2arg_inplace_op_impl!(add_div, T::add, T::div);
fused_2arg_inplace_op_impl!(sub_mul, T::sub, T::mul);
fused_2arg_inplace_op_impl!(sub_div, T::sub, T::div);
}
impl<T: NumDType> Tensor<T> {
pub(crate) fn fused_2arg_inplace_op<F1, F2>(
&self,
src1: &Tensor<T>,
src2: &Tensor<T>,
f_inplace: F2,
f: F1,
op_name: &'static str
) -> crate::Result<()>
where
F1: Fn(T, T) -> T,
F2: Fn(T, T) -> T,
{
let dst = self;
let _ = Tensor::<T>::same_shape_binary_op(dst, src1, op_name)?;
let _ = Tensor::<T>::same_shape_binary_op(dst, src2, op_name)?;
let mut dst_storage = dst.storage_write()?;
let src1_storage = src1.storage_read()?;
let src2_storage = src2.storage_read()?;
let dst_layout = dst.layout();
let src1_layout = src1.layout();
let src2_layout = src2.layout();
let dst_data = dst_storage.data_mut();
let src1_data = src1_storage.data();
let src2_data = src2_storage.data();
use StorageIndices::{Contiguous, Uncontiguous};
match (dst_layout.storage_indices(), src1_layout.storage_indices(), src2_layout.storage_indices()) {
(Contiguous(d_idx), Contiguous(s1_idx), Contiguous(s2_idx)) => {
let d_slice = &mut dst_data[d_idx.begin_index..d_idx.end_index];
let s1_slice = &src1_data[s1_idx.begin_index..s1_idx.end_index];
let s2_slice = &src2_data[s2_idx.begin_index..s2_idx.end_index];
d_slice.iter_mut()
.zip(s1_slice.iter())
.zip(s2_slice.iter())
.for_each(|((d, &s1), &s2)| {
*d = f_inplace(*d, f(s1, s2));
});
}
(Contiguous(d_idx), Contiguous(s1_idx), Uncontiguous(s2_idx)) => {
let d_slice = &mut dst_data[d_idx.begin_index..d_idx.end_index];
let s1_slice = &src1_data[s1_idx.begin_index..s1_idx.end_index];
d_slice.iter_mut()
.zip(s1_slice.iter())
.zip(s2_idx)
.for_each(|((d, &s1), s2_i)| {
*d = f_inplace(*d, f(s1, src2_data[s2_i]));
});
}
(Contiguous(d_idx), Uncontiguous(s1_idx), Contiguous(s2_idx)) => {
let d_slice = &mut dst_data[d_idx.begin_index..d_idx.end_index];
let s2_slice = &src2_data[s2_idx.begin_index..s2_idx.end_index];
d_slice.iter_mut()
.zip(s1_idx)
.zip(s2_slice.iter())
.for_each(|((d, s1_i), &s2)| {
*d = f_inplace(*d, f(src1_data[s1_i], s2));
});
}
(Contiguous(d_idx), Uncontiguous(s1_idx), Uncontiguous(s2_idx)) => {
let d_slice = &mut dst_data[d_idx.begin_index..d_idx.end_index];
d_slice.iter_mut()
.zip(s1_idx)
.zip(s2_idx)
.for_each(|((d, s1_i), s2_i)| {
*d = f_inplace(*d, f(src1_data[s1_i], src2_data[s2_i]));
});
}
(Uncontiguous(d_idx), Contiguous(s1_idx), Contiguous(s2_idx)) => {
let s1_slice = &src1_data[s1_idx.begin_index..s1_idx.end_index];
let s2_slice = &src2_data[s2_idx.begin_index..s2_idx.end_index];
d_idx.zip(s1_slice.iter())
.zip(s2_slice.iter())
.for_each(|((d_i, &s1), &s2)| {
dst_data[d_i] = f_inplace(dst_data[d_i], f(s1, s2));
});
}
(Uncontiguous(d_idx), Contiguous(s1_idx), Uncontiguous(s2_idx)) => {
let s1_slice = &src1_data[s1_idx.begin_index..s1_idx.end_index];
d_idx.zip(s1_slice.iter())
.zip(s2_idx)
.for_each(|((d_i, &s1), s2_i)| {
dst_data[d_i] = f_inplace(dst_data[d_i], f(s1, src2_data[s2_i]));
});
}
(Uncontiguous(d_idx), Uncontiguous(s1_idx), Contiguous(s2_idx)) => {
let s2_slice = &src2_data[s2_idx.begin_index..s2_idx.end_index];
d_idx.zip(s1_idx)
.zip(s2_slice.iter())
.for_each(|((d_i, s1_i), &s2)| {
dst_data[d_i] = f_inplace(dst_data[d_i], f(src1_data[s1_i], s2));
});
}
(Uncontiguous(d_idx), Uncontiguous(s1_idx), Uncontiguous(s2_idx)) => {
d_idx.zip(s1_idx)
.zip(s2_idx)
.for_each(|((d_i, s1_i), s2_i)| {
let v = f(src1_data[s1_i], src2_data[s2_i]);
dst_data[d_i] = f_inplace(dst_data[d_i], v);
});
}
}
Ok(())
}
pub(crate) fn fused_1arg_inplace_op<F1, F2>(
&self,
rhs: &Tensor<T>,
f_inplace: F1,
f: F2,
op_name: &'static str
) -> crate::Result<()>
where
F1: Fn(T, T) -> T,
F2: Fn(T) -> T,
{
let lhs = self;
let _ = Tensor::<T>::same_shape_binary_op(lhs, rhs, op_name)?;
let mut lhs_storage = lhs.storage_write()?;
let rhs_storage = rhs.storage_read()?;
let lhs_layout = lhs.layout();
let rhs_layout = rhs.layout();
assert_eq!(lhs_layout.dims(), rhs_layout.dims(), "lhs dims != rhs dim2");
let lhs = lhs_storage.data_mut();
let rhs = rhs_storage.data();
match (lhs_layout.storage_indices(), rhs_layout.storage_indices()) {
(StorageIndices::Contiguous(lhs_index), StorageIndices::Contiguous(rhs_index)) => {
let lhs = &mut lhs[lhs_index.begin_index..lhs_index.end_index];
let rhs = &rhs[rhs_index.begin_index..rhs_index.end_index];
lhs.iter_mut().zip(rhs.iter())
.for_each(|(l, &r)| *l = f_inplace(*l, f(r)));
}
(StorageIndices::Contiguous(lhs_index), StorageIndices::Uncontiguous(rhs_index)) => {
let lhs = &mut lhs[lhs_index.begin_index..lhs_index.end_index];
lhs.iter_mut().zip(rhs_index)
.for_each(|(l, rhs_index)| *l = f_inplace(*l, f(rhs[rhs_index])));
}
(StorageIndices::Uncontiguous(lhs_index), StorageIndices::Contiguous(rhs_index)) => {
let rhs = &rhs[rhs_index.begin_index..rhs_index.end_index];
lhs_index.zip(rhs.iter())
.for_each(|(lhs_index, &r)| lhs[lhs_index] = f_inplace(lhs[lhs_index], f(r)) );
}
(StorageIndices::Uncontiguous(lhs_index), StorageIndices::Uncontiguous(rhs_index)) => {
lhs_index.zip(rhs_index)
.for_each(|(lhs_index, rhs_index)| lhs[lhs_index] = f_inplace(lhs[lhs_index], f(rhs[rhs_index])) );
}
}
Ok(())
}
}
impl<T: WithDType> Tensor<T> {
pub fn check_implace_op(&self) -> crate::Result<()> {
Ok(())
}
}