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