Skip to main content

Retrosynthesis toolkit: single-step prediction, multi-step route planning, synthesizability scoring

Project description

synomega

Retrosynthesis toolkit: single-step prediction → multi-step route planning → synthesizability scoring.

synthesizability   is this target reachable from purchasable material, in N steps?
     ↑
search             Retro* / MCTS / best-first over an AND-OR graph
     ↑
singlestep         product SMILES -> ranked reactant candidates

The layers are decoupled by a deliberately narrow interface: a single-step backend only implements predict(smiles, top_k) -> [Prediction]. Whether it is a graph neural network, a transformer, or plain template matching is invisible to the planner.

Install

pip install -e .            # core: rdkit + numpy, no torch
pip install -e '.[gnn]'     # adds the D-MPNN neural backend

The neural backend is an optional extra on purpose — the template-rule backend runs anywhere, with no GPU and no torch.

Quick start

from synomega import Planner, SynthesizabilityScorer
from synomega.singlestep import TemplateGNN
from synomega.stock import InMemoryStock

model   = TemplateGNN.from_pretrained("../ml-template-gnn/runs/uspto50k_r0_min10")
stock   = InMemoryStock.from_keys_file("emolecules.keys.gz")
planner = Planner(model, stock, algorithm="retrostar")

result = planner.plan("CC(=O)Nc1ccccc1", max_depth=5, time_limit=60)
print(result.solved)
print(result.best_route.describe())
target: CC(=O)Nc1ccccc1
solved: True  steps: 2  depth: 2  bb_coverage: 1.00
  [1] CC(=O)O.Nc1ccccc1>>CC(=O)Nc1ccccc1  (score=0.4348)
  [2] O=[N+]([O-])c1ccccc1>>Nc1ccccc1  (score=0.2174)

Synthesizability

scorer = SynthesizabilityScorer(planner)

r = scorer.score("CC(=O)Nc1ccccc1", max_steps=5)
r.solved         # True — a complete route to purchasable material exists
r.bb_coverage    # 1.0 — fraction of leaves that are buyable
r.min_depth      # 2 — steps in the shortest solved route

report = scorer.score_batch(targets, max_steps=5)
report.solve_rate         # headline benchmark number
report.mean_bb_coverage
report.to_dataframe()

The two synthesizability metrics

These get conflated in the literature; synomega keeps them apart because they answer different questions.

Metric Meaning Use it for
solved@N / solve_rate Binary: does a route of depth ≤ N exist whose leaves are all purchasable? Comparing against published numbers
bb_coverage@N Continuous: fraction of the best route's leaves that are purchasable Ranking molecules by how close they are

bb_coverage matters because most targets are unsolved at realistic step limits. A 5-step route with 4 of 5 leaves buyable scores 0.8, not 0 — so a near-miss is distinguishable from a total failure.

CLI

# one-time: precompute InChIKeys so later loads take seconds, not minutes
synomega build-stock --catalogue emolecules.smi.gz --out emolecules.keys.gz

synomega plan  --target "CC(=O)Nc1ccccc1" --model runs/uspto50k_r0_min10 \
             --stock emolecules.keys.gz --stock-is-keys --max-steps 5

synomega score --targets targets.smi --model runs/uspto50k_r0_min10 \
             --stock emolecules.keys.gz --stock-is-keys \
             --max-steps 5 --out report.json

Search algorithms

Algorithm Character When
retrostar Expands the frontier molecule with the lowest estimated total route cost (Chen et al. 2020) Default
mcts UCT with greedy rollouts; tolerates an unreliable top-1 When the single-step model is weak
bfs Best-first on g + h Baseline, debugging

All three share the AND-OR graph, the budget, and the route extractor, so they are directly comparable.

Design notes

AND-OR graph, not a tree. A molecule node is solved if it is in stock or any of its reactions is solved; a reaction is solved if all its reactants are. Molecules are interned by InChIKey, so an intermediate reached down two branches is one node expanded once. Cycles are rejected when the edge is created — a route that makes X from X is not a route.

Batched expansion. Search is naturally "expand one node, call the model once", which leaves a GPU idle. The algorithms pull a batch of frontier nodes and issue one predict_batch.

Caching. Search revisits the same molecule constantly. Planner(cache=True) (the default) memoizes expansions; cache_path= persists them to SQLite so the cache survives across runs.

Stock membership is by InChIKey, so keys computed here match a vendor catalogue written by a different toolkit. There is deliberately no Bloom-filter backend: false positives would inflate solve-rate and quietly break comparability with published numbers.

Status

Implemented and tested: chem layer, single-step interface + template-rule and D-MPNN backends, expansion cache, AND-OR graph, all three search algorithms, route extraction/scoring/serialization, synthesizability metrics, CLI.

Reserved but not implemented: reaction conditions (RouteStep.conditions is always None); a condition model can fill it without touching the search layer.

pytest          # 43 tests

Project details


Download files

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

Source Distribution

synomega-0.1.0.tar.gz (44.2 kB view details)

Uploaded Source

Built Distribution

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

synomega-0.1.0-py3-none-any.whl (51.6 kB view details)

Uploaded Python 3

File details

Details for the file synomega-0.1.0.tar.gz.

File metadata

  • Download URL: synomega-0.1.0.tar.gz
  • Upload date:
  • Size: 44.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for synomega-0.1.0.tar.gz
Algorithm Hash digest
SHA256 eb7c02aa65c6af50124697fa1a329a93b1ae25e885248f8ef2b0438a4c2a3685
MD5 5277356863554f372df2dbc813ef6b8e
BLAKE2b-256 09a5d946b4aad0dc0fdf2d72abe13890873547bf12d035d58fe96c16eb96f687

See more details on using hashes here.

File details

Details for the file synomega-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: synomega-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 51.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for synomega-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 223f6ec4ab686c7f9ba4a16e7ffb284ca5645f00f02ff9773f6a66410fe8e43a
MD5 695eacc017680ebb8e50d2b3ee746e27
BLAKE2b-256 6cba67db5394c59b6acb83b1779f6ab8ee3ab959facd7c8abd83fd1718d16492

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page