apache_datasketches/tuple/jaccard.rs
1use super::input::ArrayOfDoublesInput;
2use crate::error::SketchError;
3use apache_datasketches_sys::array_of_doubles_input::ArrayOfDoublesInputRef;
4use apache_datasketches_sys::array_of_doubles_jaccard::ffi as sys;
5
6/// The result of a Tuple Jaccard similarity computation — returned by both
7/// [`array_of_doubles_jaccard_similarity`] and
8/// [`tuple_jaccard_similarity`](crate::tuple::generic::tuple_jaccard_similarity):
9/// a confidence interval around the estimated Jaccard index of two sketches,
10/// in `[0.0, 1.0]`.
11///
12/// This is a distinct type from the theta module's `JaccardBounds` with the
13/// same shape — the two sketch families are independently feature-gated and
14/// do not share types. (Deliberately not an intra-doc link: `theta` may not
15/// be compiled in.)
16#[derive(Debug, Clone, Copy, PartialEq)]
17pub struct JaccardBounds {
18 /// Lower bound of the confidence interval around [`Self::estimate`].
19 pub lower_bound: f64,
20 /// The estimated Jaccard index.
21 pub estimate: f64,
22 /// Upper bound of the confidence interval around [`Self::estimate`].
23 pub upper_bound: f64,
24}
25
26impl From<sys::TupleJaccardBoundsFfi> for JaccardBounds {
27 fn from(ffi: sys::TupleJaccardBoundsFfi) -> Self {
28 Self {
29 lower_bound: ffi.lower_bound,
30 estimate: ffi.estimate,
31 upper_bound: ffi.upper_bound,
32 }
33 }
34}
35
36/// Estimates the Jaccard index (intersection-over-union) of two
37/// ArrayOfDoubles sketches, each of which may independently be a
38/// [`super::ArrayOfDoublesSketch`] or a
39/// [`super::CompactArrayOfDoublesSketch`].
40///
41/// Only the keys matter — the per-entry values do not affect the result.
42///
43/// Returns [`SketchError::InvalidConfig`] if the two sketches disagree on
44/// `num_values`, for consistency with the other set operations (the
45/// underlying computation would tolerate a mismatch, but accepting one here
46/// would let a genuine modelling error pass silently).
47pub fn array_of_doubles_jaccard_similarity(
48 a: &impl ArrayOfDoublesInput,
49 b: &impl ArrayOfDoublesInput,
50) -> Result<JaccardBounds, SketchError> {
51 let (a_num, b_num) = (a.get_num_values(), b.get_num_values());
52 if a_num != b_num {
53 return Err(SketchError::InvalidConfig(format!(
54 "num_values mismatch: a has {a_num}, b has {b_num}"
55 )));
56 }
57 let ffi = match (a.as_input(), b.as_input()) {
58 (ArrayOfDoublesInputRef::Sketch(a), ArrayOfDoublesInputRef::Sketch(b)) => {
59 sys::tuple_jaccard_sketch_sketch(a, b)
60 }
61 (ArrayOfDoublesInputRef::Sketch(a), ArrayOfDoublesInputRef::Compact(b)) => {
62 sys::tuple_jaccard_sketch_compact(a, b)
63 }
64 (ArrayOfDoublesInputRef::Compact(a), ArrayOfDoublesInputRef::Sketch(b)) => {
65 sys::tuple_jaccard_compact_sketch(a, b)
66 }
67 (ArrayOfDoublesInputRef::Compact(a), ArrayOfDoublesInputRef::Compact(b)) => {
68 sys::tuple_jaccard_compact_compact(a, b)
69 }
70 };
71 Ok(ffi.into())
72}