Metadata-Version: 2.4
Name: rustina
Version: 0.3.0
Classifier: Development Status :: 5 - Production/Stable
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Rust
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
Requires-Dist: rdkit>=2024.3 ; extra == 'prep'
Requires-Dist: molscrub>=0.2.2 ; extra == 'prep'
Requires-Dist: meeko>=0.7.1 ; extra == 'prep'
Requires-Dist: scipy>=1.12.0 ; extra == 'prep'
Requires-Dist: gemmi>=0.6.0 ; extra == 'prep'
Provides-Extra: prep
License-File: LICENSE
Summary: CPU molecular docking with native GNINA-compatible CNN rescoring
Author: Adrien H. Cerdan
Requires-Python: >=3.9
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Homepage, https://github.com/AdrienCerdan/rustina
Project-URL: Issues, https://github.com/AdrienCerdan/rustina/issues
Project-URL: Repository, https://github.com/AdrienCerdan/rustina

# Rustina

Rustina is a CPU molecular-docking engine written in Rust. It provides Vina and
Vinardo empirical scoring, Rustina/Vina/QuickVina2 search, RILC and BFGS local
optimization, and native inference for the GNINA 1.3 default three-model CNN
ensemble and its optional distilled `fast` model. The supported v1 interfaces
are Python and the command-line program.

Rustina v1 targets Linux x86-64 and Python 3.9 or newer.

Search policy is explicit:

- `rustina` (default) is the optimized CPU policy with capped steps, RILC,
  early termination, and restart/polish-reuse heuristics.
- `vina` follows the AutoDock Vina 1.2.3 Monte Carlo/BFGS policy and defaults
  to 0.375 A grid spacing.
- `qvina2` uses that Vina budget with the official QuickVina 2 BFGS-history
  significance test.

The compatibility modes reject custom steps, BFGS iteration counts, and RILC.
Use `search_mode="rustina"` for experimental optimizer or budget combinations.

Rustina v1 also includes opt-in template docking for congeneric ligands through
`dock_reference()` and `screen_reference()`.

## Installation

Install the Python package:

```bash
python -m pip install rustina
```

Ligand preparation is an optional extra because it installs RDKit, molscrub,
Meeko, SciPy, and Gemmi:

```bash
python -m pip install "rustina[prep]"
```

For a source checkout, build a production extension with:

```bash
maturin develop --release
```

## Python API

Inputs to docking and scoring are prepared PDBQT paths or raw PDBQT strings.
CNN rescoring uses Rustina's bundled GNINA 1.3 default ensemble. Set
`cnn="fast"` for GNINA's distilled model, `skip_cnn=True` to disable rescoring,
or supply a native weight path through `cnn`.

```python
import rustina

poses = rustina.dock(
    receptor="data/receptor.pdbqt",
    ligand="data/ligand.pdbqt",
    cx=-14.0,
    cy=18.0,
    cz=-15.0,
    sx=14.0,
    sy=18.0,
    sz=15.0,
    runs=8,
    local_optimizer="rilc",
    threads=8,
    seed=42,
)
print(poses[0]["affinity"], poses[0]["cnn_score"])
```

Screen multiple ligands while reusing the receptor grid:

```python
results = rustina.screen(
    receptor="data/receptor.pdbqt",
    ligands=["ligand-1.pdbqt", "ligand-2.pdbqt"],
    cx=-14.0,
    cy=18.0,
    cz=-15.0,
    sx=14.0,
    sy=18.0,
    sz=15.0,
    threads=8,
)
```

Optional ligand preparation accepts SMILES, structure files/blocks, or an
RDKit molecule:

```python
states = rustina.prepare_ligand("CC(=O)Nc1ccc(O)cc1", random_seed=42)
print(states[0]["pdbqt"])
```

### Template docking

Template docking is a supported, opt-in v1 feature for congeneric series when a
ligand with known coordinates is already in the receptor coordinate frame. It requires the
`prep` extra because maximum-common-substructure matching is performed with
RDKit:

```python
poses = rustina.dock_reference(
    receptor="receptor.pdbqt",
    ligand="query.sdf",
    reference="co-crystal-ligand.sdf",
    cx=-14.0,
    cy=18.0,
    cz=-15.0,
    sx=14.0,
    sy=18.0,
    sz=15.0,
    runs=8,
    seed=42,
)
print(
    poses[0]["relaxed_core_rmsd"],
    poses[0]["physical_score"],
    poses[0]["reference_satisfied"],
)
```

The mapped heavy-atom core is restrained by a soft flat-bottom potential during
search, followed by a short unrestrained relaxation. Results report constrained
and relaxed core RMSD, physical and guided scores, the restraint penalty,
mapping identity, both MCS coverage fractions, and whether the relaxed core
remains within the default 1.0 Angstrom satisfaction threshold.

Automatic guidance requires at least six mapped heavy atoms and 50% query
coverage. An explicit `atom_map` can define a smaller anchor. Rustina fails
rather than silently switching to free docking when automatic guidance does not
meet these gates. `screen_reference()` applies the same workflow to a series
while reusing one receptor grid.

Up to eight symmetry-distinct MCS mappings are evaluated deterministically.
The exact requested run budget is distributed across them globally; mappings do
not multiply the run count. Candidates are merged and deduplicated before one
CNN rescore using the requested `cnn_pool_size`.

#### Timing smoke test

A release-mode `5SAK_ZRY` methyl-analog smoke test used four total runs, four
mapping hypotheses, empirical scoring, matched pose-pool budgets, and three
seeds. Median wall times were:

| CPU threads | Normal docking | Template docking | Difference |
|---:|---:|---:|---:|
| 2 | 0.711 s | 0.732 s | +3% |
| 8 | 0.620 s | 0.682 s | +10% |

The two-thread end-to-end template call, including MCS generation and final
relaxation, took 0.758 s median. These numbers characterize one small smoke
case, not expected performance across ligand series. The solved-congeneric
qualification requirements are documented in
[`docs/benchmarks/REFERENCE_DOCKING_PROTOCOL.md`](docs/benchmarks/REFERENCE_DOCKING_PROTOCOL.md).

Template docking assumes the reference and target receptor use the same
coordinate frame. It does not align receptor structures or provide shape-only
or pharmacophore guidance. `5SAK_ZRY` demonstrates restraint behavior and
physical validity only; its methyl analog has no experimental pose and is not
an accuracy benchmark.

`rustina.build_profile()` returns `debug` or `release`. Performance results are
valid only when the actually imported extension reports `release`.

## Command line

```bash
rustina dock \
  --receptor data/receptor.pdbqt \
  --ligand data/ligand.pdbqt \
  --output docked.pdbqt \
  --cx -14 --cy 18 --cz -15 \
  --sx 14 --sy 18 --sz 15 \
  --runs 8 --threads 8 --seed 42

rustina score \
  --receptor data/receptor.pdbqt \
  --ligand data/ligand.pdbqt \
  --cnn
```

Use `rustina dock --help` and `rustina score --help` for the complete supported
options.

## Reproducibility and benchmarks

Docking is deterministic when `seed` is provided. Release benchmarks must
record the Rustina version, build profile, input dataset revision, complete
arguments, CPU model, and raw per-target results. Historical pre-v1 research is
preserved by the `pre-v1-research` tag; v1 benchmark qualification lives under
`docs/benchmarks/`.

### PoseBusters + Astex Benchmark (393 targets, budget=8, seed=42)

Inputs use experimental coordinates converted to PDBQT via Meeko.

#### rustina (native RILC)

| Mode       | RMSD<=2A | PB-valid | Both  | Median s |
|------------|----------|----------|-------|----------|
| fast       | 74.1%    | 93.3%    | 71.3% | 0.61     |
| default    | —        | —        | —     | —        |
| empirical  | 64.3%    | 97.1%    | 63.3% | 0.30     |

#### rustina_vina

| Mode       | RMSD<=2A | PB-valid | Both  | Median s |
|------------|----------|----------|-------|----------|
| fast       | 77.5%    | 95.3%    | 74.9% | 5.04     |
| default    | 76.8%    | 94.5%    | 74.2% | 6.15     |
| empirical  | 72.4%    | 96.5%    | 70.7% | 3.59     |

#### rustina_qvina2

| Mode       | RMSD<=2A | PB-valid | Both  | Median s |
|------------|----------|----------|-------|----------|
| fast       | 75.6%    | 95.6%    | 73.3% | 2.54     |
| default    | 74.0%    | 94.1%    | 71.8% | 3.11     |
| empirical  | 71.0%    | 97.0%    | 70.0% | 2.28     |

#### C++ reference (paired, same targets)

| Engine       | RMSD<=2A | PB-valid | Both  | Median s |
|--------------|----------|----------|-------|----------|
| vina 1.2.3   | 71.5%    | 98.3%    | 70.8% | 5.50     |
| qvina2       | 73.4%    | 98.6%    | 72.8% | 3.30     |

CNN fast is the recommended mode: +3-5% accuracy over empirical at ~1.1x cost.
CNN default (GNINA 1.3 ensemble) is marginally better but 1.3-1.9x slower.
Native RILC + CNN fast achieves 74% accuracy at 0.6s — 8x faster than BFGS modes.

### Controlled Benchmark — ETKDG Conformers (393 targets, budget=8, seed=42)

Generated ETKDGv3+UFF start conformers; crystallographic coordinates are
evaluation-only.

| Engine          | Mode | RMSD<=2A | PB-valid | Both  | Median s |
|-----------------|------|----------|----------|-------|----------|
| rustina         | fast | 66.4%    | 63.3%    | 63.3% | 0.64     |
| rustina_qvina2  | fast | 72.4%    | 69.8%    | 69.8% | 2.40     |

ETKDG conformers cost ~5-9% accuracy on PoseBusters vs experimental
coordinates, but only ~2% on Astex. The qvina2 mode is more robust to
conformer quality.

Rustina is research software. Docking scores and predicted poses are not a
substitute for experimental evidence or clinical decision-making.

## Attribution and license

Rustina is MIT licensed. Its algorithms and bundled CNN parameters build on
AutoDock Vina, Smina, QuickVina2, GNINA, Vina-GPU, and related published work.
See [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) and
[CITATION.cff](CITATION.cff) for provenance and citations. Model conversion is
documented in [docs/models.md](docs/models.md).

