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 diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml new file mode 100644 index 0000000..563b87d --- /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 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..c2b1a24 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,43 @@ +name: Tests + +on: + push: + branches: [ "main" ] + 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 and generate coverage report + run: | + .venv/bin/pytest --cov-report=xml + - name: Upload coverage report to Codecov + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + file: ./coverage.xml + flags: unittests + verbose: true 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/.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/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/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/experiments/run.py b/experiments/run.py index a2b2eaa..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 @@ -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, @@ -150,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, @@ -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/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/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/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..864d6cd 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,59 @@ ) -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, state = 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, state + + +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 +93,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 +103,82 @@ 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, 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( + 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 +214,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 +240,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 +308,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 +327,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 +347,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 +360,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/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/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/train/trainer.py b/lagrangebench/train/trainer.py index b4dc3ea..322b6c5 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, @@ -186,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/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/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": "iVBORw0KGgoAAAANSUhEUgAAA+cAAAF2CAYAAAAMW+lzAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAACRHUlEQVR4nOzdeVxVdf7H8ReLbCq4oCyKQkluKOCGkGYLRUkLZYa2aGY5LW6hmTqKZTaW5WQuDdkyNlMumWVlRpnaYhKmgvuaC24XIYSrF9nuPb8//HUbEk1MvSzv5+NxH8z9ns8553PPzHju555zvh8nwzAMRERERERERMRhnB2dgIiIiIiIiEhtp+JcRERERERExMFUnIuIiIiIiIg4mIpzEREREREREQdTcS4iIiIiIiLiYCrORURERERERBxMxbmIiIiIiIiIg6k4FxEREREREXEwFeciIiIiIiIiDqbiXERERESkGnn44YcJDg6+6HXr1av3p3HBwcHcfvvtF7UPEbk4Ks5FREREREREHMzV0QmIiIiIiMiFe+utt7DZbI5OQ0QuMRXnIiIiIiLVSJ06dRydwiVRVFSEm5sbzs66mVcEdFu7SK303HPP4eTkxO7du3nwwQfx8fGhSZMmTJw4EcMwOHToEHfddRfe3t74+/szffr0cuvPmjWL9u3b4+XlRcOGDenSpQvz588vF3PkyBEeeeQR/Pz8cHd3p3379rz77rtX8mOKiIhUSydPnmTkyJEEBwfj7u5O06ZNufnmm9m4cSNw9jPnBw4cwMnJiVdffZW5c+dy9dVX4+7uTteuXfn555//dH+ZmZk0adKE66+/nlOnTpVbtmbNGrp164aHhwdXXXUV//nPf85af9++ffTt25dGjRrh5eVF9+7d+eKLL8rFfPvttzg5ObFw4UImTJhAs2bN8PLywmw225+DP3LkCAkJCdSrV48mTZowevRorFbrRRxBkepJV85FarHExETatm3LSy+9xBdffMGUKVNo1KgRb775JjfeeCMvv/wyH3zwAaNHj6Zr165cd911vPXWWwwfPpx7772XESNGUFRUxObNm0lPT+f+++8HIDs7m+7du+Pk5MTQoUNp0qQJX375JYMHD8ZsNjNy5EjHfnAREZEq7PHHH+ejjz5i6NChtGvXjl9//ZU1a9awY8cOOnXqdM715s+fz8mTJ/nb3/6Gk5MT06ZN45577mHfvn3nvNr+888/ExcXR5cuXfj000/x9PS0L9u7dy/33nsvgwcPZuDAgbz77rs8/PDDdO7cmfbt2wNnzvkxMTEUFhYyfPhwGjduzHvvvcedd97JRx99xN13311ufy+88AJubm6MHj2a4uJi3NzcALBarcTFxREVFcWrr77KN998w/Tp07n66qt54okn/uohFakeDBGpdSZNmmQAxpAhQ+xjZWVlRvPmzQ0nJyfjpZdeso+fOHHC8PT0NAYOHGgYhmHcddddRvv27c+7/cGDBxsBAQFGbm5uufF+/foZPj4+RmFh4aX7MCIiIjWMj4+P8dRTT51z+cCBA42WLVva3+/fv98AjMaNGxt5eXn28U8//dQAjM8//7zcunXr1jUMwzDWrFljeHt7G/Hx8UZRUVG5fbRs2dIAjO+//94+dvz4ccPd3d0YNWqUfWzkyJEGYPzwww/2sZMnTxohISFGcHCwYbVaDcMwjNWrVxuAcdVVV531PWDgwIEGYEyePLnceGRkpNG5c+dzHgeRmka3tYvUYo8++qj9P7u4uNClSxcMw2Dw4MH28QYNGtC6dWv27dtnf3/48OFz3iZnGAZLlizhjjvuwDAMcnNz7a+4uDgKCgrst+WJiIjI2Ro0aEB6ejpHjx6t1HqJiYk0bNjQ/r5nz54A9nP4/1q9ejVxcXHcdNNNfPzxx7i7u58V065dO/s2AJo0aVLuOwHA8uXL6datGz169LCP1atXjyFDhnDgwAG2b99ebpsDBw4sd3X+fz3++OPl3vfs2bPC3EVqKhXnIrVYixYtyr338fHBw8MDX1/fs8ZPnDgBwLPPPku9evXo1q0boaGhPPXUU/z444/22JycHPLz85k7dy5NmjQp9xo0aBAAx48fv8yfTEREpPqaNm0aW7duJSgoiG7duvHcc89dUJH6x/P6b4X6b+fw3xQVFREfH09kZCQffvih/dbyP9veb9v83+0dPHiQ1q1bnxXXtm1b+/L/FRISUuG+PDw8aNKkyXn3JVLTqTgXqcVcXFwuaAzOXBGHMyfbXbt2sXDhQnr06MGSJUvo0aMHkyZNArC3dnnwwQdZsWJFha9rr732Mn0iERGR6u++++5j3759zJo1i8DAQF555RXat2/Pl19+ed71/uwc/ht3d3fi4+NJT08nNTX1L2+vMs511fxc+xKpTTQhnIhUWt26dUlMTCQxMZGSkhLuueceXnzxRcaNG0eTJk2oX78+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": "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4+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": [ "
" ] diff --git a/poetry.lock b/poetry.lock index 90a8939..fdc1a02 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" @@ -491,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" @@ -1024,13 +1046,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 +1066,7 @@ matplotlib-inline = ">=0.1" nest-asyncio = "*" packaging = "*" psutil = "*" -pyzmq = ">=20" +pyzmq = ">=24" tornado = ">=6.1" traitlets = ">=5.4.0" @@ -1320,13 +1342,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 +1834,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 +2042,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 +2321,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] @@ -2341,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" @@ -2598,28 +2616,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 +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 = "cff829acb8a0fe416685555b22f113d3d1ecd147826e8833490e9de58fcb0908" +content-hash = "5fc2e88ec569a667ab5076bf43acf88c3bf3d7d359756359b31a9ccdd25148d7" diff --git a/pyproject.toml b/pyproject.toml index ead5b6d..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" @@ -31,10 +60,11 @@ 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" -black = ">=23.3.0" -ruff = "0.0.265" +pytest-cov = ">=4.1.0" +ruff = "0.1.8" ipykernel = ">=6.25.1" [tool.poetry.group.docs.dependencies] @@ -52,9 +82,29 @@ exclude = [ ".git", ".venv", "venv", + "docs/_build", + "dist" ] +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 +] + +[tool.pytest.ini_options] +testpaths = "tests/" +addopts = "--cov=lagrangebench" +filterwarnings = [ + # ignore all deprecation warnings except from lagrangebench + "ignore::DeprecationWarning:^(?!.*lagrangebench).*" +] + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" diff --git a/requirements_cuda.txt b/requirements_cuda.txt index e13e528..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 \ No newline at end of file +wget 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 0000000..a91e5d4 Binary files /dev/null and b/tests/3D_LJ_3_1214every1/test.h5 differ diff --git a/tests/3D_LJ_3_1214every1/train.h5 b/tests/3D_LJ_3_1214every1/train.h5 new file mode 100644 index 0000000..47f6e1e Binary files /dev/null and b/tests/3D_LJ_3_1214every1/train.h5 differ diff --git a/tests/3D_LJ_3_1214every1/valid.h5 b/tests/3D_LJ_3_1214every1/valid.h5 new file mode 100644 index 0000000..f84944d Binary files /dev/null and b/tests/3D_LJ_3_1214every1/valid.h5 differ diff --git a/tests/case_test.py b/tests/case_test.py new file mode 100644 index 0000000..373eb28 --- /dev/null +++ b/tests/case_test.py @@ -0,0 +1,209 @@ +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, # two past velocities + 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], # 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], + ] + ), + ).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() 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 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() 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()