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: no Rust
toolchain at install time, one wheel per platform for CPython 3.9+.
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
# Sketches merge, and serialise 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) follow the same shape, including
to_bytes / from_bytes. 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. Use an exact count, a Count-Min-derived bound, or the
Rust-side AttributeDegree::from_hll_floor. 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— the pre-1.2agm_boundvalue, renamed for what it is. An estimate, not a ceiling: it lands below the true cardinality routinely. Never clamp to it.
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
Recoverable core errors — out-of-range sketch parameters, malformed
serialised payloads, a merge across precisions — raise
samkhya.SamkhyaError, a subclass of Exception.
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.2.tar.gz.
File metadata
- Download URL: samkhya-1.2.2.tar.gz
- Upload date:
- Size: 180.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e742fbc1e3f6f75e02a3b52cee3b72b5219a52b267ac538a1cbe248a0b559e65
|
|
| MD5 |
d8f066462847ef38d1b467ec112ed888
|
|
| BLAKE2b-256 |
3c8178e9db579e02e01776c6d8612db5b835dc78c242d24cbe377c412fb15f09
|
File details
Details for the file samkhya-1.2.2-cp39-abi3-manylinux_2_34_x86_64.whl.
File metadata
- Download URL: samkhya-1.2.2-cp39-abi3-manylinux_2_34_x86_64.whl
- Upload date:
- Size: 297.0 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 |
1d3b892fe86a98fdac90c4fb0a2b11a3c264e8a02164513e95426a3eba22a2f0
|
|
| MD5 |
ddfc2bd0f0989595aceeadfec5423fd4
|
|
| BLAKE2b-256 |
05b1ef0a6ac3c28cbc0ff69c3ecc2568e9f81e17566b40437f3cf3839538ee9e
|