Skip to content

Commit c6a1940

Browse files
Matt KatzMatt Katz
authored andcommitted
add tests and remove from filter
1 parent 7e40e81 commit c6a1940

3 files changed

Lines changed: 143 additions & 2 deletions

File tree

vortex-array/src/arrays/filter/execute/listview.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ use vortex_mask::MaskValues;
99

1010
use crate::arrays::ListViewArray;
1111
use crate::arrays::filter::execute::filter_validity;
12-
use crate::arrays::listview;
1312
use crate::arrays::listview::ListViewArrayExt;
14-
use crate::arrays::listview::ListViewRebuildMode;
1513

1614
/// [`ListViewArray`] filter implementation.
1715
///
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
//! Tests for `compute_referenced_elements_mask`, `compute_density`, and
5+
//! `estimate_density` on `ListViewArray`.
6+
7+
use vortex_buffer::buffer;
8+
use vortex_error::VortexResult;
9+
use vortex_mask::Mask;
10+
11+
use super::common::create_basic_listview;
12+
use super::common::create_empty_lists_listview;
13+
use super::common::create_large_listview;
14+
use super::common::create_overlapping_listview;
15+
use crate::IntoArray;
16+
use crate::arrays::ListViewArray;
17+
use crate::arrays::PrimitiveArray;
18+
use crate::arrays::listview::ListViewArrayExt;
19+
use crate::expr::stats::Precision;
20+
use crate::expr::stats::Stat;
21+
use crate::scalar::ScalarValue;
22+
use crate::validity::Validity;
23+
24+
const EPS: f32 = 1e-6;
25+
26+
#[test]
27+
fn full_density_no_overlap() -> VortexResult<()> {
28+
let lv = create_basic_listview();
29+
let exact = lv.compute_density().expect("non-empty elements");
30+
let est = lv.estimate_density()?.expect("non-empty elements");
31+
32+
assert!((exact - 1.0).abs() < EPS, "exact density {exact}");
33+
assert!((est - 1.0).abs() < EPS, "estimate density {est}");
34+
Ok(())
35+
}
36+
37+
#[test]
38+
fn sparse_no_overlap_matches_exact() -> VortexResult<()> {
39+
let lv = create_large_listview();
40+
let exact = lv.compute_density().expect("non-empty");
41+
let est = lv.estimate_density()?.expect("non-empty");
42+
43+
assert!((exact - 0.5).abs() < EPS, "exact density {exact}");
44+
assert!((est - 0.5).abs() < EPS, "estimate density {est}");
45+
Ok(())
46+
}
47+
48+
#[test]
49+
fn all_empty_lists_is_zero_density() -> VortexResult<()> {
50+
let lv = create_empty_lists_listview();
51+
let exact = lv.compute_density().expect("elements has length 1");
52+
let est = lv.estimate_density()?.expect("elements has length 1");
53+
54+
assert_eq!(exact, 0.0);
55+
assert_eq!(est, 0.0);
56+
Ok(())
57+
}
58+
59+
#[test]
60+
fn overlap_full_coverage_clamps_estimate() -> VortexResult<()> {
61+
let lv = create_overlapping_listview();
62+
let exact = lv.compute_density().expect("non-empty");
63+
let est = lv.estimate_density()?.expect("non-empty");
64+
65+
assert!((exact - 1.0).abs() < EPS, "exact density {exact}");
66+
assert!((est - 1.0).abs() < EPS, "estimate density {est}");
67+
Ok(())
68+
}
69+
70+
#[test]
71+
fn overlap_differential_exact_lower_than_estimate() -> VortexResult<()> {
72+
// Two rows both pointing at elements[0..5) over a 20-element buffer.
73+
// Unique referenced = 5 → exact = 0.25
74+
// sum(sizes) = 10 → estimate = 0.50
75+
let elements = PrimitiveArray::from_iter(0i32..20).into_array();
76+
let offsets = buffer![0u32, 0].into_array();
77+
let sizes = buffer![5u32, 5].into_array();
78+
let lv = ListViewArray::try_new(elements, offsets, sizes, Validity::NonNullable)?;
79+
80+
let exact = lv.compute_density().expect("non-empty");
81+
let est = lv.estimate_density()?.expect("non-empty");
82+
83+
assert!((exact - 0.25).abs() < EPS, "exact density {exact}");
84+
assert!((est - 0.50).abs() < EPS, "estimate density {est}");
85+
assert!(est > exact, "estimate must overcount overlapping views");
86+
Ok(())
87+
}
88+
89+
#[test]
90+
fn empty_elements_returns_none() -> VortexResult<()> {
91+
let elements = PrimitiveArray::from_iter::<[i32; 0]>([]).into_array();
92+
let offsets = buffer![0u32; 0].into_array();
93+
let sizes = buffer![0u32; 0].into_array();
94+
let lv = ListViewArray::try_new(elements, offsets, sizes, Validity::NonNullable)?;
95+
96+
assert!(lv.compute_density().is_none());
97+
assert!(lv.estimate_density()?.is_none());
98+
Ok(())
99+
}
100+
101+
#[test]
102+
fn estimate_uses_cached_sum_stat() -> VortexResult<()> {
103+
let lv = create_basic_listview();
104+
// Pre-populate Stat::Sum with a deliberately-wrong 5 so we can prove
105+
// estimate_density reads from the cache instead of computing fresh.
106+
lv.sizes()
107+
.statistics()
108+
.set(Stat::Sum, Precision::Exact(ScalarValue::from(5u64)));
109+
110+
let est = lv.estimate_density()?.expect("non-empty");
111+
assert!(
112+
(est - 0.5).abs() < EPS,
113+
"estimate {est} should reflect cached Sum=5, not computed Sum=10",
114+
);
115+
Ok(())
116+
}
117+
118+
#[test]
119+
fn referenced_mask_set_bits_match_views() -> VortexResult<()> {
120+
// create_large_listview: 10 lists of size 50 at offsets [0,100,200,...,900].
121+
// Bits [i*100 .. i*100+50) set, the rest unset.
122+
let lv = create_large_listview();
123+
let mask = lv
124+
.compute_referenced_elements_mask()
125+
.expect("non-empty elements");
126+
let bits = match mask {
127+
Mask::Values(v) => v,
128+
other => panic!("expected Values mask for partial coverage, got {other:?}"),
129+
};
130+
131+
assert_eq!(bits.true_count(), 500);
132+
// Spot-check the boundaries of the first and last views.
133+
let bb = bits.bit_buffer();
134+
assert!(bb.value(0));
135+
assert!(bb.value(49));
136+
assert!(!bb.value(50));
137+
assert!(!bb.value(99));
138+
assert!(bb.value(100));
139+
assert!(bb.value(949));
140+
assert!(!bb.value(950));
141+
Ok(())
142+
}

vortex-array/src/arrays/listview/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
pub(super) mod common;
55

66
mod basic;
7+
mod density;
78
mod filter;
89
mod nested;
910
mod nullability;

0 commit comments

Comments
 (0)