chematic

CI PyPI crates.io npm docs.rs License

Open in Colab

日本語 | 中文

A cheminformatics library for Python, Rust, and the browser.

Cheminformatics that's fast by default, safe by design.
Pure Rust · Zero C/C++ · Python · WebAssembly · Live Demo

chematic RDKit (Python) RDKit.js (WASM)
Get started pip install chematic conda / cmake required no Python bindings
Browser bundle 719 KB not available ~30 MB (~42× larger)
Batch fingerprints ~78 µs/mol (2–3× faster) ~160–235 µs/mol
Memory safety compiler-enforced (Rust) C++ C++
Build from source cargo build only cmake + clang + Boost Emscripten SDK

All numbers are reproducible — see benchmark details.
WASM sizes: chematic 719 KB · RDKit.js ~30 MB · Indigo WASM ~40 MB

Feature maturity at a glance:

Feature Status
SMILES / SMARTS / fingerprints / descriptors Stable
3D conformer generation (DG + MMFF94) Experimental
pKa / ADMET Rule-based screening (not for clinical use)
IUPAC name generation Partial (25+ classes)
Pure-Rust InChI Approximate (enable native-inchi feature for exact)

What you get

$ python -c "import chematic; print(chematic.from_smiles('CC(=O)Oc1ccccc1C(=O)O').describe())"
Molecular weight 180.2 Da, formula C9H8O4.
LogP 1.31 (mildly lipophilic), TPSA 63.6 Ų.
HBD 1, HBA 3, 3 rotatable bond(s), 1 aromatic ring(s).
Drug-likeness: no Lipinski rule-of-5 violations. likely orally bioavailable (passes Veber criteria).
QED 0.56 (0 = non-drug-like, 1 = ideal).
Structural alerts: Brenk alert.

One pip install. No RDKit, no conda, no C compiler. Works in Python, Rust, the browser, and AI agents.

# HTML report — self-contained, opens in any browser and renders in Jupyter
mols = [chematic.from_smiles(s) for s in smiles_list]
report = chematic.report(mols, names=compound_names)
report.save("report.html")   # or: display(report) in Jupyter

# Side-by-side comparison
cmp = chematic.compare(aspirin, ibuprofen, names=("Aspirin", "Ibuprofen"))
cmp.save("compare.html")

Common Use Cases

Scenario How chematic helps
HTML report chematic.report(mols, output="report.html") — self-contained compound grid, no server needed
Drug screening 190+ descriptors, ADMET, PAINS/Brenk, QED — batch over thousands of compounds
Molecule search ECFP4/MACCS fingerprints, Tanimoto, LSH approximate nearest-neighbour
AI agent / MCP Built-in MCP server — Claude Desktop can call chemistry tools directly
Browser app 719 KB WASM bundle, zero backend required, React/Vue/Svelte ready
Jupyter notebook mol renders SVG inline; descriptors_df() returns a pandas DataFrame
Batch analysis Rayon-parallel descriptor/fingerprint/3D pipelines; SDF/CSV in, CSV out
Rust server Pure-Rust crates with no C/C++ toolchain; Axum/Actix compatible

Full worked examples → Use cases


When to use chematic

Use chematic if:

  • You want chemistry in the browser (WASM, 719 KB, no server required)
  • You need a pure Rust stack with no C++ toolchain dependencies
  • You deploy to environments where pip install rdkit is impractical (Cloudflare Workers, Lambda, embedded)
  • You build AI agents and want native MCP tool integration
  • You process molecules in batch at high throughput (ECFP4: 2–3× faster than RDKit, Rayon-parallel)
  • You want pip install chematic to just work — anywhere, no compiler needed

Use RDKit if:

  • You need maximum ecosystem compatibility and 20+ years of production validation
  • You need publication-quality 3D structures with ML-assisted torsion corrections (RDKit's ETKDGv3)
  • You need bit-exact standard InChI without enabling the native-inchi feature
  • You depend on community plugins written against the RDKit Python API

Quick Start

Installation

# Python — no C/C++ compiler required
pip install chematic

# Rust
cargo add chematic --features "smiles,perception,chem,3d,fp"

# JavaScript/TypeScript
npm install @kent-tokyo/chematic

Python

import chematic

mol = chematic.from_smiles("CC(=O)Oc1ccccc1C(=O)O")  # aspirin

# In Jupyter, type `mol` in a cell — 2D structure renders automatically
mol

# Access 190+ descriptors as properties
print(mol.mw, mol.logp, mol.tpsa)           # 180.16  1.31  63.6
print(mol.lipinski_passes, mol.pains_passes) # True   True

# Substructure search
mol.has_substructure("[OH]")   # True
mol.find_matches("[CX3](https://github.com/kent-tokyo/chematic/blob/main/=O)O") # → [[1, 2, 3], [7, 8, 9]]

# Natural-language summary (one paragraph)
print(mol.describe())

# Structured Markdown report — paste into LLM, Jupyter, or save as .md
print(mol.review())
# → # Molecular Review\n## Structure\n## Physical Properties\n## Drug-likeness\n## ADMET...

# Structural diff between two molecules
ibuprofen = chematic.from_smiles("CC(C)Cc1ccc(CC(C)C(=O)O)cc1")
d = mol.diff(ibuprofen)  # {"summary": "+C7, -O2. ΔLogP +2.75 ...", "delta_mw": 66.1, ...}

# Batch processing — parallel, numpy-ready
fps = chematic.bulk.ecfp4(["CCO", "c1ccccc1", "CC(=O)O"])  # (3, 2048) uint8

# One-liner DataFrame
df = chematic.descriptors_df(["CCO", "c1ccccc1", "CC(=O)O"])
df[["mw", "logp", "tpsa", "qed"]]

For Rust and JavaScript/TypeScript examples, see the documentation.

Migrating from RDKit

chematic.rdkit_compat provides a lightweight RDKit-compatible subset so existing scripts port with minimal changes:

from chematic import rdkit_compat as Chem
from chematic.rdkit_compat import Descriptors, rdMolDescriptors, DataStructs

mol = Chem.MolFromSmiles("CC(=O)Oc1ccccc1C(=O)O")
Descriptors.MolWt(mol)                       # 180.16
fp = rdMolDescriptors.GetMorganFingerprintAsBitVect(mol, 2, nBits=2048)
DataStructs.TanimotoSimilarity(fp, fp)       # 1.0

It is not a full RDKit clone, and unsupported options fail loudly. See the RDKit compatibility guide for the compatibility matrix, differential-validation results vs RDKit, and runnable examples.

Diagnostics

import chematic
chematic.doctor()
# chematic v0.7.0
# Python 3.12.x  |  darwin arm64
#
# Descriptor accuracy (benchmark 2026-07-17, v0.7.0 vs RDKit 2026.03.3):
#   MW / HBA / HBD / ARC  100%   (4,999-mol ChEMBL subset)
#   TPSA                  100%   within ±0.1 Ų
#   LogP (Crippen)        100%*  (max Δ = 1.1×10⁻¹³)
#   Stereocenter count    99.96% (legacy) / 98.6% (new CIP FindPotentialStereo)
#   CIP R/S label         96.30% vs modern rdCIPLabeler (96.83% vs legacy)
# ...

For AI / LLM Developers

chematic ships a native MCP (Model Context Protocol) server — the first cheminformatics library with built-in AI agent integration.

// Claude Desktop (~/.config/claude/claude_desktop_config.json)
{
  "mcpServers": {
    "chematic": { "command": "chematic-mcp" }
  }
}

20 chemistry tools are callable from any MCP-compatible agent (full list in the chematic-mcp README):

Tool What it does
name_to_smiles Resolve "aspirin", "caffeine", … to SMILES via PubChem (the only tool that makes a network call)
calc_properties MW, exact mass, Crippen LogP, TPSA, HBD, HBA, rotatable bonds, QED
smarts_match Substructure search
pains_check / brenk_check Flag assay interference or reactive groups
generate_3d 3D coordinates via rule-based placement + DREIDING force-field minimization
find_mcs Maximum common substructure
+ 13 more ecfp4, tanimoto, canonical_smiles, admet_profile, boiled_egg, sa_score, lipinski_check, retrosynthesis, smiles_to_moljson, moljson_to_smiles, representation_router, molecule_context_pack, parse_smiles

Transport: stdio (JSON-RPC 2.0 over stdin/stdout) only. Runs as a local process; there is no hosted Remote MCP endpoint, no authentication, and no public service SLA — a remote-ready refactor is under consideration but not implemented.


Why Pure Rust?

Fast

Rust's zero-cost abstractions and ownership model eliminate overhead at the source. chematic's ECFP4 fingerprint batch pipeline runs at ~78 µs/mol on a diverse molecule corpus — 2–3× faster than RDKit's Python API on the same hardware, via Rayon parallelism across all CPU cores. No GIL, no interpreter overhead, no FFI call overhead hidden inside a _sys crate.

Safe

chematic's own ~15,000 lines of Rust contain ~6 unsafe blocks, all confined to the optional native-inchi FFI layer (below). No C++ heap corruptions. No segfaults from malformed SMILES input. No platform-specific build failures from -sys crates. The compiler enforces memory safety at every call site chematic itself wrote.

The native-inchi feature is the single opt-in exception — it vendors the IUPAC InChI C library (v1.07.5) for bit-exact standard InChI. All other chematic crates stay FFI-free and unsafe-free. This count is chematic's own source only, not its dependency tree — the optional depict feature (SVG/PDF/EPS rendering) pulls in a font/image-rendering stack (resvg/usvg/rustybuzz/tiny-skia/zune-jpeg) that is not unsafe-free; see the comparison table footnote below for a measured count.

Anywhere

Pure Rust compiles to wasm32-unknown-unknown natively — no Emscripten, no cmake, no clang. The npm package @kent-tokyo/chematic is 719 KB gzip — ~42× smaller than RDKit.js. One codebase runs on Linux, macOS, Windows, and in every browser.


Benchmarks & Validation

Metric Result Corpus
ECFP4 throughput ~78 µs/mol (2–3× vs RDKit, diverse corpus) 5,000-mol ChEMBL subset
HBA / HBD / aromatic ring count 100% RDKit agreement 4,999-mol ChEMBL subset
TPSA 100% RDKit agreement within ±0.1 Ų 4,999-mol ChEMBL subset
LogP (Crippen) 100% RDKit agreement* 4,999-mol ChEMBL subset
Stereocenter count 99.96% vs legacy†; 98.6% vs new CIP 4,999-mol ChEMBL subset
CIP R/S label agreement 96.30% vs modern rdCIPLabeler‡; 96.83% vs legacy 5,000-mol ChEMBL subset
WASM bundle 719 KB gzip

*LogP max Δ = 1.1×10⁻¹³ across 4,999 molecules — within float64 rounding error.
†Stereocenter count: ~99.96% vs legacy CalcNumAtomStereoCenters (a handful of molecules where chematic matches FindPotentialStereo and legacy under-counts); ~98.6% vs new-CIP FindPotentialStereo (cage/bridgehead molecules where both chematic and legacy correctly return fewer than the new oracle). chematic is calibrated between both extremes. This measures whether an atom is flagged as a stereocenter, not whether its R/S label is correct — see the next row.
‡CIP R/S label agreement measures, for atoms both oracles agree are stereocenters, whether the assigned R/S descriptor matches — a stricter, separate check from stereocenter count agreement above. This row is chematic's default assign_cip() path. The separate chematic-cip engine now reaches 99.38% raw / 99.64% oracle-stable (Milestone 4 gate closed) and is reachable opt-in via assign_cip_with_mode(mol, CipMode::Accurate) (Rust), Mol.cip_stereo(mode="accurate") (Python), or cip_assignments_accurate_json (WASM) — see docs/cip_accurate_rfc.md. No default path changed; this row's 96.30% is unaffected.

All numbers are reproducible with the scripts in this repo.
Full history → benchmarks/ · Methodology → validation/


Comparison with Other Cheminformatics Libraries

Feature chematic RDKit (rdkit-sys) OpenBabel FFI RDKit.js (WASM)
C/C++ dependencies None (default) Extensive C++ Extensive C++ C++ via Emscripten
WASM binary size ~1.9 MB (719 KB gzip) N/A (no WASM) N/A (no WASM) ~30 MB
Build requirement cargo build only cmake + clang cmake + clang Emscripten SDK
WASM target support Full (native) No No Yes (Emscripten)
Python bindings Yes (pip install chematic, PyO3) Yes (rdkit-sys) Yes No
Unsafe Rust None in own crates Extensive Extensive N/A
Feature chematic RDKit (rdkit-sys) OpenBabel FFI RDKit.js (WASM)
OpenSMILES parser Full Full Full Full
SMILES writer / canonical Yes Yes Yes Yes
Kekulization 4-pass (incl. Edmonds' blossom) Yes Yes Yes
Ring perception (SSSR) Yes + iterative augmentation Yes Yes Yes
SDF/MOL V2000+V3000 + SD fields Yes Yes Yes Yes
Tripos MOL2 format Yes (parser + writer) Yes Yes No
2D depiction (SVG, CPK colors, PDF, EPS) Yes Yes Yes Yes
ECFP/FCFP fingerprints (2/4/6) All variants + bitvec Yes Yes Yes
AtomPair / Torsion / MACCS FP Yes Yes Yes Yes
MAP4 fingerprint Yes (Minervini 2020) No (external pkg) No No
Molecular descriptors 190+ descriptor values (71 functions; MQN×42, BCUT2D, autocorr2d return multi-value arrays) ~30 ~20 ~30
Topological descriptors Yes (Petitjean, Hosoya Z, ECI, Moran, Geary) Partial Partial No
BRICS / RECAP fragmentation Yes Yes No Yes
Murcko scaffold Yes Yes No Yes
Tautomer normalisation Yes Yes No Yes
MCS Yes Yes No Yes
Stereoisomer enumeration Yes Yes No Yes
CIP stereo (R/S, E/Z) detail Yes (per-atom JSON) Yes Yes Yes
Allene cumulated stereo (C=C=C) Yes (@/@@, round-trip stable) Yes Partial No
3D coordinate generation Yes (DG + MMFF94/DREIDING + L-BFGS) Yes (ETKDG) Yes Yes
3D shape descriptors (PMI/NPR/USR/…) Yes Yes No Yes
3D GETAWAY descriptors (HATS-matrix) Yes (19-dim; whim_getaway_combined 29-dim) Yes No No
MMFF94 force field (all 7 energy terms) Yes Yes Yes No
UFF force field (metals, organometallics) Yes No Yes No
AutoDock PDBQT format (parse + write) Yes (docking pipeline ready) Via Python API Yes No
SDF with partial charges Yes (write_sdf_with_charges) Yes Yes No
MaxMin / Butina diversity picking Yes Yes No No
Reaction SMILES/SMIRKS Yes Yes Yes Yes
InChI / InChIKey Yes — pure-Rust + IUPAC-exact via native-inchi C lib required C lib required C lib required
pKa prediction Yes (15 SMARTS rules) No No No
ADMET profile (BBB/Caco-2/hERG/CYP3A4) Yes + BOILED-Egg Partial No Partial
MCP server (AI agent API) Yes — 20 tools incl. Name→SMILES (stdio only) No No No
IUPAC name generation Yes (25+ classes) No No Partial
Name → SMILES (PubChem proxy) Yes (name_to_smiles MCP tool) No No No
Maintenance (2026) Active Active Minimal Active

† Default build only. The optional native-inchi feature adds a C-compiler dependency for the vendored IUPAC InChI C library (v1.07.5). This is about C/C++ FFI specifically — the depict feature below pulls in pure-Rust rendering crates, so it doesn't add a C compiler dependency even though it isn't unsafe-free (see ‡).

‡ chematic's own ~15,000 lines of Rust: unsafe-free outside native-inchi's ~6 FFI blocks (see "Safe" above) — a real, verifiable claim about code chematic wrote, and categorically different from RDKit/OpenBabel's C++ FFI unsafe (uncheckable by any compiler at that boundary) even where the raw count is comparable. It is not true of the full dependency tree: the optional depict feature (SVG/PDF/EPS rendering) pulls in resvg/usvg/rustybuzz/tiny-skia/zune-jpeg, pure-Rust crates that are themselves not unsafe-free — measured directly (unsafe fn/impl/trait/{ openings): tiny-skia 151, zune-jpeg 79, rustybuzz 14, image 8, fontdb 3, tiny-skia-path 3 (258 total in this set alone). chematic-py (pip install chematic) and the npm package both depend on chematic-depict directly, so this applies to both real-world install paths, not just an edge case.


JavaScript / TypeScript (WebAssembly)

719 KB gzip — ~42× smaller than RDKit.js. No Emscripten, no cmake. Drop-in for browser or Node.js.

npm install @kent-tokyo/chematic
import init, { parse_smiles, get_descriptors_json, tanimoto_ecfp4,
               generate_3d_minimized_pdb, enumerate_stereo_isomers_json,
               maxmin_picks_ecfp4_json } from '@kent-tokyo/chematic';

await init();

const mol = parse_smiles('CC(=O)Oc1ccccc1C(=O)O'); // aspirin
console.log(mol.molecular_weight(), mol.qed(), mol.lipinski_passes());

// All descriptors as a JSON object
const desc = JSON.parse(get_descriptors_json(mol));

// Fingerprint similarity
const caffeine = parse_smiles('Cn1cnc2c1c(=O)n(c(=O)n2C)C');
console.log(tanimoto_ecfp4(mol, caffeine));  // 0.26

// 3D coordinates, stereoisomers, diversity picking
const pdb = generate_3d_minimized_pdb(mol);
const isomers = JSON.parse(enumerate_stereo_isomers_json(parse_smiles('C(F)(Cl)Br')));
const picks = JSON.parse(maxmin_picks_ecfp4_json('["CC","c1ccccc1","CCO","CCCC"]', 2));

130+ exported functions cover descriptors, fingerprints, 3D geometry, reactions, diversity picking, and SDF round-trips. See the full WASM API reference for all exports.

Crate Reference

Crate Description Tests
chematic-core Atom, Bond, Molecule, Element, kekulization (no deps); mutable add/remove_atom/bond, fragments(), is_connected(), formula_with_isotopes, validate_valence; StereoGroup/StereoGroupKind 71
chematic-smiles OpenSMILES parser, writer, canonical SMILES; stereo parity correction (pre-solves RDKit #8775 — @/@@ auto-flipped on odd permutations); allene cumulated double bond stereo (C=C=C @/@@, round-trip stable) 109
chematic-perception SSSR, Hückel aromaticity + antiaromaticity (4n+2 rule), apply_aromaticity, aromatize/kekulize_inplace, assign_stereo_from_2d, assign_ez_from_2d, cip_ez_descriptor; zero-order/dative bonds excluded from ring perception 101
chematic-mol MOL/SDF V2000+V3000 (R/W with 2D coords, +partial charge writing), CML (R/W), CDXML (R); SdfRecord with coords+props; MDL RXN R/W; V3000 stereo-group COLLECTION R/W; AutoDock PDBQT (parse + write); ChemicalJSON (parse_cjson/write_cjson, Avogadro/MolSSI format); 2D wedge/hash tetrahedral parity + E/Z double-bond direction now perceived automatically on read (read_mol_with_diagnostics/read_mol_v3000_with_diagnostics, typed opt-in diagnostics) 130+
chematic-depict 2D SVG (CPK colors, highlighting, grid), DepictData, detect_crossings, render_svg_with_metadata, reaction SVG; PDF output (depict_pdf/depict_pdf_opts via svg2pdf); EPS output (depict_eps/depict_eps_opts, pure Rust); tiny_skia PNG is optional png feature (default on, disabled for WASM) 64
chematic-chem 190+ descriptor values (71 functions), tautomers, scaffold, BRICS, QED, standardize, CIP; pKa prediction (15 SMARTS rules); ADMET profile (BBB/Caco-2/hERG/CYP3A4); HBA 100% RDKit agreement (4 999 / 4 999 mol benchmark); TPSA 100% ±0.1 Ų / LogP 100%* / HBD 100% / stereocenter count 99.96% (legacy) / 98.6% (new CIP) vs RDKit (4,999-mol ChEMBL); CIP R/S label agreement 96.30% (default), 99.64% oracle-stable via opt-in CipMode::Accurate (5,000-mol ChEMBL, see docs/cip_accurate_rfc.md); topological descriptors (petitjean_index, graph_diameter, graph_radius, graph_eccentricities, eccentric_connectivity_index, hosoya_index, moran_autocorr, geary_autocorr); schultz_mti, gutman_mti, vabc (Bondi radii vdW volume), gravitational_index; clean_stereo_groups() in standardize 662
chematic-fp ECFP2/4/6, FCFP4/6, MACCS, TopoPF, AtomPair, Torsion, Layered, Pattern, Pharmacophore, Reaction, MAP4 (Minervini 2020, not in RDKit) — Tanimoto/Dice; bulk similarity 185
chematic-ff MMFF94 all 7 terms (Halgren 1996): Bond/Angle/Torsion/vdW/Elec + OOP (117 entries) + Stretch-Bend (282 entries); steepest-descent + L-BFGS optimizer, torsion scan, energy breakdown; DREIDING typing; UFF (metals/organometallics: Zn, Fe, Cu, …) 98
chematic-smarts SMARTS, VF2, MCS with chirality matching; SmartsCache (LRU compilation cache, 5–20×); named_pattern() library (20 functional group patterns); atom map :N in SMARTS ([O;D1;H0:3] — stored as metadata, not a match criterion); [kN] ring-size primitive; VF2 early-exit when query > target atom count; find_matches_with_rings — share SSSR across multi-pattern batches 142
chematic-3d 3D coordinate generation, distance geometry constraints, ETKDG KB (40 torsion patterns, adaptive noise), force-field minimization, shape descriptors, ConformerEnsemble with RMSD pruning, PDB/XYZ; GETAWAY HATS-matrix (full 19-dim implementation); whim_getaway_combined() now 29-dim 265
chematic-rxn Reaction SMILES/SMIRKS, run_reactants/run_reactants_strict; retro_disconnect() — 60 retro-SMIRKS templates (AmideBond/Ester/Ether/CNBond/CCBond/CSBond) + SA Score ranking; parity-aware @/@@ SMIRKS stereo filtering; E/Z double-bond stereo filtering in run_reactants (ez_stereo_outward, smirks_ez_stereo_ok) 137
chematic-inchi InChI/InChIKey: pure-Rust approximation (WASM) + IUPAC-standard via native-inchi feature (vendored C lib 1.07.5, bit-exact); parse_inchi reader; verified canonical-SMILES dedup (dedup::{group_candidates, deduplicate_verified}, fail-closed on legacy-CIP-unresolved specified tetrahedral stereo) 96 (+16*)
chematic-cip Opt-in accurate CIP engine (assign_cip_accurate_experimental, hierarchical digraph, Rules 1a/1b/2/4b/5, RDKit-compatible MANCUDE fractional atomic numbers) — the default assign_cip()/CipMode::LegacyFast is unchanged
chematic-wasm 130+ WASM exports — npm: @kent-tokyo/chematic (published 0.7.0, in sync with crates.io/PyPI); pKa/ADMET/BBB/Caco-2/hERG/CYP3A4; smiles_to_pdbqt, minimize_uff_json 211
chematic-iupac Local IUPAC name generation — 25+ compound classes: alkanes, cycloalkanes, alkenes/alkynes, alcohols, amines, halides, aldehydes, ketones, acids, esters, amides, piperidine, morpholine, piperazine, naphthalene, sulfides 47
chematic-mcp MCP (Model Context Protocol) server — AI agent integration; 20 tools: parse_smiles, calc_properties, ecfp4, tanimoto, smarts_match, canonical_smiles, find_mcs, generate_3d, pains_check, brenk_check, sa_score, admet_profile, boiled_egg, lipinski_check, name_to_smiles, retrosynthesis, smiles_to_moljson, moljson_to_smiles, representation_router, molecule_context_pack 31
chematic-py PyO3 Python bindings (pip install chematic); 300+ API endpoints: from_smiles(), Mol.descriptors(), Mol.minimize_dreiding(), from_cxsmiles(), from_rxn_file()/to_rxn_file(), parse_sdf_with_coords(), Mol.ring_families(), tanimoto_matrix(), iter_sdf(), SimilarityIndex; mol.to_pdf()/mol.to_eps() (depict); from_cjson()/mol.to_cjson() (ChemicalJSON); mol.schultz_mti, mol.gutman_mti, mol.vabc, mol.gravitational_index; bulk.substructure_match(smarts, mols) (parallel VF2 on pre-parsed Mol objects); mol.describe() (LLM/MCP-ready natural-language summary); mol.diff(other) (element + descriptor diff); Sprint 18–27 coverage 300+
chematic-ewald PME Ewald summation, B-spline interpolation (cubic, phase-corrected) 16
chematic Umbrella crate with feature flags (all sub-crates, incl. iupac, inchi) 1
cargo test --workspace --lib --quiet                                          # 2,746 tests, all passing (2026-07-26)
cargo test -p chematic-inchi --features native-inchi --test standard_inchi  # +16 IUPAC-exact InChI tests

Recent Development

v0.7.0 (2026-07-26): 2D wedge/hash + E/Z stereo now perceived automatically from MOL/SDF, verified canonical-SMILES dedup, CIP Rule-5 phosphorus fix, native InChI explicit-H/isotope fix

  • chematic-mol/chematic-perception: MOL V2000/V3000/SDF readers now perceive both tetrahedral wedge/hash parity (PR #154) and E/Z double-bond direction (PR #162) automatically on read, CIP-independent, via read_mol_with_diagnostics/read_mol_v3000_with_diagnostics — typed opt-in diagnostics (StereoDiagnostic/EzDirectionDiagnostic) never guess on malformed/ambiguous input. Broad-corpus validation (4,999 molecules vs. RDKit 2026.03.3): E/Z — 622 RDKit-resolved double bonds, 0 semantic inversions, 0 false positives. Also fixed a V2000 MDL-code-4 bug, two V3000 CFG bugs, and a V2000 writer bug found while building this. PR #162's own corpus reconciliation surfaced a new, not-yet-fixed gap: canonical_smiles() can drop an already-correctly-write()-encoded E/Z marker for some molecules (aromaticity-unaware carrier grouping) — not yet filed as an issue
  • chematic-inchi: new verified canonical-SMILES dedup (dedup module) — fast canonical-SMILES candidate bucketing reconciled against native-InChI verified identity; fails closed (VerificationUnavailable) rather than risk a false merge whenever a specified stereocenter's legacy-CIP ranking is unresolved (closes a real false-VerifiedDuplicate found via live 5,000-molecule corpus verification). Follow-up tracked as #161 (an accurate-CIP preflight could recover most of the conservative cases)
  • chematic-cip: CIP Rule-5 pseudoasymmetric r/s phosphorus fix
  • chematic-inchi: native InChI (native-inchi feature) explicit-hydrogen/isotope conversion fix
  • Full details in CHANGELOG.md's [Unreleased] section

v0.6.0 (2026-07-25): RDKit-bit-exact ECFP4 cross-language stable API, canonical SMILES E/Z marker consistency, opt-in authoritative aromaticity demotion

  • chematic-fp/Python/WASM: promoted the RDKit-bit-exact Morgan/ECFP4 path to a documented, cross-language opt-in API (Mol.rdkit_ecfp4() in Python, rdkit_ecfp4_bitvec() in WASM), generalized to an independently oracle-verified (radius, fpSize) matrix (4×5 = 20 cells, closed enums so an unsupported combination can't be constructed). Rust/Python/WASM verified byte-identical by actually building and running all three, not "identical by construction." ecfp4()'s default behavior is unchanged
  • chematic-smiles: fixed canonical output placing an E/Z direction marker on a different substituent bond depending on input atom order — permutation invariance for marker placement 93.0% → 98.1% (264/282 known-divergent molecules now converge; 18 residual cases deliberately left unresolved to avoid corrupting a shared-carrier-bond edge case, tracked as #149). Also investigated and ruled out a previously-suspected bridged-bicyclic canonicalization bug — the two SMILES in question turned out to be genuinely different molecules per independent RDKit InChI verification, not a chematic defect
  • chematic-perception: new opt-in apply_aromaticity_authoritative_experimental — makes aromatic-flag promotion/demotion fully authoritative to the Hückel model's actual verdict in both directions (the default apply_aromaticity/apply_aromaticity_ex are unchanged, verified byte-identical). Fixed a ring-fusion-bond misclassification affecting fused diazines (quinazoline/quinoxaline/purine-shaped rings) as part of this work, with a beneficial side effect of also resolving 32 pre-existing false-positive regression pins
  • Full details and known limitations in CHANGELOG.md

v0.5.0 (2026-07-23): CIP-independent 2D wedge parity, charge-aware kekulization (6 previously-hard-failing molecule classes), non-silent PAINS/Brenk budget matching

  • chematic-perception: new local_parity_from_wedges/apply_local_parity_from_wedges — computes Atom.chirality/stereo_neighbor_order directly from wedge/hash bonds and 2D coordinates without any CIP ranking, so a CIP tie can't erase a known local parity; sign convention measured against RDKit's raw chiral tag, not derived by analogy. Not yet wired into any reader's default parse path
  • chematic-core: kekulize()'s atom-matching rules were charge-blind and missing Tellurium — tropylium, imidazolium, pyridinium, pyrylium, tellurophene, and phosphole now kekulize successfully, bond-for-bond identical to RDKit, with zero regressions; Element::normal_valences() gained a source-verified Tellurium entry, fixing a resulting ECFP4 aromaticity mismatch
  • chematic-perception: charge-aware Hückel π-electron counting — tropylium, imidazolium, pyridinium, and pyrylium now match RDKit's aromatic atom/bond flags exactly (tellurophene/phosphole and a broader authoritative-flag-demotion fix remain open, tracked separately)
  • chematic-smiles: fixed two writer bugs — bracket-forced atoms (isotope/charge/atom-map) were silently dropping implicit hydrogens ([NH4+][N+]), and standalone wedge bonds with no adjacent double bond were written as meaningless SMILES //\ tokens
  • chematic-smarts/chematic-chem: PAINS/Brenk substructure matching could hang for minutes on symmetric targets; VF2 now takes an explicit visit budget with a genuine three-way outcome (Found/NotFound/BudgetExhausted) that's never silently folded into a false negative — see #139 for the follow-up (symmetry-aware candidate ordering) that would let the shipped budget resolve some now-conservatively-flagged cases correctly instead of just safely
  • Full details, corpus-level before/after numbers, and known limitations in CHANGELOG.md

v0.4.30 (2026-07-17): chematic-cip opt-in wired to every surface, SMARTS [rN] fix, new RDKit-parity aromaticity engine, 5 stereo-metadata bug fixes

  • chematic-smarts: fixed [rN] (ring-size SMARTS, e.g. [r5]/[r6]) being wrongly aliased to [kN]'s any-ring semantics — RDKit's real [rN] means "this atom's smallest ring is exactly size N", a materially different predicate (confirmed empirically: on a fusion atom shared between a 5-ring and 6-ring, RDKit's [k6] matches but [r6] doesn't). No ring-model change — [rN] now has its own MinRingSize primitive computed from chematic's existing SSSR. SMARTS match-set agreement vs RDKit 96.9% → 99.93% on a 5,000-molecule corpus, 0 regressions; see docs/rdkit_compat.md's "SMARTS-R0"/"SMARTS-R1" entries
  • Milestone 5A: opt-in access to the accurate engine from every public surface — chematic_chem::assign_cip_with_mode(mol, CipMode::Accurate) (Rust), Mol.cip_stereo(mode="accurate") + Mol.cip_stereo_unresolved() (Python), cip_assignments_accurate_json/cip_unresolved_json (WASM). Every default (assign_cip(), cip_stereo(), the un-suffixed WASM functions) is unchanged — this is additive only, not a default switch; see docs/cip_accurate_rfc.md's Milestone 5A entry for the merge semantics (accurate tetrahedral R/S + legacy E/Z, since the accurate engine doesn't compute bond stereo) and the "never guess" contract (ties/budget-outs surface explicitly, never silently backfilled)
  • Milestone 4 gate closed: 99.64% oracle-stable agreement (raw 99.38%, 4160/4186) on a full-corpus, representation-stability-stratified score — the last residual (11 phosphorus cyclophosphazene rows) turned out to be an oracle instability (RDKit's own labels change under a chemically-neutral Kekulé respelling of the identical molecule), not a chematic defect; the 15-row Rule 5 cage family remains deferred, unaffected by this gate
  • The accurate engine (a provenance-carrying, sphere-by-sphere digraph comparator — Rules 1a/1b/2 — plus RDKit-compatible MANCUDE fractional atomic numbers for aromatic ring stereocenters) is available through the opt-in APIs above but is not yet the default implementation behind assign_cip()
  • Found and fixed a real ~10-14x perf regression (SSSR misused for a boolean ring-bond check, replaced with an O(V+E) bridge-edge DFS); CI Criterion-gate bootstrap fix; a Criterion-gate reliability finding (pseudo-replication, #70) — process-level redesign (independent process-run observations, two-stage screening, same-binary null control) landed. Follow-up (#117) fixed two further, more specific bugs found via real CI runs: Stage 1's 3-block sign-test could structurally never fail (replaced with a pure magnitude-routing screen, threshold chosen from an offline evaluation against 28 historical no-op runs) and Stage 2's sign-test was independently magnitude-blind to small build/codegen variance (fixed with a combined sign-test + practical-effect-threshold gate). The gate stays non-required; the same-binary null control's blind spot to cross-binary codegen variance is a known, reported, not-yet-fixed gap
  • Milestone 4A: CipCode::LowerR/LowerS — Rule 5 (pseudoasymmetry), scoped to 2 verified-independent rows; a three-armed symmetric-cage family (15 rows) was found to be provably unreachable by this pairwise architecture and deferred as Milestone 4A-2 (needs symmetry/automorphism detection)
  • Milestone 4A-0: re-froze the residual fresh at 34 rows and mechanically classified 100% of it (0 unexplained) — 15 Rule 5/pseudoasymmetry (the 4A-2 cage family), 8 Rule 4 candidate (positively confirmed via a structural-identity check, not inferred), 11 phosphorus (9 comparator-bug "wrong" + 2 genuinely-tied)
  • chematic-perception: new opt-in assign_aromaticity_rdkit_parity_experimental/apply_aromaticity_rdkit_parity_experimental — a source-verified port of RDKit's actual aromaticity algorithm, 100.0000% atom/bond agreement with real RDKit on 4,999/5,000 comparable molecules. Not wired into the default path (RdkitLike/Huckel unchanged); default-promotion is blocked on a pre-existing, unrelated canonical-SMILES-writer sensitivity, not this engine
  • Fixed 5 instances of the same missing-metadata-copy bug (a MoleculeBuilder rebuild not calling copy_stereo_groups_from/copy_stereo_from/`copy_bond_directions_fr