orthonym.perception.molcache#

Note

Internal API. Names and behaviour may change between releases.

Per-naming-call cache of a molecule’s atom and bond tuples (audit 2026-09-03, S2).

Why: for a in mol.GetAtoms runs through RDKit’s Python sequence wrapper (rdkit/Chem/__init__.py: __iter__ -> __getitem__ -> _sizeCalc per step). A 58-atom walk costs about 88 us that way, 53 us through GetAtomWithIdx and 14 us over a materialised tuple. The engine walks the same input molecule hundreds of times per name – every dispatch predicate scans the atoms once – so on a dev split this wrapper was 24% of engine CPU.

What atoms_of(mol) / bonds_of(mol) promise:

  • Same atoms, same order as mol.GetAtoms / mol.GetBonds. The tuple is built from GetAtomWithIdx(i) for i in index order, which is exactly the wrapper’s iteration order.

  • Scope = one top-level naming call. The tuple lives in the memo scope that assembly.memo opens around name; with no open scope nothing is cached and every call rebuilds. Cross-molecule staleness is structurally impossible.

  • Never a stale tuple. Only immutable-by-convention Chem.Mol objects are cached; a Chem.RWMol (the only type on which atoms can be removed, replaced or added) is always rebuilt. The entry keeps a strong reference to the molecule, so id(mol) cannot be recycled by a new molecule while the entry exists, and a hit is also checked against the live atom/bond count.

  • Verify mode. ORTHONYM_MOLCACHE=verify rebuilds on every hit and raises MolCacheMismatchError if any cached atom’s identity, element or charge differs from the live molecule – the same continuous completeness check assembly.memo offers. ORTHONYM_MOLCACHE=off disables caching.

Atom and bond wrappers reference the live C++ objects, so property edits made in place on a Chem.Mol (aromaticity, charges, isotopes) are visible through the cached tuple exactly as through a fresh GetAtoms.

exception orthonym.perception.molcache.MolCacheMismatchError#

Bases: AssertionError

Verify mode: a cached atom/bond tuple no longer matches the live molecule.

orthonym.perception.molcache.atoms_of(mol)#

tuple(mol.GetAtoms) in index order, cached per naming call.

orthonym.perception.molcache.bonds_of(mol)#

tuple(mol.GetBonds) in index order, cached per naming call.

orthonym.perception.molcache.inchikey_of(mol)#

Chem.MolToInchiKey(mol) cached per mol object within the naming scope (Lever F, 2026-09-12). The entry pins mol (so its id cannot be recycled) and stores the _inchi_sig() fingerprint; a hit is served only when the live fingerprint is equal, so an in-place edit of charge, isotope, H count, chirality or bond order/stereo forces a recompute. RWMol is never cached. Verify mode recomputes on every hit and raises on a difference.

orthonym.perception.molcache.canon_smiles(smi)#

Chem.CanonSmiles(smi): a pure function of the string, so a process-wide LRU is exact.

orthonym.perception.molcache.cached_by_key(mol, ns, key, fresh)#

Memoise fresh under (ns, id(mol), key) for the naming scope (Lever E, 2026-09-12). The entry pins mol so its id cannot be recycled; RWMol is never cached; verify mode recomputes on every hit and raises:class:MolCacheMismatchError on a difference. The caller puts everything the value depends on into key (see ring_selection.ring_system_score).