Train and apply Transformer models that translate invalid SMILES back into valid ones.
Project description
🧬 uncorrupt-smiles
This project implements the approach described in:
Schoenmaker, L., Béquignon, O.J.M., Jespers, W. & Gerard J.P. van Westen
UnCorrupt SMILES: a novel approach to de novo design.
Journal of Cheminformatics 15, 22 (2023). https://doi.org/10.1186/s13321-023-00696-x
uncorrupt-smiles fixes broken SMILES. Give it a trained checkpoint and a SMILES string (or a CSV of them), and it hands back a valid molecule — from the CLI or from Python, one string or a streamed million-row file.
from uncorrupt_smiles import Uncorrupt
uncorrupt = Uncorrupt() # downloads and loads the bundled pretrained checkpoint, once
uncorrupt.fix_smiles("C1=CC=CC=C1(")
# -> ["c1ccccc1"]
Uncorrupt is the main entrypoint: no checkpoint path, vocab, or device to wire up — it
downloads the pretrained checkpoint bundled with this project the first time it's needed (see
Fetch example data below) and picks CUDA automatically if available.
Pass checkpoint="path/to/your.pkg" to use a self-trained checkpoint instead. Everything else —
standardizing datasets,
generating synthetic errors, training your own corrector — is secondary tooling for anyone who
wants to build a new model, built around the same three composable, argument-driven,
streaming-first building blocks: no config files, and no dataset is ever loaded fully into
memory (polars streams every CSV in and out).
📦 Installation
Requires Python 3.11+.
pip install uncorrupt-smiles
Optional extras:
pip install "uncorrupt-smiles[analysis]" # pandas/matplotlib/seaborn for the analysis/ scripts
pip install "uncorrupt-smiles[test]" # pytest, for running the test suite
To work from a clone instead (e.g. to run the test suite or modify the source):
git clone https://github.com/OlivierBeq/uncorrupt-smiles.git
cd uncorrupt-smiles
pip install -e ".[test]"
Core dependencies — torch >= 2.4, rdkit >= 2024.3.1, polars >= 1.0 — are resolved by pip
to whichever wheel (CPU or CUDA) matches your machine.
Note: checkpoints produced by earlier versions of this project (including the archive on Zenodo) use a different, incompatible checkpoint format and cannot be loaded by
Seq2Seq.load_checkpoint. Retrain to obtain a checkpoint this version can use.
🚀 Usage
Run uncorrupt-smiles <subcommand> -h for the full flag list of any subcommand. The CLI and the
Python API expose exactly the same operations, in the same order of importance: fixing SMILES
first, then the supporting tooling for anyone building their own corrector.
CLI
🩹 Fix
uncorrupt-smiles fix \
--checkpoint data/performance/transformer_multiple_12_PAPYRUS_200_16_3.pkg \
--input-csv generated/gan_ckpt100.csv --smiles-col SMILES \
--output-csv generated/gan_ckpt100_fixed.csv
📥 Fetch example data
The example datasets and pretrained checkpoint under data/, generated/, and rawdata/ are
not committed to this repository (to keep it small) — they're downloaded on demand from
Zenodo and this repo's release assets, streamed straight
to disk and checksum-verified.
uncorrupt-smiles fetch-data
Already-present files are skipped by default (pass --force to redownload). Use --only to
fetch specific files, e.g. uncorrupt-smiles fetch-data --only rawdata/gbd_8.csv.
🧹 Standardize
Canonicalizes SMILES via RDKit. Anything RDKit can't parse — or an empty string — is dropped, not silently kept.
uncorrupt-smiles standardize \
--input-csv rawdata/PAPYRUS.csv --smiles-col SMILES --separator ";" \
--output-csv data/papyrus_std.csv --threshold 200
--smiles-col and --separator are explicit arguments, so this works on any CSV — not just
PAPYRUS-shaped exports.
🧪 Generate errors
Introduces synthetic, RDKit-confirmed-invalid corruptions of valid SMILES, for training or evaluating a corrector.
uncorrupt-smiles generate-errors \
--input-csv data/papyrus_std.csv --smiles-col STD_SMILES \
--fragment-csv rawdata/gbd_8.csv --fragment-col FRAGMENT \
--train-csv data/errors/train.csv --dev-csv data/errors/dev.csv \
--invalid-type all --num-errors 1
--invalid-type selects which corruption(s) to introduce: all, multiple, or one of exists,
par, permut, ring, syntax, valence, arom.
🏋️ Train
Training, evaluation, and checkpointing all live as methods on the model itself.
uncorrupt-smiles train \
--train-csv data/errors/train.csv --dev-csv data/errors/dev.csv \
--checkpoint-out data/performance/model.pkg \
--hid-dim 128 --n-layers 2 --n-heads 4 --pf-dim 256 --batch-size 32 --epochs 20
The defaults above are sized for an 8GB GPU. For a full-scale run on more powerful hardware,
just pass larger values — e.g. --hid-dim 512 --n-heads 8 --pf-dim 1024 --batch-size 128.
--device defaults to CUDA if available, otherwise CPU. Pass --resume <checkpoint> to continue
training an existing checkpoint.
Python
🩹 Fix
from uncorrupt_smiles import Uncorrupt
uncorrupt = Uncorrupt() # or Uncorrupt(checkpoint="data/performance/model.pkg", device="cpu")
# a single SMILES, or a list
fixed = uncorrupt.fix_smiles(["C1=CC=CC=C1(", "CC(=O)Oc1ccccc1C(=O)O"])
# or stream an entire CSV in and out
uncorrupt.fix_smiles_csv(
"generated/gan_ckpt100.csv", "SMILES", "generated/gan_ckpt100_fixed.csv",
)
Uncorrupt is a thin wrapper around Seq2Seq, the model class itself — reach for it directly
if you're already holding a model (e.g. right after fit(), or loaded via
Seq2Seq.load_checkpoint(path, device)), since a checkpoint's src_vocab/trg_vocab are
attached to the model, not passed around separately:
from uncorrupt_smiles.transformer import Seq2Seq
model = Seq2Seq.load_checkpoint("data/performance/model.pkg", device="cpu")
model.fix_smiles(["C1=CC=CC=C1("])
fix_smiles/fix_smiles_csv work with a checkpoint trained by this model architecture, or any
future architecture exposing the same generate(...) method — nothing here is
Transformer-specific beyond Seq2Seq itself.
📥 Fetch example data
from uncorrupt_smiles.fetch_data import fetch_all
fetch_all()
Already-present files are skipped by default (pass force=True to redownload). Use
only=[...] to fetch specific files, e.g. fetch_all(only=["rawdata/gbd_8.csv"]).
🧹 Standardize
from uncorrupt_smiles.preprocess import standardize_smiles, standardize_stream
standardize_smiles("C(C)O") # -> "CCO"
standardize_stream(
"rawdata/PAPYRUS.csv", smiles_col="SMILES", output_csv="data/papyrus_std.csv",
length_threshold=200, separator=";",
)
🧪 Generate errors
from uncorrupt_smiles.invalid_smiles import generate_errors, reservoir_sample_fragments, write_errors_split
# stream straight to train/dev CSVs
write_errors_split(
"data/papyrus_std.csv", "STD_SMILES",
"rawdata/gbd_8.csv", "FRAGMENT",
"data/errors/train.csv", "data/errors/dev.csv",
seed=42, invalid_type="all",
)
# or generate pairs in memory, e.g. for a handful of molecules
fragments = reservoir_sample_fragments("rawdata/gbd_8.csv", "FRAGMENT", k=20_000, seed=42)
pairs = list(generate_errors(["CCO", "c1ccccc1"], fragments, seed=42))
🏋️ Train
from uncorrupt_smiles.data import iter_csv_column, make_loader
from uncorrupt_smiles.transformer import Seq2Seq
from uncorrupt_smiles.vocab import Vocab
src_vocab = Vocab.build_from_lines(iter_csv_column("data/errors/train.csv", "ERROR"))
trg_vocab = Vocab.build_from_lines(iter_csv_column("data/errors/train.csv", "STD_SMILES"))
model = Seq2Seq.build(
len(src_vocab), len(trg_vocab), max_length=202, device="cuda",
src_pad_idx=src_vocab.pad_idx, trg_pad_idx=trg_vocab.pad_idx,
hid_dim=128, n_layers=2, n_heads=4, pf_dim=256,
)
train_loader = make_loader("data/errors/train.csv", "ERROR", "STD_SMILES", src_vocab, trg_vocab, batch_size=32)
dev_loader = make_loader("data/errors/dev.csv", "ERROR", "STD_SMILES", src_vocab, trg_vocab, batch_size=32)
model.fit(train_loader, dev_loader, src_vocab, trg_vocab, epochs=20, checkpoint_path="data/performance/model.pkg")
The defaults above are sized for an 8GB GPU; pass larger hid_dim/n_heads/pf_dim/batch size
keyword arguments for a full-scale run on more powerful hardware. Use
Seq2Seq.load_checkpoint(...) to continue training an existing checkpoint.
🗂️ Package layout
| Module | Purpose |
|---|---|
src/uncorrupt_smiles/uncorrupt.py |
Uncorrupt — the main entrypoint. Wraps Seq2Seq.load_checkpoint, defaulting to the bundled pretrained checkpoint (downloaded on demand). |
src/uncorrupt_smiles/transformer.py |
The Seq2Seq model. Fixing SMILES (fix_smiles/fix_smiles_csv), training, evaluation, and checkpoint save/load are all methods on the model itself; src_vocab/trg_vocab are attached to the model, not passed around separately. |
src/uncorrupt_smiles/fetch_data.py |
Downloads example datasets / the pretrained checkpoint on demand. |
src/uncorrupt_smiles/invalid_smiles.py |
Per-SMILES error functions (exists_error, par_error, permutation, ring_error, syntax_error, valence_error, arom_error, introduce_error) plus streaming orchestration (generate_errors, write_errors_split, reservoir_sample_fragments). |
src/uncorrupt_smiles/preprocess.py |
standardize_smiles / standardize_stream. |
src/uncorrupt_smiles/vocab.py |
Vocab — character-level vocabulary, built by streaming. |
src/uncorrupt_smiles/data.py |
Streaming CSV helpers and the training IterableDataset. |
src/uncorrupt_smiles/cli.py |
The uncorrupt-smiles entrypoint. |
✅ Testing
pip install -e ".[test]"
pytest
The suite uses small synthetic fixtures and tiny model dimensions — it runs in a few seconds and comfortably fits an 8GB-VRAM/modest-CPU machine.
🙏 Acknowledgments
uncorrupt-smiles builds on the original SMILES-corrector research and codebase by Linde Schoenmaker.
📄 License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
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 uncorrupt_smiles-1.0.0.post0.tar.gz.
File metadata
- Download URL: uncorrupt_smiles-1.0.0.post0.tar.gz
- Upload date:
- Size: 48.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2c2df4d1175ebf7136d0723043d7e0099f1f6950623ed6555f1e96acc1c98612
|
|
| MD5 |
9f709b5173e56405b5e52fb270e199b5
|
|
| BLAKE2b-256 |
43c33ceff1906b3b1c355247e9bcb995af65296928015b593fa06c8cb79cda43
|
File details
Details for the file uncorrupt_smiles-1.0.0.post0-py3-none-any.whl.
File metadata
- Download URL: uncorrupt_smiles-1.0.0.post0-py3-none-any.whl
- Upload date:
- Size: 43.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
02c080e5050d18e569efb8c835d08c17c000844b9582c53ed903f91db81f4b35
|
|
| MD5 |
d8b7eabd5852919c2ce26fdbc48efb2e
|
|
| BLAKE2b-256 |
80fa6aab6c567c5f9a348ef0e0662fd5d033367140347e31e247723ff1b33dc6
|