fpsketch
Compress molecular count fingerprints to a fixed low dimension while (approximately) preserving Tanimoto similarity as a plain dot product.
Given two count fingerprints x, x' (e.g. Morgan fingerprints with counts,
{feature_id: count}), the standard chemistry similarity metric is the
min-max Tanimoto:
T_MM(x, x') = sum_i min(x_i, x'_i) / sum_i max(x_i, x'_i)
T_MM isn't a dot product, so you can't drop count fingerprints straight into
models (nearest-neighbor search, GPs, kernel methods, ...) that expect a plain
inner product. fpsketch sketches count fingerprints into a fixed-width dense
vector s = encode(x) such that
T_DP(s, s') = s.s' / (||s||^2 + ||s'||^2 - s.s') ≈ T_MM(x, x')
i.e. an ordinary dot product on the sketch approximates T_MM on the
original fingerprints.
Why this works
A unary encoding turns each count into a set of indicators,
psi(x)_{i,k} = 1[x_i > k] for k = 0 .. x_i - 1. Since
min(u, v) = sum_k 1[u > k] * 1[v > k], this makes T_DP(psi(x), psi(x'))
exactly equal to T_MM(x, x') -- no approximation yet, just a reformulation.
fpsketch then applies a CountSketch (hashing each (feature_id, level) pair
into one of m signed buckets) to that unary expansion, which keeps the
dot product unbiased while collapsing it to a fixed, low dimension.
Install
pip install fpsketch # encode_sparse only, numpy-only
pip install fpsketch[chem] # + encode_mols, pulls in rdkit
Quickstart
from fpsketch import encode_sparse
# Sparse count fingerprints you already have.
fps = [{1: 1, 2: 2, 3: 3}, {4: 4, 5: 5, 6: 6}]
sketch = encode_sparse(fps, dim=2048, seed=0)
# T_DP as a plain dot product / normalized similarity.
G = sketch @ sketch.T
sq = (sketch**2).sum(axis=1)
similarity = G / (sq[:, None] + sq[None, :] - G)
from rdkit import Chem
from fpsketch import encode_mols
mols = [Chem.MolFromSmiles(s) for s in ["CCO"]] # your list of SMILES strings
sketch = encode_mols(mols, dim=2048, seed=0) # defaults to a Morgan(radius=2) generator
If you already have your counts vectorized as a COO sparse array (molecules x
features), encode_coo skips the per-molecule dict traversal encode_sparse
does internally:
from scipy.sparse import coo_array
from fpsketch import encode_coo
counts = coo_array(...) # shape (n_molecules, n_features)
sketch = encode_coo(counts, dim=2048, seed=0)
Two sketches are only comparable if built with the same seed.
Choosing dim and num_blocks
dim=2048 is a strong default for typical fingerprint settings. For extra
safety margin, dim at 2-4x the fingerprint's effective (unfolded) dimension
is a reasonable range to sweep. num_blocks (default 4) splits dim into
that many disjoint sub-sketches, each an independent CountSketch; a dot
product on the concatenated output is equivalent to averaging the
num_blocks per-block dot-product estimates. This trades a small amount of
raw accuracy for better tail concentration across single-draw sketches, which
matters when a sketch is computed once and fed straight into a downstream
model (e.g. a GP) rather than averaged over many random seeds.
The scale parameter
By default (scale=True), the output is divided by sqrt(num_blocks) so
that raw dot products and squared norms directly approximate the true,
unnormalized dot product / count mass of the original fingerprints -- useful
if you compare sketches built with different num_blocks, or use the sketch
for anything beyond the T_DP ratio above (cosine similarity, nearest
neighbors on raw dot product, etc). That factor cancels out of the T_DP
ratio itself, so if you only ever compute Tanimoto similarity through that
ratio, scale=False is equivalent and skips one pass over the output array.
Performance note
Hashing is vectorized with numpy (a pure-numpy splitmix64 mixer, not
hashlib per element) rather than hashing one (feature, count-level) pair
at a time -- see src/fpsketch/sketching.py for details.
Development
uv sync --extra chem
uv run pytest
uv run ruff check .
uv run ruff format .
uv run mypy src
uv run pre-commit install # run the above automatically on each commit
Releasing
Versions are derived from git tags (hatch-vcs); there is no version to bump
by hand. Pushing a tag matching v* (e.g. v0.2.0) triggers
.github/workflows/publish.yml, which builds and publishes to PyPI via
Trusted Publishing -- no API
token needed, but the pypi environment must be configured as a trusted
publisher for this repo in the PyPI project settings first.
License
MIT, see LICENSE.
Metadata
Release files for fpsketch 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| fpsketch-0.1.0.tar.gz | 104.4 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fpsketch-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 116.8 kB
Release files / fpsketch-0.1.0.tar.gz
| Download URL | fpsketch-0.1.0.tar.gz |
|---|---|
| Size | 104.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6d85fc34794c3f5ecec80fd40c3de06b60aa658d4080bdd697c9841f9108c244
|
|
BLAKE2b-256 checksum How to use checksums |
b0e88f16a3f9c539a9618e89523c6cfbc08c0476b2cd310b77f09f97473ae77b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.
Transparency logRelease files / fpsketch-0.1.0-py3-none-any.whl
| Download URL | fpsketch-0.1.0-py3-none-any.whl |
|---|---|
| Size | 12.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9a106f807f9d5f817922e4ec7c03fb9a72fd3c1d84ec11b05a9f18771f681f12
|
|
BLAKE2b-256 checksum How to use checksums |
33eb4cc7cb2cc8085a62721dfed377ba094f3cd81e4bc55cf5f0a36a1cb29090
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.
Transparency logSecurity for fpsketch 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for fpsketch-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.