1use crate::ops::{broadcast_to, expand_dims, reshape, slice};
8use crate::primitive::{Primitive, ScatterReduce};
9use crate::view::normalize_axis;
10use crate::{Array, Error, Result, SliceArg, promote_types};
11
12fn single(primitive: Primitive, inputs: Vec<Array>) -> Result<Array> {
13 Ok(Array::from_op(primitive, inputs)?.swap_remove(0))
14}
15
16fn isize_shape(shape: &[usize]) -> Vec<isize> {
17 shape.iter().map(|&d| d as isize).collect()
18}
19
20fn broadcast_except(
23 a: &Array,
24 indices: &Array,
25 axis: usize,
26 a_too: bool,
27) -> Result<(Array, Array)> {
28 if indices.ndim() != a.ndim() {
29 return Err(Error::InvalidArgument(format!(
30 "indices of {} dimensions for an array of {}",
31 indices.ndim(),
32 a.ndim()
33 )));
34 }
35 let mut common = Vec::with_capacity(a.ndim());
36 for (d, (&x, &i)) in a.shape().iter().zip(indices.shape()).enumerate() {
37 common.push(match (d == axis, x, i) {
38 (true, _, _) => 0,
39 (_, x, i) if x == i || i == 1 => x,
40 (_, 1, i) if a_too => i,
41 _ => {
42 return Err(Error::ShapeMismatch {
43 op: "take_along_axis",
44 lhs: a.shape().to_vec(),
45 rhs: indices.shape().to_vec(),
46 });
47 }
48 });
49 }
50 let with = |len: usize| {
51 let mut s = common.clone();
52 s[axis] = len;
53 s
54 };
55 let a = if a_too {
56 broadcast_to(a, &with(a.shape()[axis]))?
57 } else {
58 a.clone()
59 };
60 let indices = broadcast_to(indices, &with(indices.shape()[axis]))?;
61 Ok((a, indices))
62}
63
64pub fn take_along_axis(a: &Array, indices: &Array, axis: isize) -> Result<Array> {
78 let axis = normalize_axis(axis, a.ndim())?;
79 let (a, indices) = broadcast_except(a, indices, axis, true)?;
80 single(Primitive::GatherAxis { axis }, vec![a, indices])
81}
82
83pub fn take(a: &Array, indices: &Array, axis: Option<isize>) -> Result<Array> {
100 let (a, axis) = match axis {
101 Some(axis) => (a.clone(), normalize_axis(axis, a.ndim())?),
102 None => (reshape(a, &[-1])?, 0),
103 };
104 let m = indices.size();
105 let mut along = vec![1; a.ndim()];
107 along[axis] = m as isize;
108 let mut full = a.shape().to_vec();
109 full[axis] = m;
110 let flat = broadcast_to(&reshape(indices, &along)?, &full)?;
111 let out = single(Primitive::GatherAxis { axis }, vec![a.clone(), flat])?;
112 let mut shape = isize_shape(&a.shape()[..axis]);
113 shape.extend(isize_shape(indices.shape()));
114 shape.extend(isize_shape(&a.shape()[axis + 1..]));
115 reshape(&out, &shape)
116}
117
118fn scatter(
119 a: &Array,
120 indices: &Array,
121 values: &Array,
122 axis: isize,
123 reduce: ScatterReduce,
124) -> Result<Array> {
125 let axis = normalize_axis(axis, a.ndim())?;
126 let (_, indices) = broadcast_except(a, indices, axis, false)?;
127 let values = broadcast_to(&values.astype(a.dtype())?, indices.shape())?;
128 single(
129 Primitive::ScatterAxis { axis, reduce },
130 vec![a.clone(), indices, values],
131 )
132}
133
134pub fn put_along_axis(a: &Array, indices: &Array, values: &Array, axis: isize) -> Result<Array> {
150 scatter(a, indices, values, axis, ScatterReduce::None)
151}
152
153pub fn scatter_add_along_axis(
156 a: &Array,
157 indices: &Array,
158 values: &Array,
159 axis: isize,
160) -> Result<Array> {
161 scatter(a, indices, values, axis, ScatterReduce::Sum)
162}
163
164pub fn concatenate(arrays: &[Array], axis: isize) -> Result<Array> {
180 let first = arrays
181 .first()
182 .ok_or_else(|| Error::InvalidArgument("concatenate: no arrays to join".into()))?;
183 let axis = normalize_axis(axis, first.ndim())?;
184 let dtype = arrays
185 .iter()
186 .skip(1)
187 .fold(first.dtype(), |d, a| promote_types(d, a.dtype()));
188 let arrays = arrays
189 .iter()
190 .map(|a| a.astype(dtype))
191 .collect::<Result<Vec<_>>>()?;
192 single(Primitive::Concatenate { axis }, arrays)
193}
194
195pub fn stack(arrays: &[Array], axis: isize) -> Result<Array> {
197 let first = arrays
198 .first()
199 .ok_or_else(|| Error::InvalidArgument("stack: no arrays to stack".into()))?;
200 let axis = normalize_axis(axis, first.ndim() + 1)? as isize;
201 if let Some(other) = arrays.iter().find(|a| a.shape() != first.shape()) {
202 return Err(Error::ShapeMismatch {
203 op: "stack",
204 lhs: first.shape().to_vec(),
205 rhs: other.shape().to_vec(),
206 });
207 }
208 let expanded = arrays
209 .iter()
210 .map(|a| expand_dims(a, axis))
211 .collect::<Result<Vec<_>>>()?;
212 concatenate(&expanded, axis)
213}
214
215pub fn split(a: &Array, parts: usize, axis: isize) -> Result<Vec<Array>> {
217 let d = normalize_axis(axis, a.ndim())?;
218 let n = a.shape()[d];
219 if parts == 0 || !n.is_multiple_of(parts) {
220 return Err(Error::InvalidArgument(format!(
221 "split: an axis of length {n} cannot be split into {parts} equal parts"
222 )));
223 }
224 let size = n / parts;
225 let at: Vec<usize> = (1..parts).map(|k| k * size).collect();
226 split_at(a, &at, axis)
227}
228
229pub fn split_at(a: &Array, indices: &[usize], axis: isize) -> Result<Vec<Array>> {
232 let d = normalize_axis(axis, a.ndim())?;
233 let n = a.shape()[d];
234 let mut bounds = vec![0];
235 bounds.extend(indices.iter().map(|&i| i.min(n)));
236 bounds.push(n);
237 bounds
238 .windows(2)
239 .map(|w| {
240 let mut args = vec![SliceArg::full(); a.ndim()];
241 args[d] = SliceArg::new(Some(w[0] as isize), Some(w[1].max(w[0]) as isize), 1);
242 slice(a, &args)
243 })
244 .collect()
245}