From 0b7a307cf1c46813c5d830c9be5ceb2d4c412829 Mon Sep 17 00:00:00 2001 From: Anna Ivagnes <75523024+annaivagnes@users.noreply.github.com> Date: Wed, 15 Nov 2023 14:02:16 +0100 Subject: [PATCH] Inverse problem implementation (#177) * inverse problem implementation * add tutorial7 for inverse Poisson problem * fix doc in equation, equation_interface, system_equation --------- Co-authored-by: Dario Coscia --- docs/source/_rst/_tutorial.rst | 6 +- .../_rst/tutorials/tutorial7/tutorial.rst | 217 +++++++++++ .../tutorial7/tutorial_files/output_21_0.png | Bin 0 -> 10357 bytes .../tutorial7/tutorial_files/output_8_0.png | Bin 0 -> 114138 bytes pina/condition.py | 2 +- pina/equation/__init__.py | 2 +- pina/equation/equation.py | 20 +- pina/equation/equation_interface.py | 14 + pina/equation/system_equation.py | 27 +- pina/plotter.py | 1 + pina/problem/__init__.py | 2 + pina/problem/abstract_problem.py | 14 +- pina/problem/inverse_problem.py | 71 ++++ pina/problem/spatial_problem.py | 3 +- pina/problem/timedep_problem.py | 3 +- pina/solvers/pinn.py | 51 ++- tutorials/README.md | 9 +- .../tutorial7/data/pinn_solution_0.5_0.5 | Bin 0 -> 10895 bytes tutorials/tutorial7/data/pts_0.5_0.5 | Bin 0 -> 40881 bytes tutorials/tutorial7/tutorial.ipynb | 368 ++++++++++++++++++ tutorials/tutorial7/tutorial.py | 197 ++++++++++ 21 files changed, 967 insertions(+), 40 deletions(-) create mode 100644 docs/source/_rst/tutorials/tutorial7/tutorial.rst create mode 100644 docs/source/_rst/tutorials/tutorial7/tutorial_files/output_21_0.png create mode 100644 docs/source/_rst/tutorials/tutorial7/tutorial_files/output_8_0.png create mode 100644 pina/problem/inverse_problem.py create mode 100644 tutorials/tutorial7/data/pinn_solution_0.5_0.5 create mode 100644 tutorials/tutorial7/data/pts_0.5_0.5 create mode 100644 tutorials/tutorial7/tutorial.ipynb create mode 100644 tutorials/tutorial7/tutorial.py diff --git a/docs/source/_rst/_tutorial.rst b/docs/source/_rst/_tutorial.rst index f904f83..87e6d9f 100644 --- a/docs/source/_rst/_tutorial.rst +++ b/docs/source/_rst/_tutorial.rst @@ -1,7 +1,7 @@ PINA Tutorials ============== -In this folder we collect useful tutorials in order to understand the principles and the potential of **PINA**. +In this folder we collect useful tutorials in order to understand the principles and the potential of **PINA**. Getting started with PINA ------------------------- @@ -20,6 +20,7 @@ Physics Informed Neural Networks Two dimensional Poisson problem using Extra Features Learning Two dimensional Wave problem with hard constraint + Resolution of a 2D Poisson inverse problem Neural Operator Learning @@ -36,4 +37,5 @@ Supervised Learning :maxdepth: 3 :titlesonly: - Unstructured convolutional autoencoder via continuous convolution \ No newline at end of file + Unstructured convolutional autoencoder via continuous convolution + diff --git a/docs/source/_rst/tutorials/tutorial7/tutorial.rst b/docs/source/_rst/tutorials/tutorial7/tutorial.rst new file mode 100644 index 0000000..6750ffc --- /dev/null +++ b/docs/source/_rst/tutorials/tutorial7/tutorial.rst @@ -0,0 +1,217 @@ +Tutorial 7: Resolution of an inverse problem +============================================ + +Introduction to the inverse problem +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This tutorial shows how to solve an inverse Poisson problem with +Physics-Informed Neural Networks. The problem definition is that of a +Poisson problem with homogeneous boundary conditions and it reads: +:raw-latex:`\begin{equation} +\begin{cases} +\Delta u = e^{-2(x-\mu_1)^2-2(y-\mu_2)^2} \text{ in } \Omega\, ,\\ +u = 0 \text{ on }\partial \Omega,\\ +u(\mu_1, \mu_2) = \text{ data} +\end{cases} +\end{equation}` where :math:`\Omega` is a square domain +:math:`[-2, 2] \times [-2, 2]`, and +:math:`\partial \Omega=\Gamma_1 \cup \Gamma_2 \cup \Gamma_3 \cup \Gamma_4` +is the union of the boundaries of the domain. + +This kind of problem, namely the “inverse problem”, has two main goals: +- find the solution :math:`u` that satisfies the Poisson equation; - +find the unknown parameters (:math:`\mu_1`, :math:`\mu_2`) that better +fit some given data (third equation in the system above). + +In order to achieve both the goals we will need to define an +``InverseProblem`` in PINA. + +Let’s start with useful imports. + +.. code:: ipython3 + + import matplotlib.pyplot as plt + import torch + from pytorch_lightning.callbacks import Callback + from pina.problem import SpatialProblem, InverseProblem + from pina.operators import laplacian + from pina.model import FeedForward + from pina.equation import Equation, FixedValue + from pina import Condition, Trainer + from pina.solvers import PINN + from pina.geometry import CartesianDomain + +Then, we import the pre-saved data, for (:math:`\mu_1`, +:math:`\mu_2`)=(:math:`0.5`, :math:`0.5`). These two values are the +optimal parameters that we want to find through the neural network +training. In particular, we import the ``input_points``\ (the spatial +coordinates), and the ``output_points`` (the corresponding :math:`u` +values evaluated at the ``input_points``). + +.. code:: ipython3 + + data_output = torch.load('data/pinn_solution_0.5_0.5').detach() + data_input = torch.load('data/pts_0.5_0.5') + +Moreover, let’s plot also the data points and the reference solution: +this is the expected output of the neural network. + +.. code:: ipython3 + + points = data_input.extract(['x', 'y']).detach().numpy() + truth = data_output.detach().numpy() + + plt.scatter(points[:, 0], points[:, 1], c=truth, s=8) + plt.axis('equal') + plt.colorbar() + plt.show() + + + +.. image:: tutorial_files/output_8_0.png + + +Inverse problem definition in PINA +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Then, we initialize the Poisson problem, that is inherited from the +``SpatialProblem`` and from the ``InverseProblem`` classes. We here have +to define all the variables, and the domain where our unknown parameters +(:math:`\mu_1`, :math:`\mu_2`) belong. Notice that the laplace equation +takes as inputs also the unknown variables, that will be treated as +parameters that the neural network optimizes during the training +process. + +.. code:: ipython3 + + ### Define ranges of variables + x_min = -2 + x_max = 2 + y_min = -2 + y_max = 2 + + class Poisson(SpatialProblem, InverseProblem): + ''' + Problem definition for the Poisson equation. + ''' + output_variables = ['u'] + spatial_domain = CartesianDomain({'x': [x_min, x_max], 'y': [y_min, y_max]}) + # define the ranges for the parameters + unknown_parameter_domain = CartesianDomain({'mu1': [-1, 1], 'mu2': [-1, 1]}) + + def laplace_equation(input_, output_, params_): + ''' + Laplace equation with a force term. + ''' + force_term = torch.exp( + - 2*(input_.extract(['x']) - params_['mu1'])**2 + - 2*(input_.extract(['y']) - params_['mu2'])**2) + delta_u = laplacian(output_, input_, components=['u'], d=['x', 'y']) + + return delta_u - force_term + + # define the conditions for the loss (boundary conditions, equation, data) + conditions = { + 'gamma1': Condition(location=CartesianDomain({'x': [x_min, x_max], + 'y': y_max}), + equation=FixedValue(0.0, components=['u'])), + 'gamma2': Condition(location=CartesianDomain({'x': [x_min, x_max], 'y': y_min + }), + equation=FixedValue(0.0, components=['u'])), + 'gamma3': Condition(location=CartesianDomain({'x': x_max, 'y': [y_min, y_max] + }), + equation=FixedValue(0.0, components=['u'])), + 'gamma4': Condition(location=CartesianDomain({'x': x_min, 'y': [y_min, y_max] + }), + equation=FixedValue(0.0, components=['u'])), + 'D': Condition(location=CartesianDomain({'x': [x_min, x_max], 'y': [y_min, y_max] + }), + equation=Equation(laplace_equation)), + 'data': Condition(input_points=data_input.extract(['x', 'y']), output_points=data_output) + } + + problem = Poisson() + +Then, we define the model of the neural network we want to use. Here we +used a model which impose hard constrains on the boundary conditions, as +also done in the Wave tutorial! + +.. code:: ipython3 + + model = FeedForward( + layers=[20, 20, 20], + func=torch.nn.Softplus, + output_dimensions=len(problem.output_variables), + input_dimensions=len(problem.input_variables) + ) + +After that, we discretize the spatial domain. + +.. code:: ipython3 + + problem.discretise_domain(20, 'grid', locations=['D'], variables=['x', 'y']) + problem.discretise_domain(1000, 'random', locations=['gamma1', 'gamma2', + 'gamma3', 'gamma4'], variables=['x', 'y']) + +Here, we define a simple callback for the trainer. We use this callback +to save the parameters predicted by the neural network during the +training. The parameters are saved every 100 epochs as ``torch`` tensors +in a specified directory (``tmp_dir`` in our case). The goal is to read +the saved parameters after training and plot their trend across the +epochs. + +.. code:: ipython3 + + # temporary directory for saving logs of training + tmp_dir = "tmp_poisson_inverse" + + class SaveParameters(Callback): + ''' + Callback to save the parameters of the model every 100 epochs. + ''' + def on_train_epoch_end(self, trainer, __): + if trainer.current_epoch % 100 == 99: + torch.save(trainer.solver.problem.unknown_parameters, '{}/parameters_epoch{}'.format(tmp_dir, trainer.current_epoch)) + +Then, we define the ``PINN`` object and train the solver using the +``Trainer``. + +.. code:: ipython3 + + ### train the problem with PINN + max_epochs = 5000 + pinn = PINN(problem, model, optimizer_kwargs={'lr':0.005}) + # define the trainer for the solver + trainer = Trainer(solver=pinn, accelerator='cpu', max_epochs=max_epochs, + default_root_dir=tmp_dir, callbacks=[SaveParameters()]) + trainer.train() + +One can now see how the parameters vary during the training by reading +the saved solution and plotting them. The plot shows that the parameters +stabilize to their true value before reaching the epoch :math:`1000`! + +.. code:: ipython3 + + epochs_saved = range(99, max_epochs, 100) + parameters = torch.empty((int(max_epochs/100), 2)) + for i, epoch in enumerate(epochs_saved): + params_torch = torch.load('{}/parameters_epoch{}'.format(tmp_dir, epoch)) + for e, var in enumerate(pinn.problem.unknown_variables): + parameters[i, e] = params_torch[var].data + + # Plot parameters + plt.close() + plt.plot(epochs_saved, parameters[:, 0], label='mu1', marker='o') + plt.plot(epochs_saved, parameters[:, 1], label='mu2', marker='s') + plt.ylim(-1, 1) + plt.grid() + plt.legend() + plt.xlabel('Epoch') + plt.ylabel('Parameter value') + plt.show() + + + +.. image:: tutorial_files/output_21_0.png + + diff --git a/docs/source/_rst/tutorials/tutorial7/tutorial_files/output_21_0.png b/docs/source/_rst/tutorials/tutorial7/tutorial_files/output_21_0.png new file mode 100644 index 0000000000000000000000000000000000000000..39f313bf3d8579bd8ac15203455e28f96261e87c GIT binary patch literal 10357 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 literal 0 HcmV?d00001 diff --git a/docs/source/_rst/tutorials/tutorial7/tutorial_files/output_8_0.png b/docs/source/_rst/tutorials/tutorial7/tutorial_files/output_8_0.png new file mode 100644 index 0000000000000000000000000000000000000000..4f706c37397fbb021efeb03be1aed4b5a2efcec3 GIT binary patch literal 114138 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%0AjcW)!@cgp#0@37K-xx1JqI8+;uEWX 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 example_input_pts = LabelTensor( >>> torch.tensor([[0, 0, 0]]), ['x', 'y', 'z']) >>> example_output_pts = LabelTensor(torch.tensor([[1, 2]]), ['a', 'b']) - >>> + >>> >>> Condition( >>> input_points=example_input_pts, >>> output_points=example_output_pts) diff --git a/pina/equation/__init__.py b/pina/equation/__init__.py index 7eabcc2..653aa18 100644 --- a/pina/equation/__init__.py +++ b/pina/equation/__init__.py @@ -9,4 +9,4 @@ __all__ = [ from .equation import Equation from .equation_factory import FixedFlux, FixedGradient, Laplace, FixedValue -from .system_equation import SystemEquation \ No newline at end of file +from .system_equation import SystemEquation diff --git a/pina/equation/equation.py b/pina/equation/equation.py index 2934d37..1e34ebc 100644 --- a/pina/equation/equation.py +++ b/pina/equation/equation.py @@ -8,7 +8,7 @@ class Equation(EquationInterface): """ Equation class for specifing any equation in PINA. Each ``equation`` passed to a ``Condition`` object - must be an ``Equation`` or ``SystemEquation``. + must be an ``Equation`` or ``SystemEquation``. :param equation: A ``torch`` callable equation to evaluate the residual. @@ -20,14 +20,26 @@ class Equation(EquationInterface): f'{equation}') self.__equation = equation - def residual(self, input_, output_): + def residual(self, input_, output_, params_ = None): """ Residual computation of the equation. :param LabelTensor input_: Input points to evaluate the equation. - :param LabelTensor output_: Output vectors given my a model (e.g, + :param LabelTensor output_: Output vectors given by a model (e.g, a ``FeedForward`` model). + :param dict params_: Dictionary of parameters related to the inverse + problem (if any). + If the equation is not related to an ``InverseProblem``, the + parameters are initialized to ``None`` and the residual is + computed as ``equation(input_, output_)``. + Otherwise, the parameters are automatically initialized in the + ranges specified by the user. + :return: The residual evaluation of the specified equation. :rtype: LabelTensor """ - return self.__equation(input_, output_) + if params_ is None: + result = self.__equation(input_, output_) + else: + result = self.__equation(input_, output_, params_) + return result diff --git a/pina/equation/equation_interface.py b/pina/equation/equation_interface.py index b5e4a21..5e5ec90 100644 --- a/pina/equation/equation_interface.py +++ b/pina/equation/equation_interface.py @@ -11,3 +11,17 @@ class EquationInterface(metaclass=ABCMeta): the output variables, the condition(s), and the domain(s) where the conditions are applied. """ + + @abstractmethod + def residual(self, input_, output_, params_): + """ + Residual computation of the equation. + + :param LabelTensor input_: Input points to evaluate the equation. + :param LabelTensor output_: Output vectors given by my model (e.g., a ``FeedForward`` model). + :param dict params_: Dictionary of unknown parameters, eventually + related to an ``InverseProblem``. + :return: The residual evaluation of the specified equation. + :rtype: LabelTensor + """ + pass diff --git a/pina/equation/system_equation.py b/pina/equation/system_equation.py index 910005f..28861e6 100644 --- a/pina/equation/system_equation.py +++ b/pina/equation/system_equation.py @@ -11,14 +11,14 @@ class SystemEquation(Equation): System of Equation class for specifing any system of equations in PINA. Each ``equation`` passed to a ``Condition`` object - must be an ``Equation`` or ``SystemEquation``. - A ``SystemEquation`` is specified by a list of + must be an ``Equation`` or ``SystemEquation``. + A ``SystemEquation`` is specified by a list of equations. :param Callable equation: A ``torch`` callable equation to evaluate the residual :param str reduction: Specifies the reduction to apply to the output: - ``none`` | ``mean`` | ``sum`` | ``callable``. ``none``: no reduction + ``none`` | ``mean`` | ``sum`` | ``callable``. ``none``: no reduction will be applied, ``mean``: the sum of the output will be divided by the number of elements in the output, ``sum``: the output will be summed. ``callable`` a callable function to perform reduction, @@ -43,19 +43,28 @@ class SystemEquation(Equation): raise NotImplementedError( 'Only mean and sum reductions implemented.') - def residual(self, input_, output_): + def residual(self, input_, output_, params_=None): """ - Residual computation of the equation. + Residual computation for the equations of the system. - :param LabelTensor input_: Input points to evaluate the equation. - :param LabelTensor output_: Output vectors given my a model (e.g, + :param LabelTensor input_: Input points to evaluate the system of + equations. + :param LabelTensor output_: Output vectors given by a model (e.g, a ``FeedForward`` model). - :return: The residual evaluation of the specified equation, + :param dict params_: Dictionary of parameters related to the inverse + problem (if any). + If the equation is not related to an ``InverseProblem``, the + parameters are initialized to ``None`` and the residual is + computed as ``equation(input_, output_)``. + Otherwise, the parameters are automatically initialized in the + ranges specified by the user. + + :return: The residual evaluation of the specified system of equations, aggregated by the ``reduction`` defined in the ``__init__``. :rtype: LabelTensor """ residual = torch.hstack( - [equation.residual(input_, output_) for equation in self.equations]) + [equation.residual(input_, output_, params_) for equation in self.equations]) if self.reduction == 'none': return residual diff --git a/pina/plotter.py b/pina/plotter.py index 0710720..440389e 100644 --- a/pina/plotter.py +++ b/pina/plotter.py @@ -205,6 +205,7 @@ class Plotter: plt.savefig(filename) else: plt.show() + plt.close() def plot_loss(self, trainer, diff --git a/pina/problem/__init__.py b/pina/problem/__init__.py index 2307dff..680451c 100644 --- a/pina/problem/__init__.py +++ b/pina/problem/__init__.py @@ -3,9 +3,11 @@ __all__ = [ 'SpatialProblem', 'TimeDependentProblem', 'ParametricProblem', + 'InverseProblem', ] from .abstract_problem import AbstractProblem from .spatial_problem import SpatialProblem from .timedep_problem import TimeDependentProblem from .parametric_problem import ParametricProblem +from .inverse_problem import InverseProblem diff --git a/pina/problem/abstract_problem.py b/pina/problem/abstract_problem.py index 1bea870..311dbce 100644 --- a/pina/problem/abstract_problem.py +++ b/pina/problem/abstract_problem.py @@ -109,6 +109,14 @@ class AbstractProblem(metaclass=ABCMeta): samples = condition.input_points self.input_pts[condition_name] = samples self._have_sampled_points[condition_name] = True + if hasattr(self, 'unknown_parameter_domain'): + # initialize the unknown parameters of the inverse problem given + # the domain the user gives + self.unknown_parameters = {} + for i, var in enumerate(self.unknown_variables): + range_var = self.unknown_parameter_domain.range_[var] + tensor_var = torch.rand(1, requires_grad=True) * range_var[1] + range_var[0] + self.unknown_parameters[var] = torch.nn.Parameter(tensor_var) def discretise_domain(self, n, @@ -203,6 +211,7 @@ class AbstractProblem(metaclass=ABCMeta): self.input_variables): self._have_sampled_points[location] = True + def add_points(self, new_points): """ Adding points to the already sampled points. @@ -237,7 +246,7 @@ class AbstractProblem(metaclass=ABCMeta): @property def have_sampled_points(self): """ - Check if all points for + Check if all points for ``Location`` are sampled. """ return all(self._have_sampled_points.values()) @@ -245,7 +254,7 @@ class AbstractProblem(metaclass=ABCMeta): @property def not_sampled_points(self): """ - Check which points are + Check which points are not sampled. """ # variables which are not sampled @@ -257,3 +266,4 @@ class AbstractProblem(metaclass=ABCMeta): if not is_sample: not_sampled.append(condition_name) return not_sampled + diff --git a/pina/problem/inverse_problem.py b/pina/problem/inverse_problem.py new file mode 100644 index 0000000..b9efd6b --- /dev/null +++ b/pina/problem/inverse_problem.py @@ -0,0 +1,71 @@ +"""Module for the ParametricProblem class""" +from abc import abstractmethod + +from .abstract_problem import AbstractProblem + + +class InverseProblem(AbstractProblem): + """ + The class for the definition of inverse problems, i.e., problems + with unknown parameters that have to be learned during the training process + from given data. + + Here's an example of a spatial inverse ODE problem, i.e., a spatial + ODE problem with an unknown parameter `alpha` as coefficient of the + derivative term. + + :Example: + >>> from pina.problem import SpatialProblem, InverseProblem + >>> from pina.operators import grad + >>> from pina.equation import ParametricEquation, FixedValue + >>> from pina import Condition + >>> from pina.geometry import CartesianDomain + >>> import torch + >>> + >>> class InverseODE(SpatialProblem, InverseProblem): + >>> + >>> output_variables = ['u'] + >>> spatial_domain = CartesianDomain({'x': [0, 1]}) + >>> unknown_parameter_domain = CartesianDomain({'alpha': [1, 10]}) + >>> + >>> def ode_equation(input_, output_, params_): + >>> u_x = grad(output_, input_, components=['u'], d=['x']) + >>> u = output_.extract(['u']) + >>> return params_.extract(['alpha']) * u_x - u + >>> + >>> def solution_data(input_, output_): + >>> x = input_.extract(['x']) + >>> solution = torch.exp(x) + >>> return output_ - solution + >>> + >>> conditions = { + >>> 'x0': Condition(CartesianDomain({'x': 0}), FixedValue(1.0)), + >>> 'D': Condition(CartesianDomain({'x': [0, 1]}), ParametricEquation(ode_equation)), + >>> 'data': Condition(CartesianDomain({'x': [0, 1]}), Equation(solution_data)) + """ + + @abstractmethod + def unknown_parameter_domain(self): + """ + The parameters' domain of the problem. + """ + pass + + @property + def unknown_variables(self): + """ + The parameters of the problem. + """ + return self.unknown_parameter_domain.variables + + @property + def unknown_parameters(self): + """ + The parameters of the problem. + """ + return self.__unknown_parameters + + @unknown_parameters.setter + def unknown_parameters(self, value): + self.__unknown_parameters = value + diff --git a/pina/problem/spatial_problem.py b/pina/problem/spatial_problem.py index a0483e3..67a1507 100644 --- a/pina/problem/spatial_problem.py +++ b/pina/problem/spatial_problem.py @@ -14,7 +14,7 @@ class SpatialProblem(AbstractProblem): :Example: >>> from pina.problem import SpatialProblem >>> from pina.operators import grad - >>> from pina.equations import Equation, FixedValue + >>> from pina.equation import Equation, FixedValue >>> from pina import Condition >>> from pina.geometry import CartesianDomain >>> import torch @@ -33,7 +33,6 @@ class SpatialProblem(AbstractProblem): >>> conditions = { >>> 'x0': Condition(CartesianDomain({'x': 0, 'alpha':[1, 10]}), FixedValue(1.)), >>> 'D': Condition(CartesianDomain({'x': [0, 1], 'alpha':[1, 10]}), Equation(ode_equation))} - """ @abstractmethod diff --git a/pina/problem/timedep_problem.py b/pina/problem/timedep_problem.py index 2d0179e..ee34383 100644 --- a/pina/problem/timedep_problem.py +++ b/pina/problem/timedep_problem.py @@ -14,7 +14,7 @@ class TimeDependentProblem(AbstractProblem): :Example: >>> from pina.problem import SpatialProblem, TimeDependentProblem >>> from pina.operators import grad, laplacian - >>> from pina.equations import Equation, FixedValue + >>> from pina.equation import Equation, FixedValue >>> from pina import Condition >>> from pina.geometry import CartesianDomain >>> import torch @@ -43,7 +43,6 @@ class TimeDependentProblem(AbstractProblem): >>> 'gamma1': Condition(CartesianDomain({'x':0, 't':[0, 1]}), FixedValue(0.)), >>> 'gamma2': Condition(CartesianDomain({'x':3, 't':[0, 1]}), FixedValue(0.)), >>> 'D': Condition(CartesianDomain({'x': [0, 3], 't':[0, 1]}), Equation(wave_equation))} - """ @abstractmethod diff --git a/pina/solvers/pinn.py b/pina/solvers/pinn.py index a1581a4..37d968c 100644 --- a/pina/solvers/pinn.py +++ b/pina/solvers/pinn.py @@ -11,6 +11,7 @@ from .solver import SolverInterface from ..label_tensor import LabelTensor from ..utils import check_consistency from ..loss import LossInterface +from ..problem import InverseProblem from torch.nn.modules.loss import _Loss torch.pi = torch.acos(torch.zeros(1)).item() * 2 # which is 3.1415927410125732 @@ -18,14 +19,14 @@ torch.pi = torch.acos(torch.zeros(1)).item() * 2 # which is 3.1415927410125732 class PINN(SolverInterface): """ - PINN solver class. This class implements Physics Informed Neural + PINN solver class. This class implements Physics Informed Neural Network solvers, using a user specified ``model`` to solve a specific - ``problem``. + ``problem``. It can be used for solving both forward and inverse problems. .. seealso:: - **Original reference**: Karniadakis, G. E., Kevrekidis, I. G., Lu, L., - Perdikaris, P., Wang, S., & Yang, L. (2021). + **Original reference**: Karniadakis, G. E., Kevrekidis, I. G., Lu, L., + Perdikaris, P., Wang, S., & Yang, L. (2021). Physics-informed machine learning. Nature Reviews Physics, 3(6), 422-440. `_. """ @@ -45,7 +46,7 @@ class PINN(SolverInterface): }, ): ''' - :param AbstractProblem problem: The formualation of the problem. + :param AbstractProblem problem: The formulation of the problem. :param torch.nn.Module model: The neural network model to use. :param torch.nn.Module loss: The loss function used as minimizer, default :class:`torch.nn.MSELoss`. @@ -74,12 +75,18 @@ class PINN(SolverInterface): self._loss = loss self._neural_net = self.models[0] + # inverse problem handling + if isinstance(self.problem, InverseProblem): + self._params = self.problem.unknown_parameters + else: + self._params = None + def forward(self, x): """ Forward pass implementation for the PINN solver. - :param torch.Tensor x: Input tensor. + :param torch.Tensor x: Input tensor. :return: PINN solution. :rtype: torch.Tensor """ @@ -93,17 +100,30 @@ class PINN(SolverInterface): :return: The optimizers and the schedulers :rtype: tuple(list, list) """ + # if the problem is an InverseProblem, add the unknown parameters + # to the parameters that the optimizer needs to optimize + if isinstance(self.problem, InverseProblem): + self.optimizers[0].add_param_group( + {'params': [self._params[var] for var in self.problem.unknown_variables]} + ) return self.optimizers, [self.scheduler] - + + def _clamp_inverse_problem_params(self): + for v in self._params: + self._params[v].data.clamp_( + self.problem.unknown_parameter_domain.range_[v][0], + self.problem.unknown_parameter_domain.range_[v][1]) + def _loss_data(self, input, output): return self.loss(self.forward(input), output) - def _loss_phys(self, samples, equation): - residual = equation.residual(samples, self.forward(samples)) + try: + residual = equation.residual(samples, self.forward(samples)) + except TypeError: # this occurs when the function has three inputs, i.e. inverse problem + residual = equation.residual(samples, self.forward(samples), self._params) return self.loss(torch.zeros_like(residual, requires_grad=True), residual) - def training_step(self, batch, batch_idx): """ PINN solver training step. @@ -137,15 +157,20 @@ class PINN(SolverInterface): else: raise ValueError("Batch size not supported") - # TODO for users this us hard to remebeber when creating a new solver, to fix in a smarter way + # TODO for users this us hard to remember when creating a new solver, to fix in a smarter way loss = loss.as_subclass(torch.Tensor) - # add condition losses and accumulate logging for each epoch +# # add condition losses and accumulate logging for each epoch condition_losses.append(loss * condition.data_weight) self.log(condition_name + '_loss', float(loss), prog_bar=True, logger=True, on_epoch=True, on_step=False) - # add to tot loss and accumulate logging for each epoch + # clamp unknown parameters of the InverseProblem to their domain ranges (if needed) + if isinstance(self.problem, InverseProblem): + self._clamp_inverse_problem_params() + + # TODO Fix the bug, tot_loss is a label tensor without labels + # we need to pass it as a torch tensor to make everything work total_loss = sum(condition_losses) self.log('mean_loss', float(total_loss / len(condition_losses)), prog_bar=True, logger=True, on_epoch=True, on_step=False) diff --git a/tutorials/README.md b/tutorials/README.md index 3ec2fe2..601b5e1 100644 --- a/tutorials/README.md +++ b/tutorials/README.md @@ -6,20 +6,21 @@ In this folder we collect useful tutorials in order to understand the principles | Description | Tutorial | |---------------|-----------| -Introduction to PINA for Physics Informed Neural Networks training|[[.ipynb](tutorial1/tutorial.ipynb), [.py](tutorial1/tutorial.py), [.html](http://mathlab.github.io/PINA/_rst/tutorial1/tutorial.html)]| -Building custom geometries with PINA `Location` class|[[.ipynb](tutorial1/tutorial.ipynb), [.py](tutorial1/tutorial.py), [.html](http://mathlab.github.io/PINA/_rst/tutorial1/tutorial.html)]| +Introduction to PINA for Physics Informed Neural Networks training|[[.ipynb](tutorial1/tutorial.ipynb), [.py](tutorial1/tutorial.py), [.html](http://mathlab.github.io/PINA/_rst/tutorial1/tutorial.html)]| +Building custom geometries with PINA `Location` class|[[.ipynb](tutorial1/tutorial.ipynb), [.py](tutorial1/tutorial.py), [.html](http://mathlab.github.io/PINA/_rst/tutorial1/tutorial.html)]| ## Physics Informed Neural Networks -| Description | Tutorial | +| Description | Tutorial | |---------------|-----------| Two dimensional Poisson problem using Extra Features Learning     |[[.ipynb](tutorial2/tutorial.ipynb), [.py](tutorial2/tutorial.py), [.html](http://mathlab.github.io/PINA/_rst/tutorial2/tutorial.html)]| Two dimensional Wave problem with hard constraint |[[.ipynb](tutorial3/tutorial.ipynb), [.py](tutorial3/tutorial.py), [.html](http://mathlab.github.io/PINA/_rst/tutorial3/tutorial.html)]| +Resolution of a 2D Poisson inverse problem |[[.ipynb](tutorial7/tutorial.ipynb), [.py](tutorial7/tutorial.py), [.html](http://mathlab.github.io/PINA/_rst/tutorial7/tutorial.html)]| ## Neural Operator Learning | Description | Tutorial | |---------------|-----------| -Two dimensional Darcy flow using the Fourier Neural Operator         |[[.ipynb](tutorial5/tutorial.ipynb), [.py](tutorial5/tutorial.py), [.html](http://mathlab.github.io/PINA/_rst/tutorial5/tutorial.html)]| +Two dimensional Darcy flow using the Fourier Neural Operator         |[[.ipynb](tutorial5/tutorial.ipynb), [.py](tutorial5/tutorial.py), [.html](http://mathlab.github.io/PINA/_rst/tutorial5/tutorial.html)]| ## Supervised Learning | Description | Tutorial | diff --git a/tutorials/tutorial7/data/pinn_solution_0.5_0.5 b/tutorials/tutorial7/data/pinn_solution_0.5_0.5 new file mode 100644 index 0000000000000000000000000000000000000000..82cc8dc2c2b2b42ec5dfa7274f0a80ddc486131c GIT binary patch literal 10895 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 literal 0 HcmV?d00001 diff --git a/tutorials/tutorial7/data/pts_0.5_0.5 b/tutorials/tutorial7/data/pts_0.5_0.5 new file mode 100644 index 0000000000000000000000000000000000000000..740719b692c7da453b860505d4bc1ab4a9521d4a GIT binary patch literal 40881 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" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "points = data_input.extract(['x', 'y']).detach().numpy()\n", + "truth = data_output.detach().numpy()\n", + "\n", + "plt.scatter(points[:, 0], points[:, 1], c=truth, s=8)\n", + "plt.axis('equal')\n", + "plt.colorbar()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "de7c4c83", + "metadata": {}, + "source": [ + "### Inverse problem definition in PINA" + ] + }, + { + "cell_type": "markdown", + "id": "c46410fa-2718-4fc9-977a-583fe2390028", + "metadata": {}, + "source": [ + "Then, we initialize the Poisson problem, that is inherited from the `SpatialProblem` and from the `InverseProblem` classes. We here have to define all the variables, and the domain where our unknown parameters ($\\mu_1$, $\\mu_2$) belong. Notice that the laplace equation takes as inputs also the unknown variables, that will be treated as parameters that the neural network optimizes during the training process." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "8ec0d95d-72c2-40a4-a310-21c3d6fe17d2", + "metadata": {}, + "outputs": [], + "source": [ + "### Define ranges of variables\n", + "x_min = -2\n", + "x_max = 2\n", + "y_min = -2\n", + "y_max = 2\n", + "\n", + "class Poisson(SpatialProblem, InverseProblem):\n", + " '''\n", + " Problem definition for the Poisson equation.\n", + " '''\n", + " output_variables = ['u']\n", + " spatial_domain = CartesianDomain({'x': [x_min, x_max], 'y': [y_min, y_max]})\n", + " # define the ranges for the parameters\n", + " unknown_parameter_domain = CartesianDomain({'mu1': [-1, 1], 'mu2': [-1, 1]})\n", + "\n", + " def laplace_equation(input_, output_, params_):\n", + " '''\n", + " Laplace equation with a force term.\n", + " '''\n", + " force_term = torch.exp(\n", + " - 2*(input_.extract(['x']) - params_['mu1'])**2\n", + " - 2*(input_.extract(['y']) - params_['mu2'])**2)\n", + " delta_u = laplacian(output_, input_, components=['u'], d=['x', 'y'])\n", + "\n", + " return delta_u - force_term\n", + "\n", + " # define the conditions for the loss (boundary conditions, equation, data)\n", + " conditions = {\n", + " 'gamma1': Condition(location=CartesianDomain({'x': [x_min, x_max],\n", + " 'y': y_max}),\n", + " equation=FixedValue(0.0, components=['u'])),\n", + " 'gamma2': Condition(location=CartesianDomain({'x': [x_min, x_max], 'y': y_min\n", + " }),\n", + " equation=FixedValue(0.0, components=['u'])),\n", + " 'gamma3': Condition(location=CartesianDomain({'x': x_max, 'y': [y_min, y_max]\n", + " }),\n", + " equation=FixedValue(0.0, components=['u'])),\n", + " 'gamma4': Condition(location=CartesianDomain({'x': x_min, 'y': [y_min, y_max]\n", + " }),\n", + " equation=FixedValue(0.0, components=['u'])),\n", + " 'D': Condition(location=CartesianDomain({'x': [x_min, x_max], 'y': [y_min, y_max]\n", + " }),\n", + " equation=Equation(laplace_equation)),\n", + " 'data': Condition(input_points=data_input.extract(['x', 'y']), output_points=data_output)\n", + " }\n", + "\n", + "problem = Poisson()" + ] + }, + { + "cell_type": "markdown", + "id": "6b264569-57b3-458d-bb69-8e94fe89017d", + "metadata": {}, + "source": [ + "Then, we define the model of the neural network we want to use. Here we used a model which impose hard constrains on the boundary conditions, as also done in the Wave tutorial!" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "c4170514-eb73-488e-8942-0129070e4e13", + "metadata": {}, + "outputs": [], + "source": [ + "model = FeedForward(\n", + " layers=[20, 20, 20],\n", + " func=torch.nn.Softplus,\n", + " output_dimensions=len(problem.output_variables),\n", + " input_dimensions=len(problem.input_variables)\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "16e1f085-7818-4624-92a1-bf7010dbe528", + "metadata": {}, + "source": [ + "After that, we discretize the spatial domain." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "e3e0ae40-d8c6-4c08-81e8-85adc60a94e6", + "metadata": {}, + "outputs": [], + "source": [ + "problem.discretise_domain(20, 'grid', locations=['D'], variables=['x', 'y'])\n", + "problem.discretise_domain(1000, 'random', locations=['gamma1', 'gamma2',\n", + " 'gamma3', 'gamma4'], variables=['x', 'y'])" + ] + }, + { + "cell_type": "markdown", + "id": "b272796a-888c-4795-9d88-3e13121e8f38", + "metadata": {}, + "source": [ + "Here, we define a simple callback for the trainer. We use this callback to save the parameters predicted by the neural network during the training. The parameters are saved every 100 epochs as `torch` tensors in a specified directory (`tmp_dir` in our case).\n", + "The goal is to read the saved parameters after training and plot their trend across the epochs." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "e1409953-eb1b-443b-923d-c7ec3af0dfb0", + "metadata": {}, + "outputs": [], + "source": [ + "# temporary directory for saving logs of training\n", + "tmp_dir = \"tmp_poisson_inverse\"\n", + "\n", + "class SaveParameters(Callback):\n", + " '''\n", + " Callback to save the parameters of the model every 100 epochs.\n", + " '''\n", + " def on_train_epoch_end(self, trainer, __):\n", + " if trainer.current_epoch % 100 == 99:\n", + " torch.save(trainer.solver.problem.unknown_parameters, '{}/parameters_epoch{}'.format(tmp_dir, trainer.current_epoch))" + ] + }, + { + "cell_type": "markdown", + "id": "fc6e0030-f6ae-40cf-a3b3-d21d6538e7f2", + "metadata": {}, + "source": [ + "Then, we define the `PINN` object and train the solver using the `Trainer`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "05a0f311-3cca-429b-be2c-1fa899b14e62", + "metadata": {}, + "outputs": [], + "source": [ + "### train the problem with PINN\n", + "max_epochs = 5000\n", + "pinn = PINN(problem, model, optimizer_kwargs={'lr':0.005})\n", + "# define the trainer for the solver\n", + "trainer = Trainer(solver=pinn, accelerator='cpu', max_epochs=max_epochs,\n", + " default_root_dir=tmp_dir, callbacks=[SaveParameters()])\n", + "trainer.train()" + ] + }, + { + "cell_type": "markdown", + "id": "aab51202-36a7-40d2-b96d-47af8892cd2c", + "metadata": {}, + "source": [ + "One can now see how the parameters vary during the training by reading the saved solution and plotting them. The plot shows that the parameters stabilize to their true value before reaching the epoch $1000$!" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "dd328887-7c18-4b96-ada4-c9eec630c069", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "

" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "epochs_saved = range(99, max_epochs, 100)\n", + "parameters = torch.empty((int(max_epochs/100), 2))\n", + "for i, epoch in enumerate(epochs_saved):\n", + " params_torch = torch.load('{}/parameters_epoch{}'.format(tmp_dir, epoch))\n", + " for e, var in enumerate(pinn.problem.unknown_variables): \n", + " parameters[i, e] = params_torch[var].data\n", + "\n", + "# Plot parameters\n", + "plt.close()\n", + "plt.plot(epochs_saved, parameters[:, 0], label='mu1', marker='o')\n", + "plt.plot(epochs_saved, parameters[:, 1], label='mu2', marker='s')\n", + "plt.ylim(-1, 1)\n", + "plt.grid()\n", + "plt.legend()\n", + "plt.xlabel('Epoch')\n", + "plt.ylabel('Parameter value')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f6a0ba58", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tutorials/tutorial7/tutorial.py b/tutorials/tutorial7/tutorial.py new file mode 100644 index 0000000..48fc953 --- /dev/null +++ b/tutorials/tutorial7/tutorial.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python +# coding: utf-8 + +# # Tutorial 7: Resolution of an inverse problem + +# ### Introduction to the inverse problem + +# This tutorial shows how to solve an inverse Poisson problem with Physics-Informed Neural Networks. The problem definition is that of a Poisson problem with homogeneous boundary conditions and it reads: +# \begin{equation} +# \begin{cases} +# \Delta u = e^{-2(x-\mu_1)^2-2(y-\mu_2)^2} \text{ in } \Omega\, ,\\ +# u = 0 \text{ on }\partial \Omega,\\ +# u(\mu_1, \mu_2) = \text{ data} +# \end{cases} +# \end{equation} +# where $\Omega$ is a square domain $[-2, 2] \times [-2, 2]$, and $\partial \Omega=\Gamma_1 \cup \Gamma_2 \cup \Gamma_3 \cup \Gamma_4$ is the union of the boundaries of the domain. +# +# This kind of problem, namely the "inverse problem", has two main goals: +# - find the solution $u$ that satisfies the Poisson equation; +# - find the unknown parameters ($\mu_1$, $\mu_2$) that better fit some given data (third equation in the system above). +# +# In order to achieve both the goals we will need to define an `InverseProblem` in PINA. + +# Let's start with useful imports. + +# In[1]: + + +import matplotlib.pyplot as plt +import torch +from pytorch_lightning.callbacks import Callback +from pina.problem import SpatialProblem, InverseProblem +from pina.operators import laplacian +from pina.model import FeedForward +from pina.equation import Equation, FixedValue +from pina import Condition, Trainer +from pina.solvers import PINN +from pina.geometry import CartesianDomain + + +# Then, we import the pre-saved data, for ($\mu_1$, $\mu_2$)=($0.5$, $0.5$). These two values are the optimal parameters that we want to find through the neural network training. In particular, we import the `input_points`(the spatial coordinates), and the `output_points` (the corresponding $u$ values evaluated at the `input_points`). + +# In[2]: + + +data_output = torch.load('data/pinn_solution_0.5_0.5').detach() +data_input = torch.load('data/pts_0.5_0.5') + + +# Moreover, let's plot also the data points and the reference solution: this is the expected output of the neural network. + +# In[3]: + + +points = data_input.extract(['x', 'y']).detach().numpy() +truth = data_output.detach().numpy() + +plt.scatter(points[:, 0], points[:, 1], c=truth, s=8) +plt.axis('equal') +plt.colorbar() +plt.show() + + +# ### Inverse problem definition in PINA + +# Then, we initialize the Poisson problem, that is inherited from the `SpatialProblem` and from the `InverseProblem` classes. We here have to define all the variables, and the domain where our unknown parameters ($\mu_1$, $\mu_2$) belong. Notice that the laplace equation takes as inputs also the unknown variables, that will be treated as parameters that the neural network optimizes during the training process. + +# In[4]: + + +### Define ranges of variables +x_min = -2 +x_max = 2 +y_min = -2 +y_max = 2 + +class Poisson(SpatialProblem, InverseProblem): + ''' + Problem definition for the Poisson equation. + ''' + output_variables = ['u'] + spatial_domain = CartesianDomain({'x': [x_min, x_max], 'y': [y_min, y_max]}) + # define the ranges for the parameters + unknown_parameter_domain = CartesianDomain({'mu1': [-1, 1], 'mu2': [-1, 1]}) + + def laplace_equation(input_, output_, params_): + ''' + Laplace equation with a force term. + ''' + force_term = torch.exp( + - 2*(input_.extract(['x']) - params_['mu1'])**2 + - 2*(input_.extract(['y']) - params_['mu2'])**2) + delta_u = laplacian(output_, input_, components=['u'], d=['x', 'y']) + + return delta_u - force_term + + # define the conditions for the loss (boundary conditions, equation, data) + conditions = { + 'gamma1': Condition(location=CartesianDomain({'x': [x_min, x_max], + 'y': y_max}), + equation=FixedValue(0.0, components=['u'])), + 'gamma2': Condition(location=CartesianDomain({'x': [x_min, x_max], 'y': y_min + }), + equation=FixedValue(0.0, components=['u'])), + 'gamma3': Condition(location=CartesianDomain({'x': x_max, 'y': [y_min, y_max] + }), + equation=FixedValue(0.0, components=['u'])), + 'gamma4': Condition(location=CartesianDomain({'x': x_min, 'y': [y_min, y_max] + }), + equation=FixedValue(0.0, components=['u'])), + 'D': Condition(location=CartesianDomain({'x': [x_min, x_max], 'y': [y_min, y_max] + }), + equation=Equation(laplace_equation)), + 'data': Condition(input_points=data_input.extract(['x', 'y']), output_points=data_output) + } + +problem = Poisson() + + +# Then, we define the model of the neural network we want to use. Here we used a model which impose hard constrains on the boundary conditions, as also done in the Wave tutorial! + +# In[5]: + + +model = FeedForward( + layers=[20, 20, 20], + func=torch.nn.Softplus, + output_dimensions=len(problem.output_variables), + input_dimensions=len(problem.input_variables) + ) + + +# After that, we discretize the spatial domain. + +# In[6]: + + +problem.discretise_domain(20, 'grid', locations=['D'], variables=['x', 'y']) +problem.discretise_domain(1000, 'random', locations=['gamma1', 'gamma2', + 'gamma3', 'gamma4'], variables=['x', 'y']) + + +# Here, we define a simple callback for the trainer. We use this callback to save the parameters predicted by the neural network during the training. The parameters are saved every 100 epochs as `torch` tensors in a specified directory (`tmp_dir` in our case). +# The goal is to read the saved parameters after training and plot their trend across the epochs. + +# In[7]: + + +# temporary directory for saving logs of training +tmp_dir = "tmp_poisson_inverse" + +class SaveParameters(Callback): + ''' + Callback to save the parameters of the model every 100 epochs. + ''' + def on_train_epoch_end(self, trainer, __): + if trainer.current_epoch % 100 == 99: + torch.save(trainer.solver.problem.unknown_parameters, '{}/parameters_epoch{}'.format(tmp_dir, trainer.current_epoch)) + + +# Then, we define the `PINN` object and train the solver using the `Trainer`. + +# In[8]: + + +### train the problem with PINN +max_epochs = 5000 +pinn = PINN(problem, model, optimizer_kwargs={'lr':0.005}) +# define the trainer for the solver +trainer = Trainer(solver=pinn, accelerator='cpu', max_epochs=max_epochs, + default_root_dir=tmp_dir, callbacks=[SaveParameters()]) +trainer.train() + + +# One can now see how the parameters vary during the training by reading the saved solution and plotting them. The plot shows that the parameters stabilize to their true value before reaching the epoch $1000$! + +# In[9]: + + +epochs_saved = range(99, max_epochs, 100) +parameters = torch.empty((int(max_epochs/100), 2)) +for i, epoch in enumerate(epochs_saved): + params_torch = torch.load('{}/parameters_epoch{}'.format(tmp_dir, epoch)) + for e, var in enumerate(pinn.problem.unknown_variables): + parameters[i, e] = params_torch[var].data + +# Plot parameters +plt.close() +plt.plot(epochs_saved, parameters[:, 0], label='mu1', marker='o') +plt.plot(epochs_saved, parameters[:, 1], label='mu2', marker='s') +plt.ylim(-1, 1) +plt.grid() +plt.legend() +plt.xlabel('Epoch') +plt.ylabel('Parameter value') +plt.show() +