vortex_array/expr/exprs/
not.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use std::fmt::Formatter;
5
6use vortex_compute::logical::LogicalNot;
7use vortex_dtype::DType;
8use vortex_error::VortexExpect;
9use vortex_error::VortexResult;
10use vortex_error::vortex_bail;
11use vortex_vector::Vector;
12
13use crate::ArrayRef;
14use crate::compute::invert;
15use crate::expr::ChildName;
16use crate::expr::ExecutionArgs;
17use crate::expr::ExprId;
18use crate::expr::Expression;
19use crate::expr::ExpressionView;
20use crate::expr::ScalarFnExprExt;
21use crate::expr::VTable;
22use crate::expr::VTableExt;
23use crate::expr::functions::EmptyOptions;
24use crate::scalar_fns::not;
25
26/// Expression that logically inverts boolean values.
27pub struct Not;
28
29impl VTable for Not {
30    type Instance = ();
31
32    fn id(&self) -> ExprId {
33        ExprId::new_ref("vortex.not")
34    }
35
36    fn serialize(&self, _instance: &Self::Instance) -> VortexResult<Option<Vec<u8>>> {
37        Ok(Some(vec![]))
38    }
39
40    fn deserialize(&self, _metadata: &[u8]) -> VortexResult<Option<Self::Instance>> {
41        Ok(Some(()))
42    }
43
44    fn validate(&self, expr: &ExpressionView<Self>) -> VortexResult<()> {
45        if expr.children().len() != 1 {
46            vortex_bail!(
47                "Not expression expects exactly one child, got {}",
48                expr.children().len()
49            );
50        }
51        Ok(())
52    }
53
54    fn child_name(&self, _instance: &Self::Instance, child_idx: usize) -> ChildName {
55        match child_idx {
56            0 => ChildName::from("input"),
57            _ => unreachable!("Invalid child index {} for Not expression", child_idx),
58        }
59    }
60
61    fn fmt_sql(&self, expr: &ExpressionView<Self>, f: &mut Formatter<'_>) -> std::fmt::Result {
62        write!(f, "not(")?;
63        expr.child(0).fmt_sql(f)?;
64        write!(f, ")")
65    }
66
67    fn return_dtype(&self, expr: &ExpressionView<Self>, scope: &DType) -> VortexResult<DType> {
68        let child_dtype = expr.child(0).return_dtype(scope)?;
69        if !matches!(child_dtype, DType::Bool(_)) {
70            vortex_bail!(
71                "Not expression expects a boolean child, got: {}",
72                child_dtype
73            );
74        }
75        Ok(child_dtype)
76    }
77
78    fn evaluate(&self, expr: &ExpressionView<Self>, scope: &ArrayRef) -> VortexResult<ArrayRef> {
79        let child_result = expr.child(0).evaluate(scope)?;
80        invert(&child_result)
81    }
82
83    fn execute(&self, _data: &Self::Instance, mut args: ExecutionArgs) -> VortexResult<Vector> {
84        let child = args.vectors.pop().vortex_expect("Missing input child");
85        Ok(child.into_bool().not().into())
86    }
87
88    fn is_null_sensitive(&self, _instance: &Self::Instance) -> bool {
89        false
90    }
91
92    fn is_fallible(&self, _instance: &Self::Instance) -> bool {
93        false
94    }
95
96    fn expr_v2(&self, view: &ExpressionView<Self>) -> VortexResult<Expression> {
97        ScalarFnExprExt::try_new_expr(&not::NotFn, EmptyOptions, view.children().clone())
98    }
99}
100
101/// Creates an expression that logically inverts boolean values.
102///
103/// Returns the logical negation of the input boolean expression.
104///
105/// ```rust
106/// # use vortex_array::expr::{not, root};
107/// let expr = not(root());
108/// ```
109pub fn not(operand: Expression) -> Expression {
110    Not.new_expr((), vec![operand])
111}
112
113#[cfg(test)]
114mod tests {
115    use vortex_dtype::DType;
116    use vortex_dtype::Nullability;
117
118    use super::not;
119    use crate::ToCanonical;
120    use crate::arrays::BoolArray;
121    use crate::expr::exprs::get_item::col;
122    use crate::expr::exprs::get_item::get_item;
123    use crate::expr::exprs::root::root;
124    use crate::expr::test_harness;
125
126    #[test]
127    fn invert_booleans() {
128        let not_expr = not(root());
129        let bools = BoolArray::from_iter([false, true, false, false, true, true]);
130        assert_eq!(
131            not_expr
132                .evaluate(&bools.to_array())
133                .unwrap()
134                .to_bool()
135                .bit_buffer()
136                .iter()
137                .collect::<Vec<_>>(),
138            vec![true, false, true, true, false, false]
139        );
140    }
141
142    #[test]
143    fn test_display_order_of_operations() {
144        let a = not(get_item("a", root()));
145        let b = get_item("a", not(root()));
146        assert_ne!(a.to_string(), b.to_string());
147        assert_eq!(a.to_string(), "not($.a)");
148        assert_eq!(b.to_string(), "not($).a");
149    }
150
151    #[test]
152    fn dtype() {
153        let not_expr = not(root());
154        let dtype = DType::Bool(Nullability::NonNullable);
155        assert_eq!(
156            not_expr.return_dtype(&dtype).unwrap(),
157            DType::Bool(Nullability::NonNullable)
158        );
159
160        let dtype = test_harness::struct_dtype();
161        assert_eq!(
162            not(col("bool1")).return_dtype(&dtype).unwrap(),
163            DType::Bool(Nullability::NonNullable)
164        );
165    }
166}