Skip to content

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 RWMol remains the documented mutation subset, not full RDKit RWMol compatibility.
  • canonical_smiles() is a representation, not a deduplication/cache key. canonical_smiles_stable_key() is the recommended identity API and may return None when 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.