Skip to main content

samkhya — Python bindings

Python bindings for samkhya: four portable statistics sketches (HyperLogLog, Bloom, Count-Min, equi-depth histogram) and a provable join-cardinality ceiling — an upper bound the join provably cannot exceed. Compiled Rust behind a stable-ABI (abi3-py39) wheel, so one wheel serves every CPython 3.9+ interpreter on the platforms published — currently manylinux_2_34 x86_64. Elsewhere pip falls back to the sdist, which does need a Rust toolchain and maturin.

Install

pip install samkhya

From a source checkout of this directory: pip install maturin, then maturin develop --release (editable) or maturin build --release.

Sketches

import samkhya

# Precision 14 gives 2^14 = 16384 registers; relative error ~0.8%.
hll = samkhya.HllSketch(14)
for i in range(1000):
    hll.add(str(i).encode("utf-8"))
print(round(hll.estimate()))          # ~1000

# HLL sketches merge, and every sketch serialises for transport
# (e.g. an Iceberg Puffin blob).
second = samkhya.HllSketch(14)      # same precision, or merge raises
second.add(b"1001")
hll.merge(second)
restored = samkhya.HllSketch.from_bytes(hll.to_bytes())
assert restored.estimate() == hll.estimate()

BloomFilter(n_items, fp_rate), CountMinSketch(width, depth), and EquiDepthHistogram(boundaries, counts) share the to_bytes / from_bytes shape. merge is bound only on HllSketch. Full signatures are in the type stubs: https://github.com/singhpratech/samkhya/blob/main/samkhya-py/python/samkhya/__init__.pyi

The join ceiling

join_ceiling computes a spanning-tree degree ceiling: sound for bag semantics, and exactly tight on foreign-key joins.

import samkhya

rows = [10.0, 100.0]        # 10 orders, 100 line items
joins = [(0, 1)]            # relation 0 joins relation 1
distinct = [10.0, 10.0]     # 10 distinct order keys on both sides

print(samkhya.join_ceiling(joins, rows, distinct))  # 100.0 — the true size
print(samkhya.product_bound(rows))                  # 1000.0

Without distinct_counts the ceiling degrades to the Cartesian product: given only row counts and which pairs are joined, every row can carry the same key value, so nothing below the product is provable.

distinct_counts must be a lower bound on the true distinct count. The degree is derived as rows - distinct + 1, so an overstated distinct count understates the degree and makes the ceiling unsound. Do not feed it HllSketch.estimate(), which is two-sided and exceeds the truth about half the time. From Python the sound source is an exact distinct count. Do not derive it from a Count-Min sketch: Count-Min bounds frequencies, not distinct values, so feeding it here yields rows - maxfreq + 1, which understates the degree and produces exactly the unsound ceiling this paragraph warns about. If you need a sketch-derived degree, use the Rust API — samkhya_core::degree::AttributeDegree::from_hll_floor and from_count_min produce degrees directly rather than values to pass here. Entries that are zero, larger than the row count, or absent degrade safely to "no degree information" rather than to a wrong answer.

distinct_counts is indexed per relation, not per (relation, join column): if a relation joins on several columns, pass the smallest count among them, which overstates the degree and stays sound.

Function reference

  • join_ceiling(joins, card_estimates, distinct_counts=None) -> float The bound to use; joins is a list of (left_idx, right_idx).
  • product_bound(card_estimates) -> float — Cartesian product fallback.
  • agm_bound(joins, card_estimates) -> float — compatibility shim. Its selectivity field is ignored since 1.2; it returns the product.
  • selectivity_estimate(joins, card_estimates) -> float — prod(card_estimates) * prod(clamped selectivities). An estimate, not a ceiling: it lands below the true cardinality routinely. Never clamp to it. (It is close in spirit to the pre-1.2 agm_bound, but not equal — that one applied a min * max shortcut this does not.)
  • samkhya_version() -> str and samkhya.__version__ — the underlying crate version.

Changed in 1.2 — soundness fix

A 2026-07-24 audit found the bound family shipped through 1.1 was not sound: it returned ceilings below the true cardinality in 2,179 of 3,704 measured bound-evaluations (58.8%), from multiplying a ceiling by selectivities in [0, 1], which can only shrink it. 1.2 replaces that path with the degree ceiling above: 0 violations, same trials. Two published headline numbers are withdrawn: a 40.95x bound-tightness figure and a 1.038x JOB-Slow speedup.

Errors

Malformed serialised payloads, a merge across mismatched precisions, and out-of-range HllSketch / CountMinSketch parameters raise samkhya.SamkhyaError, a subclass of Exception.

BloomFilter is the exception: out-of-range parameters are clamped, not rejected. BloomFilter(1000, 0.0) returns a filter rather than raising, because the Python binding wraps the infallible constructor. Validate fp_rate yourself if it comes from user input.

Scope

This wheel exposes the sketches and the ceiling functions, nothing else: no query-engine integration, feedback store, or correction loop — those live in the Rust crates at https://github.com/singhpratech/samkhya. The theorem, its proof, and the full degree-source API are documented at https://docs.rs/samkhya-core under samkhya_core::degree.

Licensed under Apache-2.0.

Metadata

Release files for samkhya 1.2.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for samkhya 1.2.3
File Size Uploaded
samkhya-1.2.3.tar.gz 181.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for samkhya 1.2.3
File Interpreter ABI Platform
samkhya-1.2.3-cp39-abi3-manylinux_2_34_x86_64.whl CPython 3.9 abi3 Linux glibc 2.34+ x86-64 Details

Total release size: 478.9 kB

Release files / samkhya-1.2.3.tar.gz

Download URL samkhya-1.2.3.tar.gz
Size 181.5 kB
Tags Source
SHA-256 checksum
How to use checksums
25a13c75fb18aba850d9eb8bfc53dc9015757ef1fd27ccbb55e6ce4e3899f0bb
BLAKE2b-256 checksum
How to use checksums
46a3e1afe50c5089c7c90c4f5e03ddc909f62b9bdb447aaa9b0461daf6a1b919
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.3

Release files / samkhya-1.2.3-cp39-abi3-manylinux_2_34_x86_64.whl

Download URL samkhya-1.2.3-cp39-abi3-manylinux_2_34_x86_64.whl
Size 297.4 kB
Tags CPython 3.9 Linux glibc 2.34+ x86-64 abi3
SHA-256 checksum
How to use checksums
830bffec44daad07c3510c84fb4e993a7dd746312949c2c4db9eaba844c9f0f3
BLAKE2b-256 checksum
How to use checksums
d929aed02cc787aa1255edfd973d0293d5b3f348617293f4f8e8ce3341fceb28
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.3

Release history Release notifications | RSS feed

This release

1.2.3 This release

2 release files

1.2.2

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.0

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page