orthonym.validation.reconstruct#

Note

Internal API. Names and behaviour may change between releases.

Wave 0: OPSIN-free structural verifier.

Two 0-wrong layers (a structural reconstruction verifier):
  • has_unverifiable_atoms — the wildcard fail-close predicate (this task).

  • reconstruct_and_verify / verify_or_none — a sound-over-complete name->graph reconstructor + dual oracle (Tasks 2–4).

SOUNDNESS CONTRACT (load-bearing): the reconstructor NEVER falsely CONFIRMs, and it rebuilds ONLY from name-level facts (NameFacts) that a producer derives from NAME TOKENS — parent length, replacement/unsaturation locants, the principal-group key, substituent NAMES. It must NEVER read the input graph to populate a fact; that would make the compare circular and defeat the point. On anything it does not model it returns ABSTAINED, never CONFIRMED.

orthonym.validation.reconstruct.has_unverifiable_atoms(mol)#

True iff mol carries an atom no oracle can verify.

Wave 0 scope: a dummy/wildcard atom (atomic number 0, SMILES *). Such an atom makes the input InChIKey uncomputable, so the /OPSIN round-trip oracle has no reference and FAILS OPEN — a wildcard input then ships a WRONG molecule (CC* -> ethane). Callers must fail closed (abstain) when this returns True. Wave 0 is wildcard only, matching errors.classify_scope_limit.

class orthonym.validation.reconstruct.Verdict(*values)#

Bases: str, Enum

CONFIRMED = 'confirmed'#
ABSTAINED = 'abstained'#
MISMATCH = 'mismatch'#
ERROR = 'error'#
class orthonym.validation.reconstruct.ReconResult(verdict: 'Verdict', reason: 'str', reconstructed_smiles: 'Optional[str]' = None)#

Bases: object

verdict: Verdict#
reason: str#
reconstructed_smiles: str | None = None#
class orthonym.validation.reconstruct.NameFacts(parent_kind: 'str', parent_length: 'int', replacements: 'tuple' = (), unsaturations: 'tuple' = (), principal_group: 'Optional[tuple]' = None, substituents: 'tuple' = (), indicated_h: 'tuple' = (), net_charge: 'int' = 0, isotopes: 'bool' = False)#

Bases: object

parent_kind: str#
parent_length: int#
replacements: tuple = ()#
unsaturations: tuple = ()#
principal_group: tuple | None = None#
substituents: tuple = ()#
indicated_h: tuple = ()#
net_charge: int = 0#
isotopes: bool = False#
orthonym.validation.reconstruct.reconstruct_and_verify(name_facts, input_mol)#

Rebuild an RDKit graph from name-facts ALONE and compare constitution.

CONFIRMED only on a full canonical-SMILES byte-match (stereo removed both sides); a constitution difference is MISMATCH; any unmodeled construct raises _Abstain -> ABSTAINED; a top-level exception -> ERROR. NEVER falsely CONFIRMs, NEVER raises.

orthonym.validation.reconstruct.verify_or_none(name, input_smiles, name_facts=None)#

Dual 0-wrong oracle: return name iff it is verified, else None.

  1. OPSIN-RT, STRICT: if OPSIN parses name and its parse-back has the SAME FULL InChIKey as the input (all layers – constitution AND stereo AND charge/tautomer), the name is verified. Full-key, NOT the skeleton block: a skeleton match confirms a neutralised or wrong-enantiomer name (measured), which would be a 0-wrong hole. Strict => a name that omits stereo the input asserts returns None (a safe false-negative, never a false CONFIRM).

  2. OPSIN cannot parse name AND name_facts supplied: run the reconstructor; CONFIRMED -> verified. This branch is CONSTITUTION-ONLY (Wave-0 NameFacts has no stereo fields and _normalize strips stereo both sides) – it abstains whenever the input asserts defined stereochemistry, since it cannot check what the name claims about it.

  3. Otherwise -> None (fail closed). A wildcard input is never verifiable.