Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions datasketches/src/theta/a_not_b.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@

//! Theta sketch set difference (`A and not B`).
//!
//! [`ThetaAnotB`] computes the set difference of two Theta sketches: the keys retained in `A`
//! that are not present in `B`. The logic lives in the shared raw operator (`RawThetaAnotB`) that
//! also drives the Tuple a-not-B; Theta entries carry no summary, so nothing needs to be combined.
//! [`ThetaANotB`] computes the set difference of two Theta sketches: the keys retained in `A`
//! that are not present in `B`. It shares its set-difference implementation with Tuple a-not-B;
//! Theta entries carry no summary, so nothing needs to be combined.

use crate::error::Error;
use crate::hash::DEFAULT_UPDATE_SEED;
use crate::theta::CompactThetaSketch;
use crate::theta::ThetaSketchView;
use crate::thetacommon::a_not_b::RawThetaAnotB;
use crate::thetacommon::a_not_b::ANotBOperator;

/// Set difference operator (`A and not B`) for Theta sketches.
///
Expand All @@ -35,34 +35,34 @@ use crate::thetacommon::a_not_b::RawThetaAnotB;
/// # Examples
///
/// ```
/// # use datasketches::theta::{ThetaAnotB, ThetaSketchBuilder};
/// # use datasketches::theta::{ThetaANotB, ThetaSketchBuilder};
/// let mut a = ThetaSketchBuilder::default().build();
/// a.update("apple");
/// a.update("banana");
///
/// let mut b = ThetaSketchBuilder::default().build();
/// b.update("banana");
///
/// let a_not_b = ThetaAnotB::default();
/// let a_not_b = ThetaANotB::default();
/// let result = a_not_b.compute(&a, &b, true).unwrap();
/// assert_eq!(result.num_retained(), 1); // only "apple" survives
/// ```
#[derive(Debug, Clone, Copy)]
pub struct ThetaAnotB {
raw: RawThetaAnotB,
pub struct ThetaANotB {
op: ANotBOperator,
}

impl Default for ThetaAnotB {
impl Default for ThetaANotB {
fn default() -> Self {
Self::with_seed(DEFAULT_UPDATE_SEED)
}
}

impl ThetaAnotB {
impl ThetaANotB {
/// Creates a new set difference operator for the given `seed`.
pub fn with_seed(seed: u64) -> Self {
Self {
raw: RawThetaAnotB::new(seed),
op: ANotBOperator::new(seed),
}
}

Expand All @@ -80,7 +80,7 @@ impl ThetaAnotB {
A: ThetaSketchView,
B: ThetaSketchView,
{
let parts = self.raw.compute(a, b, ordered)?;
let parts = self.op.compute(a, b, ordered)?;
Ok(CompactThetaSketch::from_parts(
parts
.entries
Expand Down
10 changes: 5 additions & 5 deletions datasketches/src/theta/hash_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
use std::hash::Hash;
use std::num::NonZeroU64;

use crate::thetacommon::RawHashTableEntry;
use crate::thetacommon::hash_table::RawHashTable;
use crate::thetacommon::RetainedEntry;
use crate::thetacommon::hash_table::SketchHashTable;

/// Specific hash table for theta sketch
///
Expand All @@ -28,7 +28,7 @@ use crate::thetacommon::hash_table::RawHashTable;
/// * After it reaches the capacity bigger than 2^lg_nom_size, every time the number of entries
/// exceeds the threshold, it will rebuild the table: only keep the min 2^lg_nom_size entries and
/// update the theta to the k-th smallest entry.
pub(super) type ThetaHashTable = RawHashTable<ThetaEntry>;
pub(super) type ThetaHashTable = SketchHashTable<ThetaEntry>;

/// A retained entry in a Theta sketch.
#[derive(Debug, Clone, Copy)]
Expand All @@ -48,7 +48,7 @@ impl ThetaEntry {
}
}

impl RawHashTableEntry for ThetaEntry {
impl RetainedEntry for ThetaEntry {
fn hash(&self) -> u64 {
self.hash.get()
}
Expand Down Expand Up @@ -90,7 +90,7 @@ mod tests {
impl ThetaHashTable {
/// Get iterator over entries.
pub fn iter(&self) -> impl Iterator<Item = u64> + '_ {
self.iter_entries().map(RawHashTableEntry::hash)
self.iter_entries().map(RetainedEntry::hash)
}
}

Expand Down
18 changes: 9 additions & 9 deletions datasketches/src/theta/intersection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ use crate::hash::DEFAULT_UPDATE_SEED;
use crate::theta::CompactThetaSketch;
use crate::theta::ThetaSketchView;
use crate::theta::hash_table::ThetaEntry;
use crate::thetacommon::intersection::RawThetaIntersection;
use crate::thetacommon::intersection::RawThetaIntersectionPolicy;
use crate::thetacommon::intersection::IntersectionMergePolicy;
use crate::thetacommon::intersection::IntersectionState;

/// Stateful intersection operator for Theta sketches.
///
/// Before the first [`update`](Self::update), the result is undefined; use
/// [`has_result`](Self::has_result) to check.
#[derive(Debug)]
pub struct ThetaIntersection {
raw: RawThetaIntersection<ThetaEntry, NoopIntersectionPolicy>,
state: IntersectionState<ThetaEntry, NoopIntersectionPolicy>,
}

impl Default for ThetaIntersection {
Expand All @@ -41,15 +41,15 @@ impl Default for ThetaIntersection {
#[derive(Debug)]
struct NoopIntersectionPolicy;

impl RawThetaIntersectionPolicy<ThetaEntry> for NoopIntersectionPolicy {
impl IntersectionMergePolicy<ThetaEntry> for NoopIntersectionPolicy {
fn merge(&self, _existing: &mut ThetaEntry, _incoming: ThetaEntry) {}
}

impl ThetaIntersection {
/// Creates a new intersection operator for the given `seed`.
pub fn with_seed(seed: u64) -> Self {
Self {
raw: RawThetaIntersection::new(seed, NoopIntersectionPolicy),
state: IntersectionState::new(seed, NoopIntersectionPolicy),
}
}

Expand All @@ -59,12 +59,12 @@ impl ThetaIntersection {
/// and every update can reduce the current set to leave the overlapping
/// subset only.
pub fn update<S: ThetaSketchView>(&mut self, sketch: &S) -> Result<(), Error> {
self.raw.update(sketch)
self.state.update(sketch)
}

/// Returns whether this operator has received at least one update.
pub fn has_result(&self) -> bool {
self.raw.has_result()
self.state.has_result()
}

/// Returns the intersection result as a compact theta sketch.
Expand All @@ -74,10 +74,10 @@ impl ThetaIntersection {
/// Panics if called before the first [`update`](Self::update).
pub fn to_sketch(&self, ordered: bool) -> CompactThetaSketch {
assert!(
self.raw.has_result(),
self.state.has_result(),
"ThetaIntersection::to_sketch() called before first update()"
);
let parts = self.raw.result(ordered);
let parts = self.state.result(ordered);
CompactThetaSketch::from_parts(
parts
.entries
Expand Down
2 changes: 1 addition & 1 deletion datasketches/src/theta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ mod serialization;
mod sketch;
mod union;

pub use self::a_not_b::ThetaAnotB;
pub use self::a_not_b::ThetaANotB;
pub use self::hash_table::ThetaEntry;
pub use self::intersection::ThetaIntersection;
pub use self::sketch::CompactThetaSketch;
Expand Down
14 changes: 9 additions & 5 deletions datasketches/src/theta/sketch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ use crate::theta::serialization;
use crate::theta::serialization::V2_PREAMBLE_EMPTY;
use crate::theta::serialization::V2_PREAMBLE_ESTIMATE;
use crate::theta::serialization::V2_PREAMBLE_PRECISE;
use crate::thetacommon::RawThetaSketchView;
use crate::thetacommon::ThetaFamilySketchView;
use crate::thetacommon::binomial_bounds;
use crate::thetacommon::constants::DEFAULT_LG_K;
use crate::thetacommon::constants::FLAGS_IS_COMPACT;
Expand All @@ -58,11 +58,13 @@ use crate::thetacommon::constants::MIN_LG_K;
///
/// This trait provides a unified input abstraction for APIs that can accept either
/// mutable [`ThetaSketch`] or immutable [`CompactThetaSketch`].
pub trait ThetaSketchView: RawThetaSketchView<ThetaEntry> {}
pub trait ThetaSketchView: ThetaFamilySketchView<Entry = ThetaEntry> {}

impl<T: RawThetaSketchView<ThetaEntry>> ThetaSketchView for T {}
impl<T: ThetaFamilySketchView<Entry = ThetaEntry>> ThetaSketchView for T {}

impl ThetaFamilySketchView for ThetaSketch {
type Entry = ThetaEntry;

impl RawThetaSketchView<ThetaEntry> for ThetaSketch {
fn seed_hash(&self) -> u16 {
ThetaSketch::seed_hash(self)
}
Expand Down Expand Up @@ -862,7 +864,9 @@ impl CompactThetaSketch {
}
}

impl RawThetaSketchView<ThetaEntry> for CompactThetaSketch {
impl ThetaFamilySketchView for CompactThetaSketch {
type Entry = ThetaEntry;

fn seed_hash(&self) -> u16 {
CompactThetaSketch::seed_hash(self)
}
Expand Down
16 changes: 8 additions & 8 deletions datasketches/src/theta/union.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,31 +24,31 @@ use crate::theta::hash_table::ThetaEntry;
use crate::thetacommon::constants::DEFAULT_LG_K;
use crate::thetacommon::constants::MAX_LG_K;
use crate::thetacommon::constants::MIN_LG_K;
use crate::thetacommon::union::RawThetaUnion;
use crate::thetacommon::union::RawThetaUnionPolicy;
use crate::thetacommon::union::UnionMergePolicy;
use crate::thetacommon::union::UnionState;

/// Stateful union operator for Theta sketches.
#[derive(Debug)]
pub struct ThetaUnion {
raw: RawThetaUnion<ThetaEntry, NoopUnionPolicy>,
state: UnionState<ThetaEntry, NoopUnionPolicy>,
}

#[derive(Debug)]
struct NoopUnionPolicy;

impl RawThetaUnionPolicy<ThetaEntry> for NoopUnionPolicy {
impl UnionMergePolicy<ThetaEntry> for NoopUnionPolicy {
fn merge(&self, _existing: &mut ThetaEntry, _incoming: ThetaEntry) {}
}

impl ThetaUnion {
/// Update this union with a given sketch.
pub fn update<S: ThetaSketchView>(&mut self, sketch: &S) -> Result<(), Error> {
self.raw.update(sketch)
self.state.update(sketch)
}

/// Return this union as a compact sketch.
pub fn to_sketch(&self, ordered: bool) -> CompactThetaSketch {
let parts = self.raw.to_compact_parts(ordered);
let parts = self.state.to_compact_parts(ordered);
CompactThetaSketch::from_parts(
parts
.entries
Expand All @@ -64,7 +64,7 @@ impl ThetaUnion {

/// Reset the union to empty state.
pub fn reset(&mut self) {
self.raw.reset();
self.state.reset();
}
}

Expand Down Expand Up @@ -162,7 +162,7 @@ impl ThetaUnionBuilder {
/// ```
pub fn build(self) -> ThetaUnion {
ThetaUnion {
raw: RawThetaUnion::new(
state: UnionState::new(
self.lg_k,
self.resize_factor,
self.sampling_probability,
Expand Down
46 changes: 25 additions & 21 deletions datasketches/src/thetacommon/a_not_b.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,23 @@ use std::collections::HashSet;

use crate::error::Error;
use crate::hash::compute_seed_hash;
use crate::thetacommon::RawHashTableEntry;
use crate::thetacommon::RawThetaSketchView;
use crate::thetacommon::RetainedEntry;
use crate::thetacommon::ThetaFamilySketchView;
use crate::thetacommon::constants::MAX_THETA;
use crate::thetacommon::hash_table::RawCompactParts;
use crate::thetacommon::hash_table::CompactSketchParts;

/// Stateless set difference (`A and not B`) operator shared by Theta and Tuple sketches.
///
/// `E` is the retained entry type. Ordinary Theta entries only contain a hash, while tuple
/// entries also carry a summary. Surviving entries are moved from `A` unchanged, so unlike the
/// union and intersection this operation needs no entry-merge policy.
/// Ordinary Theta entries only contain a hash, while tuple entries also carry a summary.
/// Surviving entries are moved from `A` unchanged, and `B` contributes only hashes, so unlike
/// the union and intersection this operation needs neither matching entry types nor an
/// entry-merge policy.
#[derive(Debug, Clone, Copy)]
pub struct RawThetaAnotB {
pub struct ANotBOperator {
seed_hash: u16,
}

impl RawThetaAnotB {
impl ANotBOperator {
/// Creates a new set difference operator for the given `seed`.
pub fn new(seed: u64) -> Self {
Self {
Expand All @@ -51,11 +52,15 @@ impl RawThetaAnotB {
///
/// Returns an error if either non-trivial input has a seed hash that differs from this
/// operator's seed.
pub fn compute<E, A, B>(&self, a: &A, b: &B, ordered: bool) -> Result<RawCompactParts<E>, Error>
pub fn compute<A, B>(
&self,
a: &A,
b: &B,
ordered: bool,
) -> Result<CompactSketchParts<A::Entry>, Error>
where
E: RawHashTableEntry,
A: RawThetaSketchView<E>,
B: RawThetaSketchView<E>,
A: ThetaFamilySketchView,
B: ThetaFamilySketchView,
{
// If A is empty the result is an (empty) copy of A. As with the union and intersection, an
// empty input carries no keys, so its seed is not validated.
Expand Down Expand Up @@ -93,7 +98,7 @@ impl RawThetaAnotB {
// mode (handled below).
let mut is_empty = false;

let entries: Vec<E> = if b.num_retained() == 0 {
let entries: Vec<A::Entry> = if b.num_retained() == 0 {
a.iter().filter(|entry| entry.hash() < theta).collect()
} else if a.is_ordered() && b.is_ordered() {
// Both inputs are sorted ascending by hash: merge-scan without a hash set. Only
Expand Down Expand Up @@ -150,10 +155,10 @@ impl RawThetaAnotB {
let out_ordered = ordered || a.is_ordered();
let mut entries = entries;
if ordered && !a.is_ordered() && entries.len() > 1 {
entries.sort_unstable_by_key(RawHashTableEntry::hash);
entries.sort_unstable_by_key(RetainedEntry::hash);
}

Ok(RawCompactParts {
Ok(CompactSketchParts {
entries,
theta,
seed_hash: self.seed_hash,
Expand All @@ -163,17 +168,16 @@ impl RawThetaAnotB {
}

/// Builds compact parts that are a copy of the view `a`.
fn parts_from_view<E, V>(a: &V, ordered: bool) -> RawCompactParts<E>
fn parts_from_view<V>(a: &V, ordered: bool) -> CompactSketchParts<V::Entry>
where
E: RawHashTableEntry,
V: RawThetaSketchView<E>,
V: ThetaFamilySketchView,
{
let mut entries: Vec<E> = a.iter().collect();
let mut entries: Vec<V::Entry> = a.iter().collect();
let out_ordered = ordered || a.is_ordered();
if ordered && !a.is_ordered() && entries.len() > 1 {
entries.sort_unstable_by_key(RawHashTableEntry::hash);
entries.sort_unstable_by_key(RetainedEntry::hash);
}
RawCompactParts {
CompactSketchParts {
entries,
theta: a.theta(),
seed_hash: a.seed_hash(),
Expand Down
Loading