orthonym.metrics.coverage_metrics#
Note
Internal API. Names and behaviour may change between releases.
Task 0.3 — the corrected three-metric coverage instrument.
Pure classifiers + aggregator. NO Java and NO Orthonym naming happen in
this module: the round-trip is performed by an injected callable
(name_to_smiles: str -> Optional[str]), so the unit tests exercise every
branch with a dict-backed fake OPSIN. The production runner
(scripts/coverage_metrics.py) supplies the real OPSIN subprocess.
Corrected metric contract (binds the ship gate — see the master plan “Metric definitions”):
coverage= emitted / total. “Emitted” = Orthonym produced a real name (NOTerrors.is_failure_name).of_emitted_rt= round-trip-valid / emitted. A name is RT-valid iff OPSIN PARSES it AND the parsed structure matches the input under the chosen matcher.confidently_wrong= emitted AND OPSIN-parses AND structure-MISMATCH, as a fraction of TOTAL. OPSIN-unparseable emissions are their OWN bucket (``unparseable``), never counted as wrong — this correction is what flips the ship gate relative to the earlier conflated metric.unparseable= emitted AND OPSIN-does-not-parse, fraction of TOTAL.abs_rt_valid= coverage × of_emitted_rt = RT-valid / total. This is the ~99% target axis.
Every metric is reported under BOTH matchers (they are NOT interchangeable):
parity— the most lenient lens, a forgiving round-trip comparison. Full standardization of both sides (fragment-parent → normalize → reionize → uncharge → canonical tautomer) then canonical-SMILES equality. This forgives (a) charge/protonation state, (b) tautomers broadly — RDKit’s canonical tautomer merges keto-enol AND amide/imidol shifts, wider than InChI mobile-H — and (c) stereo, becauseCanonicalTautomernormalizes away stereo descriptors. It answers “does the name describe the right CONSTITUTION, ignoring charge, tautomer and stereo specificity?”strict— the Orthonym house matcher: InChIKey skeleton block (formula + connectivity + mobile-H, stereo- AND charge-insensitive) PLUS net formal charge. This is exactly the constitutional key. It is charge-SENSITIVE (catches a dropped/added charge the parity lens forgives) and tautomer-tolerant only to InChI’s mobile-H scope (it does NOT merge keto-enol), while remaining stereo-insensitive.
Stereo caveat (documented, not a bug). BOTH matchers forgive stereo, so neither flags a stereo INVERSION (name says S, input is R) — it counts as RT-valid. This is by design and consistent with the RT gate being stereo-insensitive; stereo-inversion residual wrongness is owned by the C.1 stratified residual-sampling audit, not by these automated matchers. The upshot: of-emitted-RT is a (small) OVER-estimate to the extent stereo inversions occur. Crediting stereo OMISSION as a match is intended (the tier is allowed to emit constitution-only names).
pin_pct (curated gold-gate pass rate) is a DIFFERENT population and is
carried through verbatim if supplied; it is never derived here and never
presented as commensurable with coverage.
- class orthonym.metrics.coverage_metrics.Matcher(*values)#
Bases:
str,Enum- PARITY = 'parity'#
- STRICT = 'strict'#
- class orthonym.metrics.coverage_metrics.RowClassification(*values)#
Bases:
str,Enum- ABSTAINED = 'abstained'#
- RT_VALID = 'rt_valid'#
- CONFIDENTLY_WRONG = 'confidently_wrong'#
- UNPARSEABLE = 'unparseable'#
- class orthonym.metrics.coverage_metrics.RowResult(smiles: str, name: str | None, emitted: bool, opsin_parsed: bool | None, parity: orthonym.metrics.coverage_metrics.RowClassification | None, strict: orthonym.metrics.coverage_metrics.RowClassification | None)#
Bases:
object- smiles: str#
- name: str | None#
- emitted: bool#
- opsin_parsed: bool | None#
- parity: RowClassification | None#
- strict: RowClassification | None#
- orthonym.metrics.coverage_metrics.classify_row(smiles, name, name_to_smiles)#
Classify one (input, emitted-name) pair under both matchers.
A single OPSIN call is made per emitted name; the parsed SMILES is then scored by each matcher (the parse/unparseable axis is matcher-invariant, only the match/mismatch split differs).
- orthonym.metrics.coverage_metrics.aggregate(rows, pin_pct=None)#
Aggregate row results into the corrected three-metric report under both matchers.
pin_pct(curated-gold gate pass rate, a DIFFERENT population) is carried verbatim when supplied; never derived here.
- orthonym.metrics.coverage_metrics.parity_match(input_smiles, opsin_smiles)#
True iff the two structures agree under the parity matcher (charge/tautomer-forgiving, stereo-comparing). False if either side cannot be standardized (fail-CLOSED for the metric — an unstandardizable emission is not credited as a match).
- orthonym.metrics.coverage_metrics.strict_match(input_smiles, opsin_smiles)#
True iff InChIKey skeleton block AND net formal charge agree (the constitutional key). False if either side cannot be hashed.