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: 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) -> 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 — the pre-1.2 agm_bound value, 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

samkhya-1.2.2.tar.gz (180.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

samkhya-1.2.2-cp39-abi3-manylinux_2_34_x86_64.whl (297.0 kB view details)

Uploaded CPython 3.9+manylinux: glibc 2.34+ x86-64

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

Hashes for samkhya-1.2.2.tar.gz
Algorithm Hash digest
SHA256 e742fbc1e3f6f75e02a3b52cee3b72b5219a52b267ac538a1cbe248a0b559e65
MD5 d8f066462847ef38d1b467ec112ed888
BLAKE2b-256 3c8178e9db579e02e01776c6d8612db5b835dc78c242d24cbe377c412fb15f09

See more details on using hashes here.

File details

Details for the file samkhya-1.2.2-cp39-abi3-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for samkhya-1.2.2-cp39-abi3-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 1d3b892fe86a98fdac90c4fb0a2b11a3c264e8a02164513e95426a3eba22a2f0
MD5 ddfc2bd0f0989595aceeadfec5423fd4
BLAKE2b-256 05b1ef0a6ac3c28cbc0ff69c3ecc2568e9f81e17566b40437f3cf3839538ee9e

See more details on using hashes here.

Release history Release notifications | RSS feed

1.2.3

2 files

This release

1.2.2 This release

2 files

1.2.1

2 files

1.2.0

2 files

1.1.0

2 files

1.0.0

1 file

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page