Skip to content

Commit fe5dfde

Browse files
committed
Comparison compuite
Signed-off-by: Nicholas Gates <[email protected]>
1 parent 3051184 commit fe5dfde

File tree

9 files changed

+536
-9
lines changed

9 files changed

+536
-9
lines changed

vortex-compute/Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,14 @@ vortex-vector = { workspace = true }
2828
num-traits = { workspace = true }
2929

3030
[features]
31-
default = ["arithmetic", "filter", "logical"]
31+
default = [
32+
"arithmetic",
33+
"comparison",
34+
"filter",
35+
"logical",
36+
]
3237

3338
arithmetic = []
39+
comparison = []
3440
filter = []
3541
logical = []
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use std::ops::BitAnd;
5+
6+
use vortex_buffer::{BitBuffer, BufferMut};
7+
use vortex_vector::{BoolVector, VectorOps};
8+
9+
use crate::comparison::{
10+
Compare, Equal, GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual, NotEqual,
11+
};
12+
13+
impl<Op> Compare<Op> for &BoolVector
14+
where
15+
Op: BitComparisonOperator,
16+
{
17+
type Output = BoolVector;
18+
19+
fn compare(self, rhs: Self) -> Self::Output {
20+
let validity = self.validity().bitand(rhs.validity());
21+
22+
let lhs = self.bits().chunks();
23+
let rhs = rhs.bits().chunks();
24+
25+
let mut buffer = BufferMut::<u64>::with_capacity(lhs.chunk_len() + 1);
26+
buffer.extend(
27+
lhs.iter_padded()
28+
.zip(rhs.iter_padded())
29+
.map(|(a_chunk, b_chunk)| Op::apply(&a_chunk, &b_chunk)),
30+
);
31+
let bits = BitBuffer::new(buffer.freeze().into_byte_buffer(), self.len());
32+
33+
BoolVector::new(bits, validity)
34+
}
35+
}
36+
37+
pub trait BitComparisonOperator {
38+
fn apply(a: &u64, b: &u64) -> u64;
39+
}
40+
41+
impl BitComparisonOperator for Equal {
42+
fn apply(a: &u64, b: &u64) -> u64 {
43+
!(a ^ b)
44+
}
45+
}
46+
impl BitComparisonOperator for NotEqual {
47+
fn apply(a: &u64, b: &u64) -> u64 {
48+
a ^ b
49+
}
50+
}
51+
impl BitComparisonOperator for LessThan {
52+
fn apply(a: &u64, b: &u64) -> u64 {
53+
(!a) & b
54+
}
55+
}
56+
impl BitComparisonOperator for LessThanOrEqual {
57+
fn apply(a: &u64, b: &u64) -> u64 {
58+
!(a & (!b))
59+
}
60+
}
61+
impl BitComparisonOperator for GreaterThan {
62+
fn apply(a: &u64, b: &u64) -> u64 {
63+
a & (!b)
64+
}
65+
}
66+
impl BitComparisonOperator for GreaterThanOrEqual {
67+
fn apply(a: &u64, b: &u64) -> u64 {
68+
!((!a) & b)
69+
}
70+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use vortex_buffer::BitBuffer;
5+
6+
use crate::comparison::{Compare, ComparisonOperator};
7+
8+
/// Adapter to implement `Compare` for any `ComparableCollection`.
9+
pub(crate) struct ComparableCollectionAdapter<C>(pub C);
10+
11+
impl<Op, C> Compare<Op> for ComparableCollectionAdapter<C>
12+
where
13+
C: ComparableCollection,
14+
Op: ComparisonOperator<C::Item>,
15+
{
16+
type Output = BitBuffer;
17+
18+
fn compare(self, rhs: Self) -> Self::Output {
19+
assert_eq!(self.0.len(), rhs.0.len());
20+
21+
BitBuffer::from_iter((0..self.0.len()).map(|i| {
22+
let left = unsafe { self.0.item_unchecked(i) };
23+
let right = unsafe { rhs.0.item_unchecked(i) };
24+
Op::apply(&left, &right)
25+
}))
26+
}
27+
}
28+
29+
/// Marker trait for comparable collections.
30+
pub trait ComparableCollection {
31+
/// The item type that can be compared.
32+
type Item;
33+
34+
/// Get the length of the comparable collection.
35+
fn len(&self) -> usize;
36+
37+
/// Get the item at the specified index without bounds checking.
38+
unsafe fn item_unchecked(&self, index: usize) -> Self::Item;
39+
}
40+
41+
impl<T: Copy> ComparableCollection for &[T] {
42+
type Item = T;
43+
44+
fn len(&self) -> usize {
45+
<[T]>::len(self)
46+
}
47+
48+
unsafe fn item_unchecked(&self, index: usize) -> Self::Item {
49+
unsafe { *self.get_unchecked(index) }
50+
}
51+
}
52+
53+
impl<Op, T> Compare<Op> for &[T]
54+
where
55+
T: Copy,
56+
Op: ComparisonOperator<T>,
57+
{
58+
type Output = BitBuffer;
59+
60+
fn compare(self, rhs: Self) -> Self::Output {
61+
Compare::<Op>::compare(
62+
ComparableCollectionAdapter(self),
63+
ComparableCollectionAdapter(rhs),
64+
)
65+
}
66+
}
67+
68+
#[cfg(test)]
69+
mod tests {
70+
use vortex_buffer::bitbuffer;
71+
72+
use super::*;
73+
use crate::comparison::{Equal, GreaterThan, LessThan, NotEqual};
74+
75+
#[test]
76+
fn test_slice_equal() {
77+
let left: &[u32] = &[1, 2, 3, 4];
78+
let right: &[u32] = &[1, 2, 5, 4];
79+
80+
let result = Compare::<Equal>::compare(left, right);
81+
assert_eq!(result, bitbuffer![1 1 0 1]);
82+
}
83+
84+
#[test]
85+
fn test_slice_not_equal() {
86+
let left: &[u32] = &[1, 2, 3, 4];
87+
let right: &[u32] = &[1, 2, 5, 4];
88+
89+
let result = Compare::<NotEqual>::compare(left, right);
90+
assert_eq!(result, bitbuffer![0 0 1 0]);
91+
}
92+
93+
#[test]
94+
fn test_slice_less_than() {
95+
let left: &[u32] = &[1, 2, 3, 4];
96+
let right: &[u32] = &[2, 2, 1, 5];
97+
98+
let result = Compare::<LessThan>::compare(left, right);
99+
assert_eq!(result, bitbuffer![1 0 0 1]);
100+
}
101+
102+
#[test]
103+
fn test_slice_greater_than() {
104+
let left: &[u32] = &[3, 2, 1, 5];
105+
let right: &[u32] = &[1, 2, 3, 4];
106+
107+
let result = Compare::<GreaterThan>::compare(left, right);
108+
assert_eq!(result, bitbuffer![1 0 0 1]);
109+
}
110+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
//! Comparison operations for Vortex vectors.
5+
6+
use vortex_dtype::half::f16;
7+
8+
mod bool;
9+
mod collection;
10+
mod pvector;
11+
12+
/// Trait for comparison operations.
13+
///
14+
/// It may be easier to use the specific traits like [`CompareLessThan`], [`CompareGreaterThan`], etc.
15+
pub trait Compare<Op, Rhs = Self> {
16+
/// The result type after performing the operation.
17+
type Output;
18+
19+
/// Perform the comparison operation.
20+
fn compare(self, rhs: Rhs) -> Self::Output;
21+
}
22+
23+
/// Trait for comparison operators.
24+
pub trait ComparisonOperator<T> {
25+
/// Apply the operator to the two operands.
26+
fn apply(a: &T, b: &T) -> bool;
27+
}
28+
29+
/// A marker type for equality comparison operations.
30+
pub struct Equal;
31+
/// A marker type for inequality comparison operations.
32+
pub struct NotEqual;
33+
/// A marker type for less-than comparison operations.
34+
pub struct LessThan;
35+
/// A marker type for less-than-or-equal comparison operations.
36+
pub struct LessThanOrEqual;
37+
/// A marker type for greater-than comparison operations.
38+
pub struct GreaterThan;
39+
/// A marker type for greater-than-or-equal comparison operations.
40+
pub struct GreaterThanOrEqual;
41+
42+
/// Marker trait for comparable items.
43+
pub trait ComparableItem {
44+
/// Check if two items are equal.
45+
fn is_equal(lhs: &Self, rhs: &Self) -> bool;
46+
47+
/// Check if the `lhs` item is less than the `rhs` item.
48+
fn is_less_than(lhs: &Self, rhs: &Self) -> bool;
49+
}
50+
51+
impl<T: ComparableItem> ComparisonOperator<T> for Equal {
52+
fn apply(a: &T, b: &T) -> bool {
53+
T::is_equal(a, b)
54+
}
55+
}
56+
57+
impl<T: ComparableItem> ComparisonOperator<T> for NotEqual {
58+
fn apply(a: &T, b: &T) -> bool {
59+
!T::is_equal(a, b)
60+
}
61+
}
62+
63+
impl<T: ComparableItem> ComparisonOperator<T> for LessThan {
64+
fn apply(a: &T, b: &T) -> bool {
65+
T::is_less_than(a, b)
66+
}
67+
}
68+
69+
impl<T: ComparableItem> ComparisonOperator<T> for GreaterThanOrEqual {
70+
fn apply(a: &T, b: &T) -> bool {
71+
!T::is_less_than(a, b)
72+
}
73+
}
74+
75+
impl<T: ComparableItem> ComparisonOperator<T> for GreaterThan {
76+
fn apply(a: &T, b: &T) -> bool {
77+
T::is_less_than(b, a)
78+
}
79+
}
80+
81+
impl<T: ComparableItem> ComparisonOperator<T> for LessThanOrEqual {
82+
fn apply(a: &T, b: &T) -> bool {
83+
!T::is_less_than(b, a)
84+
}
85+
}
86+
87+
macro_rules! impl_integer {
88+
($T:ty) => {
89+
impl ComparableItem for $T {
90+
#[inline(always)]
91+
fn is_equal(lhs: &Self, rhs: &Self) -> bool {
92+
lhs == rhs
93+
}
94+
95+
#[inline(always)]
96+
fn is_less_than(lhs: &Self, rhs: &Self) -> bool {
97+
lhs < rhs
98+
}
99+
}
100+
};
101+
}
102+
103+
impl_integer!(i8);
104+
impl_integer!(i16);
105+
impl_integer!(i32);
106+
impl_integer!(i64);
107+
impl_integer!(i128);
108+
impl_integer!(u8);
109+
impl_integer!(u16);
110+
impl_integer!(u32);
111+
impl_integer!(u64);
112+
impl_integer!(u128);
113+
114+
macro_rules! impl_float {
115+
($T:ty) => {
116+
impl ComparableItem for $T {
117+
#[inline(always)]
118+
fn is_equal(lhs: &Self, rhs: &Self) -> bool {
119+
lhs.to_bits().eq(&rhs.to_bits())
120+
}
121+
122+
#[inline(always)]
123+
fn is_less_than(lhs: &Self, rhs: &Self) -> bool {
124+
lhs.total_cmp(rhs).is_lt()
125+
}
126+
}
127+
};
128+
}
129+
130+
impl_float!(f16);
131+
impl_float!(f32);
132+
impl_float!(f64);

0 commit comments

Comments
 (0)