Summary
LoQI reproduces carbon and phosphorus stereocenters essentially perfectly, but the configuration it generates at a sulfoxide (tetrahedral sulfur) stereocenter is independent of the input SMILES chirality — it samples a fixed, ~63/37 R-biased prior regardless of whether R or S is requested.
Surfaced while building a docking ligand-prep pipeline. Many drugs are chiral sulfoxides (omeprazole/esomeprazole, modafinil/armodafinil, sulindac, oxfendazole, …), where the wrong configuration is the wrong molecule.
Evidence — enantiomer-conditioning experiment
Each stereoisomer is fed separately (30 conformers, --seed 0); the CIP label is read back from each generated 3D structure. The ETKDG column is an RDKit embedding of the same SMILES — it confirms the target is achievable and that the 3D→CIP assignment is reliable.
| input |
SMILES |
intended |
LoQI output (n=30) |
ETKDG |
| sulfoxide, R |
C[S@@](=O)c1ccccc1 |
R |
R:18, S:12 |
10/10 R |
| sulfoxide, S |
C[S@](=O)c1ccccc1 |
S |
R:20, S:10 |
10/10 S |
| phosphine oxide, R |
CC[P@](=O)(C)c1ccccc1 |
R |
R:30 |
10/10 R |
| phosphine oxide, S |
CC[P@@](=O)(C)c1ccccc1 |
S |
S:30 |
10/10 S |
| carbon control, R |
C[C@H](O)c1ccccc1 |
R |
R:30 |
10/10 R |
Both sulfoxide inputs collapse to the same ~63/37 R-biased output → the S-center configuration is not conditioned on the input. Phosphorus — also a tetrahedral heteroatom stereocenter — and carbon are conditioned perfectly.
At the population level, across ~730 achievable, fully-defined chiral conformers from a 1000-molecule ChEMBL run, carbon centers reproduce at ~100% while the residual stereo errors are dominated by sulfoxide centers.
Reproduction
Standalone; needs only the shim + RDKit. Run from the repo root.
import json, subprocess, sys, tempfile
from collections import Counter
from pathlib import Path
from rdkit import Chem, RDLogger
RDLogger.DisableLog("rdApp.*")
SHIM = "scripts/loqi_inference_shim.py"
CASES = [("sulfoxide R", "C[S@@](=O)c1ccccc1"), ("sulfoxide S", "C[S@](=O)c1ccccc1"),
("P-oxide R", "CC[P@](=O)(C)c1ccccc1"), ("P-oxide S", "CC[P@@](=O)(C)c1ccccc1")]
def cip(s):
m = Chem.MolFromSmiles(s)
return dict(Chem.FindMolChiralCenters(m, includeUnassigned=True, useLegacyImplementation=False))
payload, meta = [], {}
for label, smi in CASES:
ik = Chem.MolToInchiKey(Chem.MolFromSmiles(smi))
payload.append({"inchikey": ik, "smiles": smi}); meta[ik] = (label, cip(smi))
with tempfile.TemporaryDirectory() as t:
t = Path(t); (t/"in.json").write_text(json.dumps(payload)); o = t/"out"; o.mkdir()
subprocess.run([sys.executable, SHIM, "--input", str(t/"in.json"), "--output_dir", str(o),
"--n_confs", "30", "--seed", "0"], check=True)
for ik, (label, intended) in meta.items():
want = ",".join(map(str, intended.values())); obs = Counter()
for m in Chem.SDMolSupplier(str(o/f"{ik}.conformers.sdf"), removeHs=False):
if m is None: continue
mm = Chem.Mol(m); Chem.AssignStereochemistryFrom3D(mm)
obs[",".join(dict(Chem.FindMolChiralCenters(mm, includeUnassigned=True,
useLegacyImplementation=False)).get(i, "?") for i in intended)] += 1
print(f"{label:14s} intended {want:3s} -> {dict(obs)}")
Ruled out
- Not "chirality ignored globally": carbon and phosphorus centers condition at ~100% (input R→R, S→S).
- Not a perception artifact: the sulfoxide CIP assigns cleanly as
R/S from the 3D (never ?), and ETKDG of the same SMILES returns the intended label 10/10.
- Not an impossible/degenerate geometry: ETKDG embeds both sulfoxide enantiomers cleanly; the generated S-center is a well-formed pyramid.
Likely cause
The graph featurization appears to carry chiral tags for tetrahedral carbon and phosphorus but not for the 3-coordinate sulfoxide sulfur (RDKit represents it as S(=O) / [S+][O-], a special stereo case). Worth checking whether atom.GetChiralTag() on the sulfoxide S survives graph construction / one-hot encoding.
Environment
LoQI main @ 3f1c3fe, data/loqi.ckpt, 25-step sampler, --seed 0; PyTorch 2.9.1, Lightning 2.6.1, NVIDIA L40S. Happy to share the full failing-set SMILES + per-center analysis from the ChEMBL run.
Summary
LoQI reproduces carbon and phosphorus stereocenters essentially perfectly, but the configuration it generates at a sulfoxide (tetrahedral sulfur) stereocenter is independent of the input SMILES chirality — it samples a fixed, ~63/37 R-biased prior regardless of whether R or S is requested.
Surfaced while building a docking ligand-prep pipeline. Many drugs are chiral sulfoxides (omeprazole/esomeprazole, modafinil/armodafinil, sulindac, oxfendazole, …), where the wrong configuration is the wrong molecule.
Evidence — enantiomer-conditioning experiment
Each stereoisomer is fed separately (30 conformers,
--seed 0); the CIP label is read back from each generated 3D structure. TheETKDGcolumn is an RDKit embedding of the same SMILES — it confirms the target is achievable and that the 3D→CIP assignment is reliable.C[S@@](=O)c1ccccc1C[S@](=O)c1ccccc1CC[P@](=O)(C)c1ccccc1CC[P@@](=O)(C)c1ccccc1C[C@H](O)c1ccccc1Both sulfoxide inputs collapse to the same ~63/37 R-biased output → the S-center configuration is not conditioned on the input. Phosphorus — also a tetrahedral heteroatom stereocenter — and carbon are conditioned perfectly.
At the population level, across ~730 achievable, fully-defined chiral conformers from a 1000-molecule ChEMBL run, carbon centers reproduce at ~100% while the residual stereo errors are dominated by sulfoxide centers.
Reproduction
Standalone; needs only the shim + RDKit. Run from the repo root.
Ruled out
R/Sfrom the 3D (never?), and ETKDG of the same SMILES returns the intended label 10/10.Likely cause
The graph featurization appears to carry chiral tags for tetrahedral carbon and phosphorus but not for the 3-coordinate sulfoxide sulfur (RDKit represents it as
S(=O)/[S+][O-], a special stereo case). Worth checking whetheratom.GetChiralTag()on the sulfoxide S survives graph construction / one-hot encoding.Environment
LoQI
main@3f1c3fe,data/loqi.ckpt, 25-step sampler,--seed 0; PyTorch 2.9.1, Lightning 2.6.1, NVIDIA L40S. Happy to share the full failing-set SMILES + per-center analysis from the ChEMBL run.