vortex_array/scalar_fn/fns/mask/
mod.rs1mod kernel;
5
6pub use kernel::*;
7use vortex_error::VortexExpect;
8use vortex_error::VortexResult;
9use vortex_error::vortex_ensure;
10use vortex_session::VortexSession;
11use vortex_session::registry::CachedId;
12
13use crate::ArrayRef;
14use crate::Canonical;
15use crate::ExecutionCtx;
16use crate::IntoArray;
17use crate::arrays::Constant;
18use crate::arrays::ConstantArray;
19use crate::arrays::ScalarFnArray;
20use crate::arrays::masked::mask_validity_canonical;
21use crate::builtins::ArrayBuiltins;
22use crate::child_to_validity;
23use crate::dtype::DType;
24use crate::dtype::Nullability;
25use crate::expr::BoundExpression;
26use crate::expr::bound;
27use crate::scalar::Scalar;
28use crate::scalar_fn::Arity;
29use crate::scalar_fn::ChildName;
30use crate::scalar_fn::EmptyOptions;
31use crate::scalar_fn::ExecutionArgs;
32use crate::scalar_fn::ReduceNode;
33use crate::scalar_fn::ReduceNodeValidity;
34use crate::scalar_fn::ScalarFnId;
35use crate::scalar_fn::ScalarFnVTable;
36use crate::scalar_fn::ScalarFnVTableExt;
37use crate::scalar_fn::fns::binary::Binary;
38use crate::scalar_fn::fns::literal::Literal;
39use crate::scalar_fn::fns::operators::Operator;
40use crate::scalar_fn::is_not_null_node;
41
42#[derive(Clone)]
47pub struct Mask;
48
49impl Mask {
50 pub fn try_new(input: ArrayRef, mask: ArrayRef) -> VortexResult<ScalarFnArray> {
57 ScalarFnArray::try_new(Mask.bind(EmptyOptions), vec![input, mask])
58 }
59}
60
61impl ScalarFnVTable for Mask {
62 type Options = EmptyOptions;
63
64 fn id(&self) -> ScalarFnId {
65 static ID: CachedId = CachedId::new("vortex.mask");
66 *ID
67 }
68
69 fn serialize(&self, _options: &Self::Options) -> VortexResult<Option<Vec<u8>>> {
70 Ok(Some(vec![]))
71 }
72
73 fn deserialize(
74 &self,
75 _metadata: &[u8],
76 _session: &VortexSession,
77 ) -> VortexResult<Self::Options> {
78 Ok(EmptyOptions)
79 }
80
81 fn arity(&self, _options: &Self::Options) -> Arity {
82 Arity::Exact(2)
83 }
84
85 fn child_name(&self, _options: &Self::Options, child_idx: usize) -> ChildName {
86 match child_idx {
87 0 => ChildName::from("input"),
88 1 => ChildName::from("mask"),
89 _ => unreachable!("Invalid child index {} for Mask expression", child_idx),
90 }
91 }
92
93 fn return_dtype(&self, _options: &Self::Options, arg_dtypes: &[DType]) -> VortexResult<DType> {
94 vortex_ensure!(
95 arg_dtypes[1] == DType::Bool(Nullability::NonNullable),
96 "The mask argument to 'mask' must be a non-nullable boolean array, got {}",
97 arg_dtypes[1]
98 );
99 Ok(arg_dtypes[0].as_nullable())
100 }
101
102 fn execute(
103 &self,
104 _options: &Self::Options,
105 args: &dyn ExecutionArgs,
106 ctx: &mut ExecutionCtx,
107 ) -> VortexResult<ArrayRef> {
108 let input = args.get(0)?;
109 let mask_array = args.get(1)?;
110
111 if let Some(result) = execute_constant(&input, &mask_array)? {
112 return Ok(result);
113 }
114
115 execute_canonical(input, mask_array, ctx)
116 }
117
118 fn simplify(
119 &self,
120 _options: &Self::Options,
121 expr: &BoundExpression,
122 ) -> VortexResult<Option<BoundExpression>> {
123 let Some(mask_lit) = expr.child(1).as_opt::<Literal>() else {
124 return Ok(None);
125 };
126
127 let mask_lit = mask_lit
128 .as_bool()
129 .value()
130 .vortex_expect("Mask must be non-nullable");
131
132 if mask_lit {
133 Ok(Some(expr.child(0).clone()))
135 } else {
136 let input_dtype = expr.child(0).dtype();
138 Ok(Some(bound::lit(Scalar::null(input_dtype.as_nullable()))))
139 }
140 }
141
142 fn validity<T: ReduceNode>(
143 &self,
144 _options: &Self::Options,
145 node: &T,
146 ) -> VortexResult<ReduceNodeValidity<T>> {
147 let input_validity = is_not_null_node(&node.child(0))?;
148 Ok(ReduceNodeValidity::Reduced(input_validity.new_node(
149 Binary.bind(Operator::And),
150 &[input_validity.clone(), node.child(1)],
151 )?))
152 }
153
154 fn is_strict(&self, _options: &Self::Options) -> bool {
155 true
156 }
157}
158
159fn execute_constant(input: &ArrayRef, mask_array: &ArrayRef) -> VortexResult<Option<ArrayRef>> {
163 let len = input.len();
164
165 if let Some(constant_mask) = mask_array.as_opt::<Constant>() {
166 let mask_value = constant_mask.scalar().as_bool().value().unwrap_or(false);
167 return if mask_value {
168 input.cast(input.dtype().as_nullable()).map(Some)
169 } else {
170 Ok(Some(
171 ConstantArray::new(Scalar::null(input.dtype().as_nullable()), len).into_array(),
172 ))
173 };
174 }
175
176 if let Some(constant_input) = input.as_opt::<Constant>()
177 && constant_input.scalar().is_null()
178 {
179 return Ok(Some(
180 ConstantArray::new(Scalar::null(input.dtype().as_nullable()), len).into_array(),
181 ));
182 }
183
184 Ok(None)
185}
186
187fn execute_canonical(
189 input: ArrayRef,
190 mask_array: ArrayRef,
191 ctx: &mut ExecutionCtx,
192) -> VortexResult<ArrayRef> {
193 let validity = child_to_validity(Some(&mask_array), Nullability::Nullable);
194 let canonical = input.execute::<Canonical>(ctx)?;
195 Ok(mask_validity_canonical(canonical, validity, ctx)?.into_array())
196}
197
198#[cfg(test)]
199mod test {
200 use vortex_error::VortexExpect;
201
202 use crate::dtype::DType;
203 use crate::dtype::Nullability::Nullable;
204 use crate::dtype::PType;
205 use crate::expr::bound;
206 use crate::expr::lit;
207 use crate::expr::mask;
208 use crate::scalar::Scalar;
209
210 #[test]
211 fn test_simplify() {
212 let input_expr = lit(42u32);
213 let true_mask_expr = lit(true);
214 let false_mask_expr = lit(false);
215
216 let mask_true_expr = mask(input_expr.clone(), true_mask_expr);
217 let simplified_true = mask_true_expr
218 .bind(&DType::Null)
219 .and_then(|expr| expr.optimize())
220 .vortex_expect("Simplification");
221 assert_eq!(
222 &simplified_true,
223 &input_expr.bind(&DType::Null).vortex_expect("bind")
224 );
225
226 let mask_false_expr = mask(input_expr, false_mask_expr);
227 let simplified_false = mask_false_expr
228 .bind(&DType::Null)
229 .and_then(|expr| expr.optimize())
230 .vortex_expect("Simplification");
231 let expected_null_expr = bound::lit(Scalar::null(DType::Primitive(PType::U32, Nullable)));
232 assert_eq!(&simplified_false, &expected_null_expr);
233 }
234}