From 530dc4f58e5aa936f25fb3788caa8cd6e5f252ed Mon Sep 17 00:00:00 2001 From: Artur Toshev Date: Sat, 30 Dec 2023 01:17:24 +0000 Subject: [PATCH 01/16] vmap evaluation loop --- README.md | 10 +- configs/defaults.yaml | 2 + experiments/run.py | 2 + lagrangebench/data/data.py | 2 +- lagrangebench/defaults.py | 1 + lagrangebench/evaluate/metrics.py | 2 +- lagrangebench/evaluate/rollout.py | 246 +++++++++++++++++++----------- lagrangebench/train/trainer.py | 6 +- requirements_cuda.txt | 2 +- 9 files changed, 179 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index a2d54ba..7663290 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ pip install --upgrade jax[cuda12_pip]==0.4.20 -f https://storage.googleapis.com/ ``` ### MacOS -Currently, only the CPU installation works. You will need to change a few small things to get it going: +Currently, only the CPU installation works. You will need to change a few small things to get it going: - Clone installation: in `pyproject.toml` change the torch version from `2.1.0+cpu` to `2.1.0`. Then, remove the `poetry.lock` file and run `poetry install --only main`. - Configs: You will need to set `f64: False` and `num_workers: 0` in the `configs/` files. @@ -47,10 +47,10 @@ Although the current [`jax-metal==0.0.5` library](https://pypi.org/project/jax-m ## Usage ### Standalone benchmark library -A general tutorial is provided in the example notebook "Training GNS on the 2D Taylor Green Vortex" under `./notebooks/tutorial.ipynb` on the [LagrangeBench repository](https://github.com/tumaer/lagrangebench). The notebook covers the basics of LagrangeBench, such as loading a dataset, setting up a case, training a model from scratch and evaluating it's performance. +A general tutorial is provided in the example notebook "Training GNS on the 2D Taylor Green Vortex" under `./notebooks/tutorial.ipynb` on the [LagrangeBench repository](https://github.com/tumaer/lagrangebench). The notebook covers the basics of LagrangeBench, such as loading a dataset, setting up a case, training a model from scratch and evaluating its performance. ### Running in a local clone (`main.py`) -Alternatively, experiments can also be set up with `main.py`, based around extensive YAML config files and cli arguments (check [`configs/`](configs/)). By default, the arguments have priority as: 1) passed cli arguments, 2) YAML config and 3) [`defaults.py`](lagrangebench/defaults.py) (`lagrangebench` defaults). +Alternatively, experiments can also be set up with `main.py`, based on extensive YAML config files and cli arguments (check [`configs/`](configs/)). By default, the arguments have priority as: 1) passed cli arguments, 2) YAML config and 3) [`defaults.py`](lagrangebench/defaults.py) (`lagrangebench` defaults). When loading a saved model with `--model_dir` the config from the checkpoint is automatically loaded and training is restarted. For more details check the [`experiments/`](experiments/) directory and the [`run.py`](experiments/run.py) file. @@ -94,8 +94,8 @@ The datasets are hosted on Zenodo under the DOI: [10.5281/zenodo.10021925](https ### Notebooks -Whe provide three notebooks that show LagrangeBench functionalities, namely: -- [`tutorial.ipynb`](notebooks/tutorial.ipynb) with a general overview of LagrangeBench library, with trainin and evaluation of a simple GNS model, +We provide three notebooks that show LagrangeBench functionalities, namely: +- [`tutorial.ipynb`](notebooks/tutorial.ipynb) with a general overview of LagrangeBench library, with training and evaluation of a simple GNS model, - [`datasets.ipynb`](notebooks/datasets.ipynb) with more details and visualizations on the datasets, and - [`gns_data.ipynb`](notebooks/gns_data.ipynb) showing how to train models within LagrangeBench on the datasets from the paper [Learning to Simulate Complex Physics with Graph Networks](https://arxiv.org/abs/2002.09405). diff --git a/configs/defaults.yaml b/configs/defaults.yaml index 220f977..0771f6a 100644 --- a/configs/defaults.yaml +++ b/configs/defaults.yaml @@ -114,3 +114,5 @@ metrics_infer: metrics_stride_infer: 1 out_type_infer: pkl eval_n_trajs_infer: -1 +# batch size for validation/testing +batch_size_infer: 2 diff --git a/experiments/run.py b/experiments/run.py index a2b2eaa..fbd8507 100644 --- a/experiments/run.py +++ b/experiments/run.py @@ -123,6 +123,7 @@ def train_or_infer(args: Namespace): eval_steps=args.config.eval_steps, metrics_stride=args.config.metrics_stride, num_workers=args.config.num_workers, + batch_size_infer=args.config.batch_size_infer, ) _, _, _ = trainer( step_max=args.config.step_max, @@ -160,6 +161,7 @@ def train_or_infer(args: Namespace): n_extrap_steps=args.config.n_extrap_steps, seed=args.config.seed, metrics_stride=args.config.metrics_stride_infer, + batch_size=args.config.batch_size_infer, ) split = "test" if args.config.test else "valid" diff --git a/lagrangebench/data/data.py b/lagrangebench/data/data.py index 9cefd2d..1c976bd 100644 --- a/lagrangebench/data/data.py +++ b/lagrangebench/data/data.py @@ -242,7 +242,7 @@ def get_window(self, idx: int): def __getitem__(self, idx: int): """ Get a sequence of positions (of size windows) from the dataset at index idx. - + Returns: Array of shape (num_particles_max, input_seq_length + 1, dim). Along axis=1 the position sequence (length input_seq_length) and the last position to diff --git a/lagrangebench/defaults.py b/lagrangebench/defaults.py index 9e98b99..9cb3c22 100644 --- a/lagrangebench/defaults.py +++ b/lagrangebench/defaults.py @@ -59,6 +59,7 @@ class defaults: out_type: str = "none" # type of output. None means no rollout is stored n_extrap_steps: int = 0 # number of extrapolation steps metrics_stride: int = 10 # stride for e_kin and sinkhorn + batch_size_infer: int = 2 # batch size for validation/testing # logging log_steps: int = 1000 # number of steps between logs diff --git a/lagrangebench/evaluate/metrics.py b/lagrangebench/evaluate/metrics.py index bbda2c4..6d977b0 100644 --- a/lagrangebench/evaluate/metrics.py +++ b/lagrangebench/evaluate/metrics.py @@ -45,7 +45,7 @@ def __init__( metadata: Metadata of the dataset. loss_ranges: List of horizon lengths to compute the loss for. input_seq_length: Length of the input sequence. - stride: Rollout subsample frequency for Sinkhorn. + stride: Rollout subsample frequency for e_kin and sinkhorn. ot_backend: Backend for sinkhorn computation. "ott" or "pot". """ if active_metrics is None: diff --git a/lagrangebench/evaluate/rollout.py b/lagrangebench/evaluate/rollout.py index e643491..4742d70 100644 --- a/lagrangebench/evaluate/rollout.py +++ b/lagrangebench/evaluate/rollout.py @@ -3,13 +3,14 @@ import os import pickle import time -import warnings +from functools import partial from typing import Callable, Iterable, List, Optional, Tuple import haiku as hk import jax import jax.numpy as jnp import jax_md.partition as partition +from jax import jit, vmap from torch.utils.data import DataLoader from lagrangebench.data import H5Dataset @@ -18,6 +19,7 @@ from lagrangebench.evaluate.metrics import MetricsComputer, MetricsDict from lagrangebench.utils import ( broadcast_from_batch, + broadcast_to_batch, get_kinematic_mask, load_haiku, set_seed, @@ -25,12 +27,58 @@ ) -def eval_single_rollout( +@partial(jit, static_argnames=["model_apply", "case_integrate"]) +def _forward_eval( + params: hk.Params, + state: hk.State, + sample: Tuple[jnp.ndarray, jnp.ndarray], + current_positions: jnp.ndarray, + target_positions: jnp.ndarray, + model_apply: Callable, + case_integrate: Callable, +) -> jnp.ndarray: + """Run one update of the 'current_state' using the trained model + + Args: + params: Haiku model parameters + state: Haiku model state + current_positions: Set of historic positions of shape (n_nodel, t_window, dim) + target_positions: used to get the next state of kinematic particles, i.e. those + who are not update using the ML model, e.g. boundary particles + model_apply: model function + case_integrate: integration function from case.integrate + + Return: + current_positions: after shifting the historic position sequence by one, i.e. by + the newly computed most recent position + """ + _, particle_type = sample + + # predict acceleration and integrate + pred, _ = model_apply(params, state, sample) + next_position = case_integrate(pred, current_positions) + + # update only the positions of non-boundary particles + kinematic_mask = get_kinematic_mask(particle_type) + next_position = jnp.where( + kinematic_mask[:, None], + target_positions, + next_position, + ) + + current_positions = jnp.concatenate( + [current_positions[:, 1:], next_position[:, None, :]], axis=1 + ) # as next model input + + return current_positions + + +def eval_batched_rollout( model_apply: Callable, case, params: hk.Params, state: hk.State, - traj_i: Tuple[jnp.ndarray, jnp.ndarray], + traj_batch_i: Tuple[jnp.ndarray, jnp.ndarray], neighbors: partition.NeighborList, metrics_computer: MetricsComputer, n_rollout_steps: int, @@ -44,7 +92,7 @@ def eval_single_rollout( case: CaseSetupFn class. params: Haiku params. state: Haiku state. - traj_i: Trajectory to evaluate. + traj_batch_i: Trajectory to evaluate. neighbors: Neighbor list. metrics_computer: MetricsComputer with the desired metrics. n_rollout_steps: Number of rollout steps. @@ -54,64 +102,80 @@ def eval_single_rollout( Returns: A tuple with (predicted rollout, metrics, neighbor list). """ - pos_input, particle_type = traj_i + # particle type is treated as a static property defined by state at t=0 + pos_input_batch, particle_type_batch = traj_batch_i + batch_size, n_nodes_max, _, dim = pos_input_batch.shape + # if n_rollout_steps set to -1, use the whole trajectory - if n_rollout_steps < 0: - n_rollout_steps = pos_input.shape[1] - t_window + if n_rollout_steps == -1: + n_rollout_steps = pos_input_batch.shape[2] - t_window - initial_positions = pos_input[:, 0:t_window] # (n_nodes, t_window, dim) - traj_len = n_rollout_steps + n_extrap_steps # (n_nodes, traj_len - t_window, dim) - ground_truth_positions = pos_input[:, t_window : t_window + traj_len] - current_positions = initial_positions # (n_nodes, t_window, dim) - n_nodes, _, dim = ground_truth_positions.shape + current_positions_batch = pos_input_batch[:, :, 0:t_window] + # (batch, n_nodes, t_window, dim) + traj_len = n_rollout_steps + n_extrap_steps + target_positions_batch = pos_input_batch[:, :, t_window : t_window + traj_len] - predictions = jnp.zeros((traj_len, n_nodes, dim)) + predictions_batch = jnp.zeros((batch_size, traj_len, n_nodes_max, dim)) + neighbors_batch = broadcast_to_batch(neighbors, batch_size) + preprocess_eval_vmap = vmap(case.preprocess_eval, in_axes=(0, 0)) + + forward_eval = partial( + _forward_eval, + model_apply=model_apply, + case_integrate=case.integrate, + ) + forward_eval_vmap = vmap(forward_eval, in_axes=(None, None, 0, 0, 0)) step = 0 while step < n_rollout_steps + n_extrap_steps: - sample = (current_positions, particle_type) - features, neighbors = case.preprocess_eval(sample, neighbors) + sample_batch = (current_positions_batch, particle_type_batch) - if neighbors.did_buffer_overflow is True: - edges_ = neighbors.idx.shape - print(f"(eval) Reallocate neighbors list {edges_} at step {step}") - _, neighbors = case.allocate_eval(sample) - print(f"(eval) To list {neighbors.idx.shape}") + # 1. preprocess features + features_batch, neighbors_batch = preprocess_eval_vmap( + sample_batch, neighbors_batch + ) - continue + # 2. check whether list overflowed and fix it if so + if neighbors_batch.did_buffer_overflow.sum() > 0: + # check if the neighbor list is too small for any of the samples + # if so, reallocate the neighbor list - # predict - pred, _ = model_apply(params, state, (features, particle_type)) + print(f"(eval) Reallocate neighbors list at step {step}") + ind = jnp.argmax(neighbors_batch.did_buffer_overflow) + sample = broadcast_from_batch(sample_batch, index=ind) - next_position = case.integrate(pred, current_positions) + _, nbrs_temp = case.allocate_eval(sample) + print( + f"(eval) From {neighbors_batch.idx[ind].shape} to {nbrs_temp.idx.shape}" + ) + neighbors_batch = broadcast_to_batch(nbrs_temp, batch_size) - if n_extrap_steps == 0: - kinematic_mask = get_kinematic_mask(particle_type) - next_position_ground_truth = ground_truth_positions[:, step] + # To run the loop N times even if sometimes + # did_buffer_overflow > 0 we directly return to the beginning - next_position = jnp.where( - kinematic_mask[:, None], - next_position_ground_truth, - next_position, - ) - else: - warnings.warn("kinematic mask not applied in extrapolation mode.") + continue - predictions = predictions.at[step].set(next_position) - current_positions = jnp.concatenate( - [current_positions[:, 1:], next_position[:, None, :]], axis=1 + # 3. run forward model + current_positions_batch = forward_eval_vmap( + params, + state, + (features_batch, particle_type_batch), + current_positions_batch, + target_positions_batch[:, :, step], + ) + + # 4. write predicted next position to output array + predictions_batch = predictions_batch.at[:, step].set( + current_positions_batch[:, :, -1] # most recently predicted positions ) step += 1 - # (n_nodes, traj_len - t_window, dim) -> (traj_len - t_window, n_nodes, dim) - ground_truth_positions = ground_truth_positions.transpose(1, 0, 2) + # (batch, n_nodes, time, dim) -> (batch, time, n_nodes, dim) + target_positions_batch = target_positions_batch.transpose(0, 2, 1, 3) + metrics_batch = vmap(metrics_computer)(predictions_batch, target_positions_batch) - return ( - predictions, - metrics_computer(predictions, ground_truth_positions), - neighbors, - ) + return (predictions_batch, metrics_batch, broadcast_from_batch(neighbors_batch, 0)) def eval_rollout( @@ -147,23 +211,25 @@ def eval_rollout( Returns: Metrics per trajectory. """ + batch_size = loader_eval.batch_size t_window = loader_eval.dataset.input_seq_length eval_metrics = {} if rollout_dir is not None: os.makedirs(rollout_dir, exist_ok=True) - for i, traj_i in enumerate(loader_eval): - # remove batch dimension - assert traj_i[0].shape[0] == 1, "Batch dimension should be 1" - traj_i = broadcast_from_batch(traj_i, index=0) # (nodes, t, dim) + for i, traj_batch_i in enumerate(loader_eval): + # numpy to jax + traj_batch_i = jax.tree_map(lambda x: jnp.array(x), traj_batch_i) + # (pos_input_batch, particle_type_batch) = traj_batch_i + # pos_input_batch.shape = (batch, num_particles, seq_length, dim) - example_rollout, metrics, neighbors = eval_single_rollout( + example_rollout_batch, metrics_batch, neighbors = eval_batched_rollout( model_apply=model_apply, case=case, params=params, state=state, - traj_i=traj_i, + traj_batch_i=traj_batch_i, # (batch, nodes, t, dim) neighbors=neighbors, metrics_computer=metrics_computer, n_rollout_steps=n_rollout_steps, @@ -171,41 +237,48 @@ def eval_rollout( n_extrap_steps=n_extrap_steps, ) - eval_metrics[f"rollout_{i}"] = metrics + for j in range(batch_size): + # write metrics to output dictionary + ind = i * batch_size + j + eval_metrics[f"rollout_{ind}"] = broadcast_from_batch(metrics_batch, j) if rollout_dir is not None: - pos_input = traj_i[0].transpose(1, 0, 2) # (t, nodes, dim) - initial_positions = pos_input[:t_window] - example_full = jnp.concatenate([initial_positions, example_rollout], axis=0) - example_rollout = { - "predicted_rollout": example_full, # (t, nodes, dim) - "ground_truth_rollout": pos_input, # (t, nodes, dim) - } - - file_prefix = f"{rollout_dir}/rollout_{i}" - if out_type == "vtk": - for j in range(pos_input.shape[0]): - filename_vtk = file_prefix + f"_{j}.vtk" - state_vtk = { - "r": example_rollout["predicted_rollout"][j], - "tag": traj_i[1], - } - write_vtk(state_vtk, filename_vtk) - - for j in range(pos_input.shape[0]): - filename_vtk = file_prefix + f"_ref_{j}.vtk" - state_vtk = { - "r": example_rollout["ground_truth_rollout"][j], - "tag": traj_i[1], - } - write_vtk(state_vtk, filename_vtk) - if out_type == "pkl": - filename = f"{file_prefix}.pkl" - - with open(filename, "wb") as f: - pickle.dump(example_rollout, f) - - if (i + 1) == n_trajs: + # (batch, nodes, t, dim) -> (batch, t, nodes, dim) + pos_input_batch = traj_batch_i[0].transpose(0, 2, 1, 3) + + for j in range(batch_size): # write every trajectory to file + pos_input = pos_input_batch[j] + example_rollout = example_rollout_batch[j] + + initial_positions = pos_input[:t_window] + example_full = jnp.concatenate([initial_positions, example_rollout]) + example_rollout = { + "predicted_rollout": example_full, # (t, nodes, dim) + "ground_truth_rollout": pos_input, # (t, nodes, dim) + } + + file_prefix = f"{rollout_dir}/rollout_{i*batch_size+j}" + if out_type == "vtk": # write vtk files for each time step + for k in range(pos_input.shape[0]): + # predictions + state_vtk = { + "r": example_rollout["predicted_rollout"][k], + "tag": traj_batch_i[1][j], + } + write_vtk(state_vtk, f"{file_prefix}_{k}.vtk") + # ground truth reference + state_vtk = { + "r": example_rollout["ground_truth_rollout"][k], + "tag": traj_batch_i[1][j], + } + write_vtk(state_vtk, f"{file_prefix}_ref_{k}.vtk") + if out_type == "pkl": + filename = f"{file_prefix}.pkl" + + with open(filename, "wb") as f: + pickle.dump(example_rollout, f) + + if (i * batch_size + j + 1) >= n_trajs: break if rollout_dir is not None: @@ -232,6 +305,7 @@ def infer( n_extrap_steps: int = defaults.n_extrap_steps, seed: int = defaults.seed, metrics_stride: int = defaults.metrics_stride, + batch_size: int = defaults.batch_size_infer, ): """ Infer on a dataset, compute metrics and optionally save rollout in out_type format. @@ -250,6 +324,8 @@ def infer( out_type: Output type. Either "none", "vtk" or "pkl". n_extrap_steps: Number of extrapolation steps. seed: Seed. + metrics_stride: Stride for e_kin and sinkhorn. + batch_size: Batch size for inference. Returns: eval_metrics: Metrics per trajectory. @@ -268,7 +344,7 @@ def infer( loader_test = DataLoader( dataset=data_test, - batch_size=1, + batch_size=batch_size, collate_fn=numpy_collate, worker_init_fn=seed_worker, generator=generator, @@ -281,7 +357,7 @@ def infer( stride=metrics_stride, ) # Precompile model - model_apply = jax.jit(model.apply) + model_apply = jit(model.apply) # init values pos_input_and_target, particle_type = next(iter(loader_test)) diff --git a/lagrangebench/train/trainer.py b/lagrangebench/train/trainer.py index b4dc3ea..8420832 100644 --- a/lagrangebench/train/trainer.py +++ b/lagrangebench/train/trainer.py @@ -113,6 +113,7 @@ def Trainer( eval_steps: int = defaults.eval_steps, metrics_stride: int = defaults.metrics_stride, num_workers: int = defaults.num_workers, + batch_size_infer: int = defaults.batch_size_infer, ) -> Callable: """ Builds a function that automates model training and evaluation. @@ -146,6 +147,9 @@ def Trainer( out_type: Output type. log_steps: Wandb/screen logging frequency. eval_steps: Evaluation and checkpointing frequency. + metrics_stride: stride for e_kin and sinkhorn. + num_workers: number of workers for data loading. + batch_size_infer: batch size for validation/testing. Returns: Configured training function. @@ -169,7 +173,7 @@ def Trainer( ) loader_valid = DataLoader( dataset=data_valid, - batch_size=1, + batch_size=batch_size_infer, collate_fn=numpy_collate, worker_init_fn=seed_worker, generator=generator, diff --git a/requirements_cuda.txt b/requirements_cuda.txt index e13e528..40fbba4 100644 --- a/requirements_cuda.txt +++ b/requirements_cuda.txt @@ -17,4 +17,4 @@ pyvista PyYAML torch>=2.1.0+cpu wandb -wget \ No newline at end of file +wget From c75b771e68d35810a3cc6efabaf73ba8ef42db5d Mon Sep 17 00:00:00 2001 From: Artur Toshev Date: Sat, 30 Dec 2023 01:23:04 +0000 Subject: [PATCH 02/16] update the tutorial notebook --- notebooks/tutorial.ipynb | 121 +++++++++++++++++++++++++++++++-------- 1 file changed, 96 insertions(+), 25 deletions(-) diff --git a/notebooks/tutorial.ipynb b/notebooks/tutorial.ipynb index 82e38d6..936a77e 100644 --- a/notebooks/tutorial.ipynb +++ b/notebooks/tutorial.ipynb @@ -205,38 +205,80 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/home/ggalletti/git/lagrangebench/venv/lib/python3.10/site-packages/jax/_src/ops/scatter.py:94: FutureWarning: scatter inputs have incompatible types: cannot safely cast value from dtype=int64 to dtype=int32 with jax_numpy_dtype_promotion='standard'. In future JAX releases this will result in an error.\n", - " warnings.warn(\"scatter inputs have incompatible types: cannot safely cast \"\n" + "/home/atoshev/code/lagrangebench/.venv/lib/python3.10/site-packages/jax/_src/ops/scatter.py:94: FutureWarning: scatter inputs have incompatible types: cannot safely cast value from dtype=int64 to dtype=int32 with jax_numpy_dtype_promotion='standard'. In future JAX releases this will result in an error.\n", + " warnings.warn(\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0000, train/loss: 2.17292.\n", + "0100, train/loss: 0.18065.\n", + "0200, train/loss: 0.19340.\n", + "0300, train/loss: 0.20835.\n", + "0400, train/loss: 0.14294.\n", + "0500, train/loss: 0.11689.\n", + "(eval) Reallocate neighbors list at step 3\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/atoshev/code/lagrangebench/.venv/lib/python3.10/site-packages/jax/_src/ops/scatter.py:94: FutureWarning: scatter inputs have incompatible types: cannot safely cast value from dtype=int64 to dtype=int32 with jax_numpy_dtype_promotion='standard'. In future JAX releases this will result in an error.\n", + " warnings.warn(\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(eval) From (2, 21057) to (2, 21200)\n", + "(eval) Reallocate neighbors list at step 4\n", + "(eval) From (2, 21200) to (2, 21835)\n", + "(eval) Reallocate neighbors list at step 7\n", + "(eval) From (2, 21835) to (2, 30975)\n", + "(eval) Reallocate neighbors list at step 8\n", + "(eval) From (2, 30975) to (2, 35677)\n", + "{'val/loss': 0.0032759700912061017, 'val/mse1': 1.752762669147577e-06, 'val/mse10': 0.0004931334458300185, 'val/mse5': 6.879239107686073e-05, 'val/stdloss': 0.00293470282787705, 'val/stdmse1': 1.673463006869998e-06, 'val/stdmse10': 0.0004534740995101451, 'val/stdmse5': 6.43755024564491e-05}\n", + "0600, train/loss: 0.02715.\n", + "0700, train/loss: 1.58997.\n", + "0800, train/loss: 1.85135.\n", + "Reallocate neighbors list at step 805\n", + "From (2, 21057) to (2, 20792)\n", + "0900, train/loss: 0.01133.\n", + "1000, train/loss: 0.01651.\n", + "(eval) Reallocate neighbors list at step 3\n", + "(eval) From (2, 20792) to (2, 21027)\n", + "(eval) Reallocate neighbors list at step 6\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/atoshev/code/lagrangebench/.venv/lib/python3.10/site-packages/jax/_src/ops/scatter.py:94: FutureWarning: scatter inputs have incompatible types: cannot safely cast value from dtype=int64 to dtype=int32 with jax_numpy_dtype_promotion='standard'. In future JAX releases this will result in an error.\n", + " warnings.warn(\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "0000, train/loss: 2.17808.\n", - "0100, train/loss: 0.19394.\n", - "0200, train/loss: 0.19751.\n", - "0300, train/loss: 0.20027.\n", - "0400, train/loss: 0.15017.\n", - "0500, train/loss: 0.14875.\n", - "{'val/loss': 0.006475041204928584, 'val/mse1': 3.5806455399026536e-06, 'val/mse5': 0.00014116973568971617, 'val/mse10': 0.0009921582776032162, 'val/stdloss': 0.0, 'val/stdmse1': 0.0, 'val/stdmse5': 0.0, 'val/stdmse10': 0.0}\n", - "0600, train/loss: 0.02190.\n", - "0700, train/loss: 1.62371.\n", - "Reallocate neighbors list at step 772\n", - "From (2, 21057) to (2, 20557)\n", - "0800, train/loss: 0.18237.\n", - "Reallocate neighbors list at step 804\n", - "From (2, 20557) to (2, 20742)\n", - "0900, train/loss: 0.01483.\n", - "1000, train/loss: 0.19956.\n", - "{'val/loss': 0.003817330574772867, 'val/mse1': 2.793629854284794e-06, 'val/mse5': 9.147089474639231e-05, 'val/mse10': 0.0005903546941926859, 'val/stdloss': 0.0, 'val/stdmse1': 0.0, 'val/stdmse5': 0.0, 'val/stdmse10': 0.0}\n" + "(eval) From (2, 21027) to (2, 23572)\n", + "(eval) Reallocate neighbors list at step 8\n", + "(eval) From (2, 23572) to (2, 27870)\n", + "(eval) Reallocate neighbors list at step 19\n", + "(eval) From (2, 27870) to (2, 31962)\n", + "{'val/loss': 0.00248120749930739, 'val/mse1': 1.393298525555248e-06, 'val/mse10': 0.0003490763834267208, 'val/mse5': 4.809697254341651e-05, 'val/stdloss': 0.002061295717414723, 'val/stdmse1': 1.3039043218413363e-06, 'val/stdmse10': 0.00029981220563334287, 'val/stdmse5': 4.274236635219637e-05}\n" ] } ], @@ -254,6 +296,7 @@ " lr_start=5e-4,\n", " log_steps=100,\n", " eval_steps=500,\n", + " batch_size_infer=1,\n", ")\n", "\n", "params, state, _ = trainer(step_max=1000)" @@ -269,7 +312,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -286,9 +329,36 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 9, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(eval) Reallocate neighbors list at step 5\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/atoshev/code/lagrangebench/.venv/lib/python3.10/site-packages/jax/_src/ops/scatter.py:94: FutureWarning: scatter inputs have incompatible types: cannot safely cast value from dtype=int64 to dtype=int32 with jax_numpy_dtype_promotion='standard'. In future JAX releases this will result in an error.\n", + " warnings.warn(\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(eval) From (2, 20597) to (2, 22350)\n", + "(eval) Reallocate neighbors list at step 6\n", + "(eval) From (2, 22350) to (2, 23725)\n", + "(eval) Reallocate neighbors list at step 8\n", + "(eval) From (2, 23725) to (2, 28452)\n" + ] + } + ], "source": [ "metrics = lagrangebench.infer(\n", " gns,\n", @@ -301,18 +371,19 @@ " n_rollout_steps=20,\n", " rollout_dir=\"rollouts/\",\n", " out_type=\"pkl\",\n", + " batch_size=1,\n", ")[\"rollout_0\"]\n", "rollout = pickle.load(open(\"rollouts/rollout_0.pkl\", \"rb\"))" ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 10, "metadata": {}, "outputs": [ { "data": { - "image/png": "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nIhUWt26dUlMTCQxMZGSkhLuueceXnzxRcaNG0eTJk2oX78+VquV2NhYR6cqIiJSLQUEBPDkk0/y5JNPcvz4cTp16sSLL77Ibbfd9pe37eTkxAcffMBdd91F3759+fLLL7n++usvalstW7Zk165dZ43v3LnTvlxELoyunItIpfz666/l3ru5udGuXTsMw6C0tBQXFxf69OnDkiVL2Lp161nr5+TkXKlURUREqh2r1UpBQUG5saZNmxIYGEhxcfEl24+bmxsff/wxXbt25Y477mDdunUXtZ3evXuzbt060tLS7GMWi4W5c+cSHBxMu3btLlXKIjWerpyLSKXccsst+Pv7c+211+Ln58eOHTuYPXs28fHx1K9fH4CXXnqJ1atXExUVxWOPPUa7du3Iy8tj48aNfPPNN+Tl5Tn4U4iIiFRNJ0+epHnz5tx7772Eh4dTr149vvnmG37++WemT59+Sffl6enJsmXLuPHGG7ntttv47rvvCAsLq9Q2xo4dy4IFC7jtttsYPnw4jRo14r333mP//v0sWbIEZ2ddCxS5UCrORaRS/va3v/HBBx/wz3/+k1OnTtG8eXOGDx/OhAkT7DF+fn6sW7eOyZMn8/HHH/PGG2/QuHFj2rdvz8svv+zA7EVERKo2Ly8vnnzySb7++ms+/vhjbDYbrVq14o033rgs/b69vb356quvuO6667j55pv54YcfaNWq1QWv7+fnx9q1a3n22WeZNWsWRUVFdOzYkc8//5z4+PhLnq9ITeZk/JUZHURERERERETkL9N9JiIiIiIiIiIOpuJcRERERERExMFUnIuIiIiIiIg4mIpzEREREREREQdTcS4iIiIiIiLiYCrORURERERERBys1vQ5t9lsHD16lPr16+Pk5OTodERERDAMg5MnTxIYGIizs34v/6t0rhcRkaqmMuf6WlOcHz16lKCgIEenISIicpZDhw7RvHlzR6dR7elcLyIiVdWFnOtrTXFev3594MxB8fb2dnA2IiIiYDabCQoKsp+j5K/RuV5ERKqaypzra01x/tvtbd7e3jphi4hIlaJbsC8NnetFRKSqupBzvR5wExEREREREXEwFeciIiIiIiIiDqbiXERERERERMTBVJyLiIiIiIiIOJiKcxEREREREREHU3EuIiIiIiIi4mAqzkVEREREREQcTMW5iIiIiIiIiIOpOBcR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", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA+cAAAF2CAYAAAAMW+lzAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAACV4klEQVR4nOzdeVxVdf7H8dcFZFPBnU0UUnIXEBQhHbUoKiqZzNAWzSyrKdPQTE2xdSjLMpcZss1myjTTrNQo03YZTAUVd3PBDQQRUJDt3vP7w1+3uSOamHpZ3s/H4zyI7/18z/ncU/G9n3vO+X5NhmEYiIiIiIiIiIjdONg7AREREREREZH6TsW5iIiIiIiIiJ2pOBcRERERERGxMxXnIiIiIiIiInam4lxERERERETEzlSci4iIiIiIiNiZinMRERERERERO1NxLiIiIiIiImJnKs5FRERERERE7EzFuYiIiIhILXLfffcREBBw0X0bNWr0h3EBAQHccsstF3UMEbk4Ks5FRERERERE7MzJ3gmIiIiIiMiFe+utt7BYLPZOQ0QuMRXnIiIiIiK1SIMGDeydwiVRWlqKs7MzDg66mVcEdFu7SL31zDPPYDKZ2LVrF/fccw+enp60bNmSqVOnYhgGBw8eZODAgXh4eODt7c2MGTNs+s+ePZsuXbrg7u5O06ZNCQ8PZ8GCBTYxhw8f5v7778fLywsXFxe6dOnCu+++eyXfpoiISK1z8uRJxo4dS0BAAC4uLrRq1Yrrr7+ejRs3Amc/c75//35MJhOvvvoq8+bNo127dri4uNCzZ09++eWXPzxeRkYGLVu2pH///pw6dcrmtZ9++olevXrh6urKVVddxb/+9a+z+u/du5fBgwfTrFkz3N3d6d27NytWrLCJ+e677zCZTCxcuJApU6bg5+eHu7s7RUVF1ufgDx8+TFxcHI0aNaJly5aMHz8es9l8EWdQpHbSlXORei4+Pp5OnTrx0ksvsWLFCl544QWaNWvGm2++ybXXXsvLL7/Mhx9+yPjx4+nZsyd/+ctfeOutt3j88ce54447GDNmDKWlpWzevJm0tDTuuusuAHJycujduzcmk4nHHnuMli1b8uWXXzJy5EiKiooYO3asfd+4iIhIDfXwww/zySef8Nhjj9G5c2eOHz/OTz/9xPbt2+nRo8c5+y1YsICTJ0/y0EMPYTKZmD59Orfffjt79+4959X2X375hZiYGMLDw/nss89wc3OzvrZnzx7uuOMORo4cyfDhw3n33Xe57777CAsLo0uXLsCZ8T4qKoqSkhIef/xxmjdvzvvvv89tt93GJ598wl//+leb4z3//PM4Ozszfvx4ysrKcHZ2BsBsNhMTE0NERASvvvoq33zzDTNmzKBdu3Y88sgjf/aUitQOhojUS9OmTTMAY9SoUda2yspKo3Xr1obJZDJeeukla/uJEycMNzc3Y/jw4YZhGMbAgQONLl26nHf/I0eONHx8fIy8vDyb9iFDhhienp5GSUnJpXszIiIidYinp6fx6KOPnvP14cOHG23btrX+vm/fPgMwmjdvbuTn51vbP/vsMwMwvvjiC5u+DRs2NAzDMH766SfDw8PDiI2NNUpLS22O0bZtWwMwfvjhB2vbsWPHDBcXF2PcuHHWtrFjxxqA8eOPP1rbTp48aQQGBhoBAQGG2Ww2DMMwvv32WwMwrrrqqrM+AwwfPtwAjOeee86mPTQ01AgLCzvneRCpa3Rbu0g998ADD1j/2dHRkfDwcAzDYOTIkdb2Jk2a0KFDB/bu3Wv9/dChQ+e8Vc4wDJYsWcKtt96KYRjk5eVZt5iYGAoLC6235omIiIitJk2akJaWxpEjR6rVLz4+nqZNm1p/79u3L4B1/P5v3377LTExMVx33XUsXboUFxeXs2I6d+5s3QdAy5YtbT4PAKxcuZJevXrRp08fa1ujRo0YNWoU+/fvZ9u2bTb7HD58uM3V+f/28MMP2/zet2/fKnMXqatUnIvUc23atLH53dPTE1dXV1q0aHFW+4kTJwB46qmnaNSoEb169SIoKIhHH32Un3/+2Rqbm5tLQUEB8+bNo2XLljbbiBEjADh27NhlfmciIiK10/Tp08nMzMTf359evXrxzDPPXFCR+r9j+m+F+m/j929KS0uJjY0lNDSUjz/+2Hpr+R/t77d9/vf+Dhw4QIcOHc6K69Spk/X1/xYYGFjlsVxdXWnZsuV5jyVS16k4F6nnHB0dL6gNzlwRhzMD7s6dO1m4cCF9+vRhyZIl9OnTh2nTpgFYl3e55557WLVqVZXbNddcc5nekYiISO125513snfvXmbPno2vry+vvPIKXbp04csvvzxvvz8av3/j4uJCbGwsaWlppKSk/On9Vce5rpqf61gi9YkmhBORi9KwYUPi4+OJj4+nvLyc22+/nRdffJFJkybRsmVLGjdujNlsJjo62t6pioiI1Do+Pj787W9/429/+xvHjh2jR48evPjii9x0001/et8mk4kPP/yQgQMHMnjwYL788kv69+9/Uftq27YtO3fuPKt9x44d1tdF5MLoyrmIVNvx48dtfnd2dqZz584YhkFFRQWOjo4MGjSIJUuWkJmZeVb/3NzcK5WqiIhIrWI2myksLLRpa9WqFb6+vpSVlV2y4zg7O7N06VJ69uzJrbfeyrp16y5qPzfffDPr1q0jNTXV2lZcXMy8efMICAigc+fOlyplkTpPV85FpNpuuOEGvL29ueaaa/Dy8mL79u3MmTOH2NhYGjduDMBLL73Et99+S0REBA8++CCdO3cmPz+fjRs38s0335Cfn2/ndyEiIlLznDx5ktatW3PHHXcQHBxMo0aN+Oabb/jll1+YMWPGJT2Wm5sby5cv59prr+Wmm27i+++/p2vXrtXax8SJE/noo4+46aabePzxx2nWrBnvv/8++/btY8mSJTg46FqgyIVScS4i1fbQQw/x4Ycf8tprr3Hq1Clat27N448/zpQpU6wxXl5erFu3jueee46lS5fyj3/8g+bNm9OlSxdefvllO2YvIiJSc7m7u/O3v/2Nr7/+mqVLl2KxWGjfvj3/+Mc/Lst63x4eHnz11Vf85S9/4frrr+fHH3+kffv2F9zfy8uLtWvX8tRTTzF79mxKS0vp3r07X3zxBbGxsZc8X5G6zGT8mRkdRERERERERORP030mIiIiIiIiInam4lxERERERETEzlSci4iIiIiIiNiZinMRERERERERO1NxLiIiIiIiImJnKs5FRERERERE7KzerHNusVg4cuQIjRs3xmQy2TsdERERDMPg5MmT+Pr64uCg78svBY33IiJSk1RrrDcuwpw5c4y2bdsaLi4uRq9evYy0tLTzxn/88cdGhw4dDBcXF6Nr167GihUrbF5fsmSJcf311xvNmjUzACM9Pb3K/axdu9YYMGCA4e7ubjRu3Njo27evUVJSckE5Hzx40AC0adOmTZu2GrcdPHjwgsYy+WMa77Vp06ZNW03cLmSsr/aV80WLFpGQkEBycjIRERHMnDmTmJgYdu7cSatWrc6KX7t2LUOHDiUpKYlbbrmFBQsWEBcXx8aNG+natSsAxcXF9OnThzvvvJMHH3ywyuOmpqZy4403MmnSJGbPno2TkxObNm264CsNjRs3BuDgwYN4eHhU922LiIhcckVFRfj7+1vHKPnzNN6LiEhNUp2x3mQYhlGdnUdERNCzZ0/mzJkDnLl9zN/fn9GjRzNx4sSz4uPj4ykuLmb58uXWtt69exMSEkJycrJN7P79+wkMDCQ9PZ2QkBCb13r37s3111/P888/X510rYqKivD09KSwsFCDtYiI1Agamy49nVMREalJqjMuVesBt/LycjZs2EB0dPTvO3BwIDo6mtTU1Cr7pKam2sQDxMTEnDO+KseOHSMtLY1WrVoRFRWFl5cX/fr146effqpO+iIiIiIiIiI1UrWK87y8PMxmM15eXjbtXl5eZGdnV9knOzu7WvFV2bt3LwDPPPMMDz74ICkpKfTo0YPrrruO3bt3V9mnrKyMoqIim01ERERERESkJqoVU8NaLBYAHnroIUaMGEFoaCivv/46HTp04N13362yT1JSEp6entbN39//SqYsIiIiIiIicsGqVZy3aNECR0dHcnJybNpzcnLw9vauso+3t3e14qvi4+MDQOfOnW3aO3XqRFZWVpV9Jk2aRGFhoXU7ePDgBR9PRERERERE5EqqVnHu7OxMWFgYq1evtrZZLBZWr15NZGRklX0iIyNt4gFWrVp1zviqBAQE4Ovry86dO23ad+3aRdu2bavs4+LigoeHh80mIiIiIiIiUhNVeym1hIQEhg8fTnh4OL169WLmzJkUFxczYsQIAIYNG4afnx9JSUkAjBkzhn79+jFjxgxiY2NZuHAh69evZ968edZ95ufnk5WVxZEjRwCsRbi3tzfe3t6YTCaefPJJpk2bRnBwMCEhIbz//vvs2LGDTz755E+fBBERERERERF7qnZxHh8fT25uLomJiWRnZxMSEkJKSop10resrCybtcejoqJYsGABU6ZMYfLkyQQFBbFs2TLrGucAn3/+ubW4BxgyZAgA06ZN45lnngFg7NixlJaW8sQTT5Cfn09wcDCrVq2iXbt2F/XGRURERERERGqKaq9zXltp3VMREalpNDZdejqnIiJSk1y2dc5FRESk5ps7dy4BAQG4uroSERHBunXrzhu/ePFiOnbsiKurK926dWPlypU2rxuGQWJiIj4+Pri5uREdHX3WUqYvvvgiUVFRuLu706RJk7OOMX/+fEwmU5XbsWPHAPju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Om1Lc/uoq9thtVplwYIFkp2dLYWFhbJ37155/PHHpU+fPlJZWem0He44JnVKS0ulU6dOsnLlyka34Q5jQh2Lp8z3rqS0z9LS0kSr1crWrVsdjqdWq1WtJriU0v6q72H8tnalfXb16lXx9/eXuXPnSn5+vuzYsUOCg4PlvffeU6sJLqe0z1JTU8Xf3182bdokly5dkn/+858SHR0t06ZNU6sJLtfc647k5GRJTEy0x1+6dEk6deokv/vd7yQvL09WrFgh3t7esnv37jbNi8W5E8uXL5devXqJVquVkSNHyrFjx+z3jR07VmbNmuUQv2XLFunbt69otVoZOHCg7Ny508UZNwSg0cv69evtMfXbMm/ePHu7jUajTJo0SU6cOOH65OuZPn26hIaGilarlR49esj06dOloKDAfn9HGRMRkT179ggAyc/Pb3CfO4/HgQMHGt2f6vK12WySkpIiRqNRdDqdjBs3rkEbIyIiJDU11WFZU881NdpSWFjo9Llz4MABp21pbh91dTvKy8tl/Pjx8sgjj4ivr69ERETIr371qwZFdkcYkzqrV68WPz8/KSkpaXQb7jAm1PF4wnzvakr6LCIiotHndv3jjidTuo896GEszkWU99nRo0fFZDKJTqeTqKgo+cMf/iA1NTUuzlpdSvqsurpa3n77bYmOjha9Xi/h4eHym9/8Rm7fvu36xFXS3OuOWbNmydixYxusM2zYMNFqtRIVFeVQU7UVjQj/d4GIiIiIiIhITfzMOREREREREZHKWJwTERERERERqYzFOREREREREZHKWJwTERERERERqYzFOREREREREZHKWJwTERERERERqYzFOREREREREZHKWJwTERERERERqYzFOREREREREZHKWJwTERERERERqYzFOREREREREZHKWJwTERERERERqez/AF3T1S80RJxoAAAAAElFTkSuQmCC", "text/plain": [ "
" ] From 4cdcfe57450e9f90c74cc4a998da59a9ba297b72 Mon Sep 17 00:00:00 2001 From: Artur Toshev Date: Sat, 30 Dec 2023 02:11:40 +0000 Subject: [PATCH 03/16] case_setup tests --- lagrangebench/data/data.py | 2 +- tests/case_test.py | 215 +++++++++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 tests/case_test.py diff --git a/lagrangebench/data/data.py b/lagrangebench/data/data.py index 9cefd2d..1c976bd 100644 --- a/lagrangebench/data/data.py +++ b/lagrangebench/data/data.py @@ -242,7 +242,7 @@ def get_window(self, idx: int): def __getitem__(self, idx: int): """ Get a sequence of positions (of size windows) from the dataset at index idx. - + Returns: Array of shape (num_particles_max, input_seq_length + 1, dim). Along axis=1 the position sequence (length input_seq_length) and the last position to diff --git a/tests/case_test.py b/tests/case_test.py new file mode 100644 index 0000000..178aa86 --- /dev/null +++ b/tests/case_test.py @@ -0,0 +1,215 @@ +import unittest + +import jax +import jax.numpy as jnp +import numpy as np + +from lagrangebench.case_setup import case_builder + + +class TestCaseBuilder(unittest.TestCase): + """Class for unit testing the case builder functions.""" + + def setUp(self): + self.metadata = { + "num_particles_max": 3, + "periodic_boundary_conditions": [True, True, True], + "default_connectivity_radius": 0.3, + "bounds": [[0.0, 1.0], [0.0, 1.0], [0.0, 1.0]], + "acc_mean": [0.0, 0.0, 0.0], + "acc_std": [1.0, 1.0, 1.0], + "vel_mean": [0.0, 0.0, 0.0], + "vel_std": [1.0, 1.0, 1.0], + } + + bounds = np.array(self.metadata["bounds"]) + box = bounds[:, 1] - bounds[:, 0] + + self.case = case_builder( + box, + self.metadata, + input_seq_length=3, # one past velocity + isotropic_norm=False, + noise_std=0.0, + external_force_fn=None, + ) + self.key = jax.random.PRNGKey(0) + + # position input shape (num_particles, sequence_len, dim) = (3, 5, 3) + self.position_data = np.array( + [ + [ + [0.5, 0.5, 0.5], + [0.5, 0.5, 0.5], + [0.5, 0.5, 0.5], + [0.5, 0.5, 0.5], + [0.5, 0.5, 0.5], + ], + [ + [0.7, 0.5, 0.5], + [0.9, 0.5, 0.5], + [0.1, 0.5, 0.5], + [0.3, 0.5, 0.5], + [0.5, 0.5, 0.5], + ], + [ + [0.8, 0.6, 0.5], + [0.8, 0.6, 0.5], + [0.9, 0.6, 0.5], + [0.2, 0.6, 0.5], + [0.6, 0.6, 0.5], + ], + ] + ) + self.particle_types = np.array([0, 0, 0]) + + key, features, target_dict, neighbors = self.case.allocate( + self.key, (self.position_data, self.particle_types) + ) + self.neighbors = neighbors + + def test_allocate(self): + # test PBC and velocity and acceleration computation without noise + key, features, target_dict, neighbors = self.case.allocate( + self.key, (self.position_data, self.particle_types) + ) + self.assertTrue( + ( + neighbors.idx == jnp.array([[0, 1, 2, 2, 1, 3], [0, 1, 1, 2, 2, 3]]) + ).all(), + "Wrong edge list after allocate", + ) + + self.assertTrue((key != self.key).all(), "Key not updated at allocate") + + self.assertTrue( + jnp.isclose( + target_dict["vel"], + jnp.array([[0.0, 0.0, 0.0], [0.2, 0.0, 0.0], [0.3, 0.0, 0.0]]), + ).all(), + "Wrong target velocity at allocate", + ) + + self.assertTrue( + jnp.isclose( + target_dict["acc"], + jnp.array([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.2, 0.0, 0.0]]), + ).all(), + "Wrong target acceleration at allocate", + ) + + self.assertTrue( + jnp.isclose( + features["vel_hist"], + jnp.array( + [ + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.2, 0.0, 0.0, 0.2, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.1, 0.0, 0.0], + ] + ), + ).all(), + "Wrong historic velocities at allocate", + ) + + most_recent_displacement = jnp.array( + [ + [0.0, 0.0, 0.0], # edge 0-0 + [0.0, 0.0, 0.0], # edge 1-1 + [-0.2, 0.1, 0.0], # edge 2-1 + [0.0, 0.0, 0.0], # edge 2-2 + [0.2, -0.1, 0.0], # edge 1-2 + [0.0, 0.0, 0.0], # edge 3-3 + ] + ) + r0 = self.metadata["default_connectivity_radius"] + normalized_displ = most_recent_displacement / r0 + normalized_dist = ((normalized_displ**2).sum(-1, keepdims=True)) ** 0.5 + + self.assertTrue( + jnp.isclose(features["rel_disp"], normalized_displ).all(), + "Wrong relative displacement at allocate", + ) + self.assertTrue( + jnp.isclose(features["rel_dist"], normalized_dist).all(), + "Wrong relative distance at allocate", + ) + + def test_preprocess_base(self): + # preprocess is 1-to-1 the same as allocate, up to the neighbors' computation + _, _, _, neighbors_new = self.case.preprocess( + self.key, (self.position_data, self.particle_types), 0.0, self.neighbors, 0 + ) + + self.assertTrue( + (self.neighbors.idx == neighbors_new.idx).all(), + "Wrong edge list after preprocess", + ) + + def test_preprocess_unroll(self): + # test getting the second available target acceleration + _, _, target_dict, _ = self.case.preprocess( + self.key, (self.position_data, self.particle_types), 0.0, self.neighbors, 1 + ) + + self.assertTrue( + jnp.isclose( + target_dict["acc"], + jnp.array( + [ + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.1, 0.0, 0.0], + ] + ), + atol=1e-07, + ).all(), + "Wrong target acceleration at preprocess", + ) + + def test_preprocess_noise(self): + # test that both potential targets are corrected with the proper noise + # we choose noise_std=0.01 to guarantee that no particle will jump periodically + _, features, target_dict, _ = self.case.preprocess( + self.key, (self.position_data, self.particle_types), 0.01, self.neighbors, 0 + ) + vel_next1 = jnp.array([[0.0, 0.0, 0.0], [0.2, 0.0, 0.0], [0.3, 0.0, 0.0]]) + correct_target_acc = vel_next1 - features["vel_hist"][:, 3:6] + self.assertTrue( + jnp.isclose(correct_target_acc, target_dict["acc"], atol=1e-7).all(), + "Wrong target acceleration at preprocess", + ) + + # with one push-forward step on top + _, features, target_dict, _ = self.case.preprocess( + self.key, (self.position_data, self.particle_types), 0.01, self.neighbors, 1 + ) + vel_next2 = jnp.array([[0.0, 0.0, 0.0], [0.2, 0.0, 0.0], [0.4, 0.0, 0.0]]) + correct_target_acc = vel_next2 - vel_next1 + self.assertTrue( + jnp.isclose(correct_target_acc, target_dict["acc"], atol=1e-7).all(), + "Wrong target acceleration at preprocess with 1 pushforward step", + ) + + def test_allocate_eval(self): + pass + + def test_preprocess_eval(self): + pass + + def test_integrate(self): + # given the reference acceleration, compute the next position + correct_acceletation = { + "acc": jnp.array([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.2, 0.0, 0.0]]) + } + + new_pos = self.case.integrate(correct_acceletation, self.position_data[:, :3]) + + self.assertTrue( + jnp.isclose(new_pos, self.position_data[:, 3]).all(), + "Wrong new position at integration", + ) + + +if __name__ == "__main__": + unittest.main() From f569951cd9731e0d59ab91df3251a2ca3fd57755 Mon Sep 17 00:00:00 2001 From: Artur Toshev Date: Sat, 30 Dec 2023 02:12:38 +0000 Subject: [PATCH 04/16] add pushforward tests --- tests/pushforward_test.py | 44 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 tests/pushforward_test.py diff --git a/tests/pushforward_test.py b/tests/pushforward_test.py new file mode 100644 index 0000000..06d77d8 --- /dev/null +++ b/tests/pushforward_test.py @@ -0,0 +1,44 @@ +import unittest + +import jax +import numpy as np + +from lagrangebench import PushforwardConfig +from lagrangebench.train.strats import push_forward_sample_steps + + +class TestPushForward(unittest.TestCase): + """Class for unit testing the push-forward functions.""" + + def setUp(self): + self.pf = PushforwardConfig( + steps=[-1, 20000, 50000, 100000], + unrolls=[0, 1, 3, 20], + probs=[4.05, 4.05, 1.0, 1.0], + ) + + self.key = jax.random.PRNGKey(42) + + def body_steps(self, step, unrolls, probs): + dump = [] + for _ in range(1000): + self.key, unroll_steps = push_forward_sample_steps(self.key, step, self.pf) + dump.append(unroll_steps) + + # Note: np.unique returns sorted array + unique, counts = np.unique(dump, return_counts=True) + self.assertTrue((unique == unrolls).all(), "Wrong unroll steps") + self.assertTrue( + np.allclose(counts / 1000, probs, atol=0.05), + "Wrong probabilities of unroll steps", + ) + + def test_pf_step_1(self): + self.body_steps(1, np.array([0]), np.array([1.0])) + + def test_pf_step_60000(self): + self.body_steps(60000, np.array([0, 1, 3]), np.array([0.45, 0.45, 0.1])) + + +if __name__ == "__main__": + unittest.main() From 02db4b577c6e96c16dca4f2ab34deb90500762cc Mon Sep 17 00:00:00 2001 From: Artur Toshev Date: Sun, 31 Dec 2023 05:27:04 +0000 Subject: [PATCH 05/16] add rollout tests and small LJ dataset with 3 particles --- experiments/run.py | 2 +- lagrangebench/evaluate/rollout.py | 9 +- tests/3D_LJ_3_1214every1/metadata.json | 53 ++++++++ tests/3D_LJ_3_1214every1/test.h5 | Bin 0 -> 20653 bytes tests/3D_LJ_3_1214every1/train.h5 | Bin 0 -> 43329 bytes tests/3D_LJ_3_1214every1/valid.h5 | Bin 0 -> 20961 bytes tests/case_test.py | 12 +- tests/rollout_test.py | 172 +++++++++++++++++++++++++ 8 files changed, 235 insertions(+), 13 deletions(-) create mode 100644 tests/3D_LJ_3_1214every1/metadata.json create mode 100644 tests/3D_LJ_3_1214every1/test.h5 create mode 100644 tests/3D_LJ_3_1214every1/train.h5 create mode 100644 tests/3D_LJ_3_1214every1/valid.h5 create mode 100644 tests/rollout_test.py diff --git a/experiments/run.py b/experiments/run.py index fbd8507..000fade 100644 --- a/experiments/run.py +++ b/experiments/run.py @@ -151,7 +151,7 @@ def train_or_infer(args: Namespace): metrics = infer( model, case, - data_test, + data_test if args.config.test else data_valid, load_checkpoint=args.config.model_dir, metrics=args.config.metrics_infer, rollout_dir=args.config.rollout_dir, diff --git a/lagrangebench/evaluate/rollout.py b/lagrangebench/evaluate/rollout.py index 4742d70..864d6cd 100644 --- a/lagrangebench/evaluate/rollout.py +++ b/lagrangebench/evaluate/rollout.py @@ -55,7 +55,8 @@ def _forward_eval( _, particle_type = sample # predict acceleration and integrate - pred, _ = model_apply(params, state, sample) + pred, state = model_apply(params, state, sample) + next_position = case_integrate(pred, current_positions) # update only the positions of non-boundary particles @@ -70,7 +71,7 @@ def _forward_eval( [current_positions[:, 1:], next_position[:, None, :]], axis=1 ) # as next model input - return current_positions + return current_positions, state def eval_batched_rollout( @@ -156,13 +157,15 @@ def eval_batched_rollout( continue # 3. run forward model - current_positions_batch = forward_eval_vmap( + current_positions_batch, state_batch = forward_eval_vmap( params, state, (features_batch, particle_type_batch), current_positions_batch, target_positions_batch[:, :, step], ) + # the state is not passed out of this loop, so no not really relevant + state = broadcast_from_batch(state_batch, 0) # 4. write predicted next position to output array predictions_batch = predictions_batch.at[:, step].set( diff --git a/tests/3D_LJ_3_1214every1/metadata.json b/tests/3D_LJ_3_1214every1/metadata.json new file mode 100644 index 0000000..f0e04bf --- /dev/null +++ b/tests/3D_LJ_3_1214every1/metadata.json @@ -0,0 +1,53 @@ +{ + "solver": "JAXMD", + "dim": 3, + "dx": 1.4, + "dt": 0.005, + "t_end": 10.0, + "sequence_length_train": 1214, + "num_trajs_train": 1, + "sequence_length_test": 405, + "num_trajs_test": 1, + "num_particles_max": 3, + "periodic_boundary_conditions": [ + true, + true, + true + ], + "bounds": [ + [ + 0.0, + 5.0 + ], + [ + 0.0, + 5.0 + ], + [ + 0.0, + 5.0 + ] + ], + "default_connectivity_radius": 3.0, + "vel_mean": [ + -5.573862482677328e-10, + 4.917874996124283e-10, + -1.3441651125489784e-09 + ], + "vel_std": [ + 0.006350979674607515, + 0.005811989773064852, + 0.003586509730666876 + ], + "acc_mean": [ + -3.2785833076198756e-11, + -6.557166615239751e-11, + 0.0 + ], + "acc_std": [ + 0.0011505373986437917, + 0.0005201193853281438, + 0.00039340186049230397 + ], + "description": "System of 3 Lennard-Jones particles in a periodic 3D box simulated with JAX-MD. Can be used to test the preprocessing and rollout utilities." +} diff --git a/tests/3D_LJ_3_1214every1/test.h5 b/tests/3D_LJ_3_1214every1/test.h5 new file mode 100644 index 0000000000000000000000000000000000000000..a91e5d4fd98956fbf73f279fc2f404c15fe39bef GIT binary patch literal 20653 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 N^ZE_xs*Z-}{{c*fG<*O6 literal 0 HcmV?d00001 diff --git a/tests/case_test.py b/tests/case_test.py index 178aa86..373eb28 100644 --- a/tests/case_test.py +++ b/tests/case_test.py @@ -28,7 +28,7 @@ def setUp(self): self.case = case_builder( box, self.metadata, - input_seq_length=3, # one past velocity + input_seq_length=3, # two past velocities isotropic_norm=False, noise_std=0.0, external_force_fn=None, @@ -103,7 +103,7 @@ def test_allocate(self): features["vel_hist"], jnp.array( [ - [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], # particle 1, two past vels. [0.2, 0.0, 0.0, 0.2, 0.0, 0.0], [0.0, 0.0, 0.0, 0.1, 0.0, 0.0], ] @@ -155,13 +155,7 @@ def test_preprocess_unroll(self): self.assertTrue( jnp.isclose( target_dict["acc"], - jnp.array( - [ - [0.0, 0.0, 0.0], - [0.0, 0.0, 0.0], - [0.1, 0.0, 0.0], - ] - ), + jnp.array([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.1, 0.0, 0.0]]), atol=1e-07, ).all(), "Wrong target acceleration at preprocess", diff --git a/tests/rollout_test.py b/tests/rollout_test.py new file mode 100644 index 0000000..a1dbf96 --- /dev/null +++ b/tests/rollout_test.py @@ -0,0 +1,172 @@ +import unittest +from argparse import Namespace + +import haiku as hk +import jax +import jax.numpy as jnp +import numpy as np +from jax import config as jax_config +from jax import jit, vmap +from jax_md import space +from torch.utils.data import DataLoader + +jax_config.update("jax_enable_x64", True) + +from lagrangebench.case_setup import case_builder +from lagrangebench.data import H5Dataset +from lagrangebench.data.utils import get_dataset_stats, numpy_collate +from lagrangebench.evaluate import MetricsComputer +from lagrangebench.evaluate.rollout import eval_batched_rollout +from lagrangebench.utils import broadcast_from_batch + + +class TestInferBuilder(unittest.TestCase): + """Class for unit testing the evaluate_single_rollout function.""" + + def setUp(self): + self.config = Namespace( + data_dir="tests/3D_LJ_3_1214every1", # Lennard-Jones dataset + input_seq_length=3, # two past velocities + metrics=["mse"], + n_rollout_steps=100, + isotropic_norm=False, + noise_std=0.0, + ) + + data_valid = H5Dataset( + split="valid", + dataset_path=self.config.data_dir, + name="lj3d", + input_seq_length=self.config.input_seq_length, + extra_seq_length=self.config.n_rollout_steps, + ) + self.loader_valid = DataLoader( + dataset=data_valid, batch_size=1, collate_fn=numpy_collate + ) + + self.metadata = data_valid.metadata + self.normalization_stats = get_dataset_stats( + self.metadata, self.config.isotropic_norm, self.config.noise_std + ) + + bounds = np.array(self.metadata["bounds"]) + box = bounds[:, 1] - bounds[:, 0] + self.displacement_fn, self.shift_fn = space.periodic(side=box) + + self.case = case_builder( + box, + self.metadata, + self.config.input_seq_length, + noise_std=self.config.noise_std, + ) + + self.key = jax.random.PRNGKey(0) + + def test_rollout(self): + isl = self.loader_valid.dataset.input_seq_length + + # get one validation trajectory from the debug dataset + traj_batch_i = next(iter(self.loader_valid)) + traj_batch_i = jax.tree_map(lambda x: jnp.array(x), traj_batch_i) + # remove batch dimension + self.assertTrue(traj_batch_i[0].shape[0] == 1, "We test only batch size 1") + traj_i = broadcast_from_batch(traj_batch_i, index=0) + positions = traj_i[0] # (nodes, t, dim) = (3, 405, 3) + + displ_vmap = vmap(self.displacement_fn, (0, 0)) + displ_dvmap = vmap(displ_vmap, (0, 0)) + vels = displ_dvmap(positions[:, 1:], positions[:, :-1]) # (3, 404, 3) + accs = vels[:, 1:] - vels[:, :-1] # (3, 403, 3) + stats = self.normalization_stats["acceleration"] + accs = (accs - stats["mean"]) / stats["std"] + + class CheatingModel(hk.Module): + def __init__(self, target, start): + super().__init__() + self.target = target + self.start = start + + def __call__(self, x): + i = hk.get_state( + "counter", + shape=[], + dtype=jnp.int32, + init=hk.initializers.Constant(self.start), + ) + hk.set_state("counter", i + 1) + return {"acc": self.target[:, i]} + + def setup_model(target, start): + def model(x): + return CheatingModel(target, start)(x) + + model = hk.without_apply_rng(hk.transform_with_state(model)) + params, state = model.init(None, None) + model_apply = model.apply + model_apply = jit(model_apply) + return params, state, model_apply + + params, state, model_apply = setup_model(accs, 0) + + # proof that the above "model" works + out, state = model_apply(params, state, None) + pred_acc = stats["mean"] + out["acc"] * stats["std"] + pred_pos = self.shift_fn(positions[:, isl - 1], vels[:, isl - 2] + pred_acc) + pred_pos = jnp.asarray(pred_pos, dtype=jnp.float32) + target_pos = positions[:, isl] + + assert jnp.isclose(pred_pos, target_pos, atol=1e-7).all(), "Wrong setup" + + params, state, model_apply = setup_model(accs, isl - 2) + _, neighbors = self.case.allocate_eval((positions[:, :isl], traj_i[1])) + + metrics_computer = MetricsComputer( + ["mse"], + self.case.displacement, + self.metadata, + isl, + ) + + example_rollout_batch, metrics_batch, neighbors = eval_batched_rollout( + model_apply=model_apply, + case=self.case, + params=params, + state=state, + traj_batch_i=traj_batch_i, + neighbors=neighbors, + metrics_computer=metrics_computer, + n_rollout_steps=self.config.n_rollout_steps, + t_window=isl, + ) + example_rollout = broadcast_from_batch(example_rollout_batch, index=0) + metrics = broadcast_from_batch(metrics_batch, index=0) + + self.assertTrue( + jnp.isclose( + metrics["mse"].mean(), + jnp.array(0.0), + atol=1e-6, + ).all(), + "Wrong rollout mse", + ) + + pos_input = traj_i[0].transpose(1, 0, 2) # (t, nodes, dim) + initial_positions = pos_input[:isl] + example_full = np.concatenate([initial_positions, example_rollout], axis=0) + rollout_dict = { + "predicted_rollout": example_full, # (t, nodes, dim) + "ground_truth_rollout": pos_input, # (t, nodes, dim) + } + + self.assertTrue( + jnp.isclose( + rollout_dict["predicted_rollout"][100, 0], + rollout_dict["ground_truth_rollout"][100, 0], + atol=1e-6, + ).all(), + "Wrong rollout prediction", + ) + + +if __name__ == "__main__": + unittest.main() From fede0f2925a3bbcb890fa0d9518514bf140240e1 Mon Sep 17 00:00:00 2001 From: Artur Toshev Date: Tue, 2 Jan 2024 00:16:15 +0000 Subject: [PATCH 06/16] upgrade ruff and replace black with it --- .pre-commit-config.yaml | 16 +-- lagrangebench/case_setup/partition.py | 2 +- lagrangebench/models/gns.py | 5 +- lagrangebench/train/trainer.py | 7 +- poetry.lock | 139 +++++++------------------- pyproject.toml | 13 ++- 6 files changed, 58 insertions(+), 124 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e69d0f7..19338da 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,17 +21,9 @@ repos: - id: trailing-whitespace - id: end-of-file-fixer - id: requirements-txt-fixer - - repo: https://github.com/pycqa/isort - rev: 5.12.0 - hooks: - - id: isort - args: [ --profile, black ] - - repo: https://github.com/ambv/black - rev: 23.3.0 - hooks: - - id: black - args: ['--config=./pyproject.toml'] - - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: 'v0.0.265' + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: 'v0.1.8' hooks: - id: ruff + args: [ --fix ] + - id: ruff-format diff --git a/lagrangebench/case_setup/partition.py b/lagrangebench/case_setup/partition.py index 753c2cc..a6ad2a5 100644 --- a/lagrangebench/case_setup/partition.py +++ b/lagrangebench/case_setup/partition.py @@ -300,7 +300,7 @@ def scan_body(carry, input): if not is_sparse(format): capacity_limit = N - 1 if mask_self else N elif format is NeighborListFormat.Sparse: - capacity_limit = N * (N - 1) if mask_self else N ** 2 + capacity_limit = N * (N - 1) if mask_self else N**2 else: capacity_limit = N * (N - 1) // 2 if max_occupancy > capacity_limit: diff --git a/lagrangebench/models/gns.py b/lagrangebench/models/gns.py index 680b3d2..9020231 100644 --- a/lagrangebench/models/gns.py +++ b/lagrangebench/models/gns.py @@ -84,7 +84,10 @@ def _processor(self, graph: jraph.GraphsTuple) -> jraph.GraphsTuple: """Sequence of Graph Network blocks.""" def update_edge_features( - edge_features, sender_node_features, receiver_node_features, _ # globals_ + edge_features, + sender_node_features, + receiver_node_features, + _, # globals_ ): update_fn = build_mlp( self._latent_size, self._latent_size, self._blocks_per_step diff --git a/lagrangebench/train/trainer.py b/lagrangebench/train/trainer.py index 8420832..9c57bb0 100644 --- a/lagrangebench/train/trainer.py +++ b/lagrangebench/train/trainer.py @@ -11,7 +11,6 @@ import optax from jax import vmap from torch.utils.data import DataLoader -from wandb.wandb_run import Run from lagrangebench.data import H5Dataset from lagrangebench.data.utils import numpy_collate @@ -28,6 +27,7 @@ save_haiku, set_seed, ) +from wandb.wandb_run import Run from .strats import push_forward_build, push_forward_sample_steps @@ -190,10 +190,7 @@ def Trainer( opt_init, opt_update = optax.adamw(learning_rate=lr_scheduler, weight_decay=1e-8) # loss config - if loss_weight is None: - loss_weight = LossConfig() - else: - loss_weight = LossConfig(**loss_weight) + loss_weight = LossConfig() if loss_weight is None else LossConfig(**loss_weight) # pushforward config if pushforward is None: pushforward = PushforwardConfig() diff --git a/poetry.lock b/poetry.lock index 90a8939..2385764 100644 --- a/poetry.lock +++ b/poetry.lock @@ -84,21 +84,22 @@ test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] [[package]] name = "attrs" -version = "23.1.0" +version = "23.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" files = [ - {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, - {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, ] [package.extras] cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]", "pre-commit"] +dev = ["attrs[tests]", "pre-commit"] docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] [[package]] name = "babel" @@ -114,52 +115,6 @@ files = [ [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] -[[package]] -name = "black" -version = "23.12.1" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.8" -files = [ - {file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"}, - {file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"}, - {file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"}, - {file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"}, - {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"}, - {file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"}, - {file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"}, - {file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"}, - {file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"}, - {file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"}, - {file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"}, - {file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"}, - {file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"}, - {file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"}, - {file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"}, - {file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"}, - {file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"}, - {file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"}, - {file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"}, - {file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"}, - {file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"}, - {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - [[package]] name = "certifi" version = "2023.11.17" @@ -1024,13 +979,13 @@ files = [ [[package]] name = "ipykernel" -version = "6.27.1" +version = "6.28.0" description = "IPython Kernel for Jupyter" optional = false python-versions = ">=3.8" files = [ - {file = "ipykernel-6.27.1-py3-none-any.whl", hash = "sha256:dab88b47f112f9f7df62236511023c9bdeef67abc73af7c652e4ce4441601686"}, - {file = "ipykernel-6.27.1.tar.gz", hash = "sha256:7d5d594b6690654b4d299edba5e872dc17bb7396a8d0609c97cb7b8a1c605de6"}, + {file = "ipykernel-6.28.0-py3-none-any.whl", hash = "sha256:c6e9a9c63a7f4095c0a22a79f765f079f9ec7be4f2430a898ddea889e8665661"}, + {file = "ipykernel-6.28.0.tar.gz", hash = "sha256:69c11403d26de69df02225916f916b37ea4b9af417da0a8c827f84328d88e5f3"}, ] [package.dependencies] @@ -1044,7 +999,7 @@ matplotlib-inline = ">=0.1" nest-asyncio = "*" packaging = "*" psutil = "*" -pyzmq = ">=20" +pyzmq = ">=24" tornado = ">=6.1" traitlets = ">=5.4.0" @@ -1320,13 +1275,13 @@ test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pyt [[package]] name = "jupyter-core" -version = "5.5.1" +version = "5.6.1" description = "Jupyter core package. A base package on which Jupyter projects rely." optional = false python-versions = ">=3.8" files = [ - {file = "jupyter_core-5.5.1-py3-none-any.whl", hash = "sha256:220dfb00c45f0d780ce132bb7976b58263f81a3ada6e90a9b6823785a424f739"}, - {file = "jupyter_core-5.5.1.tar.gz", hash = "sha256:1553311a97ccd12936037f36b9ab4d6ae8ceea6ad2d5c90d94a909e752178e40"}, + {file = "jupyter_core-5.6.1-py3-none-any.whl", hash = "sha256:3d16aec2e1ec84b69f7794e49c32830c1d950ad149526aec954c100047c5f3a7"}, + {file = "jupyter_core-5.6.1.tar.gz", hash = "sha256:5139be639404f7f80f3db6f687f47b8a8ec97286b4fa063c984024720e7224dc"}, ] [package.dependencies] @@ -1812,17 +1767,6 @@ files = [ {file = "msgpack-1.0.7.tar.gz", hash = "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87"}, ] -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.5" -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - [[package]] name = "nest-asyncio" version = "1.5.8" @@ -2031,17 +1975,6 @@ files = [ qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] testing = ["docopt", "pytest (<6.0.0)"] -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - [[package]] name = "pexpect" version = "4.9.0" @@ -2321,13 +2254,13 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pytest" -version = "7.4.3" +version = "7.4.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"}, - {file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"}, + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, ] [package.dependencies] @@ -2598,28 +2531,28 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruff" -version = "0.0.265" -description = "An extremely fast Python linter, written in Rust." +version = "0.1.8" +description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.0.265-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:30ddfe22de6ce4eb1260408f4480bbbce998f954dbf470228a21a9b2c45955e4"}, - {file = "ruff-0.0.265-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:a11bd0889e88d3342e7bc514554bb4461bf6cc30ec115821c2425cfaac0b1b6a"}, - {file = "ruff-0.0.265-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a9b38bdb40a998cbc677db55b6225a6c4fadcf8819eb30695e1b8470942426b"}, - {file = "ruff-0.0.265-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a8b44a245b60512403a6a03a5b5212da274d33862225c5eed3bcf12037eb19bb"}, - {file = "ruff-0.0.265-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b279fa55ea175ef953208a6d8bfbcdcffac1c39b38cdb8c2bfafe9222add70bb"}, - {file = "ruff-0.0.265-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5028950f7af9b119d43d91b215d5044976e43b96a0d1458d193ef0dd3c587bf8"}, - {file = "ruff-0.0.265-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4057eb539a1d88eb84e9f6a36e0a999e0f261ed850ae5d5817e68968e7b89ed9"}, - {file = "ruff-0.0.265-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d586e69ab5cbf521a1910b733412a5735936f6a610d805b89d35b6647e2a66aa"}, - {file = "ruff-0.0.265-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa17b13cd3f29fc57d06bf34c31f21d043735cc9a681203d634549b0e41047d1"}, - {file = "ruff-0.0.265-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:9ac13b11d9ad3001de9d637974ec5402a67cefdf9fffc3929ab44c2fcbb850a1"}, - {file = "ruff-0.0.265-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:62a9578b48cfd292c64ea3d28681dc16b1aa7445b7a7709a2884510fc0822118"}, - {file = "ruff-0.0.265-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d0f9967f84da42d28e3d9d9354cc1575f96ed69e6e40a7d4b780a7a0418d9409"}, - {file = "ruff-0.0.265-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1d5a8de2fbaf91ea5699451a06f4074e7a312accfa774ad9327cde3e4fda2081"}, - {file = "ruff-0.0.265-py3-none-win32.whl", hash = "sha256:9e9db5ccb810742d621f93272e3cc23b5f277d8d00c4a79668835d26ccbe48dd"}, - {file = "ruff-0.0.265-py3-none-win_amd64.whl", hash = "sha256:f54facf286103006171a00ce20388d88ed1d6732db3b49c11feb9bf3d46f90e9"}, - {file = "ruff-0.0.265-py3-none-win_arm64.whl", hash = "sha256:c78470656e33d32ddc54e8482b1b0fc6de58f1195586731e5ff1405d74421499"}, - {file = "ruff-0.0.265.tar.gz", hash = "sha256:53c17f0dab19ddc22b254b087d1381b601b155acfa8feed514f0d6a413d0ab3a"}, + {file = "ruff-0.1.8-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:7de792582f6e490ae6aef36a58d85df9f7a0cfd1b0d4fe6b4fb51803a3ac96fa"}, + {file = "ruff-0.1.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c8e3255afd186c142eef4ec400d7826134f028a85da2146102a1172ecc7c3696"}, + {file = "ruff-0.1.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff78a7583020da124dd0deb835ece1d87bb91762d40c514ee9b67a087940528b"}, + {file = "ruff-0.1.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd8ee69b02e7bdefe1e5da2d5b6eaaddcf4f90859f00281b2333c0e3a0cc9cd6"}, + {file = "ruff-0.1.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a05b0ddd7ea25495e4115a43125e8a7ebed0aa043c3d432de7e7d6e8e8cd6448"}, + {file = "ruff-0.1.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e6f08ca730f4dc1b76b473bdf30b1b37d42da379202a059eae54ec7fc1fbcfed"}, + {file = "ruff-0.1.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f35960b02df6b827c1b903091bb14f4b003f6cf102705efc4ce78132a0aa5af3"}, + {file = "ruff-0.1.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d076717c67b34c162da7c1a5bda16ffc205e0e0072c03745275e7eab888719f"}, + {file = "ruff-0.1.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6a21ab023124eafb7cef6d038f835cb1155cd5ea798edd8d9eb2f8b84be07d9"}, + {file = "ruff-0.1.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ce697c463458555027dfb194cb96d26608abab920fa85213deb5edf26e026664"}, + {file = "ruff-0.1.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:db6cedd9ffed55548ab313ad718bc34582d394e27a7875b4b952c2d29c001b26"}, + {file = "ruff-0.1.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:05ffe9dbd278965271252704eddb97b4384bf58b971054d517decfbf8c523f05"}, + {file = "ruff-0.1.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5daaeaf00ae3c1efec9742ff294b06c3a2a9db8d3db51ee4851c12ad385cda30"}, + {file = "ruff-0.1.8-py3-none-win32.whl", hash = "sha256:e49fbdfe257fa41e5c9e13c79b9e79a23a79bd0e40b9314bc53840f520c2c0b3"}, + {file = "ruff-0.1.8-py3-none-win_amd64.whl", hash = "sha256:f41f692f1691ad87f51708b823af4bb2c5c87c9248ddd3191c8f088e66ce590a"}, + {file = "ruff-0.1.8-py3-none-win_arm64.whl", hash = "sha256:aa8ee4f8440023b0a6c3707f76cadce8657553655dcbb5fc9b2f9bb9bee389f6"}, + {file = "ruff-0.1.8.tar.gz", hash = "sha256:f7ee467677467526cfe135eab86a40a0e8db43117936ac4f9b469ce9cdb3fb62"}, ] [[package]] @@ -3395,4 +3328,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<=3.11" -content-hash = "cff829acb8a0fe416685555b22f113d3d1ecd147826e8833490e9de58fcb0908" +content-hash = "75b93513f61a13a42dfc7c658251a31d00aac15c4cb8b426c4d3104f4e27e1e6" diff --git a/pyproject.toml b/pyproject.toml index ead5b6d..f77a292 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,8 +33,7 @@ wget = "^3.2" [tool.poetry.group.dev.dependencies] pre-commit = ">=3.3.1" pytest = ">=7.3.1" -black = ">=23.3.0" -ruff = "0.0.265" +ruff = "0.1.8" ipykernel = ">=6.25.1" [tool.poetry.group.docs.dependencies] @@ -53,8 +52,18 @@ exclude = [ ".venv", "venv", ] +show-fixes = true line-length = 88 +[tool.ruff.lint] +select = [ + "E", # pycodestyle + "F", # Pyflakes + "SIM", # flake8-simplify + "I", # isort + # "D", # pydocstyle - consider in the future +] + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" From ebbd3b7794dc1f5f178885f6a3804c61418ce377 Mon Sep 17 00:00:00 2001 From: Artur Toshev Date: Tue, 2 Jan 2024 03:32:17 +0000 Subject: [PATCH 07/16] set up pytest --- .gitignore | 1 + pyproject.toml | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/.gitignore b/.gitignore index 1d6f09a..c28dee5 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ venv*/ rollouts profile dist +.coverage # Sphinx documentation docs/_build/ diff --git a/pyproject.toml b/pyproject.toml index f77a292..8addf58 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,8 +31,10 @@ torch = {version = "2.1.0+cpu", source = "torchcpu"} wget = "^3.2" [tool.poetry.group.dev.dependencies] +# mypy = ">=1.8.0" - consider in the future pre-commit = ">=3.3.1" pytest = ">=7.3.1" +pytest-cov = ">=4.1.0" ruff = "0.1.8" ipykernel = ">=6.25.1" @@ -64,6 +66,13 @@ select = [ # "D", # pydocstyle - consider in the future ] +[tool.pytest.ini_options] +testpaths = ["./tests"] +addopts = "--cov=lagrangebench" +filterwarnings = [ + "ignore::DeprecationWarning:^(?!.*lagrangebench).*" +] + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" From ab76f0a23e7cdb4bdbfc57a71762421f954f0348 Mon Sep 17 00:00:00 2001 From: Artur Toshev Date: Tue, 2 Jan 2024 03:33:37 +0000 Subject: [PATCH 08/16] fix some deprecation warnings --- lagrangebench/case_setup/case.py | 10 ++++------ lagrangebench/train/strats.py | 2 +- lagrangebench/utils.py | 2 +- tests/neighbors_test.py | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/lagrangebench/case_setup/case.py b/lagrangebench/case_setup/case.py index 764fba7..0925d2d 100644 --- a/lagrangebench/case_setup/case.py +++ b/lagrangebench/case_setup/case.py @@ -3,7 +3,7 @@ from typing import Callable, Dict, Optional, Tuple, Union import jax.numpy as jnp -from jax import jit, lax, random, vmap +from jax import Array, jit, lax, vmap from jax_md import space from jax_md.dataclasses import dataclass, static_field from jax_md.partition import NeighborList, NeighborListFormat @@ -15,16 +15,14 @@ from .features import FeatureDict, TargetDict, physical_feature_builder from .partition import neighbor_list -TrainCaseOut = Tuple[random.KeyArray, FeatureDict, TargetDict, NeighborList] +TrainCaseOut = Tuple[Array, FeatureDict, TargetDict, NeighborList] EvalCaseOut = Tuple[FeatureDict, NeighborList] SampleIn = Tuple[jnp.ndarray, jnp.ndarray] -AllocateFn = Callable[[random.KeyArray, SampleIn, float, int], TrainCaseOut] +AllocateFn = Callable[[Array, SampleIn, float, int], TrainCaseOut] AllocateEvalFn = Callable[[SampleIn], EvalCaseOut] -PreprocessFn = Callable[ - [random.KeyArray, SampleIn, float, NeighborList, int], TrainCaseOut -] +PreprocessFn = Callable[[Array, SampleIn, float, NeighborList, int], TrainCaseOut] PreprocessEvalFn = Callable[[SampleIn, NeighborList], EvalCaseOut] IntegrateFn = Callable[[jnp.ndarray, jnp.ndarray], jnp.ndarray] diff --git a/lagrangebench/train/strats.py b/lagrangebench/train/strats.py index 33088a8..da47056 100644 --- a/lagrangebench/train/strats.py +++ b/lagrangebench/train/strats.py @@ -10,7 +10,7 @@ def add_gns_noise( - key: jax.random.KeyArray, + key: jax.Array, pos_input: jnp.ndarray, particle_type: jnp.ndarray, input_seq_length: int, diff --git a/lagrangebench/utils.py b/lagrangebench/utils.py index 4ebf2c5..d31657d 100644 --- a/lagrangebench/utils.py +++ b/lagrangebench/utils.py @@ -174,7 +174,7 @@ def write_vtk(data_dict, path): data_pv.save(path) -def set_seed(seed: int) -> Tuple[jax.random.KeyArray, Callable, torch.Generator]: +def set_seed(seed: int) -> Tuple[jax.Array, Callable, torch.Generator]: """Set seeds for jax, random and torch.""" # first PRNG key key = jax.random.PRNGKey(seed) diff --git a/tests/neighbors_test.py b/tests/neighbors_test.py index 5f872f8..4579145 100644 --- a/tests/neighbors_test.py +++ b/tests/neighbors_test.py @@ -1,7 +1,7 @@ import unittest import numpy as np -from jax.config import config +from jax import config config.update("jax_enable_x64", True) import jax.numpy as jnp From eb0deea86974356a1121adc5b6999d251bbef12b Mon Sep 17 00:00:00 2001 From: Artur Toshev Date: Tue, 2 Jan 2024 03:52:49 +0000 Subject: [PATCH 09/16] add pytest-cov to poetry.lock and update both requirements.txt to pyproject.toml versions --- docs/requirements.txt | 4 +- poetry.lock | 87 ++++++++++++++++++++++++++++++++++++++++++- requirements_cuda.txt | 4 +- 3 files changed, 90 insertions(+), 5 deletions(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 08a80fe..a19b4eb 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -2,7 +2,7 @@ cloudpickle dm_haiku>=0.0.10 -e3nn_jax>=0.20.0 +e3nn_jax==0.20.3 h5py jax[cpu]==0.4.20 jax_md>=0.2.8 @@ -15,6 +15,6 @@ pyvista PyYAML sphinx==7.2.6 sphinx-rtd-theme==1.3.0 -torch>=2.1.0+cpu +torch==2.1.0+cpu wandb wget diff --git a/poetry.lock b/poetry.lock index 2385764..fdc1a02 100644 --- a/poetry.lock +++ b/poetry.lock @@ -446,6 +446,73 @@ mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.6.1)", "types-Pill test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] test-no-images = ["pytest", "pytest-cov", "pytest-xdist", "wurlitzer"] +[[package]] +name = "coverage" +version = "7.4.0" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36b0ea8ab20d6a7564e89cb6135920bc9188fb5f1f7152e94e8300b7b189441a"}, + {file = "coverage-7.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0676cd0ba581e514b7f726495ea75aba3eb20899d824636c6f59b0ed2f88c471"}, + {file = "coverage-7.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0ca5c71a5a1765a0f8f88022c52b6b8be740e512980362f7fdbb03725a0d6b9"}, + {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7c97726520f784239f6c62506bc70e48d01ae71e9da128259d61ca5e9788516"}, + {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:815ac2d0f3398a14286dc2cea223a6f338109f9ecf39a71160cd1628786bc6f5"}, + {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:80b5ee39b7f0131ebec7968baa9b2309eddb35b8403d1869e08f024efd883566"}, + {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5b2ccb7548a0b65974860a78c9ffe1173cfb5877460e5a229238d985565574ae"}, + {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:995ea5c48c4ebfd898eacb098164b3cc826ba273b3049e4a889658548e321b43"}, + {file = "coverage-7.4.0-cp310-cp310-win32.whl", hash = "sha256:79287fd95585ed36e83182794a57a46aeae0b64ca53929d1176db56aacc83451"}, + {file = "coverage-7.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:5b14b4f8760006bfdb6e08667af7bc2d8d9bfdb648351915315ea17645347137"}, + {file = "coverage-7.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04387a4a6ecb330c1878907ce0dc04078ea72a869263e53c72a1ba5bbdf380ca"}, + {file = "coverage-7.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea81d8f9691bb53f4fb4db603203029643caffc82bf998ab5b59ca05560f4c06"}, + {file = "coverage-7.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74775198b702868ec2d058cb92720a3c5a9177296f75bd97317c787daf711505"}, + {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76f03940f9973bfaee8cfba70ac991825611b9aac047e5c80d499a44079ec0bc"}, + {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:485e9f897cf4856a65a57c7f6ea3dc0d4e6c076c87311d4bc003f82cfe199d25"}, + {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6ae8c9d301207e6856865867d762a4b6fd379c714fcc0607a84b92ee63feff70"}, + {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bf477c355274a72435ceb140dc42de0dc1e1e0bf6e97195be30487d8eaaf1a09"}, + {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:83c2dda2666fe32332f8e87481eed056c8b4d163fe18ecc690b02802d36a4d26"}, + {file = "coverage-7.4.0-cp311-cp311-win32.whl", hash = "sha256:697d1317e5290a313ef0d369650cfee1a114abb6021fa239ca12b4849ebbd614"}, + {file = "coverage-7.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:26776ff6c711d9d835557ee453082025d871e30b3fd6c27fcef14733f67f0590"}, + {file = "coverage-7.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:13eaf476ec3e883fe3e5fe3707caeb88268a06284484a3daf8250259ef1ba143"}, + {file = "coverage-7.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846f52f46e212affb5bcf131c952fb4075b55aae6b61adc9856222df89cbe3e2"}, + {file = "coverage-7.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26f66da8695719ccf90e794ed567a1549bb2644a706b41e9f6eae6816b398c4a"}, + {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:164fdcc3246c69a6526a59b744b62e303039a81e42cfbbdc171c91a8cc2f9446"}, + {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:316543f71025a6565677d84bc4df2114e9b6a615aa39fb165d697dba06a54af9"}, + {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bb1de682da0b824411e00a0d4da5a784ec6496b6850fdf8c865c1d68c0e318dd"}, + {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0e8d06778e8fbffccfe96331a3946237f87b1e1d359d7fbe8b06b96c95a5407a"}, + {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a56de34db7b7ff77056a37aedded01b2b98b508227d2d0979d373a9b5d353daa"}, + {file = "coverage-7.4.0-cp312-cp312-win32.whl", hash = "sha256:51456e6fa099a8d9d91497202d9563a320513fcf59f33991b0661a4a6f2ad450"}, + {file = "coverage-7.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:cd3c1e4cb2ff0083758f09be0f77402e1bdf704adb7f89108007300a6da587d0"}, + {file = "coverage-7.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e9d1bf53c4c8de58d22e0e956a79a5b37f754ed1ffdbf1a260d9dcfa2d8a325e"}, + {file = "coverage-7.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:109f5985182b6b81fe33323ab4707011875198c41964f014579cf82cebf2bb85"}, + {file = "coverage-7.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc9d4bc55de8003663ec94c2f215d12d42ceea128da8f0f4036235a119c88ac"}, + {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc6d65b21c219ec2072c1293c505cf36e4e913a3f936d80028993dd73c7906b1"}, + {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a10a4920def78bbfff4eff8a05c51be03e42f1c3735be42d851f199144897ba"}, + {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b8e99f06160602bc64da35158bb76c73522a4010f0649be44a4e167ff8555952"}, + {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7d360587e64d006402b7116623cebf9d48893329ef035278969fa3bbf75b697e"}, + {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:29f3abe810930311c0b5d1a7140f6395369c3db1be68345638c33eec07535105"}, + {file = "coverage-7.4.0-cp38-cp38-win32.whl", hash = "sha256:5040148f4ec43644702e7b16ca864c5314ccb8ee0751ef617d49aa0e2d6bf4f2"}, + {file = "coverage-7.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:9864463c1c2f9cb3b5db2cf1ff475eed2f0b4285c2aaf4d357b69959941aa555"}, + {file = "coverage-7.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:936d38794044b26c99d3dd004d8af0035ac535b92090f7f2bb5aa9c8e2f5cd42"}, + {file = "coverage-7.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:799c8f873794a08cdf216aa5d0531c6a3747793b70c53f70e98259720a6fe2d7"}, + {file = "coverage-7.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7defbb9737274023e2d7af02cac77043c86ce88a907c58f42b580a97d5bcca9"}, + {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1526d265743fb49363974b7aa8d5899ff64ee07df47dd8d3e37dcc0818f09ed"}, + {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf635a52fc1ea401baf88843ae8708591aa4adff875e5c23220de43b1ccf575c"}, + {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:756ded44f47f330666843b5781be126ab57bb57c22adbb07d83f6b519783b870"}, + {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0eb3c2f32dabe3a4aaf6441dde94f35687224dfd7eb2a7f47f3fd9428e421058"}, + {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bfd5db349d15c08311702611f3dccbef4b4e2ec148fcc636cf8739519b4a5c0f"}, + {file = "coverage-7.4.0-cp39-cp39-win32.whl", hash = "sha256:53d7d9158ee03956e0eadac38dfa1ec8068431ef8058fe6447043db1fb40d932"}, + {file = "coverage-7.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:cfd2a8b6b0d8e66e944d47cdec2f47c48fef2ba2f2dff5a9a75757f64172857e"}, + {file = "coverage-7.4.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:c530833afc4707fe48524a44844493f36d8727f04dcce91fb978c414a8556cc6"}, + {file = "coverage-7.4.0.tar.gz", hash = "sha256:707c0f58cb1712b8809ece32b68996ee1e609f71bd14615bd8f87a1293cb610e"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + [[package]] name = "cycler" version = "0.12.1" @@ -2274,6 +2341,24 @@ tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + [[package]] name = "python-dateutil" version = "2.8.2" @@ -3328,4 +3413,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<=3.11" -content-hash = "75b93513f61a13a42dfc7c658251a31d00aac15c4cb8b426c4d3104f4e27e1e6" +content-hash = "5fc2e88ec569a667ab5076bf43acf88c3bf3d7d359756359b31a9ccdd25148d7" diff --git a/requirements_cuda.txt b/requirements_cuda.txt index 40fbba4..0bc59df 100644 --- a/requirements_cuda.txt +++ b/requirements_cuda.txt @@ -4,7 +4,7 @@ cloudpickle dm_haiku>=0.0.10 -e3nn_jax>=0.20.0 +e3nn_jax==0.20.3 h5py jax[cuda12_pip]==0.4.20 jax_md>=0.2.8 @@ -15,6 +15,6 @@ optax>=0.1.7 ott-jax>=0.4.2 pyvista PyYAML -torch>=2.1.0+cpu +torch==2.1.0+cpu wandb wget From dd6ac20c15328b3113c02860b5a73b381a7b7997 Mon Sep 17 00:00:00 2001 From: Artur Toshev Date: Tue, 2 Jan 2024 04:49:54 +0000 Subject: [PATCH 10/16] add metadata to pyproject.toml --- pyproject.toml | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8addf58..e2d9371 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,11 +3,40 @@ name = "lagrangebench" version = "0.0.2" description = "LagrangeBench: A Lagrangian Fluid Mechanics Benchmarking Suite" authors = [ + "Artur Toshev, Gianluca Galletti " +] +maintainers = [ "Artur Toshev ", "Gianluca Galletti ", ] license = "MIT" readme = "README.md" +homepage = "https://lagrangebench.readthedocs.io/" +documentation = "https://lagrangebench.readthedocs.io/" +repository = "https://github.com/tumaer/lagrangebench" +keywords = [ + "smoothed-particle-hydrodynamics", + "benchmark-suite", + "lagrangian-dynamics", + "graph-neural-networks", + "lagrangian-particles", +] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Operating System :: MacOS", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Scientific/Engineering :: Physics", + "Topic :: Scientific/Engineering :: Hydrology", + "Topic :: Software Development :: Libraries :: Python Modules", + "Typing :: Typed", +] [tool.poetry.dependencies] python = ">=3.9,<=3.11" @@ -53,6 +82,8 @@ exclude = [ ".git", ".venv", "venv", + "docs/_build", + "dist" ] show-fixes = true line-length = 88 @@ -67,9 +98,10 @@ select = [ ] [tool.pytest.ini_options] -testpaths = ["./tests"] +testpaths = "tests/" addopts = "--cov=lagrangebench" filterwarnings = [ + # ignore all deprecation warnings except from lagrangebench "ignore::DeprecationWarning:^(?!.*lagrangebench).*" ] From 6e7143d13abe50ae6db0a06483a585e8647ba709 Mon Sep 17 00:00:00 2001 From: Artur Toshev Date: Tue, 2 Jan 2024 06:16:20 +0000 Subject: [PATCH 11/16] add github workflows for testing --- .github/workflows/tests.yml | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..b893e11 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,36 @@ +name: Tests + +on: + push: + branches: [ "unit_tests" ] + pull_request: + branches: [ "main" ] + +permissions: + contents: read + +jobs: + tests: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.10"] + + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install Poetry + run: | + python -m pip install --upgrade pip + pip install poetry + poetry config virtualenvs.in-project true + - name: Install dependencies + run: | + poetry install + - name: Run pytest + run: | + .venv/bin/pytest From fa46be1915b94c74978a7174016d824c93fa6720 Mon Sep 17 00:00:00 2001 From: Artur Toshev Date: Tue, 2 Jan 2024 21:37:12 +0000 Subject: [PATCH 12/16] add ruff workflow --- .github/workflows/ruff.yml | 8 ++++++++ .github/workflows/tests.yml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/ruff.yml diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml new file mode 100644 index 0000000..e8133f2 --- /dev/null +++ b/.github/workflows/ruff.yml @@ -0,0 +1,8 @@ +name: Ruff +on: [push, pull_request] +jobs: + ruff: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: chartboost/ruff-action@v1 \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b893e11..54db343 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,7 +2,7 @@ name: Tests on: push: - branches: [ "unit_tests" ] + branches: [ "main" ] pull_request: branches: [ "main" ] From 14df74404ca20c00f592ca8e1d3a8b3d59335b79 Mon Sep 17 00:00:00 2001 From: Artur Toshev Date: Wed, 3 Jan 2024 01:32:31 +0100 Subject: [PATCH 13/16] run ruff linter --- .github/workflows/ruff.yml | 2 +- experiments/run.py | 2 +- lagrangebench/train/trainer.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index e8133f2..563b87d 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -5,4 +5,4 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: chartboost/ruff-action@v1 \ No newline at end of file + - uses: chartboost/ruff-action@v1 diff --git a/experiments/run.py b/experiments/run.py index 000fade..33494ea 100644 --- a/experiments/run.py +++ b/experiments/run.py @@ -8,9 +8,9 @@ import jax.numpy as jnp import jmp import numpy as np +import wandb import yaml -import wandb from experiments.utils import setup_data, setup_model from lagrangebench import Trainer, infer from lagrangebench.case_setup import case_builder diff --git a/lagrangebench/train/trainer.py b/lagrangebench/train/trainer.py index 9c57bb0..322b6c5 100644 --- a/lagrangebench/train/trainer.py +++ b/lagrangebench/train/trainer.py @@ -11,6 +11,7 @@ import optax from jax import vmap from torch.utils.data import DataLoader +from wandb.wandb_run import Run from lagrangebench.data import H5Dataset from lagrangebench.data.utils import numpy_collate @@ -27,7 +28,6 @@ save_haiku, set_seed, ) -from wandb.wandb_run import Run from .strats import push_forward_build, push_forward_sample_steps From 7b0b3b8533438613d45bf22e7c8821e04f78bca4 Mon Sep 17 00:00:00 2001 From: Artur Toshev Date: Wed, 3 Jan 2024 03:04:43 +0100 Subject: [PATCH 14/16] add pushlishing workflow --- .github/workflows/publish.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..790c529 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,33 @@ +name: Publish to PyPI + +on: + release: + types: [created] + +jobs: + build-n-publish: + name: Build and publish to PyPI + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' + - name: Install Poetry + run: | + python -m pip install --upgrade pip + pip install poetry + poetry config virtualenvs.in-project true + - name: Install dependencies + run: | + poetry install + - name: Build and publish + env: + POETRY_PYPI_TOKEN_PYPI: ${{ secrets.POETRY_PYPI_TOKEN_PYPI }} + run: | + poetry version $(git describe --tags --abbrev=0) + poetry add $(cat requirements.txt) + poetry build + poetry publish From 3bd9445f403e99d290bc641fa803c02b605a930a Mon Sep 17 00:00:00 2001 From: Artur Toshev Date: Fri, 5 Jan 2024 15:36:52 +0100 Subject: [PATCH 15/16] add codecov to tests workflow --- .github/workflows/tests.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 54db343..c027ae9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -34,3 +34,9 @@ jobs: - name: Run pytest run: | .venv/bin/pytest + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + with: + verbose: true From e10922c7365baa616da58980e3d8a9caeeed4619 Mon Sep 17 00:00:00 2001 From: Artur Toshev Date: Fri, 5 Jan 2024 15:49:24 +0100 Subject: [PATCH 16/16] add coverage report generation --- .github/workflows/tests.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c027ae9..c2b1a24 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -31,12 +31,13 @@ jobs: - name: Install dependencies run: | poetry install - - name: Run pytest + - name: Run pytest and generate coverage report run: | - .venv/bin/pytest - - name: Upload coverage reports to Codecov + .venv/bin/pytest --cov-report=xml + - name: Upload coverage report to Codecov uses: codecov/codecov-action@v3 - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: + token: ${{ secrets.CODECOV_TOKEN }} + file: ./coverage.xml + flags: unittests verbose: true