Compatibility scope
This is the v1.0 compatibility boundary and public contract. “Compatible” means the operation is implemented and covered by its stated contract; it does not mean that chematic is a drop-in reimplementation of RDKit or ChemDraw.
The issue-specific matrix for rich RXN, CDXML presentation, Markush/polymer,
and nucleic-acid boundaries is maintained in
docs/issue-473-rich-document-boundary.md.
v1.0 release contract
The following boundaries are intentional release decisions, not unfinished claims to be inferred as complete. v1.0 may ship with these limits when the documented supported paths, typed failures, and regression gates remain stable:
- CDXML remains loss-preserving document-level bounded editing, not a complete arbitrary nested-object editor/writer; complex polymer/Markush expansion remains bounded and explicit rather than a general topology engine.
- Python
RWMolremains the documented mutation subset, not full RDKitRWMolcompatibility. canonical_smiles()is a representation, not a deduplication/cache key.canonical_smiles_stable_key()is the recommended identity API and may returnNonewhen stability is not proven, including coupled E/Z systems represented through aromatic direction stashes.- Aromaticity model selection and CIP mode are explicit compatibility choices; neither claims universal RDKit parity, and unresolved CIP is not guessed.
- 3D generation and MMFF94 remain Experimental. The named fixed-seed RDKit-compatible ETKDGv3 path has bounded source-build parity evidence; successful output from it or the general 3D APIs does not imply universal topology, platform-bitwise, conformational-quality, or complete force-field parity.
Evidence boundary
The v1.0 release decision is based on reproducible repository-local gates: the S1-S4 checks, binding contract suite, focused Miri, sanitizer jobs, fuzz regressions, and dependency/license checks. Independent external review, third-party audit, hosted CI execution, and external oracle campaigns are supplementary evidence and are not presented as completed v1.0 guarantees. They remain post-release follow-up work with findings published separately.
Python RWMol
Supported: construction from an empty molecule or Mol; AddAtom, AddBond,
RemoveAtom, RemoveBond; GetNumAtoms, GetNumBonds, and GetMol; and the
documented SINGLE, DOUBLE, TRIPLE, and AROMATIC bond orders.
This is an intentionally small subset. Arbitrary atom/bond property mutation, mid-edit RDKit-style atom/bond proxy iteration, conformer/query/reaction editing, sanitization flags, and full exception/return-value parity are not supported. Unsupported options fail explicitly.
Atom-level wildcards and R-group labels
The ordinary molecule interchange path distinguishes real carbon, generic
*, unnumbered R, and numbered R1..R9999. MOL V2000 uses R# plus
M RGP for numbered labels; V3000 uses R# plus RGROUPS; SDF inherits its
MOL record contract; and CDXML uses bounded GenericNickname nodes. These
forms round-trip through the core molecule sidecar without presenting the
label as a periodic-table element.
Arbitrary pseudoatom text and R-group numbers outside 1..=9999 are not part
of this contract and fail parsing instead of becoming carbon. SMILES has no
matching R-group-label side channel: all supported wildcard/R-group atoms are
written as *, so use MOL/SDF/CDXML or the Rust/WASM label API when identity
must survive editing. This atom-level contract does not implement Markush
alternative selection or polymer semantics.
The bounded R-group decomposition API accepts labelled terminal wildcard sites
and mapped core atoms, normalizes input atom order, preserves labels, and
returns input-aligned Core/R1/R2 columns in Rust, Python, and WASM/Node.
Duplicate labels, non-terminal placeholders, ambiguous multiple attachments,
and match-limit exhaustion fail explicitly. Multi-core/MCS alignment,
tautomer/core enumeration, multiple substituents per site, and RDKit's GA
scoring are not part of this contract.
CDXML
Supported: bounded molecular parsing with coordinates; loss-preserving,
multi-page CdxmlDocument parsing and exact-source write(); page count/ID
inspection; JSON command edits for page attributes and opaque presentation
objects, including nested ReplaceObjectPath; and preservation of unknown
attributes, unknown objects, and untouched XML across pages and bindings.
This is not a complete ChemDraw editor/writer. Arbitrary semantic editing of
every nested CDXML object, regeneration from an edited molecular graph, and
full templates, reactions, Markush alternatives, polymer, query, and presentation
semantics/rendering are not supported. The bounded atom-level */R/R<n>
identity described above is supported. Presentation objects are preserved and
can be edited as opaque XML, but chematic does not claim to interpret every
ChemDraw presentation semantic.
Polymer and Markush expansion
Expansion is bounded and explicit. The chematic.semantic.v1 JSON contract
supports validated Markush selection and explicit repeat expansion across the
Rust, Python, WASM, and Node bindings. A repeat requires a count and either a
repeat_smiles fragment with exactly two [*] linkage markers or two explicit
repeat_endpoint_atoms. Expansion returns ExpandedSemantic with a
source_to_expanded provenance mapping.
Implicit endpoint guessing, unrestricted Markush selection, branching,
cross-linking, stochastic/nested/topology-changing polymer expansion, and
flattening unsupported semantic objects into Molecule are not supported.
Ambiguous or unsafe input returns typed SemanticError.
Nucleic-acid documents
The separate chematic.nucleic-acid.v1 contract preserves bounded, explicit
DNA/RNA document metadata: ordered strands and residues, base/known
modification identity, sugar identity, one-to-one atom references, adjacent
linear phosphodiester linkages, and annotations. Rust, Python, WASM, and Node
share the same validation envelope and typed error codes.
This is not a sequence or biopolymer chemistry inference engine. It does not infer residues from a graph, create a molecular graph, silently flatten metadata, interpret PDB/mmCIF biopolymer records, or accept branching, cross-strand/cyclic linkages, and unknown modification semantics. Protein, peptide, carbohydrate, and unrestricted nucleic-acid semantics remain outside the v1.0 contract.
RDKit and Morgan compatibility
chematic.rdkit_compat is a selected 2D compatibility layer, not a full RDKit
clone. The default ECFP/Morgan implementation is shape- and workflow-
compatible but not bit-identical: its FNV-based hashing must not be mixed with
RDKit bit positions. Named RDKit-Morgan APIs have their own documented
radius/size/options contract and do not expose every RDKit option.
Canonical SMILES, aromaticity, CIP, and stereo retain separate documented residuals and model choices. Matching function names do not promise algorithmic identity.
Aromaticity and CIP
The default aromaticity path is the compatibility-preserving per-SSSR Hückel
path with a bounded all-carbon odd/odd fused-envelope fallback (for example,
azulene). AromaticityAlgorithm::RdkitLike is an explicit opt-in whole-graph
model for fused and non-alternant systems. Purine and azulene are covered by
the RDKit-like regression gate, but the two models intentionally remain
distinct; neither mode claims universal RDKit aromaticity parity. Known
bridgehead-N and other fused-ring gaps remain documented residuals. Accurate
CIP labels acyclic phosphorus and acyclic lone-pair centres as RDKit's
CIPLabeler does, and ring sulfoxides/selenoxides; phosphorus on an unsaturated
ring (cyclophosphazene) gets RDKit's label, which flips with the ring's Kekulé
spelling in both libraries and is marked as such. A ring lone-pair centre with three single bonds
(bridgehead amine, cyclic phosphine or sulfonium) gets RDKit's label for the
spelling parsed and is marked spelling-dependent: chematic reads its @/@@
as OpenSMILES does (lone pair in the implicit-H position), while RDKit inverts
it when exactly one ring-closure digit is written on the centre; a
pseudo-asymmetric one gets RDKit's r/s. An aromatic ring lone-pair centre
stays LonePairCenter.
The default CIP path remains the fast legacy assignment. Accurate hierarchical
CIP is opt-in through CipMode::Accurate (and its named Python/WASM binding
endpoints). It improves coverage and parity for many cases but is not a
universal guarantee: symmetric cages, tied Rule 4b/pseudoasymmetric cases,
and unsupported or budget-limited structures may remain unresolved. An
unresolved result is not a guessed R/S label.
3D and force fields
3D generation and MMFF94 minimization are experimental. The legacy
generate_coords/generate_3d path is retained for compatibility and does
not promise ETKDGv3-quality conformers or success on every topology. v1.0.40
adds a named fixed-seed RDKit-compatible ETKDGv3 path. It matches RDKit on the
recorded Linux x86-64 ChEMBL 5k and RDKit.js 10k success rows; other platforms
use a 5e-4 Å coordinate regression tolerance. This bounded source result is
not a promise for other seeds, topologies, conformer ensembles, platforms or
published artifacts. The embed_pipeline_v2 path remains opt-in and reports
stage/provenance evidence with typed failure outcomes; a successful result
still guarantees sanity checks, not universal conformational-quality parity.
MMFF94 exposes the implemented energy terms and minimization APIs, but
typing, parameter coverage, charged/metal/fused-ring cases, and convergence
remain incomplete. Missing parameters or failed minimization must remain
observable as failure; callers must not interpret the presence of all seven
term implementations as universal MMFF94 coverage. Use UFF or DREIDING when
the selected molecule and policy require a different supported scope. Where
RDKit 2026.03.6 types a molecule, chematic's per-term energies equal RDKit's
on the exposed 10k and ChEMBL 5k corpora; pairs between fragments are kept
unless ignore_interfragment_interactions asks for RDKit's default. The
minimizer is a port of RDKit's BFGS run to an absolute residual-force test,
so minimized geometries and iteration counts are not RDKit's. The pipeline's
default budget is 1,000 iterations (RDKit's MMFFOptimizeMolecule default
is 200); a geometry that stops at the budget is returned with the
iteration_limit termination.
A single embed_pipeline_v2 conformer is one basin, not a search. The current
source candidate can rank an ensemble after RDKit-compatible hydrogen
placement and full-atom MMFF94 refinement. On the frozen 265-row A6 corpus,
equal-count ten-candidate comparison against RDKit 2026.09.1 gives a
−0.000012 kcal/mol median, 1.041 kcal/mol p90, and 237/265 rows within
1 kcal/mol of RDKit's best. The 9.896 kcal/mol maximum and 49 rows above
RDKit's best remain open, so this is not universal conformer-quality parity.
It is source-candidate evidence from one host, not a published-artifact claim.
A stereo-safe (RepairAndVerify) run whose minimization relaxes a declared
centre through inversion embeds again from up to seven other seeds before
it returns FinalStereoViolation; a run that succeeds at its own seed is
unchanged. This closed the cross-platform A6 regression tracked in #739:
over ten seeds of the 265 A6 rows, failures fell from 32 of 2,650 to zero.
2D layout is rule-based, not RDKit's Compute2DCoords: coordinates differ.
Depictions and MOL blocks draw the declared stereo (E/Z geometry, one wedge
per centre); every MOL block (Python to_mol_block, WASM to_mol_block,
SDWriter) carries 2D coordinates in Å, with or without stereo. As drawn, 5
of the 15,000 exposed-10k and ChEMBL-5k rows have a clash (RDKit 276) and
caged ring systems can cross themselves (adamantane-type cages are drawn as
RDKit's projection, with its one crossing; porphyrins from a template; the
rings on an octahedral metal hang 1.5 bonds out). A branch point is drawn with its
bonds 120° apart unless that crowds the drawing, where a fork of 90°, or the
narrower fork of earlier releases, is kept (231 rows have a branch atom with
two bonds under 90° apart; RDKit none).
Binding contract
Rust is the reference implementation. Python and WASM expose selected, versioned operations; Node uses the WASM surface. Binding parity means the same documented result/error contract for shared fixtures, not every Rust type or every RDKit method.
Atom-output order and connected-component source indices are exposed in Rust
and Python, but not in the WASM/Node surface. v1.0.32 adds reaction product
origin and template-map arrays to Python run_smirks_checked() and WASM/Node
run_reactants_checked(). Python arrays use returned-molecule atom indices;
WASM/Node arrays use canonical-SMILES parse order. Ordinary product SMILES
alone do not encode provenance. The source-wheel comparison does not prove
published-package parity.