vortex_array/compute/conformance/
take.rs1use vortex_buffer::buffer;
5use vortex_error::VortexExpect;
6
7use crate::ArrayRef;
8use crate::Canonical;
9use crate::ExecutionCtx;
10use crate::IntoArray as _;
11use crate::arrays::PrimitiveArray;
12use crate::dtype::Nullability;
13
14pub fn test_take_conformance(array: &ArrayRef, ctx: &mut ExecutionCtx) {
24 let len = array.len();
25
26 if len > 0 {
27 test_take_all(array, ctx);
28 test_take_none(array);
29 test_take_selective(array, ctx);
30 test_take_first_and_last(array, ctx);
31 test_take_with_nullable_indices(array, ctx);
32 test_take_repeated_indices(array, ctx);
33 }
34
35 test_empty_indices(array);
36
37 if len > 0 {
39 test_take_reverse(array, ctx);
40 test_take_single_middle(array, ctx);
41 }
42
43 if len > 3 {
44 test_take_random_unsorted(array, ctx);
45 test_take_contiguous_range(array, ctx);
46 test_take_mixed_repeated(array, ctx);
47 }
48
49 if len >= 1024 {
51 test_take_large_indices(array, ctx);
52 }
53}
54
55fn test_take_all(array: &ArrayRef, ctx: &mut ExecutionCtx) {
56 let len = array.len();
57 let indices = PrimitiveArray::from_iter(0..len as u64);
58 let result = array
59 .take(indices.into_array())
60 .vortex_expect("take should succeed in conformance test");
61
62 assert_eq!(result.len(), len);
63 assert_eq!(result.dtype(), array.dtype());
64
65 let array_canonical = array
67 .clone()
68 .execute::<Canonical>(ctx)
69 .vortex_expect("to_canonical failed on array");
70 let result_canonical = result
71 .clone()
72 .execute::<Canonical>(ctx)
73 .vortex_expect("to_canonical failed on result");
74 match (array_canonical, result_canonical) {
75 (Canonical::Primitive(orig_prim), Canonical::Primitive(result_prim)) => {
76 assert_eq!(
77 orig_prim.buffer_handle().to_host_sync(),
78 result_prim.buffer_handle().to_host_sync()
79 );
80 }
81 _ => {
82 for i in 0..len {
84 assert_eq!(
85 array
86 .execute_scalar(i, ctx)
87 .vortex_expect("scalar_at should succeed in conformance test"),
88 result
89 .execute_scalar(i, ctx)
90 .vortex_expect("scalar_at should succeed in conformance test")
91 );
92 }
93 }
94 }
95}
96
97fn test_take_none(array: &ArrayRef) {
98 let indices: PrimitiveArray = PrimitiveArray::from_iter::<[u64; 0]>([]);
99 let result = array
100 .take(indices.into_array())
101 .vortex_expect("take should succeed in conformance test");
102
103 assert_eq!(result.len(), 0);
104 assert_eq!(result.dtype(), array.dtype());
105}
106
107#[expect(clippy::cast_possible_truncation)]
108fn test_take_selective(array: &ArrayRef, ctx: &mut ExecutionCtx) {
109 let len = array.len();
110
111 let indices: Vec<u64> = (0..len as u64).step_by(2).collect();
113 let expected_len = indices.len();
114 let indices_array = PrimitiveArray::from_iter(indices.clone());
115
116 let result = array
117 .take(indices_array.into_array())
118 .vortex_expect("take should succeed in conformance test");
119 assert_eq!(result.len(), expected_len);
120
121 for (result_idx, &original_idx) in indices.iter().enumerate() {
123 assert_eq!(
124 array
125 .execute_scalar(original_idx as usize, ctx)
126 .vortex_expect("scalar_at should succeed in conformance test"),
127 result
128 .execute_scalar(result_idx, ctx)
129 .vortex_expect("scalar_at should succeed in conformance test")
130 );
131 }
132}
133
134fn test_take_first_and_last(array: &ArrayRef, ctx: &mut ExecutionCtx) {
135 let len = array.len();
136 let indices = PrimitiveArray::from_iter([0u64, (len - 1) as u64]);
137 let result = array
138 .take(indices.into_array())
139 .vortex_expect("take should succeed in conformance test");
140
141 assert_eq!(result.len(), 2);
142 assert_eq!(
143 array
144 .execute_scalar(0, ctx)
145 .vortex_expect("scalar_at should succeed in conformance test"),
146 result
147 .execute_scalar(0, ctx)
148 .vortex_expect("scalar_at should succeed in conformance test")
149 );
150 assert_eq!(
151 array
152 .execute_scalar(len - 1, ctx)
153 .vortex_expect("scalar_at should succeed in conformance test"),
154 result
155 .execute_scalar(1, ctx)
156 .vortex_expect("scalar_at should succeed in conformance test")
157 );
158}
159
160#[expect(clippy::cast_possible_truncation)]
161fn test_take_with_nullable_indices(array: &ArrayRef, ctx: &mut ExecutionCtx) {
162 let len = array.len();
163
164 let indices_vec: Vec<Option<u64>> = if len >= 3 {
166 vec![Some(0), None, Some((len - 1) as u64)]
167 } else if len >= 2 {
168 vec![Some(0), None]
169 } else {
170 vec![None]
171 };
172
173 let indices = PrimitiveArray::from_option_iter(indices_vec.clone());
174 let result = array
175 .take(indices.into_array())
176 .vortex_expect("take should succeed in conformance test");
177
178 assert_eq!(result.len(), indices_vec.len());
179 assert_eq!(
180 result.dtype(),
181 &array.dtype().with_nullability(Nullability::Nullable)
182 );
183
184 for (i, idx_opt) in indices_vec.iter().enumerate() {
186 match idx_opt {
187 Some(idx) => {
188 let expected = array
189 .execute_scalar(*idx as usize, ctx)
190 .vortex_expect("scalar_at should succeed in conformance test");
191 let actual = result
192 .execute_scalar(i, ctx)
193 .vortex_expect("scalar_at should succeed in conformance test");
194 assert_eq!(expected, actual);
195 }
196 None => {
197 assert!(
198 result
199 .execute_scalar(i, ctx)
200 .vortex_expect("scalar_at should succeed in conformance test")
201 .is_null()
202 );
203 }
204 }
205 }
206}
207
208fn test_take_repeated_indices(array: &ArrayRef, ctx: &mut ExecutionCtx) {
209 if array.is_empty() {
210 return;
211 }
212
213 let indices = buffer![0u64, 0, 0].into_array();
215 let result = array
216 .take(indices)
217 .vortex_expect("take should succeed in conformance test");
218
219 assert_eq!(result.len(), 3);
220 let first_elem = array
221 .execute_scalar(0, ctx)
222 .vortex_expect("scalar_at should succeed in conformance test");
223 for i in 0..3 {
224 assert_eq!(
225 result
226 .execute_scalar(i, ctx)
227 .vortex_expect("scalar_at should succeed in conformance test"),
228 first_elem
229 );
230 }
231}
232
233fn test_empty_indices(array: &ArrayRef) {
234 let indices = PrimitiveArray::empty::<u64>(Nullability::NonNullable);
235 let result = array
236 .take(indices.into_array())
237 .vortex_expect("take should succeed in conformance test");
238
239 assert_eq!(result.len(), 0);
240 assert_eq!(result.dtype(), array.dtype());
241}
242
243fn test_take_reverse(array: &ArrayRef, ctx: &mut ExecutionCtx) {
244 let len = array.len();
245 let indices = PrimitiveArray::from_iter((0..len as u64).rev());
247 let result = array
248 .take(indices.into_array())
249 .vortex_expect("take should succeed in conformance test");
250
251 assert_eq!(result.len(), len);
252
253 for i in 0..len {
255 assert_eq!(
256 array
257 .execute_scalar(len - 1 - i, ctx)
258 .vortex_expect("scalar_at should succeed in conformance test"),
259 result
260 .execute_scalar(i, ctx)
261 .vortex_expect("scalar_at should succeed in conformance test")
262 );
263 }
264}
265
266fn test_take_single_middle(array: &ArrayRef, ctx: &mut ExecutionCtx) {
267 let len = array.len();
268 let middle_idx = len / 2;
269
270 let indices = PrimitiveArray::from_iter([middle_idx as u64]);
271 let result = array
272 .take(indices.into_array())
273 .vortex_expect("take should succeed in conformance test");
274
275 assert_eq!(result.len(), 1);
276 assert_eq!(
277 array
278 .execute_scalar(middle_idx, ctx)
279 .vortex_expect("scalar_at should succeed in conformance test"),
280 result
281 .execute_scalar(0, ctx)
282 .vortex_expect("scalar_at should succeed in conformance test")
283 );
284}
285
286#[expect(clippy::cast_possible_truncation)]
287fn test_take_random_unsorted(array: &ArrayRef, ctx: &mut ExecutionCtx) {
288 let len = array.len();
289
290 let mut indices = Vec::with_capacity(len.min(10));
292 let mut idx = 1u64;
293 for _ in 0..len.min(10) {
294 indices.push((idx * 7 + 3) % len as u64);
295 idx = (idx * 3 + 1) % len as u64;
296 }
297
298 let indices_array = PrimitiveArray::from_iter(indices.clone());
299 let result = array
300 .take(indices_array.into_array())
301 .vortex_expect("take should succeed in conformance test");
302
303 assert_eq!(result.len(), indices.len());
304
305 for (i, &idx) in indices.iter().enumerate() {
307 assert_eq!(
308 array
309 .execute_scalar(idx as usize, ctx)
310 .vortex_expect("scalar_at should succeed in conformance test"),
311 result
312 .execute_scalar(i, ctx)
313 .vortex_expect("scalar_at should succeed in conformance test")
314 );
315 }
316}
317
318fn test_take_contiguous_range(array: &ArrayRef, ctx: &mut ExecutionCtx) {
319 let len = array.len();
320 let start = len / 4;
321 let end = len / 2;
322
323 let indices = PrimitiveArray::from_iter(start as u64..end as u64);
325 let result = array
326 .take(indices.into_array())
327 .vortex_expect("take should succeed in conformance test");
328
329 assert_eq!(result.len(), end - start);
330
331 for i in 0..(end - start) {
333 assert_eq!(
334 array
335 .execute_scalar(start + i, ctx)
336 .vortex_expect("scalar_at should succeed in conformance test"),
337 result
338 .execute_scalar(i, ctx)
339 .vortex_expect("scalar_at should succeed in conformance test")
340 );
341 }
342}
343
344#[expect(clippy::cast_possible_truncation)]
345fn test_take_mixed_repeated(array: &ArrayRef, ctx: &mut ExecutionCtx) {
346 let len = array.len();
347
348 let indices = vec![
350 0u64,
351 0,
352 1,
353 1,
354 len as u64 / 2,
355 len as u64 / 2,
356 len as u64 / 2,
357 (len - 1) as u64,
358 ];
359
360 let indices_array = PrimitiveArray::from_iter(indices.clone());
361 let result = array
362 .take(indices_array.into_array())
363 .vortex_expect("take should succeed in conformance test");
364
365 assert_eq!(result.len(), indices.len());
366
367 for (i, &idx) in indices.iter().enumerate() {
369 assert_eq!(
370 array
371 .execute_scalar(idx as usize, ctx)
372 .vortex_expect("scalar_at should succeed in conformance test"),
373 result
374 .execute_scalar(i, ctx)
375 .vortex_expect("scalar_at should succeed in conformance test")
376 );
377 }
378}
379
380#[expect(clippy::cast_possible_truncation)]
381fn test_take_large_indices(array: &ArrayRef, ctx: &mut ExecutionCtx) {
382 let len = array.len();
384 let num_indices = 10000.min(len * 3);
385
386 let indices: Vec<u64> = (0..num_indices)
388 .map(|i| ((i * 17 + 5) % len) as u64)
389 .collect();
390
391 let indices_array = PrimitiveArray::from_iter(indices.clone());
392 let result = array
393 .take(indices_array.into_array())
394 .vortex_expect("take should succeed in conformance test");
395
396 assert_eq!(result.len(), num_indices);
397
398 for i in (0..num_indices).step_by(1000) {
400 let expected_idx = indices[i] as usize;
401 assert_eq!(
402 array
403 .execute_scalar(expected_idx, ctx)
404 .vortex_expect("scalar_at should succeed in conformance test"),
405 result
406 .execute_scalar(i, ctx)
407 .vortex_expect("scalar_at should succeed in conformance test")
408 );
409 }
410}