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) -> floatThe bound to use;joinsis 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.2agm_bound, but not equal — that one applied amin * maxshortcut this does not.)samkhya_version() -> strandsamkhya.__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.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file samkhya-1.2.3.tar.gz.
File metadata
- Download URL: samkhya-1.2.3.tar.gz
- Upload date:
- Size: 181.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
25a13c75fb18aba850d9eb8bfc53dc9015757ef1fd27ccbb55e6ce4e3899f0bb
|
|
| MD5 |
9c73e76d6df4f3ed26602ddf859350d3
|
|
| BLAKE2b-256 |
46a3e1afe50c5089c7c90c4f5e03ddc909f62b9bdb447aaa9b0461daf6a1b919
|
File details
Details for the file samkhya-1.2.3-cp39-abi3-manylinux_2_34_x86_64.whl.
File metadata
- Download URL: samkhya-1.2.3-cp39-abi3-manylinux_2_34_x86_64.whl
- Upload date:
- Size: 297.4 kB
- Tags: CPython 3.9+, manylinux: glibc 2.34+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
830bffec44daad07c3510c84fb4e993a7dd746312949c2c4db9eaba844c9f0f3
|
|
| MD5 |
6e8a971783343fe485e76d979f3f8643
|
|
| BLAKE2b-256 |
d929aed02cc787aa1255edfd973d0293d5b3f348617293f4f8e8ce3341fceb28
|