Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,52 @@
# Changelog

## 0.7.0 - 2026-10-15

### Summary

nvMolKit 0.7.0 adds a single-precision mode for force field minimization (MMFF, UFF, BFGS, FIRE) and ETKDG conformer generation, which is roughly an order of magnitude faster on consumer GPUs. It also adds a fused 3D descriptor API, diversity selection (Leader and MaxMin), DISE clustering, an Atom-Atom Path (AAP) similarity metric, a persistent GPU `SubstructLibrary`, and various performance improvements to existing fingerprinting, substructure search, and 3D minimization code.

### Contributors
- Eva Xue (@evasnow1992)
- Kevin Boyd (@scal444)
- Matthew Neba (@Matthew-Neba)
- Ohad Mosafi (@ohadmo)

### Features
- Single-precision mode (`precision=PrecisionMode.SINGLE`) for MMFF and UFF force fields, BFGS and FIRE minimizers, and ETKDG distance geometry and ETK stages, available through the MMFF, UFF, `BatchedForcefield`, and `EmbedMolecules` APIs. On consumer GPUs, single precision is roughly an order of magnitude faster than double precision. Validation against RDKit is documented in the 3D minimizers guide ([#266](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/266))
- GPU-accelerated 3D descriptors through the new `nvmolkit.descriptors3d.Calc3DProperties`, which computes a user-selected set of properties in one fused pass over a batch of conformers: PMI, NPR, radius of gyration, shape factors, PBF, WHIM, RDF, MORSE, AUTOCORR3D, USR, USRCAT, and GETAWAY, matching RDKit's `rdMolDescriptors` ([#341](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/341))
- Atom-Atom Path (AAP) molecule similarity via `aap_similarity`, also usable as `metric="aap"` in `fused_leader`, `fused_maxmin`, and `fused_dise` ([#303](https://github.com/NVIDIA-BioNeMo/nvMolKit/pull/303))
- Diversity selection with `leader` and `maxmin` for distance matrices, and `fused_leader` and `fused_maxmin` for packed fingerprints or molecules, following RDKit's `LeaderPicker` and `MaxMinPicker` ([#339](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/339))
- Directed sphere exclusion (DISE) clustering with `dise` and `fused_dise` ([#339](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/339))
- Standardized clustering and diversity selection APIs. Butina, DISE, Leader, and MaxMin each come in a matrix form, which takes a precomputed distance matrix, and a fused form, which computes distances on the fly so memory grows linearly instead of quadratically. Every fused form accepts any similarity metric (`"tanimoto"`, `"cosine"`, or `"aap"`) and either output mode (`OutputMode.DEVICE` or `OutputMode.RDKIT`), so algorithms, forms, metrics, and output modes combine freely, except that `fused_butina` does not yet support AAP ([#339](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/339))
- Persistent GPU `SubstructLibrary` (`nvmolkit.substruct_library.SubstructLibrary`), the GPU counterpart of RDKit's `SubstructLibrary`. Target molecules are packed and uploaded once, then searched by any number of queries, with multi-GPU storage, asynchronous concurrent queries within a GPU-memory budget, and pattern-fingerprint prescreening ([#350](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/350))
- ETKDG now supports eigenvalue-based initial coordinate generation (`useRandomCoords=False`), matching RDKit's initialization and retry behavior ([#288](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/288))
- Device-side conformer pruning for ETKDG, so `pruneRmsThresh` can now be combined with GPU-resident output, by @Matthew-Neba ([#160](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/160))
- Conformer RMSD supports `alignment_mode="pairwise"` (the default, aligning every pair independently) and `alignment_mode="first-conformer"` (matching RDKit's `GetConformerRMSMatrix`) ([#281](https://github.com/NVIDIA-BioNeMo/nvMolKit/pull/281))

### Performance Improvements
- Per-molecule BFGS minimizer is 10-40% faster, applying to ETKDG, MMFF, and UFF on the per-molecule backend ([#342](https://github.com/NVIDIA-BioNeMo/nvMolKit/pull/342), [#355](https://github.com/NVIDIA-BioNeMo/nvMolKit/pull/355))
- Morgan fingerprint generation is up to 2x faster, through reduced CPU preprocessing and Python transfer overhead ([#267](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/267))
- Tanimoto similarity for queries with few molecules is up to 8x faster ([#284](https://github.com/NVIDIA-BioNeMo/nvMolKit/pull/284))
- Substructure search performance has been improved for queries with repeated recursive SMARTS, since each repeated pattern is now solved only once ([#313](https://github.com/NVIDIA-BioNeMo/nvMolKit/pull/313))
- ETKDG conformer generation is up to 2x faster for batches with a few hard-to-embed molecules, because retries now keep GPU batches full. The default retry budget is also now bounded per molecule instead of scaling with the largest molecule in the input ([#353](https://github.com/NVIDIA-BioNeMo/nvMolKit/pull/353))

### Breaking Changes
- `butina` and `fused_butina` now return a `ClusterDeviceResult` (cluster IDs, centroids, and cluster sizes) by default, or RDKit-format cluster tuples with `output=OutputMode.RDKIT`. The `return_centroids` argument is removed ([#269](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/269))

### Bug Fixes
- Fix `countSubstructMatches` ignoring `uniquify=True`, which returned raw embedding counts that disagreed with RDKit for symmetric queries ([#275](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/275))
- Fix SMARTS hydrogen counts excluding explicit hydrogen neighbors, so queries such as `[CH3]` now match deuterated and explicit-H targets as in RDKit ([#276](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/276))
- Fix plain query atoms (for example from `MolFromSmiles`) being packed as wildcards in substructure search, and route targets with isotopes above 255 or unsupported bond types to the RDKit fallback instead of failing ([#349](https://github.com/NVIDIA-BioNeMo/nvMolKit/pull/349))
- Fix ETKDG exiting early on molecules that fail to embed, which previously could skip scheduled retries and fail more often than RDKit ([#300](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/300))
- Fix float32 division in the Tanimoto similarity kernels using approximate rounding, which could order equal distances differently than RDKit in downstream clustering ([#338](https://github.com/NVIDIA-BioNeMo/nvMolKit/pull/338))

### Miscellaneous
- Improved shared benchmark tooling, including common CLI, timing, result-reporting, and dataset-loading utilities, and RDKit-side deadlines for long-running baselines
- Drastically improved CI coverage, and added back linters and static analysis
- Support for CUDA 13.4 and Rubin GPUs ([#304](https://github.com/NVIDIA-BioNeMo/nvMolKit/pull/304))


## 0.6.0 - 2026-08-13

### Summary
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ cmake_policy(SET CMP0167 NEW)

project(
nvmolkit
VERSION 0.6.0
VERSION 0.7.0
LANGUAGES CXX CUDA)
set(CMAKE_CUDA_STANDARD 20)
set(CMAKE_CXX_STANDARD 20)
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Conda is the recommended way to install nvMolKit, matching the recommended distr
you have a variant of conda installed and activated, such as [Miniconda](https://docs.conda.io/en/latest/miniconda.html)
or [Miniforge](https://conda-forge.org/download/).

nvMolKit v0.6.0 supports RDKit 2025.09.1 through 2026.03.5. To install:
nvMolKit v0.7.0 supports RDKit 2025.09.1 through 2026.03.5. To install:

```bash
conda install -c conda-forge nvmolkit
Expand Down Expand Up @@ -59,7 +59,7 @@ Replace `cu128` with another CUDA 12 backend from the PyTorch install page if th
compatible with your driver.

The wheel published to PyPI is built against a single RDKit release per
nvMolKit version (RDKit 2026.03.5 for nvMolKit v0.6.0), due to versioning
nvMolKit version (RDKit 2026.03.5 for nvMolKit v0.7.0), due to versioning
limitations in PyPI's dependency management system.

Use this path if you do not have a constraint on which RDKit version to use;
Expand All @@ -73,12 +73,12 @@ To install nvMolKit pinned to a particular RDKit:

```bash
RDKIT_VERSION=2025.9.6
NVMOLKIT_VERSION=0.6.0
NVMOLKIT_VERSION=0.7.0
pip install nvmolkit==${NVMOLKIT_VERSION}+rdkit${RDKIT_VERSION} \
--extra-index-url https://nvidia-bionemo.github.io/nvMolKit/wheels/rdkit${RDKIT_VERSION}/simple/
```

Replace `2025.9.6` with the RDKit version you want. Variants published for nvMolKit v0.6.0 include every version between 2025.09.1 and 2026.03.5.
Replace `2025.9.6` with the RDKit version you want. Variants published for nvMolKit v0.7.0 include every version between 2025.09.1 and 2026.03.5.


### Installation from Source
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.6.0
0.7.0
26 changes: 13 additions & 13 deletions admin/distribute/test_release_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,19 @@ def load_module(name: str):


def make_wheel(path: Path) -> None:
dist_info = "nvmolkit-0.6.0.dist-info"
purl = f"pkg:pypi/nvmolkit@0.6.0?file_name={path.name}"
dist_info = "nvmolkit-0.7.0.dist-info"
purl = f"pkg:pypi/nvmolkit@0.7.0?file_name={path.name}"
entries = {
"nvmolkit/__init__.py": b"__version__ = '0.6.0'\n",
f"{dist_info}/METADATA": (b"Metadata-Version: 2.1\nName: nvmolkit\nVersion: 0.6.0\nRequires-Dist: rdkit\n"),
"nvmolkit/__init__.py": b"__version__ = '0.7.0'\n",
f"{dist_info}/METADATA": (b"Metadata-Version: 2.1\nName: nvmolkit\nVersion: 0.7.0\nRequires-Dist: rdkit\n"),
f"{dist_info}/WHEEL": b"Wheel-Version: 1.0\nTag: cp311-cp311-manylinux_2_28_x86_64\n",
f"{dist_info}/sboms/auditwheel.cdx.json": json.dumps(
{
"metadata": {
"component": {
"bom-ref": purl,
"purl": purl,
"version": "0.6.0",
"version": "0.7.0",
}
},
"dependencies": [{"ref": purl}],
Expand Down Expand Up @@ -70,24 +70,24 @@ def assert_valid_record(path: Path) -> None:


def test_pin_and_retag_wheel_refresh_metadata_and_record(tmp_path: Path) -> None:
source = tmp_path / "nvmolkit-0.6.0-cp311-cp311-manylinux_2_28_x86_64.whl"
source = tmp_path / "nvmolkit-0.7.0-cp311-cp311-manylinux_2_28_x86_64.whl"
make_wheel(source)

assert pin_wheel_rdkit.rewrite_wheel(source, "2026.3.5", check=False)
assert not pin_wheel_rdkit.rewrite_wheel(source, "2026.3.5", check=True)
assert_valid_record(source)

output = Path(retag_wheel.retag(str(source), str(tmp_path / "variants"), "rdkit2026.3.5"))
assert output.name.startswith("nvmolkit-0.6.0+rdkit2026.3.5-")
assert output.name.startswith("nvmolkit-0.7.0+rdkit2026.3.5-")
with zipfile.ZipFile(output) as wheel:
metadata_name = next(name for name in wheel.namelist() if name.endswith(".dist-info/METADATA"))
metadata = wheel.read(metadata_name).decode()
assert "Version: 0.6.0+rdkit2026.3.5\n" in metadata
assert "Version: 0.7.0+rdkit2026.3.5\n" in metadata
assert "Requires-Dist: rdkit==2026.3.5\n" in metadata
sbom_name = next(name for name in wheel.namelist() if name.endswith("auditwheel.cdx.json"))
sbom = json.loads(wheel.read(sbom_name))
component = sbom["metadata"]["component"]
assert component["version"] == "0.6.0+rdkit2026.3.5"
assert component["version"] == "0.7.0+rdkit2026.3.5"
assert output.name in component["purl"]
assert sbom["dependencies"][0]["ref"] == component["purl"]
assert_valid_record(output)
Expand All @@ -97,22 +97,22 @@ def test_merge_index_preserves_history_and_replaces_matching_filename(tmp_path:
generated = tmp_path / "generated/index.html"
destination = tmp_path / "destination/index.html"
old_name = "nvmolkit-0.5.1-cp311.whl"
replaced_name = "nvmolkit-0.6.0-cp311.whl"
replaced_name = "nvmolkit-0.7.0-cp311.whl"
merge_simple_index.write_index(
destination,
[
merge_simple_index.Link("https://old.example/0.5.1", old_name),
merge_simple_index.Link("https://old.example/0.6.0", replaced_name),
merge_simple_index.Link("https://old.example/0.7.0", replaced_name),
],
)
merge_simple_index.write_index(
generated,
[merge_simple_index.Link("https://new.example/0.6.0", replaced_name)],
[merge_simple_index.Link("https://new.example/0.7.0", replaced_name)],
)

assert merge_simple_index.merge_index(generated, destination)
assert merge_simple_index.read_links(destination) == [
merge_simple_index.Link("https://old.example/0.5.1", old_name),
merge_simple_index.Link("https://new.example/0.6.0", replaced_name),
merge_simple_index.Link("https://new.example/0.7.0", replaced_name),
]
assert not merge_simple_index.merge_index(generated, destination)
46 changes: 46 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,51 @@
# Changelog

## 0.7.0 - 2026-10-15

### Summary

nvMolKit 0.7.0 adds a single-precision mode for force field minimization (MMFF, UFF, BFGS, FIRE) and ETKDG conformer generation, which is roughly an order of magnitude faster on consumer GPUs. It also adds a fused 3D descriptor API, diversity selection (Leader and MaxMin), DISE clustering, an Atom-Atom Path (AAP) similarity metric, a persistent GPU `SubstructLibrary`, and various performance improvements to existing fingerprinting, substructure search, and 3D minimization code.

### Contributors
- Eva Xue (@evasnow1992)
- Kevin Boyd (@scal444)
- Matthew Neba (@Matthew-Neba)
- Ohad Mosafi (@ohadmo)

### Features
- Single-precision mode (`precision=PrecisionMode.SINGLE`) for MMFF and UFF force fields, BFGS and FIRE minimizers, and ETKDG distance geometry and ETK stages, available through the MMFF, UFF, `BatchedForcefield`, and `EmbedMolecules` APIs. On consumer GPUs, single precision is roughly an order of magnitude faster than double precision. Validation against RDKit is documented in the 3D minimizers guide ([#266](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/266))
- GPU-accelerated 3D descriptors through the new `nvmolkit.descriptors3d.Calc3DProperties`, which computes a user-selected set of properties in one fused pass over a batch of conformers: PMI, NPR, radius of gyration, shape factors, PBF, WHIM, RDF, MORSE, AUTOCORR3D, USR, USRCAT, and GETAWAY, matching RDKit's `rdMolDescriptors` ([#341](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/341))
Comment thread
scal444 marked this conversation as resolved.
- Atom-Atom Path (AAP) molecule similarity via `aap_similarity`, also usable as `metric="aap"` in `fused_leader`, `fused_maxmin`, and `fused_dise` ([#303](https://github.com/NVIDIA-BioNeMo/nvMolKit/pull/303))
- Diversity selection with `leader` and `maxmin` for distance matrices, and `fused_leader` and `fused_maxmin` for packed fingerprints or molecules, following RDKit's `LeaderPicker` and `MaxMinPicker` ([#339](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/339))
- Directed sphere exclusion (DISE) clustering with `dise` and `fused_dise` ([#339](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/339))
- Standardized clustering and diversity selection APIs. Butina, DISE, Leader, and MaxMin each come in a matrix form, which takes a precomputed distance matrix, and a fused form, which computes distances on the fly so memory grows linearly instead of quadratically. Every fused form accepts any similarity metric (`"tanimoto"`, `"cosine"`, or `"aap"`) and either output mode (`OutputMode.DEVICE` or `OutputMode.RDKIT`), so algorithms, forms, metrics, and output modes combine freely, except that `fused_butina` does not yet support AAP ([#339](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/339))
- Persistent GPU `SubstructLibrary` (`nvmolkit.substruct_library.SubstructLibrary`), the GPU counterpart of RDKit's `SubstructLibrary`. Target molecules are packed and uploaded once, then searched by any number of queries, with multi-GPU storage, asynchronous concurrent queries within a GPU-memory budget, and pattern-fingerprint prescreening ([#350](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/350))
- ETKDG now supports eigenvalue-based initial coordinate generation (`useRandomCoords=False`), matching RDKit's initialization and retry behavior ([#288](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/288))
- Device-side conformer pruning for ETKDG, so `pruneRmsThresh` can now be combined with GPU-resident output, by @Matthew-Neba ([#160](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/160))
- Conformer RMSD supports `alignment_mode="pairwise"` (the default, aligning every pair independently) and `alignment_mode="first-conformer"` (matching RDKit's `GetConformerRMSMatrix`) ([#281](https://github.com/NVIDIA-BioNeMo/nvMolKit/pull/281))

### Performance Improvements
- Per-molecule BFGS minimizer is 10-40% faster, applying to ETKDG, MMFF, and UFF on the per-molecule backend ([#342](https://github.com/NVIDIA-BioNeMo/nvMolKit/pull/342), [#355](https://github.com/NVIDIA-BioNeMo/nvMolKit/pull/355))
- Morgan fingerprint generation is up to 2x faster, through reduced CPU preprocessing and Python transfer overhead ([#267](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/267))
- Tanimoto similarity for queries with few molecules is up to 8x faster ([#284](https://github.com/NVIDIA-BioNeMo/nvMolKit/pull/284))
- Substructure search performance has been improved for queries with repeated recursive SMARTS, since each repeated pattern is now solved only once ([#313](https://github.com/NVIDIA-BioNeMo/nvMolKit/pull/313))
- ETKDG conformer generation is up to 2x faster for batches with a few hard-to-embed molecules, because retries now keep GPU batches full. The default retry budget is also now bounded per molecule instead of scaling with the largest molecule in the input ([#353](https://github.com/NVIDIA-BioNeMo/nvMolKit/pull/353))

### Breaking Changes
- `butina` and `fused_butina` now return a `ClusterDeviceResult` (cluster IDs, centroids, and cluster sizes) by default, or RDKit-format cluster tuples with `output=OutputMode.RDKIT`. The `return_centroids` argument is removed ([#269](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/269))

### Bug Fixes
- Fix `countSubstructMatches` ignoring `uniquify=True`, which returned raw embedding counts that disagreed with RDKit for symmetric queries ([#275](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/275))
- Fix SMARTS hydrogen counts excluding explicit hydrogen neighbors, so queries such as `[CH3]` now match deuterated and explicit-H targets as in RDKit ([#276](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/276))
- Fix plain query atoms (for example from `MolFromSmiles`) being packed as wildcards in substructure search, and route targets with isotopes above 255 or unsupported bond types to the RDKit fallback instead of failing ([#349](https://github.com/NVIDIA-BioNeMo/nvMolKit/pull/349))
- Fix ETKDG exiting early on molecules that fail to embed, which previously could skip scheduled retries and fail more often than RDKit ([#300](https://github.com/NVIDIA-BioNeMo/nvMolKit/issues/300))
- Fix float32 division in the Tanimoto similarity kernels using approximate rounding, which could order equal distances differently than RDKit in downstream clustering ([#338](https://github.com/NVIDIA-BioNeMo/nvMolKit/pull/338))

### Miscellaneous
- Improved shared benchmark tooling, including common CLI, timing, result-reporting, and dataset-loading utilities, and RDKit-side deadlines for long-running baselines
- Drastically improved CI coverage, and added back linters and static analysis
- Support for CUDA 13.4 and Rubin GPUs ([#304](https://github.com/NVIDIA-BioNeMo/nvMolKit/pull/304))

## 0.6.0 - 2026-08-13

### Summary
Expand Down
Loading
Loading