vortex_array/aggregate_fn/fns/all_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::aggregate_fn::AggregateArgs;
12use crate::aggregate_fn::AggregateFnId;
13use crate::aggregate_fn::AggregateFnVTable;
14use crate::aggregate_fn::EmptyOptions;
15use crate::aggregate_fn::fns::nan_count::columnar_nan_count;
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 AllNan;
33
34impl AggregateFnVTable for AllNan {
35 type Options = EmptyOptions;
36 type Partial = bool;
37
38 fn id(&self) -> AggregateFnId {
39 static ID: CachedId = CachedId::new("vortex.all_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 if !matches!(batch.dtype(), DType::Primitive(ptype, _) if ptype.is_float()) {
112 *state = false;
113 return Ok(true);
114 }
115
116 *state &= nan_count(batch, ctx)? == batch.len();
117 Ok(true)
118 }
119
120 fn accumulate(
121 &self,
122 _args: AggregateArgs<'_, Self::Options>,
123 partial: &mut Self::Partial,
124 batch: &Columnar,
125 ctx: &mut ExecutionCtx,
126 ) -> VortexResult<()> {
127 if !matches!(batch.dtype(), DType::Primitive(ptype, _) if ptype.is_float()) {
130 *partial = false;
131 return Ok(());
132 }
133
134 *partial &= columnar_nan_count(batch, ctx)? == batch.len() as u64;
135 Ok(())
136 }
137
138 fn finalize(
139 &self,
140 _args: AggregateArgs<'_, Self::Options>,
141 partials: ArrayRef,
142 ) -> VortexResult<ArrayRef> {
143 Ok(partials)
144 }
145
146 fn finalize_scalar(
147 &self,
148 args: AggregateArgs<'_, Self::Options>,
149 partial: &Self::Partial,
150 ) -> VortexResult<Scalar> {
151 self.to_scalar(args, partial)
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use vortex_error::VortexResult;
158
159 use crate::IntoArray;
160 use crate::VortexSessionExecute;
161 use crate::aggregate_fn::Accumulator;
162 use crate::aggregate_fn::DynAccumulator;
163 use crate::aggregate_fn::EmptyOptions;
164 use crate::aggregate_fn::fns::all_nan::AllNan;
165 use crate::array_session;
166 use crate::arrays::PrimitiveArray;
167 use crate::dtype::DType;
168 use crate::dtype::Nullability;
169 use crate::dtype::PType;
170
171 #[test]
172 fn all_nan_aggregate_fn() -> VortexResult<()> {
173 let mut ctx = array_session().create_execution_ctx();
174 let dtype = DType::Primitive(PType::F32, Nullability::Nullable);
175 let mut acc = Accumulator::try_new(AllNan, EmptyOptions, dtype)?;
176
177 let batch = PrimitiveArray::from_option_iter([Some(f32::NAN), Some(f32::NAN)]).into_array();
178 acc.accumulate(&batch, &mut ctx)?;
179
180 assert!(bool::try_from(&acc.finish()?)?);
181 Ok(())
182 }
183
184 #[test]
185 fn all_nan_false_with_non_nan() -> VortexResult<()> {
186 let mut ctx = array_session().create_execution_ctx();
187 let dtype = DType::Primitive(PType::F32, Nullability::Nullable);
188 let mut acc = Accumulator::try_new(AllNan, EmptyOptions, dtype)?;
189
190 let batch = PrimitiveArray::from_option_iter([Some(f32::NAN), Some(1.0f32)]).into_array();
191 acc.accumulate(&batch, &mut ctx)?;
192
193 assert!(!bool::try_from(&acc.finish()?)?);
194 Ok(())
195 }
196
197 #[test]
198 fn all_nan_unsupported_for_non_float_values() -> VortexResult<()> {
199 let dtype = DType::Primitive(PType::I32, Nullability::Nullable);
200 assert!(Accumulator::try_new(AllNan, EmptyOptions, dtype).is_err());
201 Ok(())
202 }
203
204 #[test]
205 fn all_nan_false_with_null() -> VortexResult<()> {
206 let mut ctx = array_session().create_execution_ctx();
207 let dtype = DType::Primitive(PType::F32, Nullability::Nullable);
208 let mut acc = Accumulator::try_new(AllNan, EmptyOptions, dtype)?;
209
210 let batch = PrimitiveArray::from_option_iter([Some(f32::NAN), None]).into_array();
211 acc.accumulate(&batch, &mut ctx)?;
212
213 assert!(!bool::try_from(&acc.finish()?)?);
214 Ok(())
215 }
216
217 #[test]
218 fn all_nan_true_for_empty_float_values() -> VortexResult<()> {
219 let dtype = DType::Primitive(PType::F32, Nullability::Nullable);
220 let mut acc = Accumulator::try_new(AllNan, EmptyOptions, dtype)?;
221
222 assert!(bool::try_from(&acc.finish()?)?);
223 Ok(())
224 }
225}