Skip to content

Commit e1bc7ef

Browse files
Merge branch 'main' into update_csv_logger
2 parents 4e7cea8 + f10cc89 commit e1bc7ef

23 files changed

+421
-109
lines changed

.github/workflows/CI.yml

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,66 @@ on:
1212

1313
jobs:
1414
test:
15-
runs-on: "ubuntu-latest"
15+
runs-on: ${{ matrix.os }}
16+
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
os: ["ubuntu-latest", "macos-latest", "macos-13", "windows-2022"]
21+
python-version: ["3.10"]
22+
1623
defaults: # Needed for conda
1724
run:
1825
shell: bash -l {0}
1926

2027
steps:
2128
- name: Check out
22-
uses: actions/checkout@v2
29+
uses: actions/checkout@v4
30+
31+
- uses: conda-incubator/setup-miniconda@v3
32+
with:
33+
python-version: ${{ matrix.python-version }}
34+
channels: conda-forge
35+
conda-remove-defaults: "true"
36+
if: matrix.os != 'macos-13'
2337

24-
- name: Create a conda environment
25-
uses: conda-incubator/setup-miniconda@v2
38+
- uses: conda-incubator/setup-miniconda@v3
2639
with:
27-
activate-environment: torchmd-net
28-
environment-file: environment.yml
29-
miniforge-variant: Mambaforge
30-
use-mamba: true
40+
python-version: ${{ matrix.python-version }}
41+
channels: conda-forge
42+
mamba-version: "*"
43+
conda-remove-defaults: "true"
44+
if: matrix.os == 'macos-13'
3145

32-
- name: Install the package
33-
run: pip -vv install .
46+
- name: Install OS-specific conda dependencies
47+
run: |
48+
if [[ "${{ runner.os }}" == "Linux" ]]; then
49+
conda install --file conda_deps_linux.txt --channel conda-forge --override-channels
50+
elif [[ "${{ runner.os }}" == "macos-13" ]]; then
51+
mamba install --file conda_deps_osx.txt --channel conda-forge --override-channels
52+
elif [[ "${{ runner.os }}" == "macOS" ]]; then
53+
conda install --file conda_deps_osx.txt --channel conda-forge --override-channels
54+
elif [[ "${{ runner.os }}" == "Windows" ]]; then
55+
conda install --file conda_deps_win.txt --channel conda-forge --override-channels
56+
fi
57+
58+
- name: Install testing packages
59+
run: conda install -y -c conda-forge flake8 pytest psutil
3460

3561
- name: List the conda environment
3662
run: conda list
3763

64+
- name: Build and install the package
65+
run: |
66+
if [[ "${{ runner.os }}" == "Windows" ]]; then
67+
export LIB="C:/Miniconda/envs/test/Library/lib"
68+
pip -vv install .
69+
else
70+
pip -vv install .
71+
fi
72+
env:
73+
CPU_ONLY: 1
74+
3875
- name: Lint with flake8
3976
run: |
4077
# stop the build if there are Python syntax errors or undefined names
@@ -43,8 +80,13 @@ jobs:
4380
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
4481
4582
- name: Run tests
46-
run: pytest -v -s
83+
run: pytest -v -s --durations=10
84+
env:
85+
CPU_ONLY: 1
86+
SKIP_TORCH_COMPILE: ${{ runner.os == 'Windows' && 'true' || 'false' }}
87+
OMP_PREFIX: ${{ runner.os == 'macOS' && '/Users/runner/miniconda3/envs/test' || '' }}
88+
CPU_TRAIN: ${{ runner.os == 'macOS' && 'true' || 'false' }}
89+
LONG_TRAIN: "true"
4790

4891
- name: Test torchmd-train utility
4992
run: torchmd-train --help
50-

conda_deps_linux.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
h5py
2+
nnpops
3+
pip
4+
libtorch >=2.5.1
5+
pytorch-cpu >=2.5.1
6+
pytorch_geometric
7+
lightning
8+
torchmetrics
9+
tqdm
10+
gxx

conda_deps_osx.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
h5py
2+
nnpops
3+
pip
4+
libtorch>=2.5.1
5+
pytorch-cpu>=2.5.1
6+
pytorch_geometric
7+
lightning
8+
torchmetrics
9+
tqdm
10+
clangxx
11+
llvm-openmp
12+
pybind11

conda_deps_win.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
h5py
2+
pip
3+
libtorch >=2.5.1
4+
pytorch-cpu >=2.5.1
5+
pytorch_geometric
6+
lightning
7+
torchmetrics
8+
tqdm
9+
vc
10+
vc14_runtime
11+
vs2015_runtime
12+
vs2019_win-64
13+
sleef

environment.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,11 @@ channels:
33
- conda-forge
44
dependencies:
55
- h5py
6-
- matplotlib-base
76
- nnpops
87
- pip
98
- pytorch
109
- pytorch_geometric
1110
- lightning
12-
- pydantic
1311
- torchmetrics
1412
- tqdm
1513
# Dev tools

setup.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
import torch
88
from torch.utils.cpp_extension import BuildExtension, CUDAExtension, include_paths, CppExtension
99
import os
10+
import sys
11+
12+
is_windows = sys.platform == 'win32'
1013

1114
try:
1215
version = (
@@ -43,7 +46,7 @@ def set_torch_cuda_arch_list():
4346
ExtensionType = CppExtension if not use_cuda else CUDAExtension
4447
extensions = ExtensionType(
4548
name='torchmdnet.extensions.torchmdnet_extensions',
46-
sources=[os.path.join(extension_root, "extensions.cpp")] + neighbor_sources,
49+
sources=[os.path.join(extension_root, "torchmdnet_extensions.cpp")] + neighbor_sources,
4750
include_dirs=include_paths(),
4851
define_macros=[('WITH_CUDA', 1)] if use_cuda else [],
4952
)
@@ -58,5 +61,5 @@ def set_torch_cuda_arch_list():
5861
'build_ext': BuildExtension.with_options(no_python_abi_suffix=True, use_ninja=False)},
5962
include_package_data=True,
6063
entry_points={"console_scripts": ["torchmd-train = torchmdnet.scripts.train:main"]},
61-
package_data={"torchmdnet": ["extensions/torchmdnet_extensions.so"]},
64+
package_data={"torchmdnet": ["extensions/torchmdnet_extensions.so"] if not is_windows else ["extensions/torchmdnet_extensions.dll"]},
6265
)

tests/test_cfconv.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,32 @@
44

55
import pytest
66
from pytest import mark
7-
import torch as pt
8-
from torchmdnet.models.torchmd_gn import CFConv as RefCFConv
9-
from torchmdnet.models.utils import OptimizedDistance, GaussianSmearing, ShiftedSoftplus
107

11-
from NNPOps.CFConv import CFConv
12-
from NNPOps.CFConvNeighbors import CFConvNeighbors
8+
try:
9+
import NNPOps
1310

11+
nnpops_available = True
12+
except ImportError:
13+
nnpops_available = False
1414

15+
16+
@pytest.mark.skipif(not nnpops_available, reason="NNPOps not available")
1517
@mark.parametrize("device", ["cpu", "cuda"])
1618
@mark.parametrize(
1719
["num_atoms", "num_filters", "num_rbfs"],
1820
[(3, 5, 7), (3, 7, 5), (5, 3, 7), (5, 7, 3), (7, 3, 5), (7, 5, 3)],
1921
)
2022
@mark.parametrize("cutoff_upper", [5.0, 10.0])
2123
def test_cfconv(device, num_atoms, num_filters, num_rbfs, cutoff_upper):
24+
import torch as pt
25+
from torchmdnet.models.torchmd_gn import CFConv as RefCFConv
26+
from torchmdnet.models.utils import (
27+
OptimizedDistance,
28+
GaussianSmearing,
29+
ShiftedSoftplus,
30+
)
31+
from NNPOps.CFConv import CFConv
32+
from NNPOps.CFConvNeighbors import CFConvNeighbors
2233

2334
if not pt.cuda.is_available() and device == "cuda":
2435
pytest.skip("No GPU")

tests/test_datamodule.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ def test_datamodule_create(tmpdir):
2828
dl2 = data._get_dataloader(data.train_dataset, "train", store_dataloader=False)
2929
assert dl1 is not dl2
3030

31+
3132
def test_dataloader_get(tmpdir):
3233
args = load_example_args("graph-network")
3334
args["train_size"] = 800
@@ -50,17 +51,23 @@ def test_dataloader_get(tmpdir):
5051
# Assert that the dataloader is not empty
5152
assert len(data.train_dataloader()) > 0
5253

54+
5355
@mark.parametrize("energy,forces", [(True, True), (True, False), (False, True)])
5456
@mark.parametrize("has_atomref", [True, False])
5557
def test_datamodule_standardize(energy, forces, has_atomref, tmpdir):
5658
args = load_example_args("graph-network")
5759
args["standardize"] = True
58-
args["train_size"] = 800
59-
args["val_size"] = 100
60-
args["test_size"] = 100
60+
args["train_size"] = 80
61+
args["val_size"] = 10
62+
args["test_size"] = 10
6163
args["log_dir"] = tmpdir
64+
args["batch_size"] = 2
65+
args["inference_batch_size"] = 2
66+
args["num_workers"] = 0
6267

63-
dataset = DummyDataset(energy=energy, forces=forces, has_atomref=has_atomref)
68+
dataset = DummyDataset(
69+
num_samples=100, energy=energy, forces=forces, has_atomref=has_atomref
70+
)
6471
data = DataModule(args, dataset=dataset)
6572
data.prepare_data()
6673
data.setup("fit")

tests/test_dataset_comp6.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def test_dataset_s66x8():
3939
),
4040
atol=1e-4,
4141
)
42-
assert pt.allclose(sample.y, pt.tensor([[-5755.7288331]],dtype=pt.float64))
42+
assert pt.allclose(sample.y, pt.tensor([[-5755.7288331]], dtype=pt.float64))
4343
assert pt.allclose(
4444
sample.neg_dy,
4545
-pt.tensor(
@@ -65,3 +65,8 @@ def test_dataset_s66x8():
6565
data_loader = DataLoader(dataset=data_set, batch_size=32, num_workers=2)
6666
for batch in data_loader:
6767
pass
68+
69+
# Extra cleanup for Windows tests which are sensitive to open mmap files
70+
del sample
71+
del data_loader
72+
del data_set

tests/test_module.py

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from torchmdnet.module import LNNP
1313
from torchmdnet.data import DataModule
1414
from torchmdnet import priors
15+
import os
1516

1617
from utils import load_example_args, DummyDataset
1718

@@ -29,7 +30,20 @@ def test_load_model():
2930
@mark.parametrize("model_name", models.__all_models__)
3031
@mark.parametrize("use_atomref", [True, False])
3132
@mark.parametrize("precision", [32, 64])
33+
@mark.skipif(
34+
os.getenv("LONG_TRAIN", "false") == "false", reason="Skipping long train test"
35+
)
3236
def test_train(model_name, use_atomref, precision, tmpdir):
37+
import torch
38+
39+
torch.set_num_threads(1)
40+
41+
accelerator = "auto"
42+
if os.getenv("CPU_TRAIN", "false") == "true":
43+
# OSX MPS backend runs out of memory on Github Actions
44+
torch.set_default_device("cpu")
45+
accelerator = "cpu"
46+
3347
args = load_example_args(
3448
model_name,
3549
remove_prior=not use_atomref,
@@ -43,6 +57,64 @@ def test_train(model_name, use_atomref, precision, tmpdir):
4357
num_rbf=16,
4458
batch_size=8,
4559
precision=precision,
60+
num_workers=0,
61+
)
62+
datamodule = DataModule(args, DummyDataset(has_atomref=use_atomref))
63+
64+
prior = None
65+
if use_atomref:
66+
prior = getattr(priors, args["prior_model"])(dataset=datamodule.dataset)
67+
args["prior_args"] = prior.get_init_args()
68+
69+
module = LNNP(args, prior_model=prior)
70+
71+
trainer = pl.Trainer(
72+
max_steps=10,
73+
default_root_dir=tmpdir,
74+
precision=args["precision"],
75+
inference_mode=False,
76+
accelerator=accelerator,
77+
num_nodes=1,
78+
devices=1,
79+
use_distributed_sampler=False,
80+
)
81+
trainer.fit(module, datamodule)
82+
trainer.test(module, datamodule)
83+
84+
85+
@mark.parametrize("model_name", models.__all_models__)
86+
@mark.parametrize("use_atomref", [True, False])
87+
@mark.parametrize("precision", [32, 64])
88+
def test_dummy_train(model_name, use_atomref, precision, tmpdir):
89+
import torch
90+
91+
torch.set_num_threads(1)
92+
93+
accelerator = "auto"
94+
if os.getenv("CPU_TRAIN", "false") == "true":
95+
# OSX MPS backend runs out of memory on Github Actions
96+
torch.set_default_device("cpu")
97+
accelerator = "cpu"
98+
99+
extra_args = {}
100+
if model_name != "tensornet":
101+
extra_args["num_heads"] = 2
102+
103+
args = load_example_args(
104+
model_name,
105+
remove_prior=not use_atomref,
106+
train_size=0.05,
107+
val_size=0.01,
108+
test_size=0.01,
109+
log_dir=tmpdir,
110+
derivative=True,
111+
embedding_dimension=2,
112+
num_layers=1,
113+
num_rbf=4,
114+
batch_size=2,
115+
precision=precision,
116+
num_workers=0,
117+
**extra_args,
46118
)
47119
datamodule = DataModule(args, DummyDataset(has_atomref=use_atomref))
48120

@@ -53,6 +125,15 @@ def test_train(model_name, use_atomref, precision, tmpdir):
53125

54126
module = LNNP(args, prior_model=prior)
55127

56-
trainer = pl.Trainer(max_steps=10, default_root_dir=tmpdir, precision=args["precision"],inference_mode=False)
128+
trainer = pl.Trainer(
129+
max_steps=10,
130+
default_root_dir=tmpdir,
131+
precision=args["precision"],
132+
inference_mode=False,
133+
accelerator=accelerator,
134+
num_nodes=1,
135+
devices=1,
136+
use_distributed_sampler=False,
137+
)
57138
trainer.fit(module, datamodule)
58139
trainer.test(module, datamodule)

0 commit comments

Comments
 (0)