Skip to main content

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

fpsketch-0.1.0.tar.gz (104.4 kB view details)

Uploaded Source

Built Distribution

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

fpsketch-0.1.0-py3-none-any.whl (12.4 kB view details)

Uploaded Python 3

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

Hashes for fpsketch-0.1.0.tar.gz
Algorithm Hash digest
SHA256 6d85fc34794c3f5ecec80fd40c3de06b60aa658d4080bdd697c9841f9108c244
MD5 42cb973bf570cc825cd9ec6cf8168370
BLAKE2b-256 b0e88f16a3f9c539a9618e89523c6cfbc08c0476b2cd310b77f09f97473ae77b

See more details on using hashes here.

Provenance

The following attestation bundles were made for fpsketch-0.1.0.tar.gz:

Publisher: publish.yml on AustinT/fpsketch

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for fpsketch-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9a106f807f9d5f817922e4ec7c03fb9a72fd3c1d84ec11b05a9f18771f681f12
MD5 0fe8ff69a48b8a4c2b6c2a5c5a2a43b3
BLAKE2b-256 33eb4cc7cb2cc8085a62721dfed377ba094f3cd81e4bc55cf5f0a36a1cb29090

See more details on using hashes here.

Provenance

The following attestation bundles were made for fpsketch-0.1.0-py3-none-any.whl:

Publisher: publish.yml on AustinT/fpsketch

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page