Skip to content

Commit 5164913

Browse files
between(array, lower, lower_op, upper, upper_op) compute function (#2391)
1 parent ad5d9af commit 5164913

File tree

18 files changed

+957
-10
lines changed

18 files changed

+957
-10
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

encodings/alp/src/alp/compute/mod.rs

Lines changed: 164 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
mod compare;
22

3+
use std::fmt::Debug;
4+
5+
use vortex_array::arrays::ConstantArray;
36
use vortex_array::compute::{
4-
filter, scalar_at, slice, take, CompareFn, FilterFn, ScalarAtFn, SliceFn, TakeFn,
7+
between, filter, scalar_at, slice, take, BetweenFn, BetweenOptions, CompareFn, FilterFn,
8+
ScalarAtFn, SliceFn, StrictComparison, TakeFn,
59
};
610
use vortex_array::variants::PrimitiveArrayTrait;
711
use vortex_array::vtable::ComputeVTable;
812
use vortex_array::{Array, IntoArray};
13+
use vortex_dtype::NativePType;
914
use vortex_error::VortexResult;
1015
use vortex_mask::Mask;
11-
use vortex_scalar::Scalar;
16+
use vortex_scalar::{Scalar, ScalarType};
1217

1318
use crate::{match_each_alp_float_ptype, ALPArray, ALPEncoding, ALPFloat};
1419

@@ -17,6 +22,10 @@ impl ComputeVTable for ALPEncoding {
1722
Some(self)
1823
}
1924

25+
fn between_fn(&self) -> Option<&dyn BetweenFn<Array>> {
26+
Some(self)
27+
}
28+
2029
fn filter_fn(&self) -> Option<&dyn FilterFn<Array>> {
2130
Some(self)
2231
}
@@ -107,3 +116,156 @@ impl FilterFn<ALPArray> for ALPEncoding {
107116
)
108117
}
109118
}
119+
120+
impl BetweenFn<ALPArray> for ALPEncoding {
121+
fn between(
122+
&self,
123+
array: &ALPArray,
124+
lower: &Array,
125+
upper: &Array,
126+
options: &BetweenOptions,
127+
) -> VortexResult<Option<Array>> {
128+
let (Some(lower), Some(upper)) = (lower.as_constant(), upper.as_constant()) else {
129+
return Ok(None);
130+
};
131+
132+
if array.patches().is_some() {
133+
return Ok(None);
134+
}
135+
136+
match_each_alp_float_ptype!(array.ptype(), |$F| {
137+
between_impl::<$F>(array, $F::try_from(lower)?, $F::try_from(upper)?, options)
138+
})
139+
.map(Some)
140+
}
141+
}
142+
143+
fn between_impl<T: NativePType + ALPFloat>(
144+
array: &ALPArray,
145+
lower: T,
146+
upper: T,
147+
options: &BetweenOptions,
148+
) -> VortexResult<Array>
149+
where
150+
Scalar: From<T::ALPInt>,
151+
<T as ALPFloat>::ALPInt: ScalarType + Debug,
152+
{
153+
let exponents = array.exponents();
154+
155+
// There are always compared
156+
// the below bound is `value {< | <=} x`, either value encodes into the ALPInt domain
157+
// in which case we can leave the comparison unchanged `enc(value) {< | <=} x` or it doesn't
158+
// and we encode into value below enc_below(value) < value < x, in which case the comparison
159+
// becomes enc(value) < x. See `alp_scalar_compare` for more details.
160+
// note that if the value doesn't encode than value != x, so must use strict comparison.
161+
let (lower_enc, lower_strict) = T::encode_single(lower, exponents)
162+
.map(|x| (x, options.lower_strict))
163+
.unwrap_or_else(|| (T::encode_below(lower, exponents), StrictComparison::Strict));
164+
165+
// the upper value `x { < | <= } value` similarly encodes or `x < value < enc_above(value())`
166+
let (upper_enc, upper_strict) = T::encode_single(upper, exponents)
167+
.map(|x| (x, options.upper_strict))
168+
.unwrap_or_else(|| (T::encode_above(upper, exponents), StrictComparison::Strict));
169+
170+
let options = BetweenOptions {
171+
lower_strict,
172+
upper_strict,
173+
};
174+
175+
between(
176+
array.encoded(),
177+
ConstantArray::new(lower_enc, array.len()),
178+
ConstantArray::new(upper_enc, array.len()),
179+
&options,
180+
)
181+
}
182+
183+
#[cfg(test)]
184+
mod tests {
185+
use itertools::Itertools;
186+
use vortex_array::arrays::PrimitiveArray;
187+
use vortex_array::compute::{BetweenOptions, StrictComparison};
188+
use vortex_array::IntoArrayVariant;
189+
190+
use crate::alp::compute::between_impl;
191+
use crate::ALPArray;
192+
193+
fn between_test(arr: &ALPArray, lower: f32, upper: f32, options: &BetweenOptions) -> bool {
194+
let res = between_impl(arr, lower, upper, options)
195+
.unwrap()
196+
.into_bool()
197+
.unwrap()
198+
.boolean_buffer()
199+
.iter()
200+
.collect_vec();
201+
assert_eq!(res.len(), 1);
202+
203+
res[0]
204+
}
205+
206+
#[test]
207+
fn comparison_range() {
208+
let value = 0.0605_f32;
209+
let array = PrimitiveArray::from_iter([value; 1]);
210+
let encoded = crate::alp::compress::alp_encode(&array).unwrap();
211+
assert!(encoded.patches().is_none());
212+
assert_eq!(
213+
encoded
214+
.encoded()
215+
.into_primitive()
216+
.unwrap()
217+
.as_slice::<i32>(),
218+
vec![605; 1]
219+
);
220+
221+
assert!(between_test(
222+
&encoded,
223+
0.0605_f32,
224+
0.0605,
225+
&BetweenOptions {
226+
lower_strict: StrictComparison::NonStrict,
227+
upper_strict: StrictComparison::NonStrict,
228+
},
229+
));
230+
231+
assert!(!between_test(
232+
&encoded,
233+
0.0605_f32,
234+
0.0605,
235+
&BetweenOptions {
236+
lower_strict: StrictComparison::Strict,
237+
upper_strict: StrictComparison::NonStrict,
238+
},
239+
));
240+
241+
assert!(!between_test(
242+
&encoded,
243+
0.0605_f32,
244+
0.0605,
245+
&BetweenOptions {
246+
lower_strict: StrictComparison::NonStrict,
247+
upper_strict: StrictComparison::Strict,
248+
},
249+
));
250+
251+
assert!(between_test(
252+
&encoded,
253+
0.060499_f32,
254+
0.06051,
255+
&BetweenOptions {
256+
lower_strict: StrictComparison::NonStrict,
257+
upper_strict: StrictComparison::NonStrict,
258+
},
259+
));
260+
261+
assert!(between_test(
262+
&encoded,
263+
0.06_f32,
264+
0.06051,
265+
&BetweenOptions {
266+
lower_strict: StrictComparison::NonStrict,
267+
upper_strict: StrictComparison::Strict,
268+
},
269+
))
270+
}
271+
}

encodings/datetime-parts/src/compute/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ impl ComputeVTable for DateTimePartsEncoding {
4141
fn compare_fn(&self) -> Option<&dyn CompareFn<Array>> {
4242
Some(self)
4343
}
44+
45+
// TODO(joe): implement `between_fn` this is used at lot.
4446
}
4547

4648
impl SliceFn<DateTimePartsArray> for DateTimePartsEncoding {

encodings/fastlanes/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ divan = { workspace = true }
3737
rand = { workspace = true }
3838
vortex-array = { workspace = true, features = ["test-harness"] }
3939
vortex-fastlanes = { path = ".", features = ["test-harness"] }
40+
vortex-alp = { workspace = true }
4041

4142
[features]
4243
test-harness = ["dep:rand"]
@@ -53,3 +54,9 @@ harness = false
5354
name = "canonicalize_bench"
5455
harness = false
5556
required-features = ["test-harness"]
57+
58+
[[bench]]
59+
name = "compute_between"
60+
harness = false
61+
required-features = ["test-harness"]
62+

0 commit comments

Comments
 (0)