vortex_array/aggregate_fn/fns/all_non_nan/
mod.rs1use vortex_error::VortexResult;
5use vortex_session::VortexSession;
6use vortex_session::registry::CachedId;
7
8use crate::ArrayRef;
9use crate::Columnar;
10use crate::ExecutionCtx;
11use crate::IntoArray;
12use crate::aggregate_fn::AggregateArgs;
13use crate::aggregate_fn::AggregateFnId;
14use crate::aggregate_fn::AggregateFnVTable;
15use crate::aggregate_fn::EmptyOptions;
16use crate::aggregate_fn::fns::nan_count::nan_count;
17use crate::dtype::DType;
18use crate::dtype::Nullability;
19use crate::scalar::Scalar;
20
21#[derive(Clone, Debug)]
32pub struct AllNonNan;
33
34impl AggregateFnVTable for AllNonNan {
35 type Options = EmptyOptions;
36 type Partial = bool;
37
38 fn id(&self) -> AggregateFnId {
39 static ID: CachedId = CachedId::new("vortex.all_non_nan");
40 *ID
41 }
42
43 fn serialize(&self, _options: &Self::Options) -> VortexResult<Option<Vec<u8>>> {
44 Ok(Some(vec![]))
45 }
46
47 fn deserialize(
48 &self,
49 _metadata: &[u8],
50 _session: &VortexSession,
51 ) -> VortexResult<Self::Options> {
52 Ok(EmptyOptions)
53 }
54
55 fn return_dtype(&self, _options: &Self::Options, input_dtype: &DType) -> Option<DType> {
56 matches!(input_dtype, DType::Primitive(ptype, _) if ptype.is_float())
57 .then_some(DType::Bool(Nullability::Nullable))
58 }
59
60 fn partial_dtype(&self, options: &Self::Options, input_dtype: &DType) -> Option<DType> {
61 self.return_dtype(options, input_dtype)
62 }
63
64 fn empty_partial(
65 &self,
66 _args: AggregateArgs<'_, Self::Options>,
67 ) -> VortexResult<Self::Partial> {
68 Ok(true)
69 }
70
71 fn partial_from_scalar(
72 &self,
73 _args: AggregateArgs<'_, Self::Options>,
74 scalar: &Scalar,
75 ) -> VortexResult<Self::Partial> {
76 bool::try_from(scalar)
77 }
78
79 fn merge_partials(
80 &self,
81 _args: AggregateArgs<'_, Self::Options>,
82 first: Self::Partial,
83 second: Self::Partial,
84 ) -> VortexResult<Self::Partial> {
85 Ok(first && second)
86 }
87
88 fn to_scalar(
89 &self,
90 _args: AggregateArgs<'_, Self::Options>,
91 partial: &Self::Partial,
92 ) -> VortexResult<Scalar> {
93 Ok(Scalar::bool(*partial, Nullability::Nullable))
94 }
95
96 fn is_saturated(
97 &self,
98 _args: AggregateArgs<'_, Self::Options>,
99 partial: &Self::Partial,
100 ) -> bool {
101 !*partial
102 }
103
104 fn try_accumulate(
105 &self,
106 _args: AggregateArgs<'_, Self::Options>,
107 state: &mut Self::Partial,
108 batch: &ArrayRef,
109 ctx: &mut ExecutionCtx,
110 ) -> VortexResult<bool> {
111 *state &= nan_count(batch, ctx)? == 0;
112 Ok(true)
113 }
114
115 fn accumulate(
116 &self,
117 _args: AggregateArgs<'_, Self::Options>,
118 partial: &mut Self::Partial,
119 batch: &Columnar,
120 ctx: &mut ExecutionCtx,
121 ) -> VortexResult<()> {
122 let array = match batch {
125 Columnar::Constant(c) => c.clone().into_array(),
126 Columnar::Canonical(c) => c.clone().into_array(),
127 };
128 *partial &= nan_count(&array, ctx)? == 0;
129 Ok(())
130 }
131
132 fn finalize(
133 &self,
134 _args: AggregateArgs<'_, Self::Options>,
135 partials: ArrayRef,
136 ) -> VortexResult<ArrayRef> {
137 Ok(partials)
138 }
139
140 fn finalize_scalar(
141 &self,
142 args: AggregateArgs<'_, Self::Options>,
143 partial: &Self::Partial,
144 ) -> VortexResult<Scalar> {
145 self.to_scalar(args, partial)
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use vortex_error::VortexResult;
152
153 use crate::IntoArray;
154 use crate::VortexSessionExecute;
155 use crate::aggregate_fn::Accumulator;
156 use crate::aggregate_fn::DynAccumulator;
157 use crate::aggregate_fn::EmptyOptions;
158 use crate::aggregate_fn::fns::all_non_nan::AllNonNan;
159 use crate::array_session;
160 use crate::arrays::PrimitiveArray;
161 use crate::dtype::DType;
162 use crate::dtype::Nullability;
163 use crate::dtype::PType;
164
165 #[test]
166 fn all_non_nan_aggregate_fn() -> VortexResult<()> {
167 let mut ctx = array_session().create_execution_ctx();
168 let dtype = DType::Primitive(PType::F32, Nullability::Nullable);
169 let mut acc = Accumulator::try_new(AllNonNan, EmptyOptions, dtype)?;
170
171 let batch = PrimitiveArray::from_option_iter([Some(1.0f32), None, Some(3.0)]).into_array();
172 acc.accumulate(&batch, &mut ctx)?;
173
174 assert!(bool::try_from(&acc.finish()?)?);
175 Ok(())
176 }
177
178 #[test]
179 fn all_non_nan_false_with_nan() -> VortexResult<()> {
180 let mut ctx = array_session().create_execution_ctx();
181 let dtype = DType::Primitive(PType::F32, Nullability::Nullable);
182 let mut acc = Accumulator::try_new(AllNonNan, EmptyOptions, dtype)?;
183
184 let batch = PrimitiveArray::from_option_iter([Some(1.0f32), Some(f32::NAN)]).into_array();
185 acc.accumulate(&batch, &mut ctx)?;
186
187 assert!(!bool::try_from(&acc.finish()?)?);
188 Ok(())
189 }
190
191 #[test]
192 fn all_non_nan_unsupported_for_non_float() -> VortexResult<()> {
193 let dtype = DType::Primitive(PType::I32, Nullability::Nullable);
194 assert!(Accumulator::try_new(AllNonNan, EmptyOptions, dtype).is_err());
195 Ok(())
196 }
197
198 #[test]
199 fn all_non_nan_true_for_empty_float() -> VortexResult<()> {
200 let dtype = DType::Primitive(PType::F32, Nullability::Nullable);
201 let mut acc = Accumulator::try_new(AllNonNan, EmptyOptions, dtype)?;
202
203 assert!(bool::try_from(&acc.finish()?)?);
204 Ok(())
205 }
206
207 #[test]
208 fn all_non_nan_true_with_nulls() -> VortexResult<()> {
209 let mut ctx = array_session().create_execution_ctx();
210 let dtype = DType::Primitive(PType::F32, Nullability::Nullable);
211 let mut acc = Accumulator::try_new(AllNonNan, EmptyOptions, dtype)?;
212
213 let batch = PrimitiveArray::from_option_iter([Some(1.0f32), None]).into_array();
214 acc.accumulate(&batch, &mut ctx)?;
215
216 assert!(bool::try_from(&acc.finish()?)?);
217 Ok(())
218 }
219}