|
2 | 2 | // SPDX-FileCopyrightText: Copyright the Vortex contributors |
3 | 3 |
|
4 | 4 | use std::any::Any; |
5 | | -use std::sync::LazyLock; |
6 | 5 |
|
7 | | -use arcref::ArcRef; |
8 | 6 | use arrow_array::cast::AsArray; |
9 | 7 | use arrow_schema::DataType; |
10 | | -use vortex_dtype::DType; |
11 | | -use vortex_error::VortexError; |
12 | | -use vortex_error::VortexExpect; |
13 | 8 | use vortex_error::VortexResult; |
14 | | -use vortex_error::vortex_bail; |
15 | | -use vortex_error::vortex_err; |
16 | 9 |
|
17 | 10 | use crate::Array; |
18 | 11 | use crate::ArrayRef; |
19 | | -use crate::arrays::ConstantVTable; |
20 | 12 | use crate::arrow::FromArrowArray; |
21 | 13 | use crate::arrow::IntoArrowArray; |
22 | | -use crate::compute::ComputeFn; |
23 | | -use crate::compute::ComputeFnVTable; |
24 | | -use crate::compute::InvocationArgs; |
25 | | -use crate::compute::Kernel; |
26 | 14 | 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 | | -} |
41 | 15 |
|
42 | 16 | /// Point-wise logical _and_ between two Boolean arrays. |
43 | 17 | /// |
@@ -78,131 +52,7 @@ pub fn or_kleene(lhs: &dyn Array, rhs: &dyn Array) -> VortexResult<ArrayRef> { |
78 | 52 | /// This method uses Arrow-style null propagation rather than the Kleene logic semantics. This |
79 | 53 | /// semantics is also known as "Bochvar logic" and "weak Kleene logic". |
80 | 54 | 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) |
206 | 56 | } |
207 | 57 |
|
208 | 58 | /// Operations over the nullable Boolean values. |
@@ -257,39 +107,6 @@ impl Options for BooleanOperator { |
257 | 107 | } |
258 | 108 | } |
259 | 109 |
|
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 | | - |
293 | 110 | /// Implementation of `BinaryBooleanFn` using the Arrow crate. |
294 | 111 | /// |
295 | 112 | /// Note that other encodings should handle a constant RHS value, so we can assume here that |
|
0 commit comments