Structure-to-name harness
src/naming/s2n_harness.rs (test-only) measures the structure-to-name namer by
round trip through the in-process OPSIN Rust port. The namer sits behind one
function, namer, which calls the openclatura port
(naming::openclatura::name_smiles, re-exported from the external nazuchem-s2n
dependency). The app’s naming worker calls the same engine’s style-aware
entry point in its generate operation, and shows a name only after the
OPSIN port reads it back to the drawn structure.
# Fast subset (normal test)cargo test --locked -p reshiki --no-default-features --lib naming::s2n_harness::s2n_fast_subset -- --nocapture# Metamorphic relations (normal tests)cargo test --locked -p reshiki --no-default-features --lib naming::s2n_harness::metamorphic -- --nocapture# Full corpus (ignored by default; nightly in .github/workflows/s2n.yml)S2N_REPORT=s2n-report.tsv cargo test --locked -p reshiki --no-default-features --lib naming::s2n_harness::s2n_full_corpus -- --ignored --nocapture# Regenerate baseline.tsv and status-snapshot.tsv, then review both diffsS2N_UPDATE_BASELINE=1 cargo test --locked -p reshiki --no-default-features --lib naming::s2n_harness::s2n_fast_subset -- --nocaptureS2N_EXTRA=<file> with the ignored s2n_extra_file test round-trips any extra
SMILES (first tab-separated column) and prints the wrong and unparseable rows;
S2N_EXTRA_OUT=<file> writes every row, and S2N_EXTRA_GIVEN=1 round-trips the
name in the second column instead of calling the namer (e.g. upstream’s names).
S2N_DUMP=<dir> writes every classified row per check (class, category, SMILES,
name, openclatura text for parity, decline reason) for analysis, and
{fast,full}-status.tsv with the PIN status
of every named row: check, SMILES, status (preferred, not_preferred or
unverified) and reason. The ignored parity_file test in
nazuchem-s2n/src/lib.rs names the SMILES in OPENCLATURA_PARITY and
adds the same status and reason columns to each different row it writes.
Run that engine test in a nazuchem checkout at ReShiki’s pinned revision.
The fast test fails on any Wrong name and when a correct count in
tests/fixtures/s2n/baseline.tsv drops; it prints a reminder when one rises.
The full test fails on any Wrong name. The workflow runs nightly, on manual
dispatch and on pushes to feat/s2n, and uploads the full report.
Status snapshot
Section titled “Status snapshot”tests/fixtures/s2n/status-snapshot.tsv holds the name, PIN status and reason
of every fixture and generated row of the fast subset, once per SMILES.
Declined rows have status declined and the decline message as reason. The
fast test fails on any difference and prints each changed row’s old and new
name, status and reason. Every PR that changes names or statuses must
regenerate the snapshot with S2N_UPDATE_BASELINE=1, review each changed row,
and list the changes in its PR body. A new Preferred label, or a change one PR
causes in another’s results, is then visible in review.
Worker response snapshot
Section titled “Worker response snapshot”tests/fixtures/s2n/worker_snapshot.jsonl freezes the complete worker responses
for every distinct SMILES in the full bakeoff, openclatura and rules corpora.
Requests follow first occurrence in that source order, with IUPAC generation
for every input and Boughton generation for inputs containing an isotope label.
Each successful generation is followed by a Verify request for its name, a
strict Parse request for the same name, a Verify request for each alternative
name and, when present, a Verify request for its radical-ion neutral-parent
name. A few inputs from the unit tests (listed in worker_snapshot.rs) keep
every optional response field populated; the test fails if one disappears. Requests use
the app’s builders and current worker protocol.
Each JSONL row contains the request object and the original UTF-8 response
string, or an {"error":"..."} object for a worker error. The replay compares
response bytes, including JSON formatting and field order, and reports the
total differences and the first ten expected/actual responses. It also checks
that the frozen requests still cover the full corpora in the expected order.
# Replay the frozen worker responses in the normal cargo test profilecargo test --locked --lib -p reshiki naming::s2n_harness::worker_snapshot::s2n_worker_snapshot -- --exact --nocapture# Explicitly regenerate all requests and responses, then review the fixture diffRESHIKI_UPDATE_S2N_SNAPSHOT=1 cargo test --locked --lib -p reshiki naming::s2n_harness::worker_snapshot::s2n_worker_snapshot -- --exact --nocaptureRun regeneration twice and compare the complete fixture before accepting an
update. Do not normalize response JSON, sort its fields or remove variable
data: any nondeterminism must be investigated. The fixture is test data and
is excluded from formatting by bunfig.toml.
Direct IR migration gates
Section titled “Direct IR migration gates”The IR worker uses protocol 7. The pre-migration fixture used protocol 6 and
contained 4,933 requests in 3,243,897 bytes. Parse and Verify
now return nazuchem_ir::Molecule instead of semantic CXSMILES, locant arrays
and axis side channels. Generate retains its SMILES input and existing result
semantics.
The one-off migration gates ran at commit 4ab8fd7b on branch
feat/naming-ir-protocol. Their harness, worker_snapshot_migration.rs, was
removed after the migration. To rerun these historical gates, preserve the fixture
from main before that commit and create an isolated worktree at 4ab8fd7b:
# 4ab8fd7b's parent is main before the IR migration.git show 4ab8fd7b^:tests/fixtures/s2n/worker_snapshot.jsonl > /tmp/reshiki-10b-old-worker_snapshot.jsonlgit worktree add --detach /tmp/reshiki-ir-migration-4ab8fd7b 4ab8fd7bcd /tmp/reshiki-ir-migration-4ab8fd7bRESHIKI_OLD_S2N_SNAPSHOT=/tmp/reshiki-10b-old-worker_snapshot.jsonl cargo test --locked --lib -p reshiki naming::s2n_harness::worker_snapshot::migration::s2n_worker_ir_equivalence -- --ignored --exact --nocaptureThe ignored migration test at that commit replays every preserved request through
the protocol-7 worker. It normalizes the protocol field
and requires Generate response bytes to match. For Parse and Verify, it checks
status, message, warnings, provenance, options and input name, then compares
canonical chemical identity, first parent locants, semantic CX layers and explicit
biaryl axes. The pinned IR keeps the old writer’s atom identities as a prefix;
native hydrogen removal projects the prepared graph back to those identities.
The gate validates this map against per-atom chemical facts and the complete edge
set before comparing the literal parent-only first-locant vectors. Cases whose
canonical identity is unsupported on both paths are counted explicitly; their
mapped semantic groups and underlying specified stereochemistry must still match.
Set RESHIKI_OLD_S2N_SNAPSHOT to the fixture from main before 4ab8fd7b, using
another path if needed. This semantic comparison is specific to the migration;
ordinary replay still compares the current wire response byte for byte.
The full migration-equivalence run on 2026-11-09 passed with zero differences and no skipped rows across all 4,933 requests:
| Check | Result |
|---|---|
| Generate | 1,654 responses byte-identical after changing only protocol 6 to 7 |
| Parse/Verify envelope | 3,279 identical status, message, warnings, provenance, options and input-name results |
| Parse/Verify without a structure | 83 identical absent-structure results |
| Canonical chemical identity | 3,195 equal identities |
| Canonical rejection with relative stereo groups | 1 matching rejection, with equal mapped membership and equal group-cleared specified stereo |
| Biaryl axes | 2 equal mapped atom/reference/winding annotations |
The companion size gate measures the snapshot requests and Parse/Verify requests for distinct names from the pinned OPSIN oracle fixtures. It reports the largest request and response for each operation against the unchanged 32,768-byte request and 65,536-byte response limits, including explicit worker-error counts:
Run this gate from the same 4ab8fd7b worktree, with a nazuchem checkout at its
pinned revision, 6fcd7c4f9ef7b892ae97aa042b5b4e13ee1aebe3:
RESHIKI_OLD_S2N_SNAPSHOT=/tmp/reshiki-10b-old-worker_snapshot.jsonl RESHIKI_NAZUCHEM_CHECKOUT=/path/to/pinned/nazuchem cargo test --locked --lib -p reshiki naming::s2n_harness::worker_snapshot::migration::s2n_worker_ir_wire_sizes -- --ignored --exact --nocaptureThe wire-size gate also passed on 2026-11-09. It measured the 4,933 snapshot requests and 3,576 Parse/Verify requests for 1,788 distinct OPSIN oracle names:
| Measurement | Snapshot maximum | Oracle maximum | Limit |
|---|---|---|---|
| Request, any operation | 349 bytes | 349 bytes | 32,768 bytes |
| Parse response | 8,059 bytes | 18,358 bytes | 65,536 bytes |
| Verify response | 8,072 bytes | 18,371 bytes | 65,536 bytes |
undecadictane produced both oracle response maxima. The largest response used
28.03% of the existing cap; the largest request used 1.07%. Two explicit request
rejections were the Parse and Verify requests for one oracle name containing an
embedded tab. No request or response was rejected by a wire cap, so both limits
remain unchanged.
The current protocol-7 snapshot was regenerated twice on 2026-11-09. Both runs
produced 4,933 rows and 8,020,627 bytes, byte for byte identical, with SHA-256
2188b9309fb252de6a2b29d777ace8c38413c0f2e8220e01b0ffb7a81cd72262.
The ordinary byte-for-byte snapshot replay also passed.
Metamorphic tests
Section titled “Metamorphic tests”src/naming/s2n_harness/metamorphic.rs names every fast-run structure, plus
curated isotope, salt, charge, meso, E/Z and biaryl-axis cases, under four
relations that must not change the name or status:
atom_order: three seeded random atom and component orders of the SMILES.mirror: the mirror image (@and@@and the axis markers swapped) is named with every R/S, M/P and Ra/Sa swapped and r/s and E/Z kept. An achiral or meso input is its own mirror image. When only the R-before-S rule (P-31.1.4.3.4) decides the numbering, the enantiomer may be numbered the other way; its name must then differ in descriptors only and, mirrored back, name the input.atom_maps: adding atom maps 1..n (inputs with axis markers are skipped).isotope_swap: moving an isotope label to an equivalent atom.
tests/fixtures/s2n/metamorphic_known_failures.tsv lists the current failures
(relation, canonical SMILES, reason). Each entry is an open bug: the test fails
on a failure that isn’t listed and on an entry that no longer fails, so remove
the entry with the fix.
Stereo-locant omission intentionally differs from the frozen 0.4.3 spelling
at quinine’s methanol centre and a random-corpus hydrazone. Each descriptor is
the only stereounit of its kind in its parent word, and that word cites no locants;
locants inside separately enclosed substituents belong to those substituents.
The descriptors therefore drop their locants (P-91.3): (1R) and (1Z)
become (R) and (Z). The text-parity baseline records both; structure checks
still require the same complete graphs. A word without locants still retains
its stereodescriptor locant when inequivalent positions could carry it, as at
the junctions of tetradecahydrophenanthrene.
The same decorated-parent proof also removes redundant constitutional locants
when their possible placements are equivalent and the word cites no other
locants (P-14.3.3 and P-14.3.4.6). This changes frozen spellings such as
2-phenylacetic acid, 2-phenylacetonitrile and 2-cyclopentylacetic acid.
Amido prefixes retain locants when the real nitrogen supplies another
substitution site, including prefixes assembled through a temporary acid.
These locant changes cost six fast text-parity matches; the structure round-trip
ratchet is 424/434 and parity is 421/431 after excluding annotated bad goldens,
with zero wrong structures. The corpus retains its upstream source attribution,
with later corrections and local expectation overrides documented below and
in the fixture history; it is not a pristine upstream snapshot. Changed port
expectations, including aminoacetic acid in rules_cases.tsv, are checked by
OPSIN round trip.
Checks
Section titled “Checks”| Check | What it does |
|---|---|
| structure | SMILES → namer → OPSIN → structure; compare canonical isomeric SMILES (stereo, charge, isotopes). |
| name | PubChem name → OPSIN → compare with the fixture’s canonical isomeric SMILES, then namer → OPSIN → compare again. A mismatch is wrong unless listed in name_known_failures.tsv; a PubChem name OPSIN rejects is input unparseable. Both exclusions stay out of the rate. |
| parity | Namer text equals openclatura 0.4.3’s text exactly. Bakeoff goldens are eligible only when openclatura_status is correct; other statuses count as missing goldens. This is the Phase 2 port target. |
| generated | Structure round trip on a seeded, deduplicated set of molecules built from fragments: chains with branches and unsaturation, monocycles and heteroaromatics, ring assemblies, spiro, bicyclo/tricyclo, fused aromatics, functional groups, E/Z and R/S, and ring + linker + group combinations. Every generated SMILES must parse. |
Every molecule gets one class: correct (OPSIN rebuilds the same structure), wrong (OPSIN rebuilds a different structure; always a failure), declined (namer refused, or the input is outside naming’s identity rules), or unparseable (namer produced text that the OPSIN port rejects). The report lists every wrong and unparseable case and the top decline reasons.
The report also lists known PubChem-name/fixture mismatches with their expected
and actual SMILES. Its per-category and total known_failure column counts
these separately, like input_unparseable, outside total and correct.
name_known_failures.tsv keys each entry by fixture SMILES and PubChem name,
with an issue #N: diagnosis. Both runs check every entry, including random
rows the fast run does not sample: it must exist in the bakeoff corpus, its
name must still parse, and the parse must still differ from the fixture; a
listed name that now matches fails with a request to remove the entry.
Fixtures
Section titled “Fixtures”| File | Rows | Source |
|---|---|---|
bakeoff_corpus.tsv |
703 | Phase 0 bake-off: 500 random PubChem compounds and 203 curated ones in 14 categories, with PubChem IUPAC names and openclatura 0.4.3 names. |
openclatura_tests.tsv |
894 | SMILES mined from openclatura’s tests (commit 90dc5bcb), named by openclatura 0.4.3. asserted is 1 or 0 where the test asserts a name that does or doesn’t match the output. Category is the test directory. |
rules_cases.tsv |
94 | Cases of the removed rules namer, its name (empty: declined) and the port’s name; checked by s2n_rules_cases. |
baseline.tsv |
107 | Fast-subset correct counts per check and category; parity_text rows hold exact text matches. |
status-snapshot.tsv |
909 | Name, status and reason of every fast-subset fixture and generated row; see Status snapshot. |
name_known_failures.tsv |
0 | PubChem-name/fixture identity mismatches, keyed by SMILES and PubChem name, with an issue-prefixed diagnosis; excluded from the name check’s rate. |
metamorphic_known_failures.tsv |
9 | Open metamorphic-relation failures; see Metamorphic tests. |
openclatura-derived data is MIT; see licenses/openclatura/.
The fast subset is all 203 curated rows, every 10th random PubChem row, every 5th openclatura row per category, and the first 500 generated molecules (934 structures). The full run uses all fixture rows and 3,000 generated molecules.
After the ground-truth checks, the fast name baseline is 247/247, with 6 input-unparseable rows excluded. Exact parity is 380/431; the baseline still has 107 rows and the unchanged status snapshot has 909 rows. Five bakeoff rows whose PubChem names omit stereo the fixture SMILES defines (#961) carry stereo-complete names instead: cis-1,4-dimethylcyclohexane, cis-4-tert-butylcyclohexan-1-ol, trans- and cis-1,2,3,4,4a,5,6,7,8,8a-decahydronaphthalene, and the imine row’s (1E,2E)-but-2-enylidene.
Final numbers (openclatura port, 2026-10-31)
Section titled “Final numbers (openclatura port, 2026-10-31)”Phase 1 is the removed rules namer at 3a0c668f. Full corpus, correct of
total; no check has a wrong name.
| Check | Phase 1 rules namer | openclatura port |
|---|---|---|
| structure | 145 / 1597 | 1536 / 1597 |
| name | 31 / 685 | 683 / 685 |
| parity (exact text) | 105 / 1592 | 1569 / 1592 |
| generated | 1717 / 3000 | 3000 / 3000 |
Baseline (openclatura port, stage 4, 2026-10-31)
Section titled “Baseline (openclatura port, stage 4, 2026-10-31)”namer calls naming::openclatura::name_smiles, the Rust port of openclatura
0.4.3. It names one connected component: chains, monocycles, ring assemblies,
spiro systems, von Baeyer systems and fused ring systems, with heteroatom
replacement, unsaturation, suffixes, charges and stereo. Fused parents come from
upstream’s retained templates (naphthalene, 1H-indole, quinoline,
purine, 9H-carbazole) or its systematic fusion planner
(pyrazolo[3,4-d]pyrimidine, [1,2,4]triazolo[4,3-a]pyridine, dibenzo...),
with hydro prefixes, indicated and added hydrogen, bridged fused parents
(4,7-methano-1H-indene, 1,4-epoxynaphthalene), skeletal replacement on a
fusion parent, and spiro unions with fused sides. More than one component still
declines. No wrong names in any check. Unparseable rows are openclatura names
the OPSIN port cannot read (mostly chalcogen peroxides and thioperoxy
anhydrides); they are not counted as correct. Fast subset about 17 s; full
corpus about 90 s.
Stage 1 (at most one ring; 659 corpus rows): 615 correct, 0 wrong, 41
unparseable, 3 declined; text equals openclatura’s for 654 of 658 rows with a
golden name. Stage 2 (several rings, no shared bond; 324 corpus rows): all 324
correct, and the text equals openclatura’s for 320. The other 4 differ only by
input atom order: the harness names its canonical SMILES, and upstream’s
tie-breaking depends on atom order (Python itself returns the port’s name for
another atom order). Named from their original SMILES, the stage-2 corpus and
generated molecules (619) and held-out generated sets of about 16,000 ring
assemblies and spiro systems match upstream exactly except for 121 declines
and one E/Z descriptor. In 103 of those declines and the E/Z row, upstream’s
own name round-trips to a different structure; the other 18 come from
conservative checks on stereo the OPSIN reading can invert: stereocentres
ranked through a mancude-ring duplicate atom (RDKit’s CIP labels and OPSIN can
disagree there), nitrogen centres and ring centres with a lone pair (P, As,
S+). Lines marked Upstream divergence in the
nazuchem-s2n sources list every
such decline. The remaining
mr_assembly and mr_spiro rows need fused parents (naphthalene,
spiro[indene-1,1'-indene]). Previous rules-namer baseline: structure 145,
name 31, parity 105, generated 1717 (full).
Stage 3 (von Baeyer systems; 604 corpus rows had a fused or bridged block):
35 are now correct, including all 14 mr_bridged rows; 34 equal openclatura’s
text and one (a corrin skeleton) gets a von Baeyer name where upstream uses a
retained name. The other 569 are fused or fusion-eligible (555) or spiro
junctions on a polycyclic block (14). Named from their original SMILES, the
in-scope corpus and generated molecules match upstream for 212 of 214 (both
others are spiro junctions), and held-out generated sets of about 6,000
von Baeyer molecules match for 83–92%; the rest decline. Most of those
declines are stereo that upstream’s own name loses or that the OPSIN reading
cannot place: unresolved ring stereo, nitrogen and lone-pair centres,
pseudoasymmetric (r/s) centres on a von Baeyer parent, and secondary bridges
numbered in the direction OPSIN does not read. The generated tricyclo
declines (61) are fusion names upstream (decahydrocyclopenta[cd]pentalene,
octahydro-2,5-methanopentalene).
Stage 4 (fused ring systems; 569 corpus rows still declined after stage 3):
the full structure round trip has 20 declines left: 8 salts and mixtures
(more than one component), 4 inputs outside naming’s identity rules, 4 fused
parents whose localized nonaromatic pi state is not ported, 2 unresolved
multicyclic stereo, one Cr compound and one secondary bridge OPSIN numbers
differently. pubchem_random is 490 of 500 correct (Python openclatura: 495
in the bake-off). Named from their original SMILES, held-out sets of 2,023
random PubChem compounds with a fused or bridged block and 2,000 generated
fused systems match upstream’s text for 1,992 and 1,723 rows; 1,988 of the
2,000 PubChem rows whose Python name round-trips are exact. About 23,000 rows of
held-out and adversarial molecules round-trip with no wrong name. Upstream’s
own name is wrong for some inputs (protonated fused nitrogen, quinoid and
localized pi states of retained parents, bridges without a valence proof,
re-primed dispiro locants); the port declines those classes, and each such
place is marked Upstream divergence.
Intentional PIN corrections count against exact text parity with openclatura
0.4.3. Under P-14.3.4.2(c),
the suffix locant 1 is omitted only on a monosubstituted homogeneous monocycle:
2-methylcyclohexan-1-ol replaces upstream’s 2-methylcyclohexanol.
Substitution on a suffix heteroatom alone still permits omission, as in
N-methylcyclohexanamine; retained names such as 2-methylphenol and
2-methylbenzoic acid are unchanged. Four openclatura_tests.tsv expectations
have corrected suffix locants but retain upstream’s cis/trans wording; these
already differ from the generated r/s names and do not affect exact parity.
This costs three fast text matches (drug 18→16, natural_product 17→16)
and six full text matches, three of them in pubchem_random. Structure, name
and generated round-trip counts do not change. Ring-assembly parents
(P-28), such as
[1,1′-biphenyl]-4-ol for upstream’s 4-phenylphenol, cost eight more
(mr_assembly 10→3, roundtrip one). With all three changes the fast text
ratchet is 386/434.
| Check | Fast | Full |
|---|---|---|
| structure | 420 / 434 | 1536 / 1597 (96.2%) |
| name | 245 / 246 | 683 / 685 (99.7%), 18 input unparseable |
| parity (exact text) | 425 / 434 | 1569 / 1592 (98.6%) |
| generated | 500 / 500 | 3000 / 3000 (100.0%) |
| unparseable (structure) | 10 | 41 |
Structure round trip
Section titled “Structure round trip”| Category | Fast correct / total | Full correct / total |
|---|---|---|
| alcohol_amine | 13 / 13 | 13 / 13 (100.0%) |
| alkenyl_benzyl | 10 / 10 | 10 / 10 (100.0%) |
| chain_ring_prefix | 12 / 12 | 12 / 12 (100.0%) |
| data | 13 / 13 | 63 / 63 (100.0%) |
| drug | 20 / 20 | 20 / 20 (100.0%) |
| exploratory | 1 / 1 | 4 / 4 (100.0%) |
| fixtures | 22 / 22 | 106 / 106 (100.0%) |
| heterocycle | 25 / 25 | 25 / 25 (100.0%) |
| integration | 16 / 16 | 75 / 80 (93.8%) |
| mr_assembly | 10 / 10 | 10 / 10 (100.0%) |
| mr_bridged | 14 / 14 | 14 / 14 (100.0%) |
| mr_fused | 18 / 20 | 18 / 20 (90.0%) |
| mr_macro_other | 6 / 7 | 6 / 7 (85.7%) |
| mr_spiro | 7 / 7 | 7 / 7 (100.0%) |
| natural_product | 19 / 19 | 19 / 19 (100.0%) |
| pubchem_random | 49 / 50 | 490 / 500 (98.0%) |
| ring_ON_ester_amide | 10 / 10 | 10 / 10 (100.0%) |
| roundtrip | 77 / 77 | 381 / 383 (99.5%) |
| stereo | 16 / 16 | 16 / 16 (100.0%) |
| suffix_on_ring | 20 / 20 | 20 / 20 (100.0%) |
| unit | 42 / 52 | 217 / 258 (84.1%) |
| total | 420 / 434 | 1536 / 1597 (96.2%) |
Name round trip
Section titled “Name round trip”| Category | Fast correct / total | Full correct / total |
|---|---|---|
| alcohol_amine | 13 / 13 | 13 / 13 (100.0%) |
| alkenyl_benzyl | 10 / 10 | 10 / 10 (100.0%) |
| chain_ring_prefix | 12 / 12 | 12 / 12 (100.0%) |
| drug | 19 / 19 | 19 / 19 (100.0%) |
| heterocycle | 25 / 25 | 25 / 25 (100.0%) |
| mr_assembly | 10 / 10 | 10 / 10 (100.0%) |
| mr_bridged | 14 / 14 | 14 / 14 (100.0%) |
| mr_fused | 20 / 20 | 20 / 20 (100.0%) |
| mr_macro_other | 5 / 6 | 5 / 6 (83.3%) |
| mr_spiro | 7 / 7 | 7 / 7 (100.0%) |
| natural_product | 17 / 17 | 17 / 17 (100.0%) |
| pubchem_random | 49 / 49 | 487 / 488 (99.8%) |
| ring_ON_ester_amide | 10 / 10 | 10 / 10 (100.0%) |
| stereo | 14 / 14 | 14 / 14 (100.0%) |
| suffix_on_ring | 20 / 20 | 20 / 20 (100.0%) |
| total | 245 / 246 | 683 / 685 (99.7%) |
Parity (exact openclatura text)
Section titled “Parity (exact openclatura text)”| Category | Fast exact / total | Full exact / total |
|---|---|---|
| alcohol_amine | 13 / 13 | 13 / 13 (100.0%) |
| alkenyl_benzyl | 10 / 10 | 10 / 10 (100.0%) |
| chain_ring_prefix | 12 / 12 | 12 / 12 (100.0%) |
| data | 13 / 13 | 63 / 63 (100.0%) |
| drug | 20 / 20 | 20 / 20 (100.0%) |
| exploratory | 1 / 1 | 4 / 4 (100.0%) |
| fixtures | 22 / 22 | 106 / 106 (100.0%) |
| heterocycle | 25 / 25 | 25 / 25 (100.0%) |
| integration | 16 / 16 | 76 / 80 (95.0%) |
| mr_assembly | 10 / 10 | 10 / 10 (100.0%) |
| mr_bridged | 14 / 14 | 14 / 14 (100.0%) |
| mr_fused | 18 / 20 | 18 / 20 (90.0%) |
| mr_macro_other | 5 / 7 | 5 / 7 (71.4%) |
| mr_spiro | 7 / 7 | 7 / 7 (100.0%) |
| natural_product | 19 / 19 | 19 / 19 (100.0%) |
| pubchem_random | 46 / 50 | 485 / 495 (98.0%) |
| ring_ON_ester_amide | 10 / 10 | 10 / 10 (100.0%) |
| roundtrip | 77 / 77 | 380 / 383 (99.2%) |
| stereo | 16 / 16 | 16 / 16 (100.0%) |
| suffix_on_ring | 20 / 20 | 20 / 20 (100.0%) |
| unit | 51 / 52 | 256 / 258 (99.2%) |
| total | 425 / 434 | 1569 / 1592 (98.6%) |
Generated stress set
Section titled “Generated stress set”| Category | Fast correct / total | Full correct / total |
|---|---|---|
| acid | 21 / 21 | 125 / 125 (100.0%) |
| alcohol | 21 / 21 | 125 / 125 (100.0%) |
| aldehyde | 21 / 21 | 125 / 125 (100.0%) |
| alkene | 21 / 21 | 125 / 125 (100.0%) |
| alkene-stereo | 20 / 20 | 125 / 125 (100.0%) |
| alkyl | 21 / 21 | 125 / 125 (100.0%) |
| alkyne | 21 / 21 | 125 / 125 (100.0%) |
| amide | 21 / 21 | 125 / 125 (100.0%) |
| amine | 21 / 21 | 125 / 125 (100.0%) |
| bicyclo | 21 / 21 | 125 / 125 (100.0%) |
| ester | 21 / 21 | 125 / 125 (100.0%) |
| ether | 20 / 20 | 125 / 125 (100.0%) |
| fused-aromatic | 21 / 21 | 125 / 125 (100.0%) |
| halide | 21 / 21 | 125 / 125 (100.0%) |
| heteroaromatic | 21 / 21 | 125 / 125 (100.0%) |
| ketone | 21 / 21 | 125 / 125 (100.0%) |
| monocycle | 21 / 21 | 125 / 125 (100.0%) |
| n-substituted-amide | 21 / 21 | 125 / 125 (100.0%) |
| nitrile | 21 / 21 | 125 / 125 (100.0%) |
| ring-assembly | 21 / 21 | 125 / 125 (100.0%) |
| ring-with-group | 20 / 20 | 125 / 125 (100.0%) |
| spiro | 21 / 21 | 125 / 125 (100.0%) |
| tetrahedral-stereo | 20 / 20 | 125 / 125 (100.0%) |
| tricyclo | 21 / 21 | 125 / 125 (100.0%) |
| total | 500 / 500 | 3000 / 3000 (100.0%) |
Chemistry crate readiness for the port
Section titled “Chemistry crate readiness for the port”Outside its optional self-audit (audit/, which the OPSIN round trip replaces)
and its own OPSIN check, openclatura uses RDKit only to read the structure:
SMILES parsing, kekulization, ring info (FastFindRings), legacy stereo
perception (AssignStereochemistry + FindMolChiralCenters, which decides
which atoms are stereocentres) and the modern rdCIPLabeler (which decides
their labels, including pseudo-asymmetric r/s). crates/chemistry has ports of
all of these: legacy perception in stereo/perception.rs, the new CIP labeler
in stereo/cip/ (R/S, r/s, E/Z, M/P), kekulize.rs and rings.rs. Points to
settle in Phase 2:
rings::fastis a depth-first cycle basis like RDKit’sFastFindRings, not a smallest or symmetric set;rings::perceivegives the symmetric SSSR. Match each openclatura ring query to the right one.- Canonical kekulization needs caller-supplied canonical ranks; backtracking is bounded.
- The potential-stereocentre predicate in perception is crate-private.
- The naming input boundary (
naming::canonical_smiles) rejects allene and coordination stereo, and the port keeps that check.