Skip to content

Commit 5312315

Browse files
committed
MaskedArray + masked compute + is_null/is_not_null
Signed-off-by: Nicholas Gates <[email protected]>
1 parent 4c329f5 commit 5312315

File tree

13 files changed

+378
-38
lines changed

13 files changed

+378
-38
lines changed

vortex-array/src/arrays/masked/vtable/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
mod array;
55
mod canonical;
66
mod operations;
7+
mod operator;
78
mod serde;
89
mod validity;
910

@@ -28,7 +29,7 @@ impl VTable for MaskedVTable {
2829
type ComputeVTable = NotSupported;
2930
type EncodeVTable = NotSupported;
3031
type SerdeVTable = Self;
31-
type OperatorVTable = NotSupported;
32+
type OperatorVTable = Self;
3233

3334
fn id(_encoding: &Self::Encoding) -> EncodingId {
3435
EncodingId::new_ref("vortex.masked")
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use vortex_compute::mask::MaskValidity;
5+
use vortex_error::VortexResult;
6+
7+
use crate::ArrayRef;
8+
use crate::arrays::{MaskedArray, MaskedVTable};
9+
use crate::execution::{BatchKernelRef, BindCtx, kernel};
10+
use crate::vtable::OperatorVTable;
11+
12+
impl OperatorVTable<MaskedVTable> for MaskedVTable {
13+
fn bind(
14+
array: &MaskedArray,
15+
selection: Option<&ArrayRef>,
16+
ctx: &mut dyn BindCtx,
17+
) -> VortexResult<BatchKernelRef> {
18+
// A masked array performs the intersection if the mask validity with the child validity.
19+
let mask = ctx.bind_validity(&array.validity, array.len(), selection)?;
20+
let child = ctx.bind(&array.child, selection)?;
21+
22+
Ok(kernel(move || {
23+
let mask = mask.execute()?;
24+
let child = child.execute()?;
25+
Ok(MaskValidity::mask_validity(child, &mask))
26+
}))
27+
}
28+
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use std::hash::Hasher;
5+
6+
use vortex_dtype::DType;
7+
use vortex_dtype::Nullability::NonNullable;
8+
use vortex_error::VortexResult;
9+
use vortex_mask::Mask;
10+
use vortex_vector::{BoolVector, VectorOps};
11+
12+
use crate::execution::{BatchKernelRef, BindCtx, kernel};
13+
use crate::stats::{ArrayStats, StatsSetRef};
14+
use crate::vtable::{ArrayVTable, NotSupported, OperatorVTable, VTable, VisitorVTable};
15+
use crate::{
16+
ArrayBufferVisitor, ArrayChildVisitor, ArrayEq, ArrayHash, ArrayRef, EncodingId, EncodingRef,
17+
Precision, vtable,
18+
};
19+
20+
vtable!(IsNotNull);
21+
22+
#[derive(Debug, Clone)]
23+
pub struct IsNotNullArray {
24+
child: ArrayRef,
25+
stats: ArrayStats,
26+
}
27+
28+
impl IsNotNullArray {
29+
/// Create a new is_not_null array.
30+
pub fn new(child: ArrayRef) -> Self {
31+
Self {
32+
child,
33+
stats: ArrayStats::default(),
34+
}
35+
}
36+
}
37+
38+
#[derive(Debug, Clone)]
39+
pub struct IsNotNullEncoding;
40+
41+
impl VTable for IsNotNullVTable {
42+
type Array = IsNotNullArray;
43+
type Encoding = IsNotNullEncoding;
44+
type ArrayVTable = Self;
45+
type CanonicalVTable = NotSupported;
46+
type OperationsVTable = NotSupported;
47+
type ValidityVTable = NotSupported;
48+
type VisitorVTable = Self;
49+
type ComputeVTable = NotSupported;
50+
type EncodeVTable = NotSupported;
51+
type SerdeVTable = NotSupported;
52+
type OperatorVTable = Self;
53+
54+
fn id(_encoding: &Self::Encoding) -> EncodingId {
55+
EncodingId::from("vortex.is_null")
56+
}
57+
58+
fn encoding(_array: &Self::Array) -> EncodingRef {
59+
EncodingRef::from(IsNotNullEncoding.as_ref())
60+
}
61+
}
62+
63+
impl ArrayVTable<IsNotNullVTable> for IsNotNullVTable {
64+
fn len(array: &IsNotNullArray) -> usize {
65+
array.len()
66+
}
67+
68+
fn dtype(_array: &IsNotNullArray) -> &DType {
69+
&DType::Bool(NonNullable)
70+
}
71+
72+
fn stats(array: &IsNotNullArray) -> StatsSetRef<'_> {
73+
array.stats.to_ref(array.as_ref())
74+
}
75+
76+
fn array_hash<H: Hasher>(array: &IsNotNullArray, state: &mut H, precision: Precision) {
77+
array.child.array_hash(state, precision);
78+
}
79+
80+
fn array_eq(array: &IsNotNullArray, other: &IsNotNullArray, precision: Precision) -> bool {
81+
array.child.array_eq(&other.child, precision)
82+
}
83+
}
84+
85+
impl VisitorVTable<IsNotNullVTable> for IsNotNullVTable {
86+
fn visit_buffers(_array: &IsNotNullArray, _visitor: &mut dyn ArrayBufferVisitor) {
87+
// No buffers
88+
}
89+
90+
fn visit_children(array: &IsNotNullArray, visitor: &mut dyn ArrayChildVisitor) {
91+
visitor.visit_child("child", array.child.as_ref());
92+
}
93+
}
94+
95+
impl OperatorVTable<IsNotNullVTable> for IsNotNullVTable {
96+
fn bind(
97+
array: &IsNotNullArray,
98+
selection: Option<&ArrayRef>,
99+
ctx: &mut dyn BindCtx,
100+
) -> VortexResult<BatchKernelRef> {
101+
let child = ctx.bind(&array.child, selection)?;
102+
Ok(kernel(move || {
103+
let child = child.execute()?;
104+
let is_null = child.validity().to_bit_buffer();
105+
Ok(BoolVector::new(is_null, Mask::AllTrue(child.len())).into())
106+
}))
107+
}
108+
}
109+
110+
#[cfg(test)]
111+
mod tests {
112+
use vortex_buffer::{bitbuffer, buffer};
113+
use vortex_error::VortexResult;
114+
use vortex_vector::VectorOps;
115+
116+
use super::IsNotNullArray;
117+
use crate::IntoArray;
118+
use crate::arrays::PrimitiveArray;
119+
use crate::validity::Validity;
120+
121+
#[test]
122+
fn test_is_null() -> VortexResult<()> {
123+
let validity = bitbuffer![1 0 1];
124+
let array = PrimitiveArray::new(
125+
buffer![0, 1, 2],
126+
Validity::Array(validity.clone().into_array()),
127+
)
128+
.into_array();
129+
130+
let result = IsNotNullArray::new(array).execute()?.into_bool();
131+
assert!(result.validity().all_true());
132+
assert_eq!(result.bits(), &validity);
133+
134+
Ok(())
135+
}
136+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use std::hash::Hasher;
5+
use std::ops::Not;
6+
7+
use vortex_dtype::DType;
8+
use vortex_dtype::Nullability::NonNullable;
9+
use vortex_error::VortexResult;
10+
use vortex_mask::Mask;
11+
use vortex_vector::{BoolVector, VectorOps};
12+
13+
use crate::execution::{BatchKernelRef, BindCtx, kernel};
14+
use crate::stats::{ArrayStats, StatsSetRef};
15+
use crate::vtable::{ArrayVTable, NotSupported, OperatorVTable, VTable, VisitorVTable};
16+
use crate::{
17+
ArrayBufferVisitor, ArrayChildVisitor, ArrayEq, ArrayHash, ArrayRef, EncodingId, EncodingRef,
18+
Precision, vtable,
19+
};
20+
21+
vtable!(IsNull);
22+
23+
#[derive(Debug, Clone)]
24+
pub struct IsNullArray {
25+
child: ArrayRef,
26+
stats: ArrayStats,
27+
}
28+
29+
impl IsNullArray {
30+
/// Create a new is_null array.
31+
pub fn new(child: ArrayRef) -> Self {
32+
Self {
33+
child,
34+
stats: ArrayStats::default(),
35+
}
36+
}
37+
}
38+
39+
#[derive(Debug, Clone)]
40+
pub struct IsNullEncoding;
41+
42+
impl VTable for IsNullVTable {
43+
type Array = IsNullArray;
44+
type Encoding = IsNullEncoding;
45+
type ArrayVTable = Self;
46+
type CanonicalVTable = NotSupported;
47+
type OperationsVTable = NotSupported;
48+
type ValidityVTable = NotSupported;
49+
type VisitorVTable = Self;
50+
type ComputeVTable = NotSupported;
51+
type EncodeVTable = NotSupported;
52+
type SerdeVTable = NotSupported;
53+
type OperatorVTable = Self;
54+
55+
fn id(_encoding: &Self::Encoding) -> EncodingId {
56+
EncodingId::from("vortex.is_null")
57+
}
58+
59+
fn encoding(_array: &Self::Array) -> EncodingRef {
60+
EncodingRef::from(IsNullEncoding.as_ref())
61+
}
62+
}
63+
64+
impl ArrayVTable<IsNullVTable> for IsNullVTable {
65+
fn len(array: &IsNullArray) -> usize {
66+
array.len()
67+
}
68+
69+
fn dtype(_array: &IsNullArray) -> &DType {
70+
&DType::Bool(NonNullable)
71+
}
72+
73+
fn stats(array: &IsNullArray) -> StatsSetRef<'_> {
74+
array.stats.to_ref(array.as_ref())
75+
}
76+
77+
fn array_hash<H: Hasher>(array: &IsNullArray, state: &mut H, precision: Precision) {
78+
array.child.array_hash(state, precision);
79+
}
80+
81+
fn array_eq(array: &IsNullArray, other: &IsNullArray, precision: Precision) -> bool {
82+
array.child.array_eq(&other.child, precision)
83+
}
84+
}
85+
86+
impl VisitorVTable<IsNullVTable> for IsNullVTable {
87+
fn visit_buffers(_array: &IsNullArray, _visitor: &mut dyn ArrayBufferVisitor) {
88+
// No buffers
89+
}
90+
91+
fn visit_children(array: &IsNullArray, visitor: &mut dyn ArrayChildVisitor) {
92+
visitor.visit_child("child", array.child.as_ref());
93+
}
94+
}
95+
96+
impl OperatorVTable<IsNullVTable> for IsNullVTable {
97+
fn bind(
98+
array: &IsNullArray,
99+
selection: Option<&ArrayRef>,
100+
ctx: &mut dyn BindCtx,
101+
) -> VortexResult<BatchKernelRef> {
102+
let child = ctx.bind(&array.child, selection)?;
103+
Ok(kernel(move || {
104+
let child = child.execute()?;
105+
let is_null = child.validity().not().to_bit_buffer();
106+
Ok(BoolVector::new(is_null, Mask::AllTrue(child.len())).into())
107+
}))
108+
}
109+
}
110+
111+
#[cfg(test)]
112+
mod tests {
113+
use std::ops::Not;
114+
115+
use vortex_buffer::{bitbuffer, buffer};
116+
use vortex_error::VortexResult;
117+
use vortex_vector::VectorOps;
118+
119+
use crate::IntoArray;
120+
use crate::arrays::PrimitiveArray;
121+
use crate::compute::arrays::is_null::IsNullArray;
122+
use crate::validity::Validity;
123+
124+
#[test]
125+
fn test_is_null() -> VortexResult<()> {
126+
let validity = bitbuffer![1 0 1];
127+
let array = PrimitiveArray::new(
128+
buffer![0, 1, 2],
129+
Validity::Array(validity.clone().into_array()),
130+
)
131+
.into_array();
132+
133+
let result = IsNullArray::new(array).execute()?.into_bool();
134+
assert!(result.validity().all_true());
135+
assert_eq!(result.bits(), &validity.not());
136+
137+
Ok(())
138+
}
139+
}

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

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

44
pub mod arithmetic;
5+
pub mod is_not_null;
6+
pub mod is_null;
57
pub mod logical;

vortex-compute/Cargo.toml

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

3030
[features]
31-
default = ["arithmetic", "comparison", "filter", "logical"]
31+
default = ["arithmetic", "comparison", "filter", "logical", "mask"]
3232

3333
arithmetic = []
3434
comparison = []
3535
filter = []
3636
logical = []
37+
mask = []

vortex-compute/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,5 @@ pub mod comparison;
1515
pub mod filter;
1616
#[cfg(feature = "logical")]
1717
pub mod logical;
18+
#[cfg(feature = "mask")]
19+
pub mod mask;

vortex-compute/src/logical/and.rs

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
use std::ops::BitAnd;
55

6-
use vortex_mask::Mask;
76
use vortex_vector::{BoolVector, VectorOps};
87

98
use crate::logical::LogicalAnd;
@@ -28,22 +27,6 @@ impl LogicalAnd<&BoolVector> for BoolVector {
2827
}
2928
}
3029

31-
impl LogicalAnd for &Mask {
32-
type Output = Mask;
33-
34-
fn and(self, other: Self) -> Self::Output {
35-
self.bitand(other)
36-
}
37-
}
38-
39-
impl LogicalAnd<&Mask> for Mask {
40-
type Output = Mask;
41-
42-
fn and(self, other: &Mask) -> Self::Output {
43-
self.bitand(other)
44-
}
45-
}
46-
4730
#[cfg(test)]
4831
mod tests {
4932
use vortex_buffer::bitbuffer;

0 commit comments

Comments
 (0)