vortex_array/arrays/scalar_fn/
rules.rs1use itertools::Itertools;
5use vortex_error::VortexResult;
6
7use crate::ArrayRef;
8use crate::IntoArray;
9use crate::array::ArrayView;
10use crate::arrays::Constant;
11use crate::arrays::ConstantArray;
12use crate::arrays::Filter;
13use crate::arrays::ScalarFn;
14use crate::arrays::ScalarFnArray;
15use crate::arrays::Slice;
16use crate::arrays::StructArray;
17use crate::arrays::scalar_fn::ScalarFnArrayExt;
18use crate::optimizer::rules::ArrayParentReduceRule;
19use crate::optimizer::rules::ArrayReduceRule;
20use crate::optimizer::rules::ParentRuleSet;
21use crate::optimizer::rules::ReduceRuleSet;
22use crate::scalar_fn::ArrayReduceNode;
23use crate::scalar_fn::fns::pack::Pack;
24use crate::validity::Validity;
25
26pub(super) const RULES: ReduceRuleSet<ScalarFn> =
27 ReduceRuleSet::new(&[&ScalarFnPackToStructRule, &ScalarFnAbstractReduceRule]);
28
29pub(super) const PARENT_RULES: ParentRuleSet<ScalarFn> = ParentRuleSet::new(&[
30 ParentRuleSet::lift(&ScalarFnUnaryFilterPushDownRule),
31 ParentRuleSet::lift(&ScalarFnSliceReduceRule),
32]);
33
34#[derive(Debug)]
36struct ScalarFnPackToStructRule;
37impl ArrayReduceRule<ScalarFn> for ScalarFnPackToStructRule {
38 fn reduce(&self, array: ArrayView<'_, ScalarFn>) -> VortexResult<Option<ArrayRef>> {
39 let Some(pack_options) = array.scalar_fn().as_opt::<Pack>() else {
40 return Ok(None);
41 };
42
43 let validity = match pack_options.nullability {
44 crate::dtype::Nullability::NonNullable => Validity::NonNullable,
45 crate::dtype::Nullability::Nullable => Validity::AllValid,
46 };
47
48 Ok(Some(
49 StructArray::try_new(
50 pack_options.names.clone(),
51 array.children(),
52 array.len(),
53 validity,
54 )?
55 .into_array(),
56 ))
57 }
58}
59
60#[derive(Debug)]
61struct ScalarFnSliceReduceRule;
62impl ArrayParentReduceRule<ScalarFn> for ScalarFnSliceReduceRule {
63 type Parent = Slice;
64
65 fn reduce_parent(
66 &self,
67 array: ArrayView<'_, ScalarFn>,
68 parent: ArrayView<'_, Slice>,
69 _child_idx: usize,
70 ) -> VortexResult<Option<ArrayRef>> {
71 let range = parent.slice_range();
72
73 let children: Vec<_> = array
74 .iter_children()
75 .map(|c| c.slice(range.clone()))
76 .collect::<VortexResult<_>>()?;
77
78 Ok(Some(
79 ScalarFnArray::try_new_with_len(array.scalar_fn().clone(), children, range.len())?
80 .into_array(),
81 ))
82 }
83}
84
85#[derive(Debug)]
86struct ScalarFnAbstractReduceRule;
87impl ArrayReduceRule<ScalarFn> for ScalarFnAbstractReduceRule {
88 fn reduce(&self, array: ArrayView<'_, ScalarFn>) -> VortexResult<Option<ArrayRef>> {
89 let node = ArrayReduceNode::new(array.as_ref());
90 if let Some(reduced) = array.scalar_fn().reduce_array(&node)? {
91 return Ok(Some(reduced.into_array()));
92 }
93 Ok(None)
94 }
95}
96
97#[derive(Debug)]
98struct ScalarFnUnaryFilterPushDownRule;
99
100impl ArrayParentReduceRule<ScalarFn> for ScalarFnUnaryFilterPushDownRule {
101 type Parent = Filter;
102
103 fn reduce_parent(
104 &self,
105 child: ArrayView<'_, ScalarFn>,
106 parent: ArrayView<'_, Filter>,
107 _child_idx: usize,
108 ) -> VortexResult<Option<ArrayRef>> {
109 if child
112 .iter_children()
113 .filter(|c| !c.is::<Constant>())
114 .count()
115 == 1
116 {
117 let new_children: Vec<_> = child
118 .iter_children()
119 .map(|c| match c.as_opt::<Constant>() {
120 Some(array) => {
121 Ok(ConstantArray::new(array.scalar().clone(), parent.len()).into_array())
122 }
123 None => c.filter(parent.filter_mask().clone()),
124 })
125 .try_collect()?;
126
127 let new_array =
128 ScalarFnArray::try_new(child.scalar_fn().clone(), new_children)?.into_array();
129
130 return Ok(Some(new_array));
131 }
132
133 Ok(None)
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use vortex_error::VortexExpect;
140
141 use crate::array::IntoArray;
142 use crate::arrays::ChunkedArray;
143 use crate::arrays::PrimitiveArray;
144 use crate::arrays::scalar_fn::rules::ConstantArray;
145 use crate::dtype::DType;
146 use crate::dtype::Nullability;
147 use crate::dtype::PType;
148 use crate::expr::cast;
149 use crate::expr::is_null;
150 use crate::expr::root;
151
152 #[test]
153 fn test_empty_constants() {
154 let array = ChunkedArray::try_new(
155 vec![
156 ConstantArray::new(Some(1u64), 0).into_array(),
157 PrimitiveArray::from_iter(vec![2u64])
158 .into_array()
159 .apply(&cast(
160 root(),
161 DType::Primitive(PType::U64, Nullability::Nullable),
162 ))
163 .vortex_expect("casted"),
164 ],
165 DType::Primitive(PType::U64, Nullability::Nullable),
166 )
167 .vortex_expect("construction")
168 .into_array();
169
170 let expr = is_null(root());
171 array.apply(&expr).vortex_expect("expr evaluation");
172 }
173}