1use crate::{NumDType, StorageIndices, WithDType};
2use crate::Tensor;
3use paste::paste;
4
5macro_rules! fused_2arg_inplace_op_impl {
6 ($name:ident, $f_inplace:expr, $f:expr) => {
7 paste! {
8 #[doc = crate::inplace_warning_doc!()]
9 pub fn [< $name _ >](&self, a: &Self, b: &Self) -> crate::Result<()> {
10 self.[< impl_ $name _ >](a, b)?;
11 self.impl_add_mul_(a, b)
12 }
13
14 #[doc = crate::inplace_warning_doc!()]
15 pub(crate) fn [< impl_ $name _ >](&self, a: &Self, b: &Self) -> crate::Result<()> {
16 self.fused_2arg_inplace_op(a, b, $f_inplace, $f, stringify!($name))
17 }
18 }
19 };
20}
21
22impl<T: NumDType> Tensor<T> {
23 fused_2arg_inplace_op_impl!(add_mul, T::add, T::mul);
24 fused_2arg_inplace_op_impl!(add_div, T::add, T::div);
25 fused_2arg_inplace_op_impl!(sub_mul, T::sub, T::mul);
26 fused_2arg_inplace_op_impl!(sub_div, T::sub, T::div);
27}
28
29impl<T: NumDType> Tensor<T> {
30 pub(crate) fn fused_2arg_inplace_op<F1, F2>(
31 &self,
32 src1: &Tensor<T>,
33 src2: &Tensor<T>,
34 f_inplace: F2,
35 f: F1,
36 op_name: &'static str
37 ) -> crate::Result<()>
38 where
39 F1: Fn(T, T) -> T,
40 F2: Fn(T, T) -> T,
41 {
42 let dst = self;
43 let _ = Tensor::<T>::same_shape_binary_op(dst, src1, op_name)?;
44 let _ = Tensor::<T>::same_shape_binary_op(dst, src2, op_name)?;
45
46 let mut dst_storage = dst.storage_write()?;
47 let src1_storage = src1.storage_read()?;
48 let src2_storage = src2.storage_read()?;
49
50 let dst_layout = dst.layout();
51 let src1_layout = src1.layout();
52 let src2_layout = src2.layout();
53
54 let dst_data = dst_storage.data_mut();
55 let src1_data = src1_storage.data();
56 let src2_data = src2_storage.data();
57
58 use StorageIndices::{Contiguous, Uncontiguous};
59
60 match (dst_layout.storage_indices(), src1_layout.storage_indices(), src2_layout.storage_indices()) {
61 (Contiguous(d_idx), Contiguous(s1_idx), Contiguous(s2_idx)) => {
63 let d_slice = &mut dst_data[d_idx.begin_index..d_idx.end_index];
64 let s1_slice = &src1_data[s1_idx.begin_index..s1_idx.end_index];
65 let s2_slice = &src2_data[s2_idx.begin_index..s2_idx.end_index];
66
67 d_slice.iter_mut()
68 .zip(s1_slice.iter())
69 .zip(s2_slice.iter())
70 .for_each(|((d, &s1), &s2)| {
71 *d = f_inplace(*d, f(s1, s2));
72 });
73 }
74
75 (Contiguous(d_idx), Contiguous(s1_idx), Uncontiguous(s2_idx)) => {
77 let d_slice = &mut dst_data[d_idx.begin_index..d_idx.end_index];
78 let s1_slice = &src1_data[s1_idx.begin_index..s1_idx.end_index];
79
80 d_slice.iter_mut()
81 .zip(s1_slice.iter())
82 .zip(s2_idx)
83 .for_each(|((d, &s1), s2_i)| {
84 *d = f_inplace(*d, f(s1, src2_data[s2_i]));
85 });
86 }
87
88 (Contiguous(d_idx), Uncontiguous(s1_idx), Contiguous(s2_idx)) => {
90 let d_slice = &mut dst_data[d_idx.begin_index..d_idx.end_index];
91 let s2_slice = &src2_data[s2_idx.begin_index..s2_idx.end_index];
92
93 d_slice.iter_mut()
94 .zip(s1_idx)
95 .zip(s2_slice.iter())
96 .for_each(|((d, s1_i), &s2)| {
97 *d = f_inplace(*d, f(src1_data[s1_i], s2));
98 });
99 }
100
101 (Contiguous(d_idx), Uncontiguous(s1_idx), Uncontiguous(s2_idx)) => {
103 let d_slice = &mut dst_data[d_idx.begin_index..d_idx.end_index];
104
105 d_slice.iter_mut()
106 .zip(s1_idx)
107 .zip(s2_idx)
108 .for_each(|((d, s1_i), s2_i)| {
109 *d = f_inplace(*d, f(src1_data[s1_i], src2_data[s2_i]));
110 });
111 }
112
113 (Uncontiguous(d_idx), Contiguous(s1_idx), Contiguous(s2_idx)) => {
115 let s1_slice = &src1_data[s1_idx.begin_index..s1_idx.end_index];
116 let s2_slice = &src2_data[s2_idx.begin_index..s2_idx.end_index];
117
118 d_idx.zip(s1_slice.iter())
119 .zip(s2_slice.iter())
120 .for_each(|((d_i, &s1), &s2)| {
121 dst_data[d_i] = f_inplace(dst_data[d_i], f(s1, s2));
122 });
123 }
124
125 (Uncontiguous(d_idx), Contiguous(s1_idx), Uncontiguous(s2_idx)) => {
127 let s1_slice = &src1_data[s1_idx.begin_index..s1_idx.end_index];
128
129 d_idx.zip(s1_slice.iter())
130 .zip(s2_idx)
131 .for_each(|((d_i, &s1), s2_i)| {
132 dst_data[d_i] = f_inplace(dst_data[d_i], f(s1, src2_data[s2_i]));
133 });
134 }
135
136 (Uncontiguous(d_idx), Uncontiguous(s1_idx), Contiguous(s2_idx)) => {
138 let s2_slice = &src2_data[s2_idx.begin_index..s2_idx.end_index];
139
140 d_idx.zip(s1_idx)
141 .zip(s2_slice.iter())
142 .for_each(|((d_i, s1_i), &s2)| {
143 dst_data[d_i] = f_inplace(dst_data[d_i], f(src1_data[s1_i], s2));
144 });
145 }
146
147 (Uncontiguous(d_idx), Uncontiguous(s1_idx), Uncontiguous(s2_idx)) => {
149 d_idx.zip(s1_idx)
150 .zip(s2_idx)
151 .for_each(|((d_i, s1_i), s2_i)| {
152 let v = f(src1_data[s1_i], src2_data[s2_i]);
153 dst_data[d_i] = f_inplace(dst_data[d_i], v);
154 });
155 }
156 }
157
158 Ok(())
159 }
160
161 pub(crate) fn fused_1arg_inplace_op<F1, F2>(
162 &self,
163 rhs: &Tensor<T>,
164 f_inplace: F1,
165 f: F2,
166 op_name: &'static str
167 ) -> crate::Result<()>
168 where
169 F1: Fn(T, T) -> T,
170 F2: Fn(T) -> T,
171 {
172 let lhs = self;
173 let _ = Tensor::<T>::same_shape_binary_op(lhs, rhs, op_name)?;
174
175 let mut lhs_storage = lhs.storage_write()?;
176 let rhs_storage = rhs.storage_read()?;
177 let lhs_layout = lhs.layout();
178 let rhs_layout = rhs.layout();
179
180 assert_eq!(lhs_layout.dims(), rhs_layout.dims(), "lhs dims != rhs dim2");
181
182 let lhs = lhs_storage.data_mut();
183 let rhs = rhs_storage.data();
184
185 match (lhs_layout.storage_indices(), rhs_layout.storage_indices()) {
186 (StorageIndices::Contiguous(lhs_index), StorageIndices::Contiguous(rhs_index)) => {
187 let lhs = &mut lhs[lhs_index.begin_index..lhs_index.end_index];
188 let rhs = &rhs[rhs_index.begin_index..rhs_index.end_index];
189 lhs.iter_mut().zip(rhs.iter())
190 .for_each(|(l, &r)| *l = f_inplace(*l, f(r)));
191 }
192 (StorageIndices::Contiguous(lhs_index), StorageIndices::Uncontiguous(rhs_index)) => {
193 let lhs = &mut lhs[lhs_index.begin_index..lhs_index.end_index];
194 lhs.iter_mut().zip(rhs_index)
195 .for_each(|(l, rhs_index)| *l = f_inplace(*l, f(rhs[rhs_index])));
196 }
197 (StorageIndices::Uncontiguous(lhs_index), StorageIndices::Contiguous(rhs_index)) => {
198 let rhs = &rhs[rhs_index.begin_index..rhs_index.end_index];
199 lhs_index.zip(rhs.iter())
200 .for_each(|(lhs_index, &r)| lhs[lhs_index] = f_inplace(lhs[lhs_index], f(r)) );
201 }
202 (StorageIndices::Uncontiguous(lhs_index), StorageIndices::Uncontiguous(rhs_index)) => {
203 lhs_index.zip(rhs_index)
204 .for_each(|(lhs_index, rhs_index)| lhs[lhs_index] = f_inplace(lhs[lhs_index], f(rhs[rhs_index])) );
205 }
206 }
207
208 Ok(())
209 }
210}
211
212impl<T: WithDType> Tensor<T> {
213 pub fn check_implace_op(&self) -> crate::Result<()> {
214 Ok(())
218 }
219}