{ "cells": [ { "attachments": {}, "cell_type": "markdown", "id": "6f71ca5c", "metadata": {}, "source": [ "# Tutorial: Introductory Tutorial: Supervised Learning with PINA\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/tutorial21/tutorial.ipynb)\n", "\n", "\n", "> ##### ⚠️ ***Before starting:***\n", "> We assume you are already familiar with the concepts covered in the [Getting started with PINA](https://mathlab.github.io/PINA/_tutorial.html#getting-started-with-pina) tutorials. If not, we strongly recommend reviewing them before exploring this advanced topic.\n", "\n", "In this tutorial, we will demonstrate a typical use case of **PINA** for Neural Operator learning. We will cover the basics of training a Neural Operator with PINA, if you want to go further into the topic look at our dedicated [tutorials](https://mathlab.github.io/PINA/_tutorial.html#neural-operator-learning) on the topic.\n", "\n", "Let's start by importing the useful modules:" ] }, { "cell_type": "code", "execution_count": null, "id": "0981f1e9", "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", "warnings.filterwarnings(\"ignore\")\n", "\n", "from pina import Trainer\n", "from pina.solver import SupervisedSolver\n", "from pina.model import KernelNeuralOperator\n", "from pina.model.block import FourierBlock1D\n", "from pina.problem.zoo import SupervisedProblem" ] }, { "cell_type": "markdown", "id": "f0c937e6", "metadata": {}, "source": [ "## Learning Differential Operators via Neural Operator\n", "\n", "In this tutorial, we explore how **Neural Operators** can be used to learn and approximate **differential operators**, which are fundamental in modeling physical and engineering systems governed by differential equations.\n", "\n", "### What Are Neural Operators?\n", "\n", "**Neural Operators (NOs)** are a class of machine learning models designed to learn mappings *between function spaces*, unlike traditional neural networks which learn mappings between finite-dimensional vectors. In the context of differential equations, this means a Neural Operator can learn the **solution operator**:\n", "$$\n", "\\mathcal{G}(a) = u,\n", "$$\n", "where $a$ is an input function (e.g., a PDE coefficient) and $u$ is the solution function.\n", "\n", "### Why Are Neural Operators Useful?\n", "\n", "- **Mesh-free learning**: Neural Operators work directly with functions, allowing them to generalize across different spatial resolutions or grids.\n", "- **Fast inference**: Once trained, they can predict the solution of a PDE for new input data almost instantaneously.\n", "- **Physics-aware extensions**: Some variants can incorporate physical laws and constraints into the training process, improving accuracy and generalization.\n", "\n", "## Learning the 1D Advection Equation with a Neural Operator\n", "\n", "To make things concrete, we'll a Neural Operator to learn the 1D advection equation. We generate synthetic data based on the analytical solution:\n", "\n", "$$\n", "\\frac{\\partial u}{\\partial t} + c \\frac{\\partial u}{\\partial x} = 0\n", "$$\n", "\n", "For a given initial condition $u(x, 0)$, the exact solution at time $t$ is:\n", "\n", "$$\n", "u(x, t) = u(x - ct)\n", "$$\n", "\n", "We use this property to generate training data without solving the PDE numerically.\n", "\n", "### Problem Setup\n", "\n", "1. **Define the spatial domain**: We work on a 1D grid $x \\in [0, 1]$ with periodic boundary conditions.\n", "\n", "2. **Generate initial conditions**: Each initial condition $u(x, 0)$ is created as a sum of sine waves with random amplitudes and phases:\n", " $$\n", " u(x, 0) = \\sum_{k=1}^K A_k \\sin(2\\pi k x + \\phi_k)\n", " $$\n", " where $A_k \\in [0, 0.5]$ and $\\phi_k \\in [0, 2\\pi]$ are sampled randomly for each sample.\n", "\n", "3. **Compute the solution at time $t$**: \n", " Using the analytical solution, we shift each initial condition by $t=0.5$ ($c=1$), applying periodic wrap-around:\n", " $$\n", " u(x, t=0.5) = u(x - 0.5)\n", " $$\n", "\n", "4. **Create input-output pairs**: The input to the model is the function $u(x, 0)$, and the target output is $u(x, 0.5)$. These pairs can be used to train a Neural Operator to learn the underlying differential operator." ] }, { "cell_type": "code", "execution_count": 18, "id": "d331c971", "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "def generate_data(n_samples, x, c=1, t=0.5):\n", " x = x.T.repeat(n_samples, 1)\n", " u0 = torch.zeros_like(x)\n", " ut = torch.zeros_like(x)\n", " for k in range(1, 4):\n", " amplitude = torch.rand(n_samples, 1) * 0.5\n", " phase = torch.rand(n_samples, 1) * 2 * torch.pi\n", " u0 += amplitude * torch.sin(2 * torch.pi * k * x + phase)\n", " shifted_x = (x - c * t) % 1.0 # periodic shift\n", " ut += amplitude * torch.sin(2 * torch.pi * k * shifted_x + phase)\n", " return u0, ut\n", "\n", "\n", "# define discretization train\n", "x_train = torch.linspace(0, 1, 100).reshape(-1, 1)\n", "\n", "# define input and target\n", "input, target = generate_data(10000, x_train)\n", "\n", "# visualize the data\n", "plt.plot(x_train, input[0], label=f\"Input u(x, t=0)\")\n", "plt.plot(x_train, target[0], label=f\"Target u(x, t=0.5)\")\n", "plt.title(\"Generated 1D Advection Data\")\n", "plt.xlabel(\"x\")\n", "plt.legend()\n", "plt.grid(True)" ] }, { "cell_type": "markdown", "id": "1dda7888", "metadata": {}, "source": [ "## Solving the Neural Operator Problem\n", "\n", "At their core, **Neural Operators** transform an input function $a$ into an output function $u$. The general structure of a Neural Operator consists of three key components:\n", "\n", "

\n", " \"Neural\n", "

\n", "\n", "1. **Encoder**: The encoder maps the input into a specific embedding space.\n", "\n", "2. **Processor**: The processor consists of multiple layers performing **function convolutions**, which is the core computational unit in a Neural Operator. \n", "3. **Decoder**: The decoder maps the processor's output back into the desired output space.\n", "\n", "By varying the design and implementation of these three components — encoder, processor, and decoder — different Neural Operators are created, each tailored for specific applications or types of data.\n", "\n", "### Types of Neural Operators\n", "\n", "Different variants of Neural Operators are designed to solve specific tasks. Some prominent examples include:\n", "\n", "- **Fourier Neural Operator (FNO)**: \n", " The **Fourier Neural Operator** utilizes the **Fourier transform** in the processor to perform global convolutions. This enables the operator to capture long-range dependencies efficiently. FNOs are particularly useful for problems with periodic data or problems where global patterns and interactions are important. \n", " ➤ [Learn more about FNO](https://mathlab.github.io/PINA/_rst/model/fourier_neural_operator.html).\n", "\n", "- **Graph Neural Operator (GNO)**: \n", " The **Graph Neural Operator** leverages **Graph Neural Networks (GNNs)** to exchange information between nodes, enabling the operator to perform convolutions on unstructured domains, such as graphs or meshes. GNOs are especially useful for problems that naturally involve irregular data, such as graph-based datasets or data on non-Euclidean spaces. \n", " ➤ [Learn more about GNO](https://mathlab.github.io/PINA/_rst/model/graph_neural_operator.html).\n", "\n", "- **Deep Operator Network (DeepONet)**: \n", " **DeepONet** is a variant of Neural Operators designed to solve operator equations by learning mappings between input and output functions. Unlike other Neural Operators, **DeepONet** does not use the typical encoder-processor-decoder structure. Instead, it uses two distinct neural networks:\n", " \n", " 1. **Branch Network**: Takes the **function inputs** (e.g., $u(x)$) and learns a feature map of the input function.\n", " 2. **Trunk Network**: Takes the **spatial locations** (e.g., $x$) and maps them to the output space.\n", " \n", " The output of **DeepONet** is the combination of these two networks' outputs, which together provide the mapping from the input function to the output function. \n", " ➤ [Learn more about DeepONet](https://mathlab.github.io/PINA/_rst/model/deeponet.html).\n", "\n", "In this tutorial we will focus on Neural Operator which follow the Encoder - Processor - Decoder structure, which we call *Kernel* Neural Operator. Implementing kernel neural Operators in PINA is very simple, you just need to use the `KernelNeuralOperator` API.\n", "\n", "### KernelNeuralOperator API\n", "The `KernelNeuralOperator` API requires three parameters: \n", "\n", "1. `lifting_operator`: a `torch.nn.Module` apping the input to its hidden dimension (Encoder).\n", "\n", "2. `integral_kernels`: a `torch.nn.Module` representing the integral kernels mapping each hidden representation to the next one.\n", "\n", "3. `projection_operator`: a `torch.nn.Module` representing the hidden representation to the output function.\n", "\n", "To construct the kernel, you can use the Neural Operator Blocks available in PINA (see [here](https://mathlab.github.io/PINA/_rst/_code.html#blocks)) or implement you own one! Let's build a simple FNO using the `FourierBlock1D`. In particular we will:\n", "\n", "1. Define the encoder, a simple linear layer mapping the input dimension to the hidden dimension\n", "2. Define the decoder, two linear layers mapping the hidden dimension to 128 and back to the input dimension\n", "3. Define the processor, a two layer Fourier block with a specific hidden dimension.\n", "4. Combine the encoder-processor-decoder using the `KernelNeuralOperator` API to create the `model`.\n" ] }, { "cell_type": "code", "execution_count": 23, "id": "ee9b1b1a", "metadata": {}, "outputs": [], "source": [ "# 1. Define the encoder (simple linear layer 1->64)\n", "class Encoder(torch.nn.Module):\n", " def __init__(self, hidden_dim=64):\n", " super().__init__()\n", " self.enc = torch.nn.Linear(1, hidden_dim)\n", "\n", " def forward(self, x):\n", " # [B, Nx] -> [B, Nx, 1]\n", " x = x.unsqueeze(-1)\n", " # [B, Nx, 1] -> [B, Nx, 64]\n", " x = self.enc(x)\n", " # [B, Nx, 1] -> [B, 64, Nx]\n", " return x.permute(0, 2, 1)\n", "\n", "\n", "# 2. Define the decoder (two linear layer 64->128->1)\n", "class Decoder(torch.nn.Module):\n", " def __init__(self, hidden_dim=64):\n", " super().__init__()\n", " self.dec = torch.nn.Sequential(\n", " torch.nn.Linear(hidden_dim, 128),\n", " torch.nn.ReLU(),\n", " torch.nn.Linear(128, 1),\n", " )\n", "\n", " def forward(self, x):\n", " # [B, 64, Nx] -> [B, Nx, 64]\n", " x = x.permute(0, 2, 1)\n", " # [B, Nx, 64] -> [B, Nx, 1]\n", " x = self.dec(x)\n", " # [B, Nx, 1] -> [B, Nx]\n", " return x.squeeze(-1)\n", "\n", "\n", "# 3. Define the processor (two FNO blocks of size 64)\n", "class Processor(torch.nn.Module):\n", " def __init__(self, hidden_dim=64):\n", " super().__init__()\n", " self.proc = torch.nn.Sequential(\n", " FourierBlock1D(64, 64, 8, torch.nn.ReLU),\n", " FourierBlock1D(64, 64, 8, torch.nn.ReLU),\n", " )\n", "\n", " def forward(self, x):\n", " return self.proc(x)\n", "\n", "\n", "# 4. Define the model with KernelNeuralOperator\n", "model = KernelNeuralOperator(\n", " lifting_operator=Encoder(),\n", " integral_kernels=Processor(),\n", " projection_operator=Decoder(),\n", ")" ] }, { "cell_type": "markdown", "id": "4aa44dd1", "metadata": {}, "source": [ "Done! Let's now solve the Neural Operator problem. The problem we will define is a basic `SupervisedProblem`, and we will use the `SupervisedSolver` to train the Neural Operator.\n", "\n", "> **👉 We have a dedicated [tutorial](https://mathlab.github.io/PINA/tutorial16/tutorial.html) to teach how to build a Problem from scratch — have a look if you're interested!**\n", "\n", "> **👉 We have a dedicated [tutorial](http://mathlab.github.io/PINA/_rst/tutorials/tutorial18/tutorial.html) for an overview of Solvers in PINA — have a look if you're interested!**" ] }, { "cell_type": "code", "execution_count": 24, "id": "304094a0", "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": "2fceec83f20c49d48c5b22540d5a5f1b", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Training: | | 0/? [00:00" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# generate new data\n", "input, target = generate_data(100, x_train)\n", "\n", "# compute the predicted solution\n", "prediction = solver(input).detach()\n", "\n", "# plot\n", "plt.plot(x_train, input[0], label=f\"Input u(x, t=0)\")\n", "plt.plot(x_train, target[0], label=f\"Target u(x, t=0.5)\")\n", "plt.plot(x_train, prediction[0], \"--r\", label=f\"NO prediction u(x, t=0.5)\")\n", "plt.title(\"Generated 1D Advection Data\")\n", "plt.xlabel(\"x\")\n", "plt.legend()\n", "plt.grid(True)" ] }, { "cell_type": "markdown", "id": "c152bfd1", "metadata": {}, "source": [ "Nice! We can see that the network is correctly learning the solution operator and it was very simple!\n", "\n", "## What's Next?\n", "\n", "Congratulations on completing the introductory tutorial on Neural Operators! Now that you have a solid foundation, here are a few directions you can explore:\n", "\n", "1. **Experiment with Training Duration & Network Architecture** — Try different training durations and tweak the network architecture to optimize performance. Choose different integral kernels and see how the results vary.\n", "\n", "2. **Explore Other Models in `pina.model`** — Check out other models available in `pina.model` or design your own custom PyTorch module to suit your needs. What about trying a `DeepONet`?\n", "\n", "3. **...and many more!** — The possibilities are vast! Continue experimenting with advanced configurations, solvers, and features in PINA. For example, consider incorporating physics-informed terms during training to enhance model generalization.\n", "\n", "For more resources and tutorials, check out the [PINA Documentation](https://mathlab.github.io/PINA/)." ] } ], "metadata": { "kernelspec": { "display_name": "pina", "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.9.21" } }, "nbformat": 4, "nbformat_minor": 5 }