Skip to main content

AttributeDegree

Struct AttributeDegree 

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

Upper bound on how many rows of one relation can share a single value of one join attribute.

§Soundness obligation

max_degree must be at least the true maximum degree. Every constructor in this type either derives that guarantee or documents it as the caller’s obligation. Supplying an under-estimate silently makes the resulting ceiling unsound, which defeats the entire point of the envelope.

Implementations§

Source§

impl AttributeDegree

Source

pub const fn unknown(rows: u64) -> Self

The weakest sound degree: every row could share one value.

Always correct, never informative — a graph built entirely from these yields the Cartesian product.

§Examples
use samkhya_core::degree::AttributeDegree;

assert_eq!(AttributeDegree::unknown(500).max_degree(), 500);
Source

pub const fn from_distinct(rows: u64, distinct: u64) -> Self

Derive a degree bound from a row count and a distinct-value count: maxdeg ≤ rows − distinct + 1.

Assign one row to each of the distinct values, then pile every remaining row onto a single value. Nothing can beat that concentration.

§Soundness obligation

distinct must be a lower bound on the true number of distinct values. The arithmetic subtracts it, so an over-stated distinct count under-states the degree and yields a ceiling below the truth — the exact failure this module exists to prevent.

This matters because the obvious source is the wrong one: HllSketch::estimate is approximately unbiased and two-sided, so it exceeds the truth about half the time. Use from_hll_floor, which takes a value that is never above the truth.

A distinct of zero (unknown) or greater than rows (an inconsistent reading) falls back to unknown rather than producing an unsound value.

§Examples
use samkhya_core::degree::AttributeDegree;

// A key column: every value occurs once.
assert_eq!(AttributeDegree::from_distinct(100, 100).max_degree(), 1);
// 100 rows over 10 values: at worst 91 share one value.
assert_eq!(AttributeDegree::from_distinct(100, 10).max_degree(), 91);
// Unknown distinct count degrades safely.
assert_eq!(AttributeDegree::from_distinct(100, 0).max_degree(), 100);
Source

pub const fn from_upper_bound(rows: u64, upper_bound: u64) -> Self

Use a directly measured or sketch-derived upper bound on the degree.

§Soundness obligation

upper_bound must be greater than or equal to the true maximum degree. A Count-Min sketch satisfies this by construction: its frequency estimates never fall below the truth, so the maximum estimate over the inserted keys is a sound bound. An exact scan obviously satisfies it too. A sampled or averaged statistic does not.

The value is capped at rows, since no relation can have a degree above its own row count.

§Examples
use samkhya_core::degree::AttributeDegree;

assert_eq!(AttributeDegree::from_upper_bound(1_000, 37).max_degree(), 37);
// Capped at the row count.
assert_eq!(AttributeDegree::from_upper_bound(20, 999).max_degree(), 20);
Source

pub fn from_hll_floor(rows: u64, sketch: &HllSketch) -> Self

Derive a sound degree bound from an HLL sketch of the join column.

Uses HllSketch::nonzero_registers, a distinct-count floor, rather than the two-sided point estimate — see from_distinct for why that distinction decides whether the resulting ceiling is sound.

The floor saturates at the register count, so on a high-cardinality column this degrades toward unknown rather than toward a wrong answer. A Count-Min sketch (from_count_min) bounds far more tightly when one is available.

§Examples
use samkhya_core::degree::AttributeDegree;
use samkhya_core::sketches::HllSketch;

let mut hll = HllSketch::new(12).unwrap();
for i in 0..1_000u32 { hll.add(&i.to_le_bytes()); }

let degree = AttributeDegree::from_hll_floor(1_000, &hll);
// Sound: never below the true maximum degree of 1.
assert!(degree.max_degree() >= 1);
assert!(degree.max_degree() <= 1_000);
Source

pub fn from_count_min(rows: u64, sketch: &CountMinSketch) -> Self

Derive a sound degree bound from a Count-Min sketch of the join column — the tightest source available without an exact scan.

For any key k, true_freq(k) <= estimate(k) <= max counter, so the sketch’s largest counter bounds every key’s degree at once. Returns unknown when the sketch has saturated, because that chain of inequalities depends on Count-Min’s never-undercount property, which u32 saturation breaks.

This is the link that makes the ceiling portable: a Count-Min sketch written into a Puffin sidecar by one engine yields a provable join ceiling in another, with no shared catalog and no re-scan.

§Examples
use samkhya_core::degree::AttributeDegree;
use samkhya_core::sketches::CountMinSketch;

let mut cms = CountMinSketch::with_defaults();
for _ in 0..9 { cms.add(b"hot-key", 1); }
for _ in 0..2 { cms.add(b"cold-key", 1); }

let degree = AttributeDegree::from_count_min(11, &cms);
// Bounds the true maximum degree of 9 from above, and beats the
// row count the caller would otherwise have to assume.
assert!(degree.max_degree() >= 9);
assert!(degree.max_degree() <= 11);
Source

pub const fn max_degree(&self) -> u64

The bounded maximum degree.

Trait Implementations§

Source§

impl Clone for AttributeDegree

Source§

fn clone(&self) -> AttributeDegree

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for AttributeDegree

Source§

impl Debug for AttributeDegree

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Eq for AttributeDegree

Source§

impl PartialEq for AttributeDegree

Source§

fn eq(&self, other: &AttributeDegree) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for AttributeDegree

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. 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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
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.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V