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

vortex-data / vortex / 17042400864

18 Aug 2025 01:43PM UTC coverage: 88.046% (+0.1%) from 87.913%
17042400864

Pull #4177

github

web-flow
Merge c0b668f7f into abfc8534c
Pull Request #4177: feat: ArrayOperations infallible

1187 of 1273 new or added lines in 146 files covered. (93.24%)

19 existing lines in 13 files now uncovered.

56491 of 64161 relevant lines covered (88.05%)

622723.82 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};
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,856✔
26
    Ok(NAN_COUNT_FN
1,856✔
27
        .invoke(&InvocationArgs {
1,856✔
28
            inputs: &[array.into()],
1,856✔
29
            options: &(),
1,856✔
30
        })?
1,856✔
31
        .unwrap_scalar()?
1,856✔
32
        .as_primitive()
1,856✔
33
        .as_::<usize>()
1,856✔
34
        .vortex_expect("NaN count should not return null"))
1,856✔
35
}
1,856✔
36

37
struct NaNCount;
38

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

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

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

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

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

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

69
    fn is_elementwise(&self) -> bool {
1,857✔
70
        false
1,857✔
71
    }
1,857✔
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,869✔
93
        let UnaryArgs { array, .. } = UnaryArgs::<()>::try_from(args)?;
1,869✔
94
        let Some(array) = array.as_opt::<V>() else {
1,869✔
95
            return Ok(None);
671✔
96
        };
97
        let nan_count = V::nan_count(&self.0, array)?;
1,198✔
98
        Ok(Some(Scalar::from(nan_count as u64).into()))
1,198✔
99
    }
1,869✔
100
}
101

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

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

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

121
    for kernel in kernels {
2,755✔
122
        if let Some(output) = kernel.invoke(&args)? {
2,363✔
123
            return output
1,464✔
124
                .unwrap_scalar()?
1,464✔
125
                .as_primitive()
1,464✔
126
                .as_::<usize>()
1,464✔
127
                .ok_or_else(|| vortex_err!("NaN count should not return null"));
1,464✔
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,856✔
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