Skip to content

Commit a1ebc7a

Browse files
committed
simplify kleene logic
Signed-off-by: Connor Tsui <[email protected]>
1 parent 7ff7f6f commit a1ebc7a

File tree

1 file changed

+28
-43
lines changed

1 file changed

+28
-43
lines changed

vortex-compute/src/logical/kleene.rs

Lines changed: 28 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,6 @@ pub trait KleeneBinaryOp {
4141
/// Apply the operation to two [`BitBuffer`]s.
4242
fn bit_op(lhs: &BitBuffer, rhs: &BitBuffer) -> BitBuffer;
4343

44-
/// Absorbing bits when one side is all-null.
45-
///
46-
/// - AND: all-false, since `x AND null` is false or null.
47-
/// - OR: all-true, since `x OR null` is true or null.
48-
fn absorbing_bits_from_len(len: usize) -> BitBuffer;
49-
5044
/// Returns a mask of positions with absorbing values.
5145
///
5246
/// - AND: `FALSE` absorbs, so return `bits.not()` (false positions).
@@ -62,10 +56,6 @@ impl KleeneBinaryOp for KleeneAnd {
6256
lhs.bitand(rhs)
6357
}
6458

65-
fn absorbing_bits_from_len(len: usize) -> BitBuffer {
66-
BitBuffer::new_unset(len) // All false.
67-
}
68-
6959
fn absorb_bits(bits: &BitBuffer) -> BitBuffer {
7060
bits.not() // `false` absorbs nulls.
7161
}
@@ -84,10 +74,6 @@ impl KleeneBinaryOp for KleeneOr {
8474
lhs.bitor(rhs)
8575
}
8676

87-
fn absorbing_bits_from_len(len: usize) -> BitBuffer {
88-
BitBuffer::new_set(len) // All true.
89-
}
90-
9177
fn absorb_bits(bits: &BitBuffer) -> BitBuffer {
9278
bits.clone() // `true` absorbs nulls.
9379
}
@@ -181,16 +167,20 @@ fn kleene_vector_op<Op: KleeneBinaryOp>(lhs: &BoolVector, rhs: &BoolVector) -> B
181167

182168
// LHS is all valid, RHS is all null.
183169
(Mask::AllTrue(_), Mask::AllFalse(_)) => {
184-
// The result vector is valid where the LHS has an absorbing value.
185-
let result_bits = Op::absorbing_bits_from_len(len);
170+
// The result vector is valid where the LHS has an absorbing value. Since only
171+
// absorbing values produce valid results, and absorbing values equal the result of the
172+
// operation, we can reuse the LHS bits directly.
173+
let result_bits = lhs.bits().clone();
186174
let validity = Op::absorb_bits(lhs.bits());
187175
BoolVector::new(result_bits, Mask::from(validity))
188176
}
189177

190178
// LHS is all null, RHS is all valid.
191179
(Mask::AllFalse(_), Mask::AllTrue(_)) => {
192-
// The result vector is valid where the RHS has an absorbing value.
193-
let result_bits = Op::absorbing_bits_from_len(len);
180+
// The result vector is valid where the RHS has an absorbing value. Since only
181+
// absorbing values produce valid results, and absorbing values equal the result of the
182+
// operation, we can reuse the RHS bits directly.
183+
let result_bits = rhs.bits().clone();
194184
let validity = Op::absorb_bits(rhs.bits());
195185
BoolVector::new(result_bits, Mask::from(validity))
196186
}
@@ -213,52 +203,47 @@ fn kleene_vector_op<Op: KleeneBinaryOp>(lhs: &BoolVector, rhs: &BoolVector) -> B
213203

214204
// LHS is all null, RHS has specific validity.
215205
(Mask::AllFalse(_), Mask::Values(rhs_values)) => {
216-
// The result vector is valid where the RHS is valid AND has an absorbing value.
217-
let result_bits = Op::absorbing_bits_from_len(len);
206+
// The result vector is valid where the RHS is valid AND has an absorbing value. Since
207+
// only absorbing values produce valid results, we can reuse the RHS bits directly.
208+
let result_bits = rhs.bits().clone();
218209
let validity = rhs_values.bit_buffer().bitand(&Op::absorb_bits(rhs.bits()));
219210
BoolVector::new(result_bits, Mask::from(validity))
220211
}
221212

222213
// LHS has specific validity, RHS is all null.
223214
(Mask::Values(lhs_values), Mask::AllFalse(_)) => {
224-
// The result vector is valid where the LHS is valid AND has an absorbing value.
225-
let result_bits = Op::absorbing_bits_from_len(len);
215+
// The result vector is valid where the LHS is valid AND has an absorbing value. Since
216+
// only absorbing values produce valid results, we can reuse the LHS bits directly.
217+
let result_bits = lhs.bits().clone();
226218
let validity = lhs_values.bit_buffer().bitand(&Op::absorb_bits(lhs.bits()));
227219
BoolVector::new(result_bits, Mask::from(validity))
228220
}
229221

230222
// Both sides have specific validity.
231223
(Mask::Values(lhs_values), Mask::Values(rhs_values)) => {
232-
// The result is valid at position i when:
224+
// The result is valid at position `i` iff:
233225
// 1. Both lhs[i] and rhs[i] are valid (standard case), OR
234226
// 2. lhs[i] is null but rhs[i] is valid AND has an absorbing value, OR
235227
// 3. rhs[i] is null but lhs[i] is valid AND has an absorbing value.
236228
//
237229
// Absorbing values in Kleene logic:
238230
// - AND: false absorbs null (false AND null = false).
239231
// - OR: true absorbs null (true OR null = true).
232+
//
233+
// This simplifies to the gosition is valid iff:
234+
// - (lhs_valid OR rhs_absorbs) AND
235+
// - (rhs_valid OR lhs_absorbs).
236+
//
237+
// In other words, each side must either be valid or have an absorbing value that
238+
// "covers" the other side's null.
240239
let result_bits = Op::bit_op(lhs.bits(), rhs.bits());
241240

242-
// Case 1: Both operands are valid at this position.
243-
let both_valid = lhs_values.bit_buffer().bitand(rhs_values.bit_buffer());
244-
245-
// Case 2: LHS is null, but RHS is valid with an absorbing value.
246-
let lhs_null_rhs_absorbs = lhs_values
247-
.bit_buffer()
248-
.not()
249-
.bitand(rhs_values.bit_buffer())
250-
.bitand(&Op::absorb_bits(rhs.bits()));
251-
252-
// Case 3: RHS is null, but LHS is valid with an absorbing value.
253-
let rhs_null_lhs_absorbs = rhs_values
254-
.bit_buffer()
255-
.not()
256-
.bitand(lhs_values.bit_buffer())
257-
.bitand(&Op::absorb_bits(lhs.bits()));
258-
259-
let validity = both_valid
260-
.bitor(&lhs_null_rhs_absorbs)
261-
.bitor(&rhs_null_lhs_absorbs);
241+
let lhs_valid_or_rhs_absorbs =
242+
lhs_values.bit_buffer().bitor(&Op::absorb_bits(rhs.bits()));
243+
let rhs_valid_or_lhs_absorbs =
244+
rhs_values.bit_buffer().bitor(&Op::absorb_bits(lhs.bits()));
245+
let validity = lhs_valid_or_rhs_absorbs.bitand(&rhs_valid_or_lhs_absorbs);
246+
262247
BoolVector::new(result_bits, Mask::from(validity))
263248
}
264249
}

0 commit comments

Comments
 (0)