Skip to main content

RDKit SMILES writer that orders atoms by their original atom index instead of canonical rank

Project description

idxsmiles

PyPI CI License: MIT

An RDKit SMILES writer that orders atoms by their original atom index instead of RDKit's canonical rank — deterministic, human-aligned output, with stereochemistry preserved.

Why

Chem.MolToSmiles(mol) (canonical=True) picks the next atom to visit using canonical invariant ranks (Canon::rankMolAtoms), which have no relationship to the atom's index in the Mol object. The resulting SMILES atom order can look arbitrary relative to how the molecule was built or numbered.

Chem.MolToSmiles(mol, canonical=False) looks like the fix — internally its ranks array is just [0, 1, ..., N-1] (the atom indices themselves). But Canon::dfsBuildStack (RDKit's C++ traversal code) doesn't sort candidates by that rank alone: if a candidate bond is part of a ring, a large bond-type-dependent term gets added to its sort key, so a lower-index ring neighbor can still be visited after a higher-index non-ring neighbor. This is reproducible: renumber C1=CC(C)C1 so the ring-closure atom gets index 3 and the exocyclic methyl gets index 4, and canonical=False still emits atom 4 before atom 3.

Because that bias is baked into RDKit's compiled writer and can't be disabled from Python, this library implements its own DFS-based SMILES writer: atoms are always visited in strict ascending atom-index order, with no exceptions for ring bonds — while still producing valid, stereo-correct SMILES that RDKit can re-parse into the identical structure.

Installation

pip install idxsmiles

Usage

from rdkit import Chem
from idxsmiles import mol_to_smiles

mol = Chem.MolFromSmiles("CC(=O)Oc1ccccc1C(=O)O")
smiles = mol_to_smiles(mol)

mol_to_smiles(mol: Chem.Mol) -> str takes an RDKit Mol and returns a SMILES string whose atom order follows the input Mol's atom indices as closely as the molecular graph allows (ring/branch structure still constrains what's possible — the result is not necessarily perfectly monotonic, but every branch point prefers the lowest available index). Re-parsing the output reproduces the same molecule, including stereochemistry.

What's handled

  • Ring closures (including multi-digit %(NN) closures)
  • Multi-fragment SMILES (.-separated)
  • Aromaticity, charges, isotopes, atom maps
  • Standard tetrahedral chirality (@/@@), including 2-real-neighbor, lone-pair-bearing stereocentres (e.g. chiral P, S)
  • Double-bond cis/trans stereo (/, \)
  • Dative bonds (->, <-)

Out of scope

Non-tetrahedral chirality (square planar, trigonal bipyramidal, octahedral) and cumulated/shared stereo double bonds (one single bond flanking two stereo double bonds at once, e.g. conjugated diazo systems) are not supported. These raise NotImplementedError rather than silently emitting incorrect stereochemistry.

Testing

pip install rdkit pytest
pytest test_idxsmiles.py

The test suite checks round-trip structural correctness under random atom renumbering across a curated set of molecules (rings, chirality, cis/trans, dative bonds, multi-fragment), a dedicated regression test for the ring-bond bias described above, and an independent check that atoms are actually written in ascending-index order (not just that the round trip happens to produce an equivalent structure) — verified by tagging atoms with their index as an atom-map number and confirming the re-parsed output reads them back in the same order as a separately implemented reference traversal.

How it works

RDKit is still used for the low-level pieces:

  • Atom.GetSmarts() / Bond.GetSmarts() for atom/bond text (these happen to call straight into RDKit's own SMILES atom/bond writer, SmilesWrite::GetAtomSmiles / GetBondSmiles)
  • The chirality- and double-bond-stereo bookkeeping is a Python port of the logic in Canon::canonicalizeFragment / Canon::canonicalizeDoubleBond, adapted to run against this library's own traversal order instead of RDKit's canonical one.

Everything else — the DFS traversal order, ring-closure digit assignment, and branch/parenthesis structure — is implemented independently in idxsmiles.py.

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

idxsmiles-0.1.1.tar.gz (9.7 kB view details)

Uploaded Source

Built Distribution

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

idxsmiles-0.1.1-py3-none-any.whl (10.3 kB view details)

Uploaded Python 3

File details

Details for the file idxsmiles-0.1.1.tar.gz.

File metadata

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

File hashes

Hashes for idxsmiles-0.1.1.tar.gz
Algorithm Hash digest
SHA256 fa9342c28a0d5a834b704caa750be35e39d71dfd52201bcae40cffde088b7e20
MD5 50d0a1b69e99771f0a090b67a62d2ebc
BLAKE2b-256 c922c6d93947f481fd85ea341762e31e4c5946fff4c99a885575b09b17d9368b

See more details on using hashes here.

File details

Details for the file idxsmiles-0.1.1-py3-none-any.whl.

File metadata

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

File hashes

Hashes for idxsmiles-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 1e3ff65b91d1cf6c9e43123dd29df81282c92ee4d8490c20e9488b366536a9c8
MD5 d8fd1440d83a5cfcde494423eeb676d7
BLAKE2b-256 71da1b15412fddb11f1c8bce2c4c68de6af820550fa278bf21e750849d8ee1e4

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