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

vortex-data / vortex / 16605658511

29 Jul 2025 07:34PM UTC coverage: 82.482% (-0.2%) from 82.684%
16605658511

Pull #4057

github

web-flow
Merge 68833a275 into 6fb0f3e49
Pull Request #4057: feat: `ArrayEquals` kernel

0 of 134 new or added lines in 2 files covered. (0.0%)

52 existing lines in 1 file now uncovered.

45215 of 54818 relevant lines covered (82.48%)

184335.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/vortex-array/src/compute/array_equals.rs
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3

4
use std::any::Any;
5
use std::sync::LazyLock;
6

7
use arcref::ArcRef;
8
use vortex_dtype::{DType, Nullability};
9
use vortex_error::{VortexError, VortexExpect, VortexResult, vortex_bail, vortex_err};
10
use vortex_scalar::Scalar;
11

12
use crate::compute::{ComputeFn, ComputeFnVTable, InvocationArgs, Kernel, Options, Output, compare, Operator};
13
use crate::stats::{Precision, Stat, StatsProvider};
14
use crate::vtable::VTable;
15
use crate::{Array, ArrayRef, Canonical, IntoArray};
16

NEW
17
pub fn array_equals(left: &dyn Array, right: &dyn Array) -> VortexResult<bool> {
×
NEW
18
    array_equals_opts(left, right, false)
×
NEW
19
}
×
20

NEW
21
pub fn array_equals_opts(left: &dyn Array, right: &dyn Array, ignore_nullability: bool) -> VortexResult<bool> {
×
NEW
22
    Ok(ARRAY_EQUALS_FN
×
NEW
23
        .invoke(&InvocationArgs {
×
NEW
24
            inputs: &[left.into(), right.into()],
×
NEW
25
            options: &ArrayEqualsOptions { ignore_nullability },
×
NEW
26
        })?
×
NEW
27
        .unwrap_scalar()?
×
NEW
28
        .as_bool()
×
NEW
29
        .value()
×
NEW
30
        .vortex_expect("non-nullable"))
×
NEW
31
}
×
32

33
#[derive(Clone, Copy)]
34
struct ArrayEqualsOptions {
35
    ignore_nullability: bool,
36
}
37

38
impl Options for ArrayEqualsOptions {
NEW
39
    fn as_any(&self) -> &dyn Any {
×
NEW
40
        self
×
NEW
41
    }
×
42
}
43

NEW
44
pub static ARRAY_EQUALS_FN: LazyLock<ComputeFn> = LazyLock::new(|| {
×
NEW
45
    let compute = ComputeFn::new("array_equals".into(), ArcRef::new_ref(&ArrayEquals));
×
NEW
46
    for kernel in inventory::iter::<ArrayEqualsKernelRef> {
×
NEW
47
        compute.register_kernel(kernel.0.clone());
×
NEW
48
    }
×
NEW
49
    compute
×
NEW
50
});
×
51

52
struct ArrayEquals;
53
impl ComputeFnVTable for ArrayEquals {
NEW
54
    fn invoke(
×
NEW
55
        &self,
×
NEW
56
        args: &InvocationArgs,
×
NEW
57
        kernels: &[ArcRef<dyn Kernel>],
×
NEW
58
    ) -> VortexResult<Output> {
×
59
        let ArrayEqualsArgs {
NEW
60
            left,
×
NEW
61
            right,
×
NEW
62
            ignore_nullability,
×
NEW
63
        } = ArrayEqualsArgs::try_from(args)?;
×
64

NEW
65
        if ignore_nullability && !left.dtype().eq_ignore_nullability(right.dtype()) {
×
NEW
66
            return Ok(Scalar::from(false).into());
×
NEW
67
        }
×
68

NEW
69
        if !ignore_nullability && !left.dtype().eq(right.dtype()) {
×
NEW
70
            return Ok(Scalar::from(false).into());
×
NEW
71
        }
×
72

NEW
73
        if left.len() != right.len() {
×
NEW
74
            return Ok(Scalar::from(false).into());
×
NEW
75
        }
×
76

NEW
77
        if let Some(l_scalar) = left.as_constant()
×
NEW
78
            && let Some(r_scalar) = right.as_constant()
×
79
        {
NEW
80
            return Ok(Scalar::from(l_scalar.eq(&r_scalar)).into());
×
NEW
81
        }
×
82

NEW
83
        if left.is_empty() && right.is_empty() {
×
NEW
84
            return Ok(Scalar::from(true).into());
×
NEW
85
        }
×
86

NEW
87
        for stat in [
×
NEW
88
            Stat::IsConstant,
×
NEW
89
            Stat::IsSorted,
×
NEW
90
            Stat::IsStrictSorted,
×
NEW
91
            Stat::Max, // todo: can we do that with e.g. float errors?
×
NEW
92
            Stat::Min,
×
NEW
93
            Stat::Sum,
×
NEW
94
            Stat::NullCount,
×
NEW
95
            Stat::NaNCount,
×
96
            // No Stat::UncompressedSizeInBytes because arrays may physically differ and has a different metric
97
        ] {
NEW
98
            let Some(Precision::Exact(left_v)) = left.statistics().get(stat) else {
×
NEW
99
                continue;
×
100
            };
101

NEW
102
            let Some(Precision::Exact(right_v)) = right.statistics().get(stat) else {
×
NEW
103
                continue;
×
104
            };
105

NEW
106
            if !left_v.eq(&right_v) {
×
NEW
107
                return Ok(Scalar::from(false).into());
×
NEW
108
            }
×
109
        }
110

NEW
111
        let args = InvocationArgs {
×
NEW
112
            inputs: &[left.into(), right.into()],
×
NEW
113
            options: &ArrayEqualsOptions { ignore_nullability },
×
NEW
114
        };
×
115

NEW
116
        for kernel in kernels {
×
NEW
117
            if let Some(output) = kernel.invoke(&args)? {
×
NEW
118
                return Ok(output);
×
NEW
119
            }
×
120
        }
121

NEW
122
        if let Some(output) = left.invoke(&ARRAY_EQUALS_FN, &args)? {
×
NEW
123
            todo!();
×
NEW
124
        }
×
125
        
126
        // swap...
127

128
        // try to check canonical arrays if there are not canonical now
129

NEW
130
        todo!();
×
131

132
        // if no kernels matched, default running per element comparison
133
        todo!();
NEW
134
    }
×
135

NEW
136
    fn return_dtype(&self, _args: &InvocationArgs) -> VortexResult<DType> {
×
NEW
137
        Ok(DType::Bool(Nullability::NonNullable))
×
NEW
138
    }
×
139

NEW
140
    fn return_len(&self, _args: &InvocationArgs) -> VortexResult<usize> {
×
NEW
141
        Ok(1)
×
NEW
142
    }
×
143

NEW
144
    fn is_elementwise(&self) -> bool {
×
NEW
145
        false
×
NEW
146
    }
×
147
}
148

149
// todo: statistics
150
pub trait ArrayEqualsKernel: VTable {
151
    fn compare_array(
152
        &self,
153
        array: &Self::Array,
154
        other: &dyn Array,
155
        ignore_nullability: bool,
156
    ) -> VortexResult<Option<bool>>;
157
}
158

159
struct ArrayEqualsArgs<'a> {
160
    left: &'a dyn Array,
161
    right: &'a dyn Array,
162
    ignore_nullability: bool,
163
}
164

165
impl<'a> TryFrom<&InvocationArgs<'a>> for ArrayEqualsArgs<'a> {
166
    type Error = VortexError;
167

NEW
168
    fn try_from(value: &InvocationArgs<'a>) -> Result<Self, Self::Error> {
×
NEW
169
        if value.inputs.len() != 2 {
×
NEW
170
            vortex_bail!(
×
NEW
171
                "ArrayEquals function requires two arguments, got {}",
×
NEW
172
                value.inputs.len()
×
173
            );
NEW
174
        }
×
NEW
175
        let left = value.inputs[0]
×
NEW
176
            .array()
×
NEW
177
            .ok_or_else(|| vortex_err!("First argument must be an array"))?;
×
178

NEW
179
        let right = value.inputs[1]
×
NEW
180
            .array()
×
NEW
181
            .ok_or_else(|| vortex_err!("Second argument must be an array"))?;
×
182

NEW
183
        let options = value
×
NEW
184
            .options
×
NEW
185
            .as_any()
×
NEW
186
            .downcast_ref::<ArrayEqualsOptions>()
×
NEW
187
            .ok_or_else(|| vortex_err!("Invalid options type for array equals function"))?;
×
188

NEW
189
        Ok(ArrayEqualsArgs {
×
NEW
190
            left,
×
NEW
191
            right,
×
NEW
192
            ignore_nullability: options.ignore_nullability,
×
NEW
193
        })
×
NEW
194
    }
×
195
}
196

197
#[derive(Debug)]
198
pub struct ArrayEqualsKernelAdapter<V: VTable>(pub V);
199

200
pub struct ArrayEqualsKernelRef(ArcRef<dyn Kernel>);
201
inventory::collect!(ArrayEqualsKernelRef);
202

203
impl<V: VTable + ArrayEqualsKernel> ArrayEqualsKernelAdapter<V> {
NEW
204
    pub const fn lift(&'static self) -> ArrayEqualsKernelRef {
×
NEW
205
        ArrayEqualsKernelRef(ArcRef::new_ref(self))
×
NEW
206
    }
×
207
}
208

209
impl<V: VTable + ArrayEqualsKernel> Kernel for ArrayEqualsKernelAdapter<V> {
NEW
210
    fn invoke(&self, args: &InvocationArgs) -> VortexResult<Option<Output>> {
×
211
        let ArrayEqualsArgs {
NEW
212
            left,
×
NEW
213
            right,
×
NEW
214
            ignore_nullability,
×
NEW
215
        } = ArrayEqualsArgs::try_from(args)?;
×
216

NEW
217
        let Some(left) = left.as_opt::<V>() else {
×
NEW
218
            return Ok(None);
×
219
        };
220

NEW
221
        let is_equal = V::compare_array(&self.0, left, right, ignore_nullability)?;
×
NEW
222
        Ok(is_equal.map(|b| Scalar::from(b).into()))
×
NEW
223
    }
×
224
}
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