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
10 changes: 7 additions & 3 deletions benchmarks/butina_clustering_bench.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ def save_results():
if "fused" in runs:
print(f"Running fused_butina size {size} cutoff {cutoff}")
fused_result = time_it(
lambda: fused_butina(
lambda fps_mat=fps_mat, cutoff=cutoff: fused_butina(
fps_mat,
cutoff=cutoff,
metric="tanimoto",
Expand All @@ -297,7 +297,9 @@ def save_results():
f"reordering {nvmol_reordering}"
)
nvmolkit_cluster_only_result = time_it(
lambda: bench_nvmol_inner(dist_mat, cutoff, max_nl, nvmol_reordering),
lambda dist_mat=dist_mat, cutoff=cutoff, max_nl=max_nl, nvmol_reordering=nvmol_reordering: (
bench_nvmol_inner(dist_mat, cutoff, max_nl, nvmol_reordering)
),
gpu_sync=True,
runs=n_runs,
)
Expand All @@ -308,7 +310,9 @@ def save_results():
if nvmol_reordering:
print(f"Running nvmolkit_with_tanimoto size {size} cutoff {cutoff} max_nl {max_nl}")
nvmolkit_with_tanimoto_result = time_it(
lambda: bench_nvmol_with_tanimoto(fps_mat, cutoff, max_nl),
lambda fps_mat=fps_mat, cutoff=cutoff, max_nl=max_nl: bench_nvmol_with_tanimoto(
fps_mat, cutoff, max_nl
),
gpu_sync=True,
runs=n_runs,
)
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/conformer_rmsd_bench.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ def run(
gpu_pairs_per_s: float | None = None
if not no_nvmolkit:
print(" nvMolKit GPU (batched):")
result = time_it(lambda: bench_gpu_batch(mols), runs=5, warmups=2, gpu_sync=True)
result = time_it(lambda mols=mols: bench_gpu_batch(mols), runs=5, warmups=2, gpu_sync=True)
gpu_time_s = result.median_s
gpu_std_s = result.std_ms / 1000.0
gpu_pairs_per_s = total_pairs / gpu_time_s
Expand Down
4 changes: 2 additions & 2 deletions benchmarks/etkdg_bench.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def _mmff_energies(mol: Chem.Mol) -> list[float | None]:
try:
ff = AllChem.MMFFGetMoleculeForceField(mol, props, confId=conf.GetId())
energies.append(float(ff.CalcEnergy()) if ff is not None else None)
except Exception:
except Exception: # noqa: PERF203 - isolate failures to the individual conformer
energies.append(None)
return energies

Expand Down Expand Up @@ -546,7 +546,7 @@ def main() -> None:
rdkit_throughput_per_s = throughput_per_s(
rdkit_processed_count * args.confs_per_mol, results["rdkit"][0].mean_ms
)
for name, (timing, run_mols) in results.items():
for name, (timing, _run_mols) in results.items():
speedup = ""
if rdkit_throughput_per_s is not None and name != "rdkit" and timing.mean_ms > 0:
method_throughput = throughput_per_s(len(mols) * args.confs_per_mol, timing.mean_ms)
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/ff_optimize_bench.py
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ def main() -> None:
applied_num_gpus = args.num_gpus

csv_rows: list[dict[str, object]] = []
for name, (avg_ms, std_ms, energies) in results.items():
for name, (avg_ms, std_ms, _energies) in results.items():
is_nv = name == "nvmolkit"
is_rdkit = name == "rdkit"
batch_size = applied_batch_size if is_nv else "N/A"
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/mcs_bench.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ def _rdkit_params(config_row: dict) -> rdFMCS.MCSParameters:


def _rdkit_worker_init(mol_binaries: list[bytes], params: rdFMCS.MCSParameters) -> None:
global _worker_mols, _worker_params
global _worker_mols, _worker_params # noqa: PLW0603 - process-local worker cache
_worker_mols = [Chem.Mol(binary) for binary in mol_binaries]
_worker_params = params

Expand Down
2 changes: 1 addition & 1 deletion benchmarks/substruct_bench.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def time_it(func: Callable, runs: int = 1, gpu_sync: bool = False) -> tuple[floa

def _rdkit_worker_init(query_binaries: list[bytes], max_matches: int):
"""Initialize worker process with shared query data."""
global _worker_queries, _worker_params
global _worker_queries, _worker_params # noqa: PLW0603 - process-local worker cache
_worker_queries = [Chem.Mol(qb) for qb in query_binaries]
_worker_params = Chem.SubstructMatchParameters()
_worker_params.uniquify = False
Expand Down
12 changes: 4 additions & 8 deletions benchmarks/tfd_bench.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,7 @@ def verify_correctness(mol: Chem.Mol, tolerance: float = 0.01) -> bool:
if len(rdkit_result) != len(nvmol_result):
return False

for rd, nv in zip(rdkit_result, nvmol_result):
if abs(rd - nv) > tolerance:
return False

return True
return all(abs(rd - nv) <= tolerance for rd, nv in zip(rdkit_result, nvmol_result, strict=True))


def load_pkl_files(pkl_paths: List[str]) -> List[Chem.Mol]:
Expand Down Expand Up @@ -334,19 +330,19 @@ def run_benchmarks(
result["rdkit_molecules_processed"] = None

if not skip_nvmolkit:
timing = time_it(lambda: bench_nvmol_gpu_list(mols), runs=runs, warmups=warmups)
timing = time_it(lambda mols=mols: bench_nvmol_gpu_list(mols), runs=runs, warmups=warmups)
t, s = timing.mean_ms, timing.std_ms
result["nvmol_gpu_list_time_ms"] = t
result["nvmol_gpu_list_std_ms"] = s
print(f" nvMolKit (GPU list): {t:8.2f} ms (+/- {s:.2f})")

timing = time_it(lambda: bench_nvmol_gpu_numpy(mols), runs=runs, warmups=warmups)
timing = time_it(lambda mols=mols: bench_nvmol_gpu_numpy(mols), runs=runs, warmups=warmups)
t, s = timing.mean_ms, timing.std_ms
result["nvmol_gpu_numpy_time_ms"] = t
result["nvmol_gpu_numpy_std_ms"] = s
print(f" nvMolKit (GPU numpy): {t:8.2f} ms (+/- {s:.2f})")

timing = time_it(lambda: bench_nvmol_gpu_tensor(mols), runs=runs, warmups=warmups)
timing = time_it(lambda mols=mols: bench_nvmol_gpu_tensor(mols), runs=runs, warmups=warmups)
t, s = timing.mean_ms, timing.std_ms
result["nvmol_gpu_tensor_time_ms"] = t
result["nvmol_gpu_tensor_std_ms"] = s
Expand Down
7 changes: 2 additions & 5 deletions nvmolkit/mcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,13 +214,10 @@ def _all_pairs(num_mols: int, upper_triangle: bool, include_diagonal: bool) -> t
if upper_triangle:
for i in range(num_mols):
begin = i if include_diagonal else i + 1
for j in range(begin, num_mols):
pairs.append((i, j))
pairs.extend((i, j) for j in range(begin, num_mols))
else:
for i in range(num_mols):
for j in range(num_mols):
if include_diagonal or i != j:
pairs.append((i, j))
pairs.extend((i, j) for j in range(num_mols) if include_diagonal or i != j)
return tuple(pairs)


Expand Down
12 changes: 6 additions & 6 deletions nvmolkit/tests/test_batched_forcefield.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,8 +411,8 @@ def test_mmff_batched_forcefield_multi_conformer_matches_rdkit():
@pytest.mark.parametrize(
"ff_factory",
[
pytest.param(lambda mols: MMFFBatchedForcefield(mols), id="mmff"),
pytest.param(lambda mols: UFFBatchedForcefield(mols), id="uff"),
pytest.param(MMFFBatchedForcefield, id="mmff"),
pytest.param(UFFBatchedForcefield, id="uff"),
],
)
def test_batched_forcefield_metadata_and_element_view(ff_factory):
Expand All @@ -429,8 +429,8 @@ def test_batched_forcefield_metadata_and_element_view(ff_factory):
@pytest.mark.parametrize(
"ff_factory",
[
pytest.param(lambda mols: MMFFBatchedForcefield(mols), id="mmff"),
pytest.param(lambda mols: UFFBatchedForcefield(mols), id="uff"),
pytest.param(MMFFBatchedForcefield, id="mmff"),
pytest.param(UFFBatchedForcefield, id="uff"),
],
)
def test_batched_forcefield_lazy_build_and_rebuild(ff_factory):
Expand Down Expand Up @@ -459,8 +459,8 @@ def test_batched_forcefield_lazy_build_and_rebuild(ff_factory):
@pytest.mark.parametrize(
"ff_factory",
[
pytest.param(lambda mols: MMFFBatchedForcefield(mols), id="mmff"),
pytest.param(lambda mols: UFFBatchedForcefield(mols), id="uff"),
pytest.param(MMFFBatchedForcefield, id="mmff"),
pytest.param(UFFBatchedForcefield, id="uff"),
],
)
@pytest.mark.parametrize(
Expand Down
8 changes: 4 additions & 4 deletions nvmolkit/tests/test_mcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,13 @@ def _assert_result_storage(result, mol_table):
assert item.bond_mapping.shape == (item.num_bonds, 2)

if item.num_atoms:
assert np.all((0 <= item.atom_mapping[:, 0]) & (item.atom_mapping[:, 0] < mol_table[idx_a].GetNumAtoms()))
assert np.all((0 <= item.atom_mapping[:, 1]) & (item.atom_mapping[:, 1] < mol_table[idx_b].GetNumAtoms()))
assert np.all((item.atom_mapping[:, 0] >= 0) & (item.atom_mapping[:, 0] < mol_table[idx_a].GetNumAtoms()))
assert np.all((item.atom_mapping[:, 1] >= 0) & (item.atom_mapping[:, 1] < mol_table[idx_b].GetNumAtoms()))
assert len(np.unique(item.atom_mapping[:, 0])) == item.num_atoms
assert len(np.unique(item.atom_mapping[:, 1])) == item.num_atoms
if item.num_bonds:
assert np.all((0 <= item.bond_mapping[:, 0]) & (item.bond_mapping[:, 0] < mol_table[idx_a].GetNumBonds()))
assert np.all((0 <= item.bond_mapping[:, 1]) & (item.bond_mapping[:, 1] < mol_table[idx_b].GetNumBonds()))
assert np.all((item.bond_mapping[:, 0] >= 0) & (item.bond_mapping[:, 0] < mol_table[idx_a].GetNumBonds()))
assert np.all((item.bond_mapping[:, 1] >= 0) & (item.bond_mapping[:, 1] < mol_table[idx_b].GetNumBonds()))
assert len(np.unique(item.bond_mapping[:, 0])) == item.num_bonds
assert len(np.unique(item.bond_mapping[:, 1])) == item.num_bonds

Expand Down
1 change: 1 addition & 0 deletions nvmolkit/tests/test_skill.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ def test_skill_snippet_runs(snippet_idx: int, snippet: str, tmp_path: Path) -> N
capture_output=True,
text=True,
timeout=300,
check=False,
)
assert result.returncode == 0, (
f"Skill snippet {snippet_idx} failed:\n"
Expand Down
14 changes: 7 additions & 7 deletions nvmolkit/tests/test_substructure.py
Original file line number Diff line number Diff line change
Expand Up @@ -995,10 +995,10 @@ def load_smiles_file(filepath: Path, max_count: int = NUM_SMILES, max_atoms: int
mols = []
with open(filepath) as f:
for line in f:
line = line.strip()
if not line or line.startswith("#"):
stripped_line = line.strip()
if not stripped_line or stripped_line.startswith("#"):
continue
smiles = line.split()[0] if " " in line or "\t" in line else line
smiles = stripped_line.split()[0] if " " in stripped_line or "\t" in stripped_line else stripped_line
mol = Chem.MolFromSmiles(smiles)
if mol is not None and mol.GetNumAtoms() <= max_atoms:
mols.append(mol)
Expand All @@ -1017,13 +1017,13 @@ def load_smarts_file(filepath: Path) -> tuple[list[Chem.Mol], list[str]]:
smarts_strings = []
with open(filepath) as f:
for line in f:
line = line.strip()
if not line or line.startswith("#"):
stripped_line = line.strip()
if not stripped_line or stripped_line.startswith("#"):
continue
mol = Chem.MolFromSmarts(line)
mol = Chem.MolFromSmarts(stripped_line)
if mol is not None:
queries.append(mol)
smarts_strings.append(line)
smarts_strings.append(stripped_line)
return queries, smarts_strings


Expand Down
6 changes: 3 additions & 3 deletions nvmolkit/tests/test_tfd.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def test_invalid_maxdev_raises(self, simple_mol_with_conformers):
"""Test that invalid maxDev raises error."""
mol = simple_mol_with_conformers

with pytest.raises(Exception):
with pytest.raises(ValueError):
tfd.GetTFDMatrix(mol, maxDev="invalid")


Expand Down Expand Up @@ -449,12 +449,12 @@ def test_large_molecule(self):

def test_invalid_molecule_raises(self):
"""Test that None molecule raises error."""
with pytest.raises(Exception):
with pytest.raises(ValueError):
tfd.GetTFDMatrix(None)

def test_invalid_molecule_in_batch_raises(self, simple_mol_with_conformers):
"""Test that None in batch raises error."""
mols = [simple_mol_with_conformers, None]

with pytest.raises(Exception):
with pytest.raises(ValueError):
tfd.GetTFDMatrices(mols)
34 changes: 21 additions & 13 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -145,31 +145,39 @@ include = ["nvmolkit/**/*.py", "benchmarks/**/*.py", "setup.py"]

[tool.ruff.lint]
ignore = [
"B905", # Allow zip() to truncate to the shortest input.
"C901", # Checks for functions with a high McCabe complexity.
"D100", # Checks for undocumented public module definitions.
"E501", # Checks for lines that exceed the specified maximum character length.
"E741", # Checks for the use of the characters 'l', 'O', or 'I' as variable names.
"RET505", # Preserve elif chains after terminating branches.
"RUF005", # Checks for uses of the + operator to concatenate collections.
]
select = [
"A", # Builtin shadowing
"C", # C-prefixed convention checks
"D", # Documentation formatting
"DTZ", # Datetime timezone checks
"E", # Style and whitespace checks
"F", # Pyflakes correctness checks
"I", # Import sorting
"LOG", # Logging checks
"PIE", # Miscellaneous correctness checks
"PLE", # Pylint errors
"RUF", # Ruff-specific checks
"W", # Pycodestyle warnings
"A", # Builtin shadowing
"ASYNC", # Async correctness checks
"B", # Bugbear correctness checks
"C", # C-prefixed convention checks
"D", # Documentation formatting
"DTZ", # Datetime timezone checks
"E", # Style and whitespace checks
"F", # Pyflakes correctness checks
"I", # Import sorting
"LOG", # Logging checks
"PERF", # Performance anti-pattern checks
"PIE", # Miscellaneous correctness checks
"PLE", # Pylint errors
"PLW", # Pylint warnings
"RET", # Return statement checks
"RUF", # Ruff-specific checks
"SIM", # Simplification checks
"W", # Pycodestyle warnings
]

[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["D104"]
"nvmolkit/tests/test_*.py" = ["D103"]
"benchmarks/*.py" = ["F841", "D103"]
"nvmolkit/tests/test_*.py" = ["D103"]

[tool.ruff.lint.pydocstyle]
convention = "google"
Loading