vortex_array/compute/conformance/
mask.rs1use vortex_error::VortexExpect;
5use vortex_mask::Mask;
6
7use crate::ArrayRef;
8use crate::ExecutionCtx;
9use crate::IntoArray;
10use crate::RecursiveCanonical;
11use crate::arrays::BoolArray;
12use crate::arrays::bool::BoolArrayExt;
13use crate::builtins::ArrayBuiltins;
14use crate::validity::Validity;
15
16pub fn test_mask_conformance(array: &ArrayRef, ctx: &mut ExecutionCtx) {
19 let len = array.len();
20
21 if len > 0 {
22 test_heterogenous_mask(array, ctx);
23 test_empty_mask(array, ctx);
24 test_full_mask(array, ctx);
25 test_alternating_mask(array, ctx);
26 test_sparse_mask(array, ctx);
27 test_single_element_mask(array, ctx);
28 }
29
30 if len >= 5 {
31 test_double_mask(array, ctx);
32 }
33
34 if len > 0 {
35 test_nullable_mask_input(array, ctx);
36 }
37}
38
39fn test_heterogenous_mask(array: &ArrayRef, ctx: &mut ExecutionCtx) {
41 let len = array.len();
42
43 let mask_pattern: Vec<bool> = (0..len).map(|i| i % 3 != 1).collect();
45 let mask_array = Mask::from_iter(mask_pattern.clone());
46
47 let masked = array
48 .clone()
49 .mask((!&mask_array).into_array())
50 .vortex_expect("mask should succeed in conformance test")
51 .execute::<RecursiveCanonical>(ctx)
52 .vortex_expect("mask execution should succeed in conformance test")
53 .0
54 .into_array();
55 assert_eq!(masked.len(), array.len());
56
57 for (i, &masked_out) in mask_pattern.iter().enumerate() {
59 if masked_out {
60 assert!(
61 !masked
62 .is_valid(i, ctx)
63 .vortex_expect("is_valid should succeed in conformance test")
64 );
65 } else {
66 assert_eq!(
67 masked
68 .execute_scalar(i, ctx)
69 .vortex_expect("scalar_at should succeed in conformance test"),
70 array
71 .execute_scalar(i, ctx)
72 .vortex_expect("scalar_at should succeed in conformance test")
73 .into_nullable()
74 );
75 }
76 }
77}
78
79pub(super) fn test_empty_mask(array: &ArrayRef, ctx: &mut ExecutionCtx) {
81 let len = array.len();
82 let all_unmasked = vec![false; len];
83 let mask_array = Mask::from_iter(all_unmasked);
84
85 let masked = array
86 .clone()
87 .mask((!&mask_array).into_array())
88 .vortex_expect("mask should succeed in conformance test")
89 .execute::<RecursiveCanonical>(ctx)
90 .vortex_expect("mask execution should succeed in conformance test")
91 .0
92 .into_array();
93 assert_eq!(masked.len(), array.len());
94 assert!(masked.dtype().is_nullable());
95
96 for i in 0..len {
98 assert_eq!(
99 masked
100 .execute_scalar(i, ctx)
101 .vortex_expect("scalar_at should succeed in conformance test"),
102 array
103 .execute_scalar(i, ctx)
104 .vortex_expect("scalar_at should succeed in conformance test")
105 .into_nullable()
106 );
107 }
108}
109
110fn test_full_mask(array: &ArrayRef, ctx: &mut ExecutionCtx) {
112 let len = array.len();
113 let all_masked = vec![true; len];
114 let mask_array = Mask::from_iter(all_masked);
115
116 let masked = array
117 .clone()
118 .mask((!&mask_array).into_array())
119 .vortex_expect("mask should succeed in conformance test")
120 .execute::<RecursiveCanonical>(ctx)
121 .vortex_expect("mask execution should succeed in conformance test")
122 .0
123 .into_array();
124 assert_eq!(masked.len(), array.len());
125
126 for i in 0..len {
128 assert!(
129 !masked
130 .is_valid(i, ctx)
131 .vortex_expect("is_valid should succeed in conformance test")
132 );
133 }
134}
135
136fn test_alternating_mask(array: &ArrayRef, ctx: &mut ExecutionCtx) {
138 let len = array.len();
139 let pattern: Vec<bool> = (0..len).map(|i| i % 2 == 0).collect();
140 let mask_array = Mask::from_iter(pattern);
141
142 let masked = array
143 .clone()
144 .mask((!&mask_array).into_array())
145 .vortex_expect("mask should succeed in conformance test")
146 .execute::<RecursiveCanonical>(ctx)
147 .vortex_expect("mask execution should succeed in conformance test")
148 .0
149 .into_array();
150 assert_eq!(masked.len(), array.len());
151
152 for i in 0..len {
153 if i % 2 == 0 {
154 assert!(
155 !masked
156 .is_valid(i, ctx)
157 .vortex_expect("is_valid should succeed in conformance test")
158 );
159 } else {
160 assert_eq!(
161 masked
162 .execute_scalar(i, ctx)
163 .vortex_expect("scalar_at should succeed in conformance test"),
164 array
165 .execute_scalar(i, ctx)
166 .vortex_expect("scalar_at should succeed in conformance test")
167 .into_nullable()
168 );
169 }
170 }
171}
172
173fn test_sparse_mask(array: &ArrayRef, ctx: &mut ExecutionCtx) {
175 let len = array.len();
176 if len < 10 {
177 return; }
179
180 let pattern: Vec<bool> = (0..len).map(|i| i % 10 == 0).collect();
182 let mask_array = Mask::from_iter(pattern.clone());
183
184 let masked = array
185 .clone()
186 .mask((!&mask_array).into_array())
187 .vortex_expect("mask should succeed in conformance test")
188 .execute::<RecursiveCanonical>(ctx)
189 .vortex_expect("mask execution should succeed in conformance test")
190 .0
191 .into_array();
192 assert_eq!(masked.len(), array.len());
193
194 for (i, &masked_out) in pattern.iter().enumerate() {
195 let actual = masked
196 .execute_scalar(i, ctx)
197 .vortex_expect("scalar_at should succeed in conformance test");
198 if masked_out {
199 assert!(actual.is_null(), "masked index {i} should be null");
200 } else {
201 assert_eq!(
202 actual,
203 array
204 .execute_scalar(i, ctx)
205 .vortex_expect("scalar_at should succeed in conformance test")
206 .into_nullable(),
207 "unmasked index {i} should preserve its value"
208 );
209 }
210 }
211}
212
213fn test_single_element_mask(array: &ArrayRef, ctx: &mut ExecutionCtx) {
215 let len = array.len();
216
217 let mut pattern = vec![false; len];
219 pattern[0] = true;
220 let mask_array = Mask::from_iter(pattern);
221
222 let masked = array
223 .clone()
224 .mask((!&mask_array).into_array())
225 .vortex_expect("mask should succeed in conformance test")
226 .execute::<RecursiveCanonical>(ctx)
227 .vortex_expect("mask execution should succeed in conformance test")
228 .0
229 .into_array();
230 assert!(
231 !masked
232 .is_valid(0, ctx)
233 .vortex_expect("is_valid should succeed in conformance test")
234 );
235
236 for i in 1..len {
237 assert_eq!(
238 masked
239 .execute_scalar(i, ctx)
240 .vortex_expect("scalar_at should succeed in conformance test"),
241 array
242 .execute_scalar(i, ctx)
243 .vortex_expect("scalar_at should succeed in conformance test")
244 .into_nullable()
245 );
246 }
247}
248
249fn test_double_mask(array: &ArrayRef, ctx: &mut ExecutionCtx) {
251 let len = array.len();
252
253 let mask1_pattern: Vec<bool> = (0..len).map(|i| i % 3 == 0).collect();
255 let mask2_pattern: Vec<bool> = (0..len).map(|i| i % 2 == 0).collect();
256
257 let mask1 = Mask::from_iter(mask1_pattern.clone());
258 let mask2 = Mask::from_iter(mask2_pattern.clone());
259
260 let first_masked = array
261 .clone()
262 .mask((!&mask1).into_array())
263 .vortex_expect("mask should succeed in conformance test");
264 let double_masked = first_masked
265 .mask((!&mask2).into_array())
266 .vortex_expect("mask should succeed in conformance test")
267 .execute::<RecursiveCanonical>(ctx)
268 .vortex_expect("mask execution should succeed in conformance test")
269 .0
270 .into_array();
271
272 for i in 0..len {
274 if mask1_pattern[i] || mask2_pattern[i] {
275 assert!(
276 !double_masked
277 .is_valid(i, ctx)
278 .vortex_expect("is_valid should succeed in conformance test")
279 );
280 } else {
281 assert_eq!(
282 double_masked
283 .execute_scalar(i, ctx)
284 .vortex_expect("scalar_at should succeed in conformance test"),
285 array
286 .execute_scalar(i, ctx)
287 .vortex_expect("scalar_at should succeed in conformance test")
288 .into_nullable()
289 );
290 }
291 }
292}
293
294fn test_nullable_mask_input(array: &ArrayRef, ctx: &mut ExecutionCtx) {
296 let len = array.len();
297 if len < 3 {
298 return; }
300
301 let bool_values: Vec<bool> = (0..len).map(|i| i % 2 == 0).collect();
303 let validity_values: Vec<bool> = (0..len).map(|i| i % 3 != 0).collect();
304
305 let bool_array = BoolArray::from_iter(bool_values.clone());
306 let validity = Validity::from_iter(validity_values.clone());
307 let nullable_mask = BoolArray::new(bool_array.to_bit_buffer(), validity);
308
309 let mask_array = nullable_mask.to_mask_fill_null_false(ctx);
310 let masked = array
311 .clone()
312 .mask((!&mask_array).into_array())
313 .vortex_expect("mask should succeed in conformance test")
314 .execute::<RecursiveCanonical>(ctx)
315 .vortex_expect("mask execution should succeed in conformance test")
316 .0
317 .into_array();
318
319 for i in 0..len {
321 if bool_values[i] && validity_values[i] {
322 assert!(
323 !masked
324 .is_valid(i, ctx)
325 .vortex_expect("is_valid should succeed in conformance test")
326 );
327 } else {
328 assert_eq!(
329 masked
330 .execute_scalar(i, ctx)
331 .vortex_expect("scalar_at should succeed in conformance test"),
332 array
333 .execute_scalar(i, ctx)
334 .vortex_expect("scalar_at should succeed in conformance test")
335 .into_nullable()
336 );
337 }
338 }
339}