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 fromGetAtomWithIdx(i)foriin 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.memoopens aroundname; 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.Molobjects are cached; aChem.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, soid(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=verifyrebuilds on every hit and raisesMolCacheMismatchErrorif any cached atom’s identity, element or charge differs from the live molecule – the same continuous completeness checkassembly.memooffers.ORTHONYM_MOLCACHE=offdisables 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:
AssertionErrorVerify 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.neighbors_of(mol)#
tuple(tuple(n.GetIdx for n in atom.GetNeighbors) for atom in mol.GetAtoms), cached per naming call: the neighbour atom indices of every atom, in the orderGetNeighborsgives them. A walk of a fragment through these tuples costs plain list steps, where the same walk throughGetAtomWithIdx(i).GetNeighborscosts two RDKit calls and a generator per atom. The bonds of aChem.Molcannot change (an RWMol is never cached), so the tuple is exact for the scope.
- orthonym.perception.molcache.inchikey_of(mol)#
Chem.MolToInchiKey(mol)cached per mol object within the naming scope (Lever F, 2026-09-12). The entry pinsmol(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.caches(mol)#
True when:func:cached_by_key would store a value for
mol(a naming scope is open, the cache is on, andmolis not an RWMol): a caller skips building a costly key when it is False.
- orthonym.perception.molcache.cached_by_key(mol, ns, key, fresh)#
Memoise
freshunder(ns, id(mol), key)for the naming scope (Lever E, 2026-09-12). The entry pinsmolso 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 intokey(see ring_selection.ring_system_score).