• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vortex-data / vortex / 17075133033

19 Aug 2025 04:01PM UTC coverage: 87.949% (+0.09%) from 87.856%
17075133033

push

github

web-flow
feat: ArrayOperations infallible, eager validation + new_unchecked (#4177)

ArrayOperations currently return VortexResult<>, but they really should
just be infallible. A failed array op is generally indicative of
programmer or encoding error. There's really nothing interesting we can
do to handle an out-of-bounds slice() or scalar_at.

There's a lot that falls out of this, like fixing a bunch of tests,
tweaking our scalar value casting to return Option instead of Result,
etc.

---------

Signed-off-by: Andrew Duffy <andrew@a10y.dev>

1744 of 1985 new or added lines in 195 files covered. (87.86%)

36 existing lines in 27 files now uncovered.

56745 of 64520 relevant lines covered (87.95%)

624082.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.27
/vortex-array/src/compute/nan_count.rs
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3

4
use std::sync::LazyLock;
5

6
use arcref::ArcRef;
7
use vortex_dtype::DType;
8
use vortex_error::{VortexExpect, VortexResult, vortex_bail, vortex_err};
9
use vortex_scalar::{Scalar, ScalarValue};
10

11
use crate::Array;
12
use crate::compute::{ComputeFn, ComputeFnVTable, InvocationArgs, Kernel, Output, UnaryArgs};
13
use crate::stats::{Precision, Stat, StatsProviderExt};
14
use crate::vtable::VTable;
15

16
static NAN_COUNT_FN: LazyLock<ComputeFn> = LazyLock::new(|| {
710✔
17
    let compute = ComputeFn::new("nan_count".into(), ArcRef::new_ref(&NaNCount));
710✔
18
    for kernel in inventory::iter::<NaNCountKernelRef> {
1,968✔
19
        compute.register_kernel(kernel.0.clone());
1,258✔
20
    }
1,258✔
21
    compute
710✔
22
});
710✔
23

24
/// Computes the number of NaN values in the array.
25
pub fn nan_count(array: &dyn Array) -> VortexResult<usize> {
1,776✔
26
    Ok(NAN_COUNT_FN
1,776✔
27
        .invoke(&InvocationArgs {
1,776✔
28
            inputs: &[array.into()],
1,776✔
29
            options: &(),
1,776✔
30
        })?
1,776✔
31
        .unwrap_scalar()?
1,776✔
32
        .as_primitive()
1,776✔
33
        .as_::<usize>()
1,776✔
34
        .vortex_expect("NaN count should not return null"))
1,776✔
35
}
1,776✔
36

37
struct NaNCount;
38

39
impl ComputeFnVTable for NaNCount {
40
    fn invoke(
1,776✔
41
        &self,
1,776✔
42
        args: &InvocationArgs,
1,776✔
43
        kernels: &[ArcRef<dyn Kernel>],
1,776✔
44
    ) -> VortexResult<Output> {
1,776✔
45
        let UnaryArgs { array, .. } = UnaryArgs::<()>::try_from(args)?;
1,776✔
46

47
        let nan_count = nan_count_impl(array, kernels)?;
1,776✔
48

49
        // Update the stats set with the computed NaN count
50
        array.statistics().set(
1,776✔
51
            Stat::NaNCount,
1,776✔
52
            Precision::Exact(ScalarValue::from(nan_count as u64)),
1,776✔
53
        );
54

55
        Ok(Scalar::from(nan_count as u64).into())
1,776✔
56
    }
1,776✔
57

58
    fn return_dtype(&self, args: &InvocationArgs) -> VortexResult<DType> {
1,776✔
59
        let UnaryArgs { array, .. } = UnaryArgs::<()>::try_from(args)?;
1,776✔
60
        Stat::NaNCount
1,776✔
61
            .dtype(array.dtype())
1,776✔
62
            .ok_or_else(|| vortex_err!("Cannot compute NaN count for dtype {}", array.dtype()))
1,776✔
63
    }
1,776✔
64

65
    fn return_len(&self, _args: &InvocationArgs) -> VortexResult<usize> {
1,776✔
66
        Ok(1)
1,776✔
67
    }
1,776✔
68

69
    fn is_elementwise(&self) -> bool {
1,777✔
70
        false
1,777✔
71
    }
1,777✔
72
}
73

74
/// Computes the min and max of an array, returning the (min, max) values
75
pub trait NaNCountKernel: VTable {
76
    fn nan_count(&self, array: &Self::Array) -> VortexResult<usize>;
77
}
78

79
pub struct NaNCountKernelRef(ArcRef<dyn Kernel>);
80
inventory::collect!(NaNCountKernelRef);
81

82
#[derive(Debug)]
83
pub struct NaNCountKernelAdapter<V: VTable>(pub V);
84

85
impl<V: VTable + NaNCountKernel> NaNCountKernelAdapter<V> {
86
    pub const fn lift(&'static self) -> NaNCountKernelRef {
×
87
        NaNCountKernelRef(ArcRef::new_ref(self))
×
88
    }
×
89
}
90

91
impl<V: VTable + NaNCountKernel> Kernel for NaNCountKernelAdapter<V> {
92
    fn invoke(&self, args: &InvocationArgs) -> VortexResult<Option<Output>> {
1,789✔
93
        let UnaryArgs { array, .. } = UnaryArgs::<()>::try_from(args)?;
1,789✔
94
        let Some(array) = array.as_opt::<V>() else {
1,789✔
95
            return Ok(None);
671✔
96
        };
97
        let nan_count = V::nan_count(&self.0, array)?;
1,118✔
98
        Ok(Some(Scalar::from(nan_count as u64).into()))
1,118✔
99
    }
1,789✔
100
}
101

102
fn nan_count_impl(array: &dyn Array, kernels: &[ArcRef<dyn Kernel>]) -> VortexResult<usize> {
1,776✔
103
    if array.is_empty() || array.valid_count()? == 0 {
1,776✔
104
        return Ok(0);
×
105
    }
1,776✔
106

107
    if let Some(nan_count) = array
1,776✔
108
        .statistics()
1,776✔
109
        .get_as::<usize>(Stat::NaNCount)
1,776✔
110
        .and_then(Precision::as_exact)
1,776✔
111
    {
112
        // If the NaN count is already computed, return it
113
        return Ok(nan_count);
×
114
    }
1,776✔
115

116
    let args = InvocationArgs {
1,776✔
117
        inputs: &[array.into()],
1,776✔
118
        options: &(),
1,776✔
119
    };
1,776✔
120

121
    for kernel in kernels {
2,675✔
122
        if let Some(output) = kernel.invoke(&args)? {
2,283✔
123
            return output
1,384✔
124
                .unwrap_scalar()?
1,384✔
125
                .as_primitive()
1,384✔
126
                .as_::<usize>()
1,384✔
127
                .ok_or_else(|| vortex_err!("NaN count should not return null"));
1,384✔
128
        }
899✔
129
    }
130
    if let Some(output) = array.invoke(&NAN_COUNT_FN, &args)? {
392✔
131
        return output
×
132
            .unwrap_scalar()?
×
133
            .as_primitive()
×
NEW
134
            .as_::<usize>()
×
135
            .ok_or_else(|| vortex_err!("NaN count should not return null"));
×
136
    }
392✔
137

138
    if !array.is_canonical() {
392✔
139
        let canonical = array.to_canonical()?;
392✔
140
        return nan_count(canonical.as_ref());
392✔
141
    }
×
142

143
    vortex_bail!(
×
144
        "No NaN count kernel found for array type: {}",
×
145
        array.dtype()
×
146
    )
147
}
1,776✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc