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.
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 fpsketch-0.1.0.tar.gz.
File metadata
- Download URL: fpsketch-0.1.0.tar.gz
- Upload date:
- Size: 104.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6d85fc34794c3f5ecec80fd40c3de06b60aa658d4080bdd697c9841f9108c244
|
|
| MD5 |
42cb973bf570cc825cd9ec6cf8168370
|
|
| BLAKE2b-256 |
b0e88f16a3f9c539a9618e89523c6cfbc08c0476b2cd310b77f09f97473ae77b
|
Provenance
The following attestation bundles were made for fpsketch-0.1.0.tar.gz:
Publisher:
publish.yml on AustinT/fpsketch
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fpsketch-0.1.0.tar.gz -
Subject digest:
6d85fc34794c3f5ecec80fd40c3de06b60aa658d4080bdd697c9841f9108c244 - Sigstore transparency entry: 2660684489
- Sigstore integration time:
-
Permalink:
AustinT/fpsketch@545be1ad6834eda7e4d6806eb2c57dd1773a4e9c -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/AustinT
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@545be1ad6834eda7e4d6806eb2c57dd1773a4e9c -
Trigger Event:
push
-
Statement type:
File details
Details for the file fpsketch-0.1.0-py3-none-any.whl.
File metadata
- Download URL: fpsketch-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9a106f807f9d5f817922e4ec7c03fb9a72fd3c1d84ec11b05a9f18771f681f12
|
|
| MD5 |
0fe8ff69a48b8a4c2b6c2a5c5a2a43b3
|
|
| BLAKE2b-256 |
33eb4cc7cb2cc8085a62721dfed377ba094f3cd81e4bc55cf5f0a36a1cb29090
|
Provenance
The following attestation bundles were made for fpsketch-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on AustinT/fpsketch
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fpsketch-0.1.0-py3-none-any.whl -
Subject digest:
9a106f807f9d5f817922e4ec7c03fb9a72fd3c1d84ec11b05a9f18771f681f12 - Sigstore transparency entry: 2660684515
- Sigstore integration time:
-
Permalink:
AustinT/fpsketch@545be1ad6834eda7e4d6806eb2c57dd1773a4e9c -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/AustinT
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@545be1ad6834eda7e4d6806eb2c57dd1773a4e9c -
Trigger Event:
push
-
Statement type: