{ "cells": [ { "cell_type": "markdown", "id": "de19422d", "metadata": {}, "source": [ "# Tutorial: Enhancing PINNs with Extra Features to solve the Poisson Problem\n", "\n", "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/mathLab/PINA/blob/master/tutorials/tutorial2/tutorial.ipynb)\n", "\n", "This tutorial presents how to solve with Physics-Informed Neural Networks (PINNs) a 2D Poisson problem with Dirichlet boundary conditions. We will train with standard PINN's training, and with extrafeatures. For more insights on extrafeature learning please read [*An extended physics informed neural network for preliminary analysis of parametric optimal control problems*](https://www.sciencedirect.com/science/article/abs/pii/S0898122123002018).\n", "\n", "First of all, some useful imports." ] }, { "cell_type": "code", "execution_count": null, "id": "ad0b8dd7", "metadata": {}, "outputs": [], "source": [ "## routine needed to run the notebook on Google Colab\n", "try:\n", " import google.colab\n", "\n", " IN_COLAB = True\n", "except:\n", " IN_COLAB = False\n", "if IN_COLAB:\n", " !pip install \"pina-mathlab[tutorial]\"\n", "\n", "import torch\n", "import matplotlib.pyplot as plt\n", "import warnings\n", "\n", "from pina import LabelTensor, Trainer\n", "from pina.model import FeedForward\n", "from pina.solver import PINN\n", "from torch.nn import Softplus\n", "\n", "warnings.filterwarnings(\"ignore\")" ] }, { "cell_type": "markdown", "id": "492a37b4", "metadata": {}, "source": [ "## The problem definition" ] }, { "cell_type": "markdown", "id": "2c0b1777", "metadata": {}, "source": [ "The two-dimensional Poisson problem is mathematically written as:\n", "\\begin{equation}\n", "\\begin{cases}\n", "\\Delta u = 2\\pi^2\\sin{(\\pi x)} \\sin{(\\pi y)} \\text{ in } D, \\\\\n", "u = 0 \\text{ on } \\Gamma_1 \\cup \\Gamma_2 \\cup \\Gamma_3 \\cup \\Gamma_4,\n", "\\end{cases}\n", "\\end{equation}\n", "where $D$ is a square domain $[0,1]^2$, and $\\Gamma_i$, with $i=1,...,4$, are the boundaries of the square.\n", "\n", "The Poisson problem is written in **PINA** code as a class. The equations are written as *conditions* that should be satisfied in the corresponding domains. The *solution*\n", "is the exact solution which will be compared with the predicted one. If interested in how to write problems see [this tutorial](https://mathlab.github.io/PINA/_rst/tutorials/tutorial16/tutorial.html).\n", "\n", "We will directly import the problem from `pina.problem.zoo`, which contains a vast list of PINN problems and more." ] }, { "cell_type": "code", "execution_count": 4, "id": "82c24040", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The problem is made of 5 conditions: \n", "They are: ['g1', 'g2', 'g3', 'g4', 'D']\n" ] } ], "source": [ "from pina.problem.zoo import Poisson2DSquareProblem as Poisson\n", "\n", "# initialize the problem\n", "problem = Poisson()\n", "\n", "# print the conditions\n", "print(\n", " f\"The problem is made of {len(problem.conditions.keys())} conditions: \\n\"\n", " f\"They are: {list(problem.conditions.keys())}\"\n", ")\n", "\n", "# let's discretise the domain\n", "problem.discretise_domain(30, \"grid\", domains=[\"D\"])\n", "problem.discretise_domain(\n", " 100,\n", " \"grid\",\n", " domains=[\"g1\", \"g2\", \"g3\", \"g4\"],\n", ")" ] }, { "cell_type": "markdown", "id": "7086c64d", "metadata": {}, "source": [ "## Solving the problem with standard PINNs" ] }, { "cell_type": "markdown", "id": "72ba4501", "metadata": {}, "source": [ "After the problem, the feed-forward neural network is defined, through the class `FeedForward`. This neural network takes as input the coordinates (in this case $x$ and $y$) and provides the unkwown field of the Poisson problem. The residual of the equations are evaluated at several sampling points and the loss minimized by the neural network is the sum of the residuals.\n", "\n", "In this tutorial, the neural network is composed by two hidden layers of 10 neurons each, and it is trained for 1000 epochs with a learning rate of 0.006 and $l_2$ weight regularization set to $10^{-8}$. These parameters can be modified as desired. We set the `train_size` to 0.8 and `test_size` to 0.2, this mean that the discretised points will be divided in a 80%-20% fashion, where 80% will be used for training and the remaining 20% for testing." ] }, { "cell_type": "code", "execution_count": 5, "id": "e7d20d6d", "metadata": { "scrolled": true }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "GPU available: True (mps), used: False\n", "TPU available: False, using: 0 TPU cores\n", "HPU available: False, using: 0 HPUs\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "38a34ce3c1214e90be1f5e0194d80674", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Training: | | 0/? [00:00" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.figure(figsize=(12, 6))\n", "plot_solution(solver=pinn)" ] }, { "cell_type": "markdown", "id": "49142e7f", "metadata": {}, "source": [ "As you can see the solution is not very accurate, in what follows we will use **Extra Feature** as introduced in [*An extended physics informed neural network for preliminary analysis of parametric optimal control problems*](https://www.sciencedirect.com/science/article/abs/pii/S0898122123002018) to boost the training accuracy. Of course, even extra training will benefit, this tutorial is just to show that convergence using Extra Features is usally faster." ] }, { "cell_type": "markdown", "id": "20fdf23e", "metadata": {}, "source": [ "## Solving the problem with extra-features PINNs" ] }, { "cell_type": "markdown", "id": "a1e76351", "metadata": {}, "source": [ "Now, the same problem is solved in a different way.\n", "A new neural network is now defined, with an additional input variable, named extra-feature, which coincides with the forcing term in the Laplace equation. \n", "The set of input variables to the neural network is:\n", "\n", "\\begin{equation}\n", "[x, y, k(x, y)], \\text{ with } k(x, y)= 2\\pi^2\\sin{(\\pi x)}\\sin{(\\pi y)},\n", "\\end{equation}\n", "\n", "where $x$ and $y$ are the spatial coordinates and $k(x, y)$ is the added feature which is equal to the forcing term.\n", "\n", "This feature is initialized in the class `SinSin`, which is a simple `torch.nn.Module`. After declaring such feature, we can just adjust the `FeedForward` class by creating a subclass `FeedForwardWithExtraFeatures` with an adjusted forward method and the additional attribute `extra_features`.\n", "\n", "Finally, we perform the same training as before: the problem is `Poisson`, the network is composed by the same number of neurons and optimizer parameters are equal to previous test, the only change is the new extra feature." ] }, { "cell_type": "code", "execution_count": 8, "id": "ef3ad372", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "GPU available: True (mps), used: False\n", "TPU available: False, using: 0 TPU cores\n", "HPU available: False, using: 0 HPUs\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "62180078584f4dfea97d9dc6f8d20856", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Training: | | 0/? [00:00" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.figure(figsize=(12, 6))\n", "plot_solution(solver=pinn_feat)" ] }, { "cell_type": "markdown", "id": "e7bc0577", "metadata": {}, "source": [ "## Solving the problem with learnable extra-features PINNs" ] }, { "cell_type": "markdown", "id": "86c1d7b0", "metadata": {}, "source": [ "We can still do better!\n", "\n", "Another way to exploit the extra features is the addition of learnable parameter inside them.\n", "In this way, the added parameters are learned during the training phase of the neural network. In this case, we use:\n", "\n", "\\begin{equation}\n", "k(x, \\mathbf{y}) = \\beta \\sin{(\\alpha x)} \\sin{(\\alpha y)},\n", "\\end{equation}\n", "\n", "where $\\alpha$ and $\\beta$ are the abovementioned parameters.\n", "Their implementation is quite trivial: by using the class `torch.nn.Parameter` we cam define all the learnable parameters we need, and they are managed by `autograd` module!" ] }, { "cell_type": "code", "execution_count": 10, "id": "ae8716e7", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "GPU available: True (mps), used: False\n", "TPU available: False, using: 0 TPU cores\n", "HPU available: False, using: 0 HPUs\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "3eed1678b6c14cf2a190c248766815c7", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Training: | | 0/? [00:00