Skip to content

Commit d687ea7

Browse files
joseph-isaacsclaude
andcommitted
refactor[array]: move boolean ops to lazy binary expr, remove BooleanKernel dispatch
Signed-off-by: Joe Isaacs <joe@spiraldb.com> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 3cc1b6a commit d687ea7

6 files changed

Lines changed: 28 additions & 326 deletions

File tree

vortex-array/src/arrays/constant/compute/boolean.rs

Lines changed: 0 additions & 136 deletions
This file was deleted.

vortex-array/src/arrays/constant/compute/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// SPDX-FileCopyrightText: Copyright the Vortex contributors
33

44
mod binary_numeric;
5-
mod boolean;
65
mod cast;
76
mod compare;
87
mod fill_null;

vortex-array/src/compute/boolean.rs

Lines changed: 1 addition & 184 deletions
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,16 @@
22
// SPDX-FileCopyrightText: Copyright the Vortex contributors
33

44
use std::any::Any;
5-
use std::sync::LazyLock;
65

7-
use arcref::ArcRef;
86
use arrow_array::cast::AsArray;
97
use arrow_schema::DataType;
10-
use vortex_dtype::DType;
11-
use vortex_error::VortexError;
12-
use vortex_error::VortexExpect;
138
use vortex_error::VortexResult;
14-
use vortex_error::vortex_bail;
15-
use vortex_error::vortex_err;
169

1710
use crate::Array;
1811
use crate::ArrayRef;
19-
use crate::arrays::ConstantVTable;
2012
use crate::arrow::FromArrowArray;
2113
use crate::arrow::IntoArrowArray;
22-
use crate::compute::ComputeFn;
23-
use crate::compute::ComputeFnVTable;
24-
use crate::compute::InvocationArgs;
25-
use crate::compute::Kernel;
2614
use crate::compute::Options;
27-
use crate::compute::Output;
28-
use crate::vtable::VTable;
29-
30-
static BOOLEAN_FN: LazyLock<ComputeFn> = LazyLock::new(|| {
31-
let compute = ComputeFn::new("boolean".into(), ArcRef::new_ref(&Boolean));
32-
for kernel in inventory::iter::<BooleanKernelRef> {
33-
compute.register_kernel(kernel.0.clone());
34-
}
35-
compute
36-
});
37-
38-
pub(crate) fn warm_up_vtable() -> usize {
39-
BOOLEAN_FN.kernels().len()
40-
}
4115

4216
/// Point-wise logical _and_ between two Boolean arrays.
4317
///
@@ -78,131 +52,7 @@ pub fn or_kleene(lhs: &dyn Array, rhs: &dyn Array) -> VortexResult<ArrayRef> {
7852
/// This method uses Arrow-style null propagation rather than the Kleene logic semantics. This
7953
/// semantics is also known as "Bochvar logic" and "weak Kleene logic".
8054
pub fn boolean(lhs: &dyn Array, rhs: &dyn Array, op: BooleanOperator) -> VortexResult<ArrayRef> {
81-
BOOLEAN_FN
82-
.invoke(&InvocationArgs {
83-
inputs: &[lhs.into(), rhs.into()],
84-
options: &op,
85-
})?
86-
.unwrap_array()
87-
}
88-
89-
pub struct BooleanKernelRef(ArcRef<dyn Kernel>);
90-
inventory::collect!(BooleanKernelRef);
91-
92-
pub trait BooleanKernel: VTable {
93-
fn boolean(
94-
&self,
95-
array: &Self::Array,
96-
other: &dyn Array,
97-
op: BooleanOperator,
98-
) -> VortexResult<Option<ArrayRef>>;
99-
}
100-
101-
#[derive(Debug)]
102-
pub struct BooleanKernelAdapter<V: VTable>(pub V);
103-
104-
impl<V: VTable + BooleanKernel> BooleanKernelAdapter<V> {
105-
pub const fn lift(&'static self) -> BooleanKernelRef {
106-
BooleanKernelRef(ArcRef::new_ref(self))
107-
}
108-
}
109-
110-
impl<V: VTable + BooleanKernel> Kernel for BooleanKernelAdapter<V> {
111-
fn invoke(&self, args: &InvocationArgs) -> VortexResult<Option<Output>> {
112-
let inputs = BooleanArgs::try_from(args)?;
113-
let Some(array) = inputs.lhs.as_opt::<V>() else {
114-
return Ok(None);
115-
};
116-
Ok(V::boolean(&self.0, array, inputs.rhs, inputs.operator)?.map(|array| array.into()))
117-
}
118-
}
119-
120-
struct Boolean;
121-
122-
impl ComputeFnVTable for Boolean {
123-
fn invoke(
124-
&self,
125-
args: &InvocationArgs,
126-
kernels: &[ArcRef<dyn Kernel>],
127-
) -> VortexResult<Output> {
128-
let BooleanArgs { lhs, rhs, operator } = BooleanArgs::try_from(args)?;
129-
130-
let rhs_is_constant = rhs.is::<ConstantVTable>();
131-
132-
// If LHS is constant, then we make sure it's on the RHS.
133-
if lhs.is::<ConstantVTable>() && !rhs_is_constant {
134-
return Ok(boolean(rhs, lhs, operator)?.into());
135-
}
136-
137-
// If the RHS is constant and the LHS is Arrow, we can't do any better than arrow_compare.
138-
if lhs.is_arrow() && (rhs.is_arrow() || rhs_is_constant) {
139-
return Ok(arrow_boolean(lhs.to_array(), rhs.to_array(), operator)?.into());
140-
}
141-
142-
// Check if either LHS or RHS supports the operation directly.
143-
for kernel in kernels {
144-
if let Some(output) = kernel.invoke(args)? {
145-
return Ok(output);
146-
}
147-
}
148-
149-
let inverse_args = InvocationArgs {
150-
inputs: &[rhs.into(), lhs.into()],
151-
options: &operator,
152-
};
153-
for kernel in kernels {
154-
if let Some(output) = kernel.invoke(&inverse_args)? {
155-
return Ok(output);
156-
}
157-
}
158-
159-
tracing::debug!(
160-
"No boolean implementation found for LHS {}, RHS {}, and operator {:?} (or inverse)",
161-
rhs.encoding_id(),
162-
lhs.encoding_id(),
163-
operator,
164-
);
165-
166-
// If neither side implements the trait, then we delegate to Arrow compute.
167-
Ok(arrow_boolean(lhs.to_array(), rhs.to_array(), operator)?.into())
168-
}
169-
170-
fn return_dtype(&self, args: &InvocationArgs) -> VortexResult<DType> {
171-
let BooleanArgs { lhs, rhs, .. } = BooleanArgs::try_from(args)?;
172-
173-
if !lhs.dtype().is_boolean()
174-
|| !rhs.dtype().is_boolean()
175-
|| !lhs.dtype().eq_ignore_nullability(rhs.dtype())
176-
{
177-
vortex_bail!(
178-
"Boolean operations are only supported on boolean arrays: {} and {}",
179-
lhs.dtype(),
180-
rhs.dtype()
181-
)
182-
}
183-
184-
Ok(DType::Bool(
185-
(lhs.dtype().is_nullable() || rhs.dtype().is_nullable()).into(),
186-
))
187-
}
188-
189-
fn return_len(&self, args: &InvocationArgs) -> VortexResult<usize> {
190-
let BooleanArgs { lhs, rhs, .. } = BooleanArgs::try_from(args)?;
191-
192-
if lhs.len() != rhs.len() {
193-
vortex_bail!(
194-
"Boolean operations aren't supported on arrays of different lengths: {} and {}",
195-
lhs.len(),
196-
rhs.len()
197-
)
198-
}
199-
200-
Ok(lhs.len())
201-
}
202-
203-
fn is_elementwise(&self) -> bool {
204-
true
205-
}
55+
arrow_boolean(lhs.to_array(), rhs.to_array(), op)
20656
}
20757

20858
/// Operations over the nullable Boolean values.
@@ -257,39 +107,6 @@ impl Options for BooleanOperator {
257107
}
258108
}
259109

260-
struct BooleanArgs<'a> {
261-
lhs: &'a dyn Array,
262-
rhs: &'a dyn Array,
263-
operator: BooleanOperator,
264-
}
265-
266-
impl<'a> TryFrom<&InvocationArgs<'a>> for BooleanArgs<'a> {
267-
type Error = VortexError;
268-
269-
fn try_from(value: &InvocationArgs<'a>) -> VortexResult<Self> {
270-
if value.inputs.len() != 2 {
271-
vortex_bail!("Expected 2 inputs, found {}", value.inputs.len());
272-
}
273-
let lhs = value.inputs[0]
274-
.array()
275-
.ok_or_else(|| vortex_err!("Expected input 0 to be an array"))?;
276-
let rhs = value.inputs[1]
277-
.array()
278-
.ok_or_else(|| vortex_err!("Expected input 1 to be an array"))?;
279-
let operator = value
280-
.options
281-
.as_any()
282-
.downcast_ref::<BooleanOperator>()
283-
.vortex_expect("Expected options to be an operator");
284-
285-
Ok(BooleanArgs {
286-
lhs,
287-
rhs,
288-
operator: *operator,
289-
})
290-
}
291-
}
292-
293110
/// Implementation of `BinaryBooleanFn` using the Arrow crate.
294111
///
295112
/// Note that other encodings should handle a constant RHS value, so we can assume here that

vortex-array/src/compute/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ pub struct ComputeFn {
8686
pub fn warm_up_vtables() {
8787
#[allow(unused_qualifications)]
8888
between::warm_up_vtable();
89-
boolean::warm_up_vtable();
9089
cast::warm_up_vtable();
9190
compare::warm_up_vtable();
9291
invert::warm_up_vtable();
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use vortex_error::VortexResult;
5+
6+
use crate::Array;
7+
use crate::ArrayRef;
8+
use crate::compute::BooleanOperator;
9+
use crate::compute::arrow_boolean;
10+
11+
/// Execute a boolean operation between two arrays.
12+
///
13+
/// This is the entry point for boolean operations from the binary expression.
14+
/// Falls back to Arrow for the actual computation.
15+
pub(crate) fn execute_boolean(
16+
lhs: &dyn Array,
17+
rhs: &dyn Array,
18+
op: BooleanOperator,
19+
) -> VortexResult<ArrayRef> {
20+
arrow_boolean(lhs.to_array(), rhs.to_array(), op)
21+
}

0 commit comments

Comments
 (0)