Skip to main content

ArrayOfDoublesIntersection

Struct ArrayOfDoublesIntersection 

Source
pub struct ArrayOfDoublesIntersection { /* private fields */ }
Expand description

Computes the intersection of ArrayOfDoubles sketches fed via Self::update. Values are summed per index for keys present in every input.

Unlike super::ArrayOfDoublesUnion there is no builder — upstream’s array_of_doubles_intersection has a plain constructor, and the intersecting universe is defined entirely by the sketches passed to update. Only num_values (which the combine policy needs at construction time) must be supplied up front.

Upstream ships no default combine policy for this type; v1 uses sum-on-collision, mirroring the union’s policy. Additional policies (min/max, etc.) can be added later without changing this type’s shape.

Implementations§

Source§

impl ArrayOfDoublesIntersection

Source

pub fn new(num_values: u8) -> Result<Self, SketchError>

Creates a new intersection accumulator for sketches carrying num_values values per entry, with no result yet — call Self::update at least once before Self::get_result.

Returns SketchError::InvalidConfig if num_values is 0.

Examples found in repository?
examples/tuple.rs (line 65)
14fn main() {
15    // Two sketches of user IDs, each carrying [sessions, revenue] per user.
16    let mut day1 = ArrayOfDoublesSketchBuilder::new()
17        .lg_k(12)
18        .num_values(2)
19        .build()
20        .unwrap();
21    for id in 0..10_000u64 {
22        day1.update_u64(id, &[1.0, 2.50]).unwrap();
23    }
24
25    let mut day2 = ArrayOfDoublesSketchBuilder::new()
26        .lg_k(12)
27        .num_values(2)
28        .build()
29        .unwrap();
30    for id in 5_000..15_000u64 {
31        day2.update_u64(id, &[1.0, 4.00]).unwrap();
32    }
33
34    println!("Day 1 unique users (estimate): {:.0}", day1.get_estimate());
35    println!("Day 2 unique users (estimate): {:.0}", day2.get_estimate());
36    println!("Values per entry: {}", day1.get_num_values());
37
38    // Union: unique users across both days, with per-user values summed for
39    // anyone who appeared on both.
40    let mut union = ArrayOfDoublesUnionBuilder::new()
41        .lg_k(12)
42        .num_values(2)
43        .build()
44        .unwrap();
45    union.update(&day1).unwrap();
46    union.update(&day2).unwrap();
47    let combined = union.get_result(true);
48    println!(
49        "Total unique users (union estimate): {:.0}",
50        combined.get_estimate()
51    );
52
53    // Per-entry access is what distinguishes Tuple sketches from HLL/Theta/CPC:
54    // scale the retained sample's revenue back up by 1/theta to estimate the
55    // full population total.
56    let retained_revenue: f64 = combined.entries().map(|(_, values)| values[1]).sum();
57    println!(
58        "Estimated total revenue: {:.2} (from {} retained entries, theta = {:.4})",
59        retained_revenue / combined.get_theta(),
60        combined.get_num_retained(),
61        combined.get_theta()
62    );
63
64    // Intersection: users who came back on day 2.
65    let mut intersection = ArrayOfDoublesIntersection::new(2).unwrap();
66    intersection.update(&day1).unwrap();
67    intersection.update(&day2).unwrap();
68    match intersection.get_result(true) {
69        Ok(returning) => println!(
70            "Returning users (intersection estimate): {:.0}",
71            returning.get_estimate()
72        ),
73        Err(e) => println!("No intersection result: {e}"),
74    }
75
76    // A-not-b: users who only came on day 1.
77    let a_not_b = ArrayOfDoublesAnotB::new();
78    let day1_only = a_not_b.compute(&day1, &day2, true).unwrap();
79    println!(
80        "Day-1-only users (a-not-b estimate): {:.0}",
81        day1_only.get_estimate()
82    );
83
84    // Jaccard similarity of the two days' audiences.
85    let similarity = array_of_doubles_jaccard_similarity(&day1, &day2).unwrap();
86    println!(
87        "Jaccard similarity: {:.3} (range [{:.3}, {:.3}])",
88        similarity.estimate, similarity.lower_bound, similarity.upper_bound
89    );
90
91    // Serialize a compact sketch for storage/transmission, then restore it.
92    let compact = day1.compact(true);
93    let bytes = compact.serialize();
94    println!("Serialized day-1 sketch: {} bytes", bytes.len());
95    let restored = CompactArrayOfDoublesSketch::deserialize(&bytes).unwrap();
96    println!(
97        "Restored estimate: {:.0} ({} values per entry)",
98        restored.get_estimate(),
99        restored.get_num_values()
100    );
101}
Source

pub fn update( &mut self, input: &impl ArrayOfDoublesInput, ) -> Result<(), SketchError>

Narrows this intersection’s running result to also require membership in the given sketch. The first call establishes the initial universe; each subsequent call intersects further.

Returns SketchError::InvalidConfig if the sketch’s num_values differs from this intersection’s — upstream does not validate this itself, and mismatched widths would read and write out of bounds.

Examples found in repository?
examples/tuple.rs (line 66)
14fn main() {
15    // Two sketches of user IDs, each carrying [sessions, revenue] per user.
16    let mut day1 = ArrayOfDoublesSketchBuilder::new()
17        .lg_k(12)
18        .num_values(2)
19        .build()
20        .unwrap();
21    for id in 0..10_000u64 {
22        day1.update_u64(id, &[1.0, 2.50]).unwrap();
23    }
24
25    let mut day2 = ArrayOfDoublesSketchBuilder::new()
26        .lg_k(12)
27        .num_values(2)
28        .build()
29        .unwrap();
30    for id in 5_000..15_000u64 {
31        day2.update_u64(id, &[1.0, 4.00]).unwrap();
32    }
33
34    println!("Day 1 unique users (estimate): {:.0}", day1.get_estimate());
35    println!("Day 2 unique users (estimate): {:.0}", day2.get_estimate());
36    println!("Values per entry: {}", day1.get_num_values());
37
38    // Union: unique users across both days, with per-user values summed for
39    // anyone who appeared on both.
40    let mut union = ArrayOfDoublesUnionBuilder::new()
41        .lg_k(12)
42        .num_values(2)
43        .build()
44        .unwrap();
45    union.update(&day1).unwrap();
46    union.update(&day2).unwrap();
47    let combined = union.get_result(true);
48    println!(
49        "Total unique users (union estimate): {:.0}",
50        combined.get_estimate()
51    );
52
53    // Per-entry access is what distinguishes Tuple sketches from HLL/Theta/CPC:
54    // scale the retained sample's revenue back up by 1/theta to estimate the
55    // full population total.
56    let retained_revenue: f64 = combined.entries().map(|(_, values)| values[1]).sum();
57    println!(
58        "Estimated total revenue: {:.2} (from {} retained entries, theta = {:.4})",
59        retained_revenue / combined.get_theta(),
60        combined.get_num_retained(),
61        combined.get_theta()
62    );
63
64    // Intersection: users who came back on day 2.
65    let mut intersection = ArrayOfDoublesIntersection::new(2).unwrap();
66    intersection.update(&day1).unwrap();
67    intersection.update(&day2).unwrap();
68    match intersection.get_result(true) {
69        Ok(returning) => println!(
70            "Returning users (intersection estimate): {:.0}",
71            returning.get_estimate()
72        ),
73        Err(e) => println!("No intersection result: {e}"),
74    }
75
76    // A-not-b: users who only came on day 1.
77    let a_not_b = ArrayOfDoublesAnotB::new();
78    let day1_only = a_not_b.compute(&day1, &day2, true).unwrap();
79    println!(
80        "Day-1-only users (a-not-b estimate): {:.0}",
81        day1_only.get_estimate()
82    );
83
84    // Jaccard similarity of the two days' audiences.
85    let similarity = array_of_doubles_jaccard_similarity(&day1, &day2).unwrap();
86    println!(
87        "Jaccard similarity: {:.3} (range [{:.3}, {:.3}])",
88        similarity.estimate, similarity.lower_bound, similarity.upper_bound
89    );
90
91    // Serialize a compact sketch for storage/transmission, then restore it.
92    let compact = day1.compact(true);
93    let bytes = compact.serialize();
94    println!("Serialized day-1 sketch: {} bytes", bytes.len());
95    let restored = CompactArrayOfDoublesSketch::deserialize(&bytes).unwrap();
96    println!(
97        "Restored estimate: {:.0} ({} values per entry)",
98        restored.get_estimate(),
99        restored.get_num_values()
100    );
101}
Source

pub fn get_result( &self, ordered: bool, ) -> Result<CompactArrayOfDoublesSketch, SketchError>

Returns the current intersection result as a CompactArrayOfDoublesSketch, or SketchError::EmptyIntersection if Self::update has never been called. If ordered is true, the result’s entries are sorted by hash value.

Examples found in repository?
examples/tuple.rs (line 68)
14fn main() {
15    // Two sketches of user IDs, each carrying [sessions, revenue] per user.
16    let mut day1 = ArrayOfDoublesSketchBuilder::new()
17        .lg_k(12)
18        .num_values(2)
19        .build()
20        .unwrap();
21    for id in 0..10_000u64 {
22        day1.update_u64(id, &[1.0, 2.50]).unwrap();
23    }
24
25    let mut day2 = ArrayOfDoublesSketchBuilder::new()
26        .lg_k(12)
27        .num_values(2)
28        .build()
29        .unwrap();
30    for id in 5_000..15_000u64 {
31        day2.update_u64(id, &[1.0, 4.00]).unwrap();
32    }
33
34    println!("Day 1 unique users (estimate): {:.0}", day1.get_estimate());
35    println!("Day 2 unique users (estimate): {:.0}", day2.get_estimate());
36    println!("Values per entry: {}", day1.get_num_values());
37
38    // Union: unique users across both days, with per-user values summed for
39    // anyone who appeared on both.
40    let mut union = ArrayOfDoublesUnionBuilder::new()
41        .lg_k(12)
42        .num_values(2)
43        .build()
44        .unwrap();
45    union.update(&day1).unwrap();
46    union.update(&day2).unwrap();
47    let combined = union.get_result(true);
48    println!(
49        "Total unique users (union estimate): {:.0}",
50        combined.get_estimate()
51    );
52
53    // Per-entry access is what distinguishes Tuple sketches from HLL/Theta/CPC:
54    // scale the retained sample's revenue back up by 1/theta to estimate the
55    // full population total.
56    let retained_revenue: f64 = combined.entries().map(|(_, values)| values[1]).sum();
57    println!(
58        "Estimated total revenue: {:.2} (from {} retained entries, theta = {:.4})",
59        retained_revenue / combined.get_theta(),
60        combined.get_num_retained(),
61        combined.get_theta()
62    );
63
64    // Intersection: users who came back on day 2.
65    let mut intersection = ArrayOfDoublesIntersection::new(2).unwrap();
66    intersection.update(&day1).unwrap();
67    intersection.update(&day2).unwrap();
68    match intersection.get_result(true) {
69        Ok(returning) => println!(
70            "Returning users (intersection estimate): {:.0}",
71            returning.get_estimate()
72        ),
73        Err(e) => println!("No intersection result: {e}"),
74    }
75
76    // A-not-b: users who only came on day 1.
77    let a_not_b = ArrayOfDoublesAnotB::new();
78    let day1_only = a_not_b.compute(&day1, &day2, true).unwrap();
79    println!(
80        "Day-1-only users (a-not-b estimate): {:.0}",
81        day1_only.get_estimate()
82    );
83
84    // Jaccard similarity of the two days' audiences.
85    let similarity = array_of_doubles_jaccard_similarity(&day1, &day2).unwrap();
86    println!(
87        "Jaccard similarity: {:.3} (range [{:.3}, {:.3}])",
88        similarity.estimate, similarity.lower_bound, similarity.upper_bound
89    );
90
91    // Serialize a compact sketch for storage/transmission, then restore it.
92    let compact = day1.compact(true);
93    let bytes = compact.serialize();
94    println!("Serialized day-1 sketch: {} bytes", bytes.len());
95    let restored = CompactArrayOfDoublesSketch::deserialize(&bytes).unwrap();
96    println!(
97        "Restored estimate: {:.0} ({} values per entry)",
98        restored.get_estimate(),
99        restored.get_num_values()
100    );
101}
Source

pub fn has_result(&self) -> bool

Returns true if Self::update has been called at least once.

Source

pub fn get_num_values(&self) -> u8

Returns the fixed number of f64 values per entry this intersection was created with. Every input passed to Self::update must match it.

Trait Implementations§

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.