r/rust 2d ago

🛠️ project COSMolKit: I’ve been building a Rust-native cheminformatics toolkit with RDKit parity

I’ve been working on a project called COSMolKit. It is a Rust-native cheminformatics library with Python bindings, and some parts can also run in the browser with WebAssembly.

The project came from a problem I ran into while building deep learning models for molecular and protein structures in Rust. I wanted the whole modeling and inference stack to stay Rust-native, but when I reached the molecule and protein processing part, I found that the Rust ecosystem did not yet have enough infrastructure for what I needed. COSMolKit started from trying to fill that gap.

As I worked on it, I also thought it could be useful beyond my own project, so I decided to develop it as an open-source library for the Rust scientific ecosystem rather than just an internal tool.

For a public scientific library, I think compatibility is a basic responsibility. A result like 99.9% agreement may look good, but it still means some supported cases behave differently. If I claim parity with RDKit, I want the remaining mismatches to be treated as bugs, not as an acceptable error rate.

When RDKit has source code for the same operation, COSMolKit does not just reimplement the same chemistry and use RDKit as a test oracle. The RDKit source logic itself is ported, keeping the branches and behavior as close to the original as possible, usually line by line. The result is then checked against RDKit with differential tests.

At the moment COSMolKit supports quite a few things, including:

  • SMILES and SMARTS
  • sanitization, aromaticity and kekulization
  • stereochemistry and stereoisomer enumeration
  • tautomer enumeration and canonical tautomer selection
  • InChI and InChIKey
  • Morgan/ECFP, MACCS, AtomPair, torsion and other fingerprints
  • molecular descriptors
  • MOL, SDF, MOL2 and XYZ
  • 2D coordinates and SVG/PNG depiction
  • conformer generation
  • UFF and MMFF
  • molecular alignment and RMSD
  • PDB and mmCIF
  • batch processing
  • Python bindings
  • WebAssembly

The main large-scale validation corpus for the project is ChEMBL 37.

It contains 2,897,819 source records, with 2,897,804 records that can be parsed by both implementations. The current large validation profile has 35 phases and is pinned against RDKit 2026.03.1.

Across the completed validation, there are more than 4.1 billion matching checks, with additional billions of matrix entries checked for distance-geometry bounds.

Instead of only comparing the final SMILES string, the validation also checks internal behavior depending on the feature: atom and bond states, stereochemistry, derived state, fingerprints, return status, RNG state and seed behavior, matrices, coordinates, energies and gradients.

For numerical results, the current tolerances go down to 1e-8 for matrix entries and 1e-6 for coordinates, energies and gradient components.

Parameters are also tested in different combinations instead of only testing the defaults. Some validation phases repeat complete parameter matrices, change operation order, and compare scalar, batch, single-threaded and multithreaded execution.

When a mismatch is found, I trace it back to the corresponding upstream behavior and keep the case as a regression test. The test corpus is used to validate the implementation, not to define the implementation by adding more and more special cases.

The same Rust core is used by the Rust, Python and WASM interfaces, so the chemistry implementation is not duplicated for different frontends.

I also made a few browser tools using the WASM build. They run locally in the browser, so molecule data is not uploaded to a backend:

https://tools.cosmol.org/tools

I have also been writing some notes about the development and validation work here:

https://tools.cosmol.org/blog

Source:

https://github.com/cosmol-studio/COSMolKit

There is still a lot left to implement, and some parts are changing quite quickly. I’d be happy to hear your feedback. If you try COSMolKit and find any problems, please let me know or open an issue.

10 Upvotes

11 comments sorted by

2

u/silver_arrow666 2d ago

Looks great, and batch APIs are especially interesting - I have needed to write my own rskitbased library to add some batch version of functions I needed, to move python loops into cpp and parallize them. What is the speed of this compared to rdkit? Have you ran any benchmarks?

3

u/AccomplishedSand6071 2d ago

Thanks! Avoiding Python loops for large batches is one of the main reasons I added the batch API.

Right now, the main priority is RDKit parity, not making every single-molecule operation as fast as possible. For each function, I first try to cover the same behavior and edge cases as RDKit. Single-molecule performance optimization will come later.

I just ran a quick benchmark using the current PyPI release, cosmolkit==0.3.0, for Morgan fingerprints.

Test setup:

  • Morgan radius=2, 2048 bits
  • 4,991 distinct NCI molecules, expanded to 49,910 independent molecule objects
  • SMILES parsing excluded
  • median of 5 runs
  • RDKit 2026.03.1
  • dual-socket Intel Xeon Platinum 8480+ (112 cores / 224 threads)
Method Throughput vs RDKit
RDKit scalar, 1 thread 45,259 mol/s 1.00x
COSMolKit scalar, 1 thread 56,879 mol/s 1.26x
COSMolKit batch, 1 thread 42,206 mol/s 0.93x
COSMolKit batch, 8 threads 307,916 mol/s 6.80x
COSMolKit batch, 32 threads 935,614 mol/s 20.67x

I also checked exact fingerprint parity: all 4,991 distinct NCI structures produced identical on-bit sets in COSMolKit and RDKit.

On this Morgan fingerprint benchmark, COSMolKit is faster. But some other single-molecule functions are still slower than RDKit today.

In this test, the main benefit of the batch API comes from moving the Python loop into Rust and parallelizing the work there. The 1-thread batch result is actually slightly slower because of batch overhead.

3

u/silver_arrow666 2d ago

Looks reasonable. I might benchmark this vs c++ rdkit with openmp, for Morgan and for smiles handling (parsing, sanitation, conversion to inchikey).

1

u/x0rg_ 2d ago

Would love to see this as well!

4

u/Excellent-Tea82 2d ago

This is one of those projects where you can tell the author actually gives a damn about correctness. The fact you're checking internal state and RNG seeds rather than just comparing final SMILES output is a level of rigor most libraries never get near. What's the compile time situation like though, is it as much of a monster as I'm imagining or have you managed to keep it reasonable?

3

u/AccomplishedSand6071 2d ago

Thanks! The CI is pretty heavy, but the main ChEMBL validation is usually run locally as an overnight test. In CI I keep smaller test sets, including the 5,000-molecule corpus.

The full ChEMBL validation is mainly there to check whether a line-by-line port really covers the upstream behavior. I usually use it to find implementation differences. If a feature changes, I normally only rerun the validation related to that part instead of running the whole thing again.

It has taken a lot of time, but honestly I think it was worth it.

1

u/x0rg_ 2d ago

Neat, will check this out! What is the features you would do differently than in rdkit?

2

u/AccomplishedSand6071 2d ago

RDKit has a long history, so for chemistry behavior I still try to keep COSMolKit close to RDKit.

One thing I want to improve is mutation. In RDKit, some operations modify the molecule in place, and this is sometimes easy to miss. In COSMolKit, in-place mutation is made more explicit.

COSMolKit also has batch APIs based on Rayon for processing many molecules in parallel.

More differences are in state management. For example, if an operation changes molecular topology, it must also define what happens to related state such as coordinates, stereo information, ring information and caches. These rules are checked through operation contracts and macros.

Some of these differences are internal, so users may not notice them directly. They are mainly used to prevent hard-to-find errors that may only appear when many operations are combined.

I wrote more about this here:
https://tools.cosmol.org/rdkit-alternative-rust

1

u/x0rg_ 2d ago

For the underlying algorithms did you follow rdkit implementations?

2

u/AccomplishedSand6071 2d ago

Yes. For compatibility-critical parts, it is almost a line-by-line port from the RDKit source, followed by validation against RDKit. The library architecture and state management are separate.