|
| 1 | +// SPDX-License-Identifier: Apache-2.0 |
| 2 | +// SPDX-FileCopyrightText: Copyright the Vortex contributors |
| 3 | + |
| 4 | +//! Fallback execution for row kernels that cannot operate directly on partially valid inputs. |
| 5 | +//! |
| 6 | +//! This path filters every input, runs the dense kernel, and expands its output back to the |
| 7 | +//! original batch shape. |
| 8 | +
|
| 9 | +use smallvec::SmallVec; |
| 10 | +use vortex_error::VortexResult; |
| 11 | +use vortex_error::vortex_bail; |
| 12 | +use vortex_error::vortex_ensure_eq; |
| 13 | +use vortex_mask::AllOr; |
| 14 | +use vortex_mask::Mask; |
| 15 | + |
| 16 | +use super::super::RowFnExecutionArgs; |
| 17 | +use super::super::args::BorrowedRowFnArgs; |
| 18 | +use crate::ArrayRef; |
| 19 | +use crate::ExecutionCtx; |
| 20 | +use crate::IntoArray; |
| 21 | +use crate::arrays::BoolArray; |
| 22 | +use crate::arrays::MaskedArray; |
| 23 | +use crate::arrays::PrimitiveArray; |
| 24 | +use crate::builtins::ArrayBuiltins; |
| 25 | +use crate::dtype::Nullability; |
| 26 | +use crate::validity::Validity; |
| 27 | + |
| 28 | +impl RowFnExecutionArgs { |
| 29 | + /// Filter to valid rows, run the kernel, then scatter into a null-padded output. |
| 30 | + pub(super) fn filter_and_scatter( |
| 31 | + &self, |
| 32 | + kernel: impl Fn(BorrowedRowFnArgs<'_>, &mut ExecutionCtx) -> VortexResult<ArrayRef>, |
| 33 | + valid: &Mask, |
| 34 | + ctx: &mut ExecutionCtx, |
| 35 | + ) -> VortexResult<ArrayRef> { |
| 36 | + let filtered: SmallVec<[ArrayRef; 4]> = self |
| 37 | + .inputs |
| 38 | + .iter() |
| 39 | + .map(|input| input.filter(valid.clone())) |
| 40 | + .collect::<VortexResult<_>>()?; |
| 41 | + |
| 42 | + let values = kernel(self.execution_args(&filtered, valid.true_count()), ctx)?; |
| 43 | + let values = self.validate_kernel_output(values, valid.true_count(), ctx)?; |
| 44 | + |
| 45 | + self.finalize_output(self.scatter_valid(values, valid)?, valid.len()) |
| 46 | + } |
| 47 | + |
| 48 | + /// Scatter `values` (one per set bit of `valid`, in order) back to the positions of the set |
| 49 | + /// bits, producing an array of length `valid.len()` that is null at every unset position. |
| 50 | + fn scatter_valid(&self, values: ArrayRef, valid: &Mask) -> VortexResult<ArrayRef> { |
| 51 | + vortex_ensure_eq!( |
| 52 | + values.len(), |
| 53 | + valid.true_count(), |
| 54 | + "the {} kernel output must contain {} filtered rows, got {}", |
| 55 | + self.id, |
| 56 | + valid.true_count(), |
| 57 | + values.len(), |
| 58 | + ); |
| 59 | + |
| 60 | + let AllOr::Some(slices) = valid.slices() else { |
| 61 | + // The caller handles the all-true and all-false masks. |
| 62 | + vortex_bail!( |
| 63 | + "scatter_valid requires valid and invalid rows, got an all-valid or all-invalid mask" |
| 64 | + ); |
| 65 | + }; |
| 66 | + |
| 67 | + // `take` emits one row per source index. Supplying one index per original batch row expands |
| 68 | + // the compact kernel output back to the batch shape. Null rows temporarily read index zero; |
| 69 | + // any in-bounds index works because the result is masked below. |
| 70 | + let mut source_indices = vec![0u64; valid.len()]; |
| 71 | + let mut rank = 0u64; |
| 72 | + for &(start, end) in slices { |
| 73 | + for index in &mut source_indices[start..end] { |
| 74 | + *index = rank; |
| 75 | + rank += 1; |
| 76 | + } |
| 77 | + } |
| 78 | + let source_indices = |
| 79 | + PrimitiveArray::new(source_indices, Validity::NonNullable).into_array(); |
| 80 | + |
| 81 | + let expanded = values.take(source_indices)?; |
| 82 | + |
| 83 | + // A nullable gathered array cannot be wrapped because a `Masked` child must be all valid. |
| 84 | + // The general masking pass unions its nulls with the batch validity instead. |
| 85 | + if expanded.dtype().is_nullable() { |
| 86 | + let mask = BoolArray::new(valid.to_bit_buffer(), Validity::NonNullable).into_array(); |
| 87 | + return expanded.mask(mask); |
| 88 | + } |
| 89 | + |
| 90 | + // The gathered values are all valid, so attaching validity is sufficient. |
| 91 | + Ok(MaskedArray::try_new( |
| 92 | + expanded, |
| 93 | + Validity::from_mask(valid.clone(), Nullability::Nullable), |
| 94 | + )? |
| 95 | + .into_array()) |
| 96 | + } |
| 97 | +} |
0 commit comments