From 25abe3ad2be7905c0755fe98d2ea87445c56e79f Mon Sep 17 00:00:00 2001 From: Shuheng Liu Date: Wed, 29 Sep 2021 15:07:02 -0400 Subject: [PATCH 1/4] fix(loss): fix a bug when concatenating gradients --- neurodiffeq/losses.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neurodiffeq/losses.py b/neurodiffeq/losses.py index 3607fb5..7899c87 100644 --- a/neurodiffeq/losses.py +++ b/neurodiffeq/losses.py @@ -16,13 +16,13 @@ def _infinity_norm(residual, funcs, coords): def _h1_norm(residual, funcs, coords): g = grad(residual, *coords) - rg = torch.cat([residual, *g]) + rg = torch.cat([residual, *g], dim=1) return (rg ** 2).mean() def _h1_semi_norm(residual, funcs, coords): g = grad(residual, *coords) - g = torch.cat(g) + g = torch.cat(g, dim=1) return (g ** 2).mean() From fa1602018d48982397434036d1ddd8c0ce166928 Mon Sep 17 00:00:00 2001 From: Shuheng Liu Date: Wed, 29 Sep 2021 15:07:45 -0400 Subject: [PATCH 2/4] test(loss): add test cases for loss functions --- tests/test_losses.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/test_losses.py diff --git a/tests/test_losses.py b/tests/test_losses.py new file mode 100644 index 0000000..80a9be9 --- /dev/null +++ b/tests/test_losses.py @@ -0,0 +1,38 @@ +import pytest +import torch +from neurodiffeq import diff +from neurodiffeq.losses import _losses as losses + +N = 100 + + +def pde_system(u, v, w, x, y): + return [ + diff(u, x, order=2) + diff(u, y, order=2), + diff(v, x, order=2) + diff(v, y, order=2), + diff(w, x, order=2) + diff(w, y, order=2), + ] + + +def get_rfx(n_input, n_output, n_equation): + coords = [torch.rand((N, 1), requires_grad=True) for _ in range(n_input) + ] + coords_tensor = torch.cat(coords, dim=1) + funcs = [torch.sigmoid(torch.sum(coords_tensor, dim=1, keepdim=True)) for _ in range(n_output)] + residual = [diff(funcs[0], coords[0]) + funcs[0] for _ in range(n_equation)] + residual = torch.cat(residual, dim=1) + return residual, funcs, coords + + +@pytest.mark.parametrize(argnames='n_input', argvalues=[1, 3]) +@pytest.mark.parametrize(argnames='n_output', argvalues=[1, 3]) +@pytest.mark.parametrize(argnames='n_equation', argvalues=[1, 3]) +@pytest.mark.parametrize( + argnames=('loss_name', 'loss_fn'), + argvalues=losses.items(), +) +def test_losses(n_input, n_output, n_equation, loss_name, loss_fn): + r, f, x = get_rfx(n_input, n_output, n_equation) + loss = loss_fn(r, f, x) + assert loss.shape == (), f"{loss_name} doesn't output scalar" + assert loss.requires_grad, f"{loss_name} doesn't require gradient" From 4186ed55d20ea6a241911da66a415ac97170ea99 Mon Sep 17 00:00:00 2001 From: Shuheng Liu Date: Wed, 29 Sep 2021 15:36:33 -0400 Subject: [PATCH 3/4] feat(solver): update documentation for `criterion` --- neurodiffeq/solvers.py | 75 ++++++++++++++++++++++++++++++++++++------ tests/test_losses.py | 3 +- 2 files changed, 66 insertions(+), 12 deletions(-) diff --git a/neurodiffeq/solvers.py b/neurodiffeq/solvers.py index 24a237a..df27035 100644 --- a/neurodiffeq/solvers.py +++ b/neurodiffeq/solvers.py @@ -57,8 +57,19 @@ class BaseSolver(ABC, PretrainedSolver): The optimizer to be used for training. :type optimizer: `torch.nn.optim.Optimizer`, optional :param criterion: - A function that maps a PDE residual vector (torch tensor with shape (-1, 1)) to a scalar loss. - :type criterion: callable, optional + The loss function used for training. + + - If a str, must be present in the keys of `neurodiffeq.losses._losses`. + - If a `torch.nn.modules.loss._Loss` instance, just pass the instance. + - If any other callable, it must map + A) a residual tensor (shape `(n_points, n_equations)`), + B) a function values tuple (length `n_funcs`, each element a tensor of shape `(n_points, 1)`), and + C) a coordinate values tuple (length `n_coords`, each element a tensor of shape `(n_coords, 1)` + to a tensor of empty shape (i.e. a scalar). The returned tensor must be connected to the computational graph, + so that backpropagation can be performed. + + :type criterion: + str or `torch.nn.moduesl.loss._Loss` or callable :param n_batches_train: Number of batches to train in every epoch, where batch-size equals ``train_generator.size``. Defaults to 1. @@ -687,8 +698,19 @@ class SolverSpherical(BaseSolver): Defaults to a ``torch.optim.Adam`` instance that trains on all parameters of ``nets``. :type optimizer: ``torch.nn.optim.Optimizer``, optional :param criterion: - Function that maps a PDE residual tensor (of shape (-1, 1)) to a scalar loss. - :type criterion: callable, optional + The loss function used for training. + + - If a str, must be present in the keys of `neurodiffeq.losses._losses`. + - If a `torch.nn.modules.loss._Loss` instance, just pass the instance. + - If any other callable, it must map + A) a residual tensor (shape `(n_points, n_equations)`), + B) a function values tuple (length `n_funcs`, each element a tensor of shape `(n_points, 1)`), and + C) a coordinate values tuple (length `n_coords`, each element a tensor of shape `(n_coords, 1)` + to a tensor of empty shape (i.e. a scalar). The returned tensor must be connected to the computational graph, + so that backpropagation can be performed. + + :type criterion: + str or `torch.nn.moduesl.loss._Loss` or callable :param n_batches_train: Number of batches to train in every epoch, where batch-size equals ``train_generator.size``. Defaults to 1. @@ -935,8 +957,19 @@ class Solver1D(BaseSolver): Defaults to a ``torch.optim.Adam`` instance that trains on all parameters of ``nets``. :type optimizer: ``torch.nn.optim.Optimizer``, optional :param criterion: - Function that maps a ODE residual tensor (of shape (-1, 1)) to a scalar loss. - :type criterion: callable, optional + The loss function used for training. + + - If a str, must be present in the keys of `neurodiffeq.losses._losses`. + - If a `torch.nn.modules.loss._Loss` instance, just pass the instance. + - If any other callable, it must map + A) a residual tensor (shape `(n_points, n_equations)`), + B) a function values tuple (length `n_funcs`, each element a tensor of shape `(n_points, 1)`), and + C) a coordinate values tuple (length `n_coords`, each element a tensor of shape `(n_coords, 1)` + to a tensor of empty shape (i.e. a scalar). The returned tensor must be connected to the computational graph, + so that backpropagation can be performed. + + :type criterion: + str or `torch.nn.moduesl.loss._Loss` or callable :param n_batches_train: Number of batches to train in every epoch, where batch-size equals ``train_generator.size``. Defaults to 1. @@ -1108,8 +1141,19 @@ class BundleSolver1D(BaseSolver): Defaults to a ``torch.optim.Adam`` instance that trains on all parameters of ``nets``. :type optimizer: ``torch.nn.optim.Optimizer``, optional :param criterion: - Function that maps a ODE residual tensor (of shape (-1, 1)) to a scalar loss. - :type criterion: callable, optional + The loss function used for training. + + - If a str, must be present in the keys of `neurodiffeq.losses._losses`. + - If a `torch.nn.modules.loss._Loss` instance, just pass the instance. + - If any other callable, it must map + A) a residual tensor (shape `(n_points, n_equations)`), + B) a function values tuple (length `n_funcs`, each element a tensor of shape `(n_points, 1)`), and + C) a coordinate values tuple (length `n_coords`, each element a tensor of shape `(n_coords, 1)` + to a tensor of empty shape (i.e. a scalar). The returned tensor must be connected to the computational graph, + so that backpropagation can be performed. + + :type criterion: + str or `torch.nn.moduesl.loss._Loss` or callable :param n_batches_train: Number of batches to train in every epoch, where batch-size equals ``train_generator.size``. Defaults to 1. @@ -1308,8 +1352,19 @@ class Solver2D(BaseSolver): Defaults to a ``torch.optim.Adam`` instance that trains on all parameters of ``nets``. :type optimizer: ``torch.nn.optim.Optimizer``, optional :param criterion: - Function that maps a PDE residual tensor (of shape (-1, 1)) to a scalar loss. - :type criterion: callable, optional + The loss function used for training. + + - If a str, must be present in the keys of `neurodiffeq.losses._losses`. + - If a `torch.nn.modules.loss._Loss` instance, just pass the instance. + - If any other callable, it must map + A) a residual tensor (shape `(n_points, n_equations)`), + B) a function values tuple (length `n_funcs`, each element a tensor of shape `(n_points, 1)`), and + C) a coordinate values tuple (length `n_coords`, each element a tensor of shape `(n_coords, 1)` + to a tensor of empty shape (i.e. a scalar). The returned tensor must be connected to the computational graph, + so that backpropagation can be performed. + + :type criterion: + str or `torch.nn.moduesl.loss._Loss` or callable :param n_batches_train: Number of batches to train in every epoch, where batch-size equals ``train_generator.size``. Defaults to 1. diff --git a/tests/test_losses.py b/tests/test_losses.py index 80a9be9..239ff82 100644 --- a/tests/test_losses.py +++ b/tests/test_losses.py @@ -15,8 +15,7 @@ def pde_system(u, v, w, x, y): def get_rfx(n_input, n_output, n_equation): - coords = [torch.rand((N, 1), requires_grad=True) for _ in range(n_input) - ] + coords = [torch.rand((N, 1), requires_grad=True) for _ in range(n_input)] coords_tensor = torch.cat(coords, dim=1) funcs = [torch.sigmoid(torch.sum(coords_tensor, dim=1, keepdim=True)) for _ in range(n_output)] residual = [diff(funcs[0], coords[0]) + funcs[0] for _ in range(n_equation)] From 9eae9ec590af1e3aab203912c7d2599e627a2748 Mon Sep 17 00:00:00 2001 From: Shuheng Liu Date: Wed, 29 Sep 2021 15:40:39 -0400 Subject: [PATCH 4/4] chore(pypi): prepare for release of v0.5.0 --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index a996431..3c29ba3 100644 --- a/setup.py +++ b/setup.py @@ -17,14 +17,14 @@ def func(m): setuptools.setup( name="neurodiffeq", - version="0.4.0", + version="0.5.0", author="neurodiffgym", author_email="shuheng_liu@g.harvard.edu", description="A light-weight & flexible library for solving differential equations using neural networks based on PyTorch. ", long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/NeuroDiffGym/neurodiffeq", - download_url="https://github.com/NeuroDiffGym/neurodiffeq/archive/v0.4.0.tar.gz", + download_url="https://github.com/NeuroDiffGym/neurodiffeq/archive/v0.5.0.tar.gz", keywords=[ "neural network", "deep learning",