Skip to main content

Train and apply Transformer models that translate invalid SMILES back into valid ones.

Project description

🧬 uncorrupt-smiles

License Python

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


Download files

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

Source Distribution

uncorrupt_smiles-1.0.0.tar.gz (47.8 kB view details)

Uploaded Source

Built Distribution

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

uncorrupt_smiles-1.0.0-py3-none-any.whl (43.2 kB view details)

Uploaded Python 3

File details

Details for the file uncorrupt_smiles-1.0.0.tar.gz.

File metadata

  • Download URL: uncorrupt_smiles-1.0.0.tar.gz
  • Upload date:
  • Size: 47.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for uncorrupt_smiles-1.0.0.tar.gz
Algorithm Hash digest
SHA256 50b28896ea77a9585a0abf8c6394b09481a4ccab59aadb961df40a7e6653414d
MD5 1bbe2b51550fbfaf2e360e4c26bc963a
BLAKE2b-256 b520381c0f546876f1d2e33d5bbec58ec41279be47b8a27ddfd0aa7115d00216

See more details on using hashes here.

File details

Details for the file uncorrupt_smiles-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for uncorrupt_smiles-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0efad668253f8fa892ec93f270c7d8f1a50249a5839e6616e608cd06dfceba14
MD5 247782831c3c6b81b10a55ec90f24df6
BLAKE2b-256 d16238cc8f09b1051bc4e1b2a52b2a7556fe64af1468932938e7327a60306e0a

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