Skip to main content
bclxl_pocket

Lacuna

Cryptic binding pocket discovery via conformational ensemble analysis.

Most protein structure predictors (AlphaFold, Boltz, Chai) give you one static structure. But ~70% of disease-relevant proteins are considered "undruggable" not because they're biologically intractable - it's because no pocket is visible in their ground state. K-Ras was "undruggable" for 30 years until a transient cryptic pocket was found in its switch-II region. That pocket now backs sotorasib and adagrasib.

Lacuna finds those pockets. It generates a conformational ensemble from any input structure, detects pockets per conformer, and clusters them across the ensemble to surface sites that only appear transiently - ranked by a continuous crypticity score.

lacuna discover kras.pdb --conformers 20 --emit-boltz-constraints --emit-vina-boxes

Install

pip install lacuna-pockets

Optional backends (better conformational sampling):

pip install "lacuna-pockets[openmm]"   # 100ps implicit-solvent MD
pip install "lacuna-pockets[boltz]"    # Boltz-2 diffusion sampling (experimental, GPU)
pip install "lacuna-pockets[all]"      # everything

Requires Python 3.10+.


Quick start

CLI

# Discover pockets with defaults (NMA backend - physically grounded, no GPU needed)
lacuna discover protein.pdb --conformers 20

# Filter and limit output
lacuna discover protein.pdb --min-druggability 0.5 --min-persistence 0.3 --top 5

# Analyze a homodimer - detects pockets at the dimer interface (e.g. Caspase-1, IDH1)
# Reads BIOMT records from PDB; for best results use the biological assembly download from RCSB
lacuna discover protein.pdb --homodimer --conformers 20

# Optional Boltz-2 backend (experimental - see the Backends note)
lacuna discover protein.pdb --backend boltz --conformers 30

# Emit all docking file formats
lacuna discover protein.pdb --emit-boltz-constraints --emit-vina-boxes --emit-pocket-pdbs

# Generate docking files from a previous report
lacuna dock-prep kras_lacuna/pocket_report.json kras.pdb --format all

Python API

from lacuna import load_structure, detect_pockets, cluster_pockets
from lacuna.ensemble.nma_backend import NMABackend
from lacuna.io.structure import coords_array
from lacuna.io.writers import write_report, write_boltz_constraint

structure = load_structure("protein.pdb")
backend = NMABackend(seed=42)
coord_sets = backend.generate("protein.pdb", n_conformers=20)

all_coords = [coords_array(structure)] + coord_sets
pocket_lists = []
for ci, coords in enumerate(all_coords):
    pockets = detect_pockets(coords, structure)
    for p in pockets:
        p.conformer_idx = ci
    pocket_lists.append(pockets)

clusters = cluster_pockets(pocket_lists, n_conformers=len(all_coords))
for c in clusters[:5]:
    print(f"Rank {c.rank}  druggability={c.druggability:.3f}  "
          f"persistence={c.persistence:.0%}  cryptic={c.cryptic}")
    print(f"  Residues: {', '.join(c.lining_residues[:5])}")

How it works

  1. Ensemble generation - Generate N conformers via elastic network model normal mode analysis (built-in, default), OpenMM implicit-solvent MD, or experimental Boltz-2 diffusion sampling
  2. Pocket detection - Grid-based alpha-point analysis per conformer: compute distance transform, find local maxima within the 1.4-5.5 Å interaction zone, cluster nearby alpha-points into pocket candidates
  3. Cross-ensemble clustering - Greedy centroid merging clusters corresponding pockets across all conformers
  4. Druggability scoring - Gaussian volume reward centered at 300 ų + enclosure + hydrophobicity + aromaticity (Halgren 2009), scored in each conformer
  5. Scoring & ranking - Each site gets a continuous crypticity score (how much it opens relative to the apo state × druggability when open) and is flagged cryptic: true if present in <90% of conformers. Sites are then ordered by a learned ranker (default), a linear model over 23 geometric and ensemble features fitted to identify the true binding site; --rank-by selects an analytic rule instead

Outputs

File Description
pocket_report.json Ranked pocket metadata: centroid, volume + apo→open range, druggability, crypticity, persistence, lining residues
pocket_N_site.pdb Pseudoatom PDB for PyMOL/ChimeraX visualization
pocket_N_constraint.yaml Boltz YAML - add a SMILES and run boltz predict to dock into this site
pocket_N_vina.conf AutoDock Vina / Gnina / QuickVina box config

Backends

Backend Install Quality Speed Notes
nma built-in good ~0.1s/conf Elastic-network normal modes (default)
openmm lacuna[openmm] good ~2s/conf Implicit-solvent MD, 100ps
boltz lacuna[boltz] experimental ~100s/protein (GPU) Diffusion sampling, noisy (see note below)
random built-in baseline ~0.04s/conf Gaussian backbone perturbation

Auto-selection order: boltzopenmmnmarandom. On a plain pip install lacuna, the NMA backend runs automatically.

The nma backend samples physically meaningful collective motions - the same hinge-bending and breathing modes that open cryptic pockets in nature - without requiring a GPU or force field. It is the zero-dependency default.

Boltz backend status (honest note). The boltz backend runs Boltz-2 diffusion sampling on a GPU, but it currently predicts each conformer de novo from sequence (not partial diffusion from the input structure), which yields structurally divergent, noisy ensembles (150-300+ pocket clusters vs NMA's ~35). In GPU benchmarking it did not reliably improve cryptic detection over NMA. A proper apo-templated integration with sequence-based residue mapping is planned; until then, NMA is the recommended backend.


Benchmarks

66.3% of known cryptic sites recovered in the top 5 on CryptoBench's designated test fold with the PLM-assisted ranker (118/178, 95% CI 59.0-73.0), under a size-robust criterion. On the same structures that is level with P2Rank's 63.5%, above IF-SitePred's 61.8% and fpocket's 43.8%. The difference from P2Rank is not statistically separable.

Every number in this section comes from one code state, re-measured end to end after the last change to the ranker weights.

Where the interesting result is. Lacuna's own score is the least useful number here. Across four detectors spanning 2009 to 2026, coverage (whether a qualifying candidate is proposed at all) varies by 7.9 points while conversion (whether it reaches the top five) varies by 37. fpocket has the highest coverage of the four and the lowest top-5. Unioning all four covers 92.1% of targets, leaving only 7.9% invisible to everything tested. The study, its per-candidate data, and the scripts that regenerate every figure are in paper/.

Size-robust success criterion (top-5 pockets): a pocket whose lining residues reach a Jaccard overlap ≥ 0.25 with the known ligand-contact site (Jaccard = |found ∩ known| / |found ∪ known|), or whose center is within 4 Å of the site centroid. Lining residues use a true atomic-contact definition (any residue with an atom within 5 Å of the detected cavity). Recall is not used as the headline: a large pocket can engulf a small known site and score high recall while sitting nowhere near it. Both numbers print side by side in every benchmark script.

Head-to-head against other detectors

All paired on test-fold structures under the identical criterion. MDpocket is given the same NMA ensemble Lacuna uses, so that row compares analysis pipelines rather than samplers. fpocket's row is paired on 179 structures rather than 180 because it was re-collected after a rank-ordering fix; its earlier figure of 28.3% came from reading its proposals in directory-listing order (pocket10 before pocket2) instead of by fpocket's own rank, and understated it by about fifteen points.

Detector Kind Size-robust top-5 Paired vs learned-plm
Lacuna (learned-plm) ensemble + PLM 66.1% (119/180) -
P2Rank single-structure, learned 63.3% (114/180) +2.8% [-4.4, +9.4]
Lacuna (learned, default) ensemble 55.6% (100/180) +10.6% [+6.1, +15.0]
MDpocket ensemble (same input ensemble) 43.9% (79/180) +22.2% [+14.4, +30.0]
fpocket single-structure, geometric 43.6% (78/179) +22.9% [+14.5, +31.3]

With the PLM-assisted ranker Lacuna is level with P2Rank: nominally ahead by 2.8 points, but the interval spans zero, so parity is the honest word rather than a win. It beats every other detector here by margins whose intervals exclude zero, though fpocket's true margin is far smaller than this repository previously reported.

The zero-dependency default does not reach parity. At 55.6% it trails P2Rank by 7.8 points (CI -15.0 to -0.6, excluding zero). An earlier version of this file called the default indistinguishable from P2Rank; that was measured before the conformer-invariant refit and is no longer accurate. The default still beats MDpocket by +11.7% (CI +3.9 to +19.4) and fpocket by +12.3%.

Union with P2Rank reaches 76.1% and Lacuna alone catches 23 structures P2Rank misses, so the tools remain complementary rather than redundant.

The PLM-assisted ranker is an optional extra (pip install "lacuna-pockets[plm]") because it needs PyTorch and downloads an ESM-2 checkpoint. It is a separate strategy rather than the default so that identical commands give identical rankings on every machine, whether or not the extra is installed.

MDpocket is the closest relative of this work and the fair ensemble baseline. Its best configuration out of ten (isovalue and ranking rule both swept in its favour) is reported; at its default isovalue it scores 40.2%.

Independent validation

Default learned strategy, with the optional PLM-assisted ranker in the last column.

Benchmark N learned (default) Legacy recall learned-plm Notes
CryptoBench (held-out test fold) 180 55.6% 77% 66.1% largest & most diverse; the headline
PocketMiner 45 73% (33/45) 84% 80% (36/45) per-residue cryptic labels
Curated apo/holo set (this repo) 22 45% (10/22) 68% 41% (9/22) hand-picked literature cryptic pairs
COACH420 144 87% (125/144) 93% not measured general holo sites, not cryptic; see below

On the curated 22 the default edges out the PLM-assisted ranker, 10/22 against 9/22. At that sample size the difference is one structure and means nothing on its own, but it is a reminder that the PLM-assisted ranker's advantage is established on CryptoBench and does not automatically transfer.

On general binding sites, a general-purpose tool is better. COACH420 holds holo structures whose pocket is already open, which is an easier task and not the one Lacuna is built for. Paired on the same 144 structures P2Rank scores 93.8% against Lacuna's 86.8% (-6.9%, CI -12.5 to -1.4, excludes zero), having tied it on cryptic sites. Each tool wins or ties where it was designed to. The higher absolute number here reflects the easier task, not better performance: cross-dataset comparisons of the headline are not meaningful. Detail →

Datasets: PocketMiner (Meller et al. 2023, Nat. Commun.); CryptoBench (Vavra et al. 2024, Bioinformatics). The CryptoBench split follows the dataset's own homology-separated folds, and the ranker was fitted only on train folds, so the test fold is genuinely unseen.

The curated 22-target set is the hardest of the three despite being the smallest: it was assembled from published cryptic-pocket case studies and is deliberately enriched for the large-motion sites this pipeline handles worst.

python benchmarks/cryptic_benchmark.py --category cryptic     # curated set (~4 min)
python benchmarks/pocketminer_benchmark.py                    # PocketMiner (auto-downloads)
python benchmarks/cryptobench_benchmark.py                    # CryptoBench test fold (~10 min)
python benchmarks/compare_detectors_cryptobench.py --analyze  # the head-to-head table
python benchmarks/compare_mdpocket.py --folds test            # vs MDpocket (needs mdpocket on PATH)

Where the remaining gap is

Across the candidate set Lacuna generates, some cluster clears the criterion for 73.7% of structures, against the 66.1% learned-plm surfaces in the top 5. Ranking is therefore close to exhausted: it converts 89% of what detection makes available, and even perfect ordering would add only about 8 points. The remaining 26% is a detection gap, where no candidate clears the bar at any rank.

Several attempts to close it returned nothing measurable: spatial non-maximum suppression, merging adjacent sub-pockets, hard-negative mining, gradient boosting in place of the linear model, and importing P2Rank's own per-pocket confidence as a feature. That last one matters most: if per-point scoring were the missing ranking ingredient, handing the ranker P2Rank's opinion directly would have helped, and it did not (-1.1% on held-out data). P2Rank's advantage lies in proposing different candidates, not in ordering ours better.

The other limit is sampling. Stratifying the test fold by how far the pocket moves between apo and holo, Lacuna recovers 47% of the most-mobile quartile against P2Rank's 62%. The default NMA backend is harmonic and cannot generate large hinge or interface openings, and enhanced-temperature MD, metadynamics along an apo-derived collective variable, and SWISH scaled-water MD were each null against baseline at single-workstation sampling (see benchmarks/experiments/). Reliably catching those rare events needs tens to hundreds of nanoseconds across dozens of replicas per target, which is cluster scale, not a missing algorithm.

Ranking

--rank-by selects how sites are ordered. The default learned is a linear model over 23 features (pocket geometry, druggability, and ensemble-derived terms such as how far a site's centroid wanders between conformers, which single-structure detectors cannot compute). It is trained on within-structure pairs, so it optimises ordering directly rather than classifying pockets in isolation.

On CryptoBench's test fold it recovers 55.6% against 17.8% for the previous crypticity default, rising to 66.1% with learned-plm. On the curated 22-target set the ordering reverses: persistence and balanced reach 13/22 where learned reaches 10/22, though at n=22 the intervals overlap heavily. If your targets resemble the classic literature case studies more than CryptoBench, the analytic strategies are worth trying. Full ablation →

Every pocket also carries a continuous crypticity score in [0, 1], the conformational-selection signature of a cryptic site:

opening    = (max_volume − apo_volume) / max_volume        # 1.0 if absent in the apo state
crypticity = opening × peak_open_state_druggability

A constitutive pocket already formed in the input scores ≈ 0; one that is absent in the apo structure and opens into a druggable cavity scores near 1. The JSON report also includes per-pocket volume dynamics (apo_volume_A3, volume_range_A3) and max_druggability.

NMA runtime is sub-second to ~7s per protein on a laptop CPU, no GPU required. Per-size timing →


Example: K-Ras switch-II

# Download K-Ras apo (from RCSB); NMA backend (default) recovers switch-II at rank 3
lacuna discover 4OBE.pdb \
    --conformers 20 \
    --emit-boltz-constraints \
    --output kras_pockets/

# pocket_0_constraint.yaml is ready - add your SMILES:
#   - ligand:
#       id: L
#       smiles: YOUR_SMILES_HERE
boltz predict kras_pockets/pocket_0_constraint.yaml

See examples/kras_cryptic.py for a full annotated Python workflow.


Input formats

Accepts PDB or mmCIF from any predictor or database:

  • AlphaFold 2 / AlphaFold 3
  • Boltz-1 / Boltz-2
  • Chai-1
  • RCSB PDB
  • ESMFold, RoseTTAFold, OpenFold, etc.

Citation

If you use Lacuna in published research, please cite:

Moore, C. (2026). Lacuna: Cryptic Binding Pocket Discovery via Conformational Ensemble Analysis. https://github.com/mooreneural/lacuna

BibTeX:

@software{moore2026lacuna,
  author  = {Moore, Clayton W.},
  title   = {Lacuna: Cryptic Binding Pocket Discovery
             via Conformational Ensemble Analysis},
  year    = {2026},
  url     = {https://github.com/mooreneural/lacuna},
  version = {1.0.0}
}

Methodology papers Lacuna builds on:

  • Atilgan et al. (2001) Biophys. J. 80(1):505-515 - Anisotropic Network Model (NMA backend)
  • Halgren (2009) J. Chem. Inf. Model. 49(2):377-389 - SiteMap druggability scoring
  • Le Guilloux et al. (2009) BMC Bioinformatics 10:168 - fpocket alpha-sphere approach
  • Schmidtke & Barril (2010) J. Med. Chem. 53(15):5858-5867 - enclosure scoring

License

MIT - free to use, study, modify, share, and embed in closed-source or commercial work, with no copyleft obligation.

Versions 0.2.0 through 0.3.1 were released under AGPL-3.0 and remain available under those terms. MIT applies from version 1.0.0 onward. Lacuna moved back to a permissive license because its central recommendation is to combine several detectors, and a copyleft license makes that combination harder for exactly the people the work is aimed at.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lacuna_pockets-1.0.1.tar.gz (19.5 MB view details)

Uploaded Source

Built Distribution

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

lacuna_pockets-1.0.1-py3-none-any.whl (72.3 kB view details)

Uploaded Python 3

File details

Details for the file lacuna_pockets-1.0.1.tar.gz.

File metadata

  • Download URL: lacuna_pockets-1.0.1.tar.gz
  • Upload date:
  • Size: 19.5 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for lacuna_pockets-1.0.1.tar.gz
Algorithm Hash digest
SHA256 941dda1d7d742a4477fcacac8e8309629584b7bab9633d40004be550a6db77b7
MD5 c5bafb6ad7a039ccdbe583dd32798b5d
BLAKE2b-256 a9615c5c17384e83d8a40faaa797d12c21c0066ca4e6b9ddd3ba0032b9ba9f9b

See more details on using hashes here.

Provenance

The following attestation bundles were made for lacuna_pockets-1.0.1.tar.gz:

Publisher: publish.yml on mooreneural/lacuna

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

File details

Details for the file lacuna_pockets-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: lacuna_pockets-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 72.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for lacuna_pockets-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 31367015dd493b2920402f20204722606420859e92fbbc1fae2807441afa968a
MD5 959773b1b01f7897d2768fcb61fbaf79
BLAKE2b-256 163262d1dec1b8cb3ef5500b92f0557ca35ee64abf3566357521b9b97c668511

See more details on using hashes here.

Provenance

The following attestation bundles were made for lacuna_pockets-1.0.1-py3-none-any.whl:

Publisher: publish.yml on mooreneural/lacuna

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

Release history Release notifications | RSS feed

1.0.3

2 files

1.0.2

2 files

This release

1.0.1 This release

2 files

1.0.0

2 files

0.3.1

2 files

0.3.0

2 files

0.2.1

2 files

0.2.0

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

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