1use core::mem::MaybeUninit;
9use core::ops::{Add, Mul};
10
11use crate::{Exec, Result, StridedError, StridedView, StridedViewMut};
12
13fn same_len(actual: usize, expected: usize) -> Result<()> {
14 if actual == expected {
15 Ok(())
16 } else {
17 Err(StridedError::ShapeMismatch(vec![actual], vec![expected]))
18 }
19}
20
21fn product(shape: &[usize]) -> Result<usize> {
22 if shape.contains(&0) {
23 return Ok(0);
24 }
25 shape.iter().try_fold(1usize, |n, &d| {
26 n.checked_mul(d).ok_or(StridedError::OffsetOverflow)
27 })
28}
29
30fn for_each_chunk<T: Send + Sync, F: Fn(usize, &mut [T]) + Sync>(
31 output: &mut [T],
32 operation: F,
33 exec: Exec<'_>,
34) {
35 let exec = crate::threading::for_len(exec, output.len());
36 if !exec.is_serial() {
37 let ptr = crate::threading::SendPtr(output.as_mut_ptr());
38 crate::threading::parallel_for_each(0..output.len(), exec, &|range| {
39 let chunk = unsafe {
42 core::slice::from_raw_parts_mut(ptr.as_ptr().add(range.start), range.len())
43 };
44 operation(range.start, chunk);
45 });
46 return;
47 }
48 operation(0, output);
49}
50
51fn batch_source_offset(mut batch: usize, shape: &[usize], strides: &[isize]) -> isize {
53 let mut offset = 0isize;
54 for (&dim, &stride) in shape.iter().zip(strides) {
55 let coord = batch % dim;
56 batch /= dim;
57 offset += coord as isize * stride;
58 }
59 offset
60}
61
62fn dense_view<'a, T>(data: &'a [T], shape: &[usize]) -> Result<StridedView<'a, T>> {
64 let len = product(shape)?;
65 same_len(data.len(), len)?;
66 let strides = crate::col_major_strides(shape);
67 StridedView::new(data, shape, &strides, 0)
68}
69
70fn copy_strided_run<T: Copy>(dst: &mut [MaybeUninit<T>], data: &[T], base: isize, stride: isize) {
73 if stride == 1 {
74 let start = base as usize;
77 let src = &data[start..start + dst.len()];
78 for (slot, &value) in dst.iter_mut().zip(src) {
79 slot.write(value);
80 }
81 } else {
82 let mut offset = base;
83 for slot in dst.iter_mut() {
84 slot.write(data[offset as usize]);
85 offset += stride;
86 }
87 }
88}
89
90pub fn axpby_accum<T>(y: &mut [T], x: &[T], alpha: T, beta: T, exec: Exec<'_>) -> Result<()>
106where
107 T: Copy + Send + Sync + Add<Output = T> + Mul<Output = T>,
108{
109 same_len(x.len(), y.len())?;
110 for_each_chunk(
111 y,
112 |start, dst| {
113 let len = dst.len();
114 for (out, &src) in dst.iter_mut().zip(&x[start..start + len]) {
115 *out = alpha * src + beta * *out;
116 }
117 },
118 exec,
119 );
120 Ok(())
121}
122
123pub fn triangular_mask_into_uninit<T: Copy + Send + Sync>(
142 output: &mut [MaybeUninit<T>],
143 input: &[T],
144 shape: &[usize],
145 k: i64,
146 upper: bool,
147 fill: T,
148 exec: Exec<'_>,
149) -> Result<()> {
150 let src = dense_view(input, shape)?;
151 triangular_mask_view_into_uninit(output, &src, k, upper, fill, exec)
152}
153
154pub fn triangular_mask_view_into_uninit<T: Copy + Send + Sync>(
167 output: &mut [MaybeUninit<T>],
168 src: &StridedView<'_, T>,
169 k: i64,
170 upper: bool,
171 fill: T,
172 exec: Exec<'_>,
173) -> Result<()> {
174 let shape = src.dims();
175 if shape.len() < 2 {
176 return Err(StridedError::RankMismatch(shape.len(), 2));
177 }
178 let len = product(shape)?;
179 same_len(output.len(), len)?;
180 if len == 0 {
181 return Ok(());
182 }
183 let rows = shape[0];
184 let cols = shape[1];
185 let data = src.data();
186 let strides = src.strides();
187 let offset = src.offset();
188 for_each_chunk(
189 output,
190 |start, chunk| {
191 let mut pos = 0;
192 let mut batch = usize::MAX;
193 let mut batch_offset = 0isize;
194 while pos < chunk.len() {
195 let flat = start + pos;
196 let row = flat % rows;
197 let col = (flat / rows) % cols;
198 let run_batch = flat / (rows * cols);
200 if run_batch != batch {
201 batch = run_batch;
202 batch_offset = batch_source_offset(run_batch, &shape[2..], &strides[2..]);
203 }
204 let count = (rows - row).min(chunk.len() - pos);
205 let boundary = col as i128 - k as i128;
206 let split = if upper {
207 boundary.saturating_add(1)
208 } else {
209 boundary
210 }
211 .clamp(row as i128, (row + count) as i128) as usize
212 - row;
213 let (low, high) = chunk[pos..pos + count].split_at_mut(split);
214 let (kept, first_row, masked) = if upper {
215 (low, row, high)
216 } else {
217 (high, row + split, low)
218 };
219 let base = offset
220 + batch_offset
221 + (col as isize) * strides[1]
222 + (first_row as isize) * strides[0];
223 copy_strided_run(kept, data, base, strides[0]);
226 masked.fill(MaybeUninit::new(fill));
227 pos += count;
228 }
229 },
230 exec,
231 );
232 Ok(())
233}
234
235pub fn embed_diagonal_into_uninit<T: Copy + Send + Sync + 'static>(
255 output: &mut [MaybeUninit<T>],
256 input: &[T],
257 shape: &[usize],
258 axis: usize,
259 insert_axis: usize,
260 zero: T,
261 exec: Exec<'_>,
262) -> Result<()> {
263 if axis >= shape.len() {
264 return Err(StridedError::InvalidAxis {
265 axis,
266 rank: shape.len(),
267 });
268 }
269 if insert_axis > shape.len() {
270 return Err(StridedError::InvalidAxis {
271 axis: insert_axis,
272 rank: shape.len() + 1,
273 });
274 }
275 let len = product(shape)?;
276 same_len(input.len(), len)?;
277 let src = dense_view(input, shape)?;
278 embed_diagonal_view_into_uninit(output, &src, axis, insert_axis, zero, exec)
279}
280
281pub fn embed_diagonal_view_into_uninit<T: Copy + Send + Sync + 'static>(
294 output: &mut [MaybeUninit<T>],
295 src: &StridedView<'_, T>,
296 axis: usize,
297 insert_axis: usize,
298 zero: T,
299 exec: Exec<'_>,
300) -> Result<()> {
301 let mut exec = exec;
302 let shape = src.dims();
303 if axis >= shape.len() {
304 return Err(StridedError::InvalidAxis {
305 axis,
306 rank: shape.len(),
307 });
308 }
309 if insert_axis > shape.len() {
310 return Err(StridedError::InvalidAxis {
311 axis: insert_axis,
312 rank: shape.len() + 1,
313 });
314 }
315 let len = product(shape)?;
316 let out_len = len
317 .checked_mul(shape[axis])
318 .ok_or(StridedError::OffsetOverflow)?;
319 same_len(output.len(), out_len)?;
320 if out_len == 0 {
321 return Ok(());
322 }
323 isize::try_from(out_len).map_err(|_| StridedError::OffsetOverflow)?;
324 let mut out_shape: crate::InlineVec<usize> = shape.iter().copied().collect();
325 out_shape.insert(insert_axis, shape[axis]);
326 let mut dst_strides = crate::col_major_strides(&out_shape);
327 let inserted_stride = dst_strides.remove(insert_axis);
328 dst_strides[axis] = dst_strides[axis]
329 .checked_add(inserted_stride)
330 .ok_or(StridedError::OffsetOverflow)?;
331 let mut dst = StridedViewMut::new(output, shape, &dst_strides, 0)?;
332 crate::map_view::validate_destination_layout_without_alloc(shape, &dst_strides)?;
333 for_each_chunk(
336 dst.data_mut(),
337 |_, chunk| chunk.fill(MaybeUninit::new(zero)),
338 exec.reborrow(),
339 );
340 crate::map_into(&mut dst, src, MaybeUninit::new, exec)
341}