Skip to content

Commit 610a8c3

Browse files
refactor: hoist null-propagating validity into a vortex-array helper
Promote the "result is null iff any operand is null" validity — previously geo's binary_result_validity plus four inline copies in vortex-tensor — into a shared helper, vortex_array::expr::null_propagating_validity. It conjoins every child's validity, so it covers unary (l2_norm) and binary kernels alike. - vortex-array: add expr::null_propagating_validity - vortex-geo: GeoDistance / GeoContains / GeoIntersects use it; drop the local binary_result_validity - vortex-tensor: InnerProduct / CosineSimilarity / L2Denorm / L2Norm use it instead of inlining and(child validities) Signed-off-by: Nemo Yu <zyu379@wisc.edu>
1 parent 9ad9376 commit 610a8c3

9 files changed

Lines changed: 31 additions & 34 deletions

File tree

vortex-array/src/expr/exprs.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::sync::Arc;
77
use std::sync::LazyLock;
88

99
use vortex_error::VortexExpect;
10+
use vortex_error::VortexResult;
1011
use vortex_error::vortex_panic;
1112
use vortex_utils::iter::ReduceBalancedIterExt;
1213

@@ -434,6 +435,22 @@ where
434435
iter.into_iter().reduce_balanced(and)
435436
}
436437

438+
/// The validity of a scalar function whose result is null exactly when any operand is null: the
439+
/// conjunction of its children's validities.
440+
///
441+
/// This is the `ScalarFnVTable::validity` for kernels that propagate nulls and never produce a
442+
/// null from non-null inputs (comparisons, arithmetic, most geo and tensor ops). Returning it lets
443+
/// the planner derive the output's null mask without executing the kernel. Yields `None` when the
444+
/// expression has no children.
445+
pub fn null_propagating_validity(expression: &Expression) -> VortexResult<Option<Expression>> {
446+
let child_validities = expression
447+
.children()
448+
.iter()
449+
.map(Expression::validity)
450+
.collect::<VortexResult<Vec<_>>>()?;
451+
Ok(and_collect(child_validities))
452+
}
453+
437454
/// Create a new [`Binary`] using the [`Add`](Operator::Add) operator.
438455
///
439456
/// ## Example usage

vortex-geo/src/scalar_fn/contains.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use vortex_array::arrays::ScalarFnArray;
1010
use vortex_array::dtype::DType;
1111
use vortex_array::dtype::Nullability;
1212
use vortex_array::expr::Expression;
13+
use vortex_array::expr::null_propagating_validity;
1314
use vortex_array::scalar_fn::Arity;
1415
use vortex_array::scalar_fn::ChildName;
1516
use vortex_array::scalar_fn::EmptyOptions;
@@ -22,7 +23,6 @@ use vortex_session::VortexSession;
2223
use vortex_session::registry::CachedId;
2324

2425
use crate::extension::validate_geometry_operands;
25-
use crate::scalar_fn::null_propagate::binary_result_validity;
2626
use crate::scalar_fn::null_propagate::execute_null_propagating;
2727

2828
/// OGC `ST_Contains` between two native geometry operands, each a column or a constant
@@ -93,7 +93,7 @@ impl ScalarFnVTable for GeoContains {
9393
_: &Self::Options,
9494
expression: &Expression,
9595
) -> VortexResult<Option<Expression>> {
96-
binary_result_validity(expression)
96+
null_propagating_validity(expression)
9797
}
9898

9999
fn is_null_sensitive(&self, _: &Self::Options) -> bool {

vortex-geo/src/scalar_fn/distance.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use vortex_array::dtype::DType;
1212
use vortex_array::dtype::Nullability;
1313
use vortex_array::dtype::PType;
1414
use vortex_array::expr::Expression;
15+
use vortex_array::expr::null_propagating_validity;
1516
use vortex_array::scalar_fn::Arity;
1617
use vortex_array::scalar_fn::ChildName;
1718
use vortex_array::scalar_fn::EmptyOptions;
@@ -24,7 +25,6 @@ use vortex_session::VortexSession;
2425
use vortex_session::registry::CachedId;
2526

2627
use crate::extension::validate_geometry_operands;
27-
use crate::scalar_fn::null_propagate::binary_result_validity;
2828
use crate::scalar_fn::null_propagate::execute_null_propagating;
2929

3030
/// Planar (Euclidean) `ST_Distance` (no geodesic correction) between two native geometry
@@ -93,7 +93,7 @@ impl ScalarFnVTable for GeoDistance {
9393
_: &Self::Options,
9494
expression: &Expression,
9595
) -> VortexResult<Option<Expression>> {
96-
binary_result_validity(expression)
96+
null_propagating_validity(expression)
9797
}
9898

9999
fn is_null_sensitive(&self, _: &Self::Options) -> bool {

vortex-geo/src/scalar_fn/intersects.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use vortex_array::arrays::ScalarFnArray;
1010
use vortex_array::dtype::DType;
1111
use vortex_array::dtype::Nullability;
1212
use vortex_array::expr::Expression;
13+
use vortex_array::expr::null_propagating_validity;
1314
use vortex_array::scalar_fn::Arity;
1415
use vortex_array::scalar_fn::ChildName;
1516
use vortex_array::scalar_fn::EmptyOptions;
@@ -22,7 +23,6 @@ use vortex_session::VortexSession;
2223
use vortex_session::registry::CachedId;
2324

2425
use crate::extension::validate_geometry_operands;
25-
use crate::scalar_fn::null_propagate::binary_result_validity;
2626
use crate::scalar_fn::null_propagate::execute_null_propagating;
2727

2828
/// OGC `ST_Intersects` (not disjoint; boundary contact counts) between two native geometry
@@ -91,7 +91,7 @@ impl ScalarFnVTable for GeoIntersects {
9191
_: &Self::Options,
9292
expression: &Expression,
9393
) -> VortexResult<Option<Expression>> {
94-
binary_result_validity(expression)
94+
null_propagating_validity(expression)
9595
}
9696

9797
fn is_null_sensitive(&self, _: &Self::Options) -> bool {

vortex-geo/src/scalar_fn/null_propagate.rs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ use vortex_array::arrays::PrimitiveArray;
1919
use vortex_array::dtype::DType;
2020
use vortex_array::dtype::Nullability;
2121
use vortex_array::dtype::PType;
22-
use vortex_array::expr::Expression;
23-
use vortex_array::expr::and;
2422
use vortex_array::scalar::Scalar;
2523
use vortex_array::validity::Validity;
2624
use vortex_buffer::BitBuffer;
@@ -132,16 +130,6 @@ fn all_null_array<T: GeoOutput>(len: usize) -> ArrayRef {
132130
ConstantArray::new(Scalar::null(T::null_dtype()), len).into_array()
133131
}
134132

135-
/// The validity expression for a binary geo kernel: the result is null iff either operand is
136-
/// null. Returning this lets the planner derive the output's null mask symbolically (mask
137-
/// pushdown) instead of executing the whole kernel.
138-
pub(crate) fn binary_result_validity(expression: &Expression) -> VortexResult<Option<Expression>> {
139-
Ok(Some(and(
140-
expression.child(0).validity()?,
141-
expression.child(1).validity()?,
142-
)))
143-
}
144-
145133
/// Run a binary geo kernel over operands `a` and `b`, each a column or a constant literal.
146134
///
147135
/// The output is null wherever either operand is null, and its type is nullable if either operand

vortex-tensor/src/scalar_fns/cosine_similarity.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use vortex_array::arrays::scalar_fn::plugin::ScalarFnArrayVTable;
1515
use vortex_array::dtype::DType;
1616
use vortex_array::dtype::Nullability;
1717
use vortex_array::expr::Expression;
18-
use vortex_array::expr::and;
18+
use vortex_array::expr::null_propagating_validity;
1919
use vortex_array::match_each_float_ptype;
2020
use vortex_array::scalar_fn::Arity;
2121
use vortex_array::scalar_fn::ChildName;
@@ -180,10 +180,7 @@ impl ScalarFnVTable for CosineSimilarity {
180180
expression: &Expression,
181181
) -> VortexResult<Option<Expression>> {
182182
// The result is null if either input tensor is null.
183-
let lhs_validity = expression.child(0).validity()?;
184-
let rhs_validity = expression.child(1).validity()?;
185-
186-
Ok(Some(and(lhs_validity, rhs_validity)))
183+
null_propagating_validity(expression)
187184
}
188185

189186
fn is_null_sensitive(&self, _options: &Self::Options) -> bool {

vortex-tensor/src/scalar_fns/inner_product.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use vortex_array::dtype::DType;
1818
use vortex_array::dtype::NativePType;
1919
use vortex_array::dtype::Nullability;
2020
use vortex_array::expr::Expression;
21-
use vortex_array::expr::and;
21+
use vortex_array::expr::null_propagating_validity;
2222
use vortex_array::match_each_float_ptype;
2323
use vortex_array::scalar_fn::Arity;
2424
use vortex_array::scalar_fn::ChildName;
@@ -164,10 +164,7 @@ impl ScalarFnVTable for InnerProduct {
164164
expression: &Expression,
165165
) -> VortexResult<Option<Expression>> {
166166
// The result is null if either input tensor is null.
167-
let lhs_validity = expression.child(0).validity()?;
168-
let rhs_validity = expression.child(1).validity()?;
169-
170-
Ok(Some(and(lhs_validity, rhs_validity)))
167+
null_propagating_validity(expression)
171168
}
172169

173170
fn is_null_sensitive(&self, _options: &Self::Options) -> bool {

vortex-tensor/src/scalar_fns/l2_denorm.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use vortex_array::dtype::NativePType;
3131
use vortex_array::dtype::Nullability;
3232
use vortex_array::dtype::proto::dtype as pb;
3333
use vortex_array::expr::Expression;
34-
use vortex_array::expr::and;
34+
use vortex_array::expr::null_propagating_validity;
3535
use vortex_array::match_each_float_ptype;
3636
use vortex_array::scalar::Scalar;
3737
use vortex_array::scalar::ScalarValue;
@@ -256,10 +256,7 @@ impl ScalarFnVTable for L2Denorm {
256256
_options: &Self::Options,
257257
expression: &Expression,
258258
) -> VortexResult<Option<Expression>> {
259-
let normalized_validity = expression.child(0).validity()?;
260-
let norms_validity = expression.child(1).validity()?;
261-
262-
Ok(Some(and(normalized_validity, norms_validity)))
259+
null_propagating_validity(expression)
263260
}
264261

265262
fn is_null_sensitive(&self, _options: &Self::Options) -> bool {

vortex-tensor/src/scalar_fns/l2_norm.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use vortex_array::dtype::NativePType;
2525
use vortex_array::dtype::Nullability;
2626
use vortex_array::dtype::proto::dtype as pb;
2727
use vortex_array::expr::Expression;
28+
use vortex_array::expr::null_propagating_validity;
2829
use vortex_array::match_each_float_ptype;
2930
use vortex_array::scalar::Scalar;
3031
use vortex_array::scalar_fn::Arity;
@@ -184,7 +185,7 @@ impl ScalarFnVTable for L2Norm {
184185
expression: &Expression,
185186
) -> VortexResult<Option<Expression>> {
186187
// The result is null if the input tensor is null.
187-
Ok(Some(expression.child(0).validity()?))
188+
null_propagating_validity(expression)
188189
}
189190

190191
fn is_null_sensitive(&self, _options: &Self::Options) -> bool {

0 commit comments

Comments
 (0)