diff --git a/docs/source/tutorials/tutorial17/tutorial.html b/docs/source/tutorials/tutorial17/tutorial.html index 5150885..5f94bab 100644 --- a/docs/source/tutorials/tutorial17/tutorial.html +++ b/docs/source/tutorials/tutorial17/tutorial.html @@ -7620,7 +7620,7 @@ using only 20 samples:

-
+
@@ -7646,25 +7646,12 @@ using only 20 samples:

from pina import Condition, LabelTensor from pina.problem import AbstractProblem -from pina.geometry import CartesianDomain +from pina.domain import CartesianDomain
-
-
-
-
- -
-
@@ -7683,7 +7670,7 @@ using only 20 samples:

-
+
@@ -7702,7 +7689,7 @@ using only 20 samples:

output_variables = ["y"] input_variables = ["x"] - conditions = {"data": Condition(input_points=x, output_points=y)} + conditions = {"data": Condition(input=x, target=y)} problem = BayesianProblem() @@ -7717,20 +7704,6 @@ using only 20 samples:

-
-
-
-
- -
-
@@ -7793,22 +7766,22 @@ using only 20 samples:

1: {'dof': ['a', 'b', 'c', 'd'], 'name': 1} -tensor([[0.3340, 0.1983, 0.5324, 0.6622], - [0.1173, 0.6128, 0.8988, 0.1068], - [0.0888, 0.3239, 0.4673, 0.6765]]) +tensor([[0.8590, 0.8084, 0.0199, 0.3757], + [0.6026, 0.3771, 0.7906, 0.0058], + [0.0573, 0.4412, 0.0519, 0.3252]]) Torch methods can be used, label_tensor.shape=torch.Size([3, 4]) also label_tensor.requires_grad=False But we have labels as well, e.g. label_tensor.labels=['a', 'b', 'c', 'd'] And we can slice with labels: - label_tensor["a"]=LabelTensor([[0.3340], - [0.1173], - [0.0888]]) + label_tensor["a"]=LabelTensor([[0.8590], + [0.6026], + [0.0573]]) Similarly to: - label_tensor[:, 0]=LabelTensor([[0.3340], - [0.1173], - [0.0888]]) + label_tensor[:, 0]=LabelTensor([[0.8590], + [0.6026], + [0.0573]])
@@ -7893,7 +7866,7 @@ $$

-
Using default `ModelCheckpoint`. Consider installing `litmodels` package to enable `LitModelCheckpoint` for automatic upload to the Lightning model registry.
+
💡 Tip: For seamless cloud uploads and versioning, try installing [litmodels](https://pypi.org/project/litmodels/) to enable LitModelCheckpoint, which syncs automatically with the Lightning model registry.
 
@@ -7921,7 +7894,8 @@ $$

-
+
/opt/hostedtoolcache/Python/3.9.23/x64/lib/python3.9/site-packages/lightning/pytorch/trainer/configuration_validator.py: PossibleUserWarning: You defined a `validation_step` but have no `val_dataloader`. Skipping val loop.
+
   | Name         | Type       | Params | Mode 
 ----------------------------------------------------
 0 | _pina_models | ModuleList | 301    | train
@@ -7938,12 +7912,12 @@ $$

- @@ -8013,7 +7987,7 @@ var element = document.getElementById('51930d0a-9cd5-469f-bd49-7995bff6fe75');
@@ -8137,7 +8111,7 @@ $$

@@ -8355,7 +8329,7 @@ But you can easily sample by running .discretise_domain:
-
Using default `ModelCheckpoint`. Consider installing `litmodels` package to enable `LitModelCheckpoint` for automatic upload to the Lightning model registry.
+
💡 Tip: For seamless cloud uploads and versioning, try installing [litmodels](https://pypi.org/project/litmodels/) to enable LitModelCheckpoint, which syncs automatically with the Lightning model registry.
 
@@ -8382,12 +8356,20 @@ But you can easily sample by running .discretise_domain:
-
+ +
+
+ +
@@ -8457,7 +8439,7 @@ var element = document.getElementById('bebd66fb-df32-4f67-a307-6fc1efcf0201');
@@ -8494,6 +8476,6 @@ var element = document.getElementById('bebd66fb-df32-4f67-a307-6fc1efcf0201'); diff --git a/tutorials/tutorial17/tutorial.py b/tutorials/tutorial17/tutorial.py index a1cd74a..dcd50c4 100644 --- a/tutorials/tutorial17/tutorial.py +++ b/tutorials/tutorial17/tutorial.py @@ -2,80 +2,80 @@ # coding: utf-8 # # Tutorial: Introductory Tutorial: A Beginner’s Guide to PINA -# +# # [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/mathLab/PINA/blob/master/tutorials/tutorial17/tutorial.ipynb) -# +# #

# PINA logo #

-# -# +# +# # Welcome to **PINA**! -# +# # PINA [1] is an open-source Python library designed for **Scientific Machine Learning (SciML)** tasks, particularly involving: -# +# # - **Physics-Informed Neural Networks (PINNs)** # - **Neural Operators (NOs)** # - **Reduced Order Models (ROMs)** # - **Graph Neural Networks (GNNs)** # - ... -# +# # Built on **PyTorch**, **PyTorch Lightning**, and **PyTorch Geometric**, it provides a **user-friendly, intuitive interface** for formulating and solving differential problems using neural networks. -# +# # This tutorial offers a **step-by-step guide** to using PINA—starting from basic to advanced techniques—enabling users to tackle a broad spectrum of differential problems with minimal code. -# -# -# +# +# +# -# ## The PINA Workflow -# +# ## The PINA Workflow +# #

# PINA Workflow #

-# +# # Solving a differential problem in **PINA** involves four main steps: -# +# # 1. ***Problem & Data*** -# Define the mathematical problem and its physical constraints using PINA’s base classes: +# Define the mathematical problem and its physical constraints using PINA’s base classes: # - `AbstractProblem` # - `SpatialProblem` -# - `InverseProblem` +# - `InverseProblem` # - ... -# +# # Then prepare inputs by discretizing the domain or importing numerical data. PINA provides essential tools like the `Conditions` class and the `pina.domain` module to facilitate domain sampling and ensure that the input data aligns with the problem's requirements. -# +# # > **👉 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!** -# -# 2. ***Model Design*** +# +# 2. ***Model Design*** # Build neural network models as **PyTorch modules**. For graph-structured data, use **PyTorch Geometric** to build Graph Neural Networks. You can also import models from `pina.model` module! -# -# 3. ***Solver Selection*** +# +# 3. ***Solver Selection*** # Choose and configure a solver to optimize your model. Options include: # - **Supervised solvers**: `SupervisedSolver`, `ReducedOrderModelSolver` # - **Physics-informed solvers**: `PINN` and (many) variants -# - **Generative solvers**: `GAROM` +# - **Generative solvers**: `GAROM` # Solvers can be used out-of-the-box, extended, or fully customized. -# -# 4. ***Training*** +# +# 4. ***Training*** # Train your model using the `Trainer` class (built on **PyTorch Lightning**), which enables scalable and efficient training with advanced features. -# -# +# +# # By following these steps, PINA simplifies applying deep learning to scientific computing and differential problems. -# -# +# +# # ## A Simple Regression Problem in PINA # We'll start with a simple regression problem [2] of approximating the following function with a Neural Net model $\mathcal{M}_{\theta}$: -# $$y = x^3 + \epsilon, \quad \epsilon \sim \mathcal{N}(0, 9)$$ -# using only 20 samples: -# +# $$y = x^3 + \epsilon, \quad \epsilon \sim \mathcal{N}(0, 9)$$ +# using only 20 samples: +# # $$x_i \sim \mathcal{U}[-3, 3], \; \forall i \in \{1, \dots, 20\}$$ -# +# # Using PINA, we will: -# +# # - Generate a synthetic dataset. # - Implement a **Bayesian regressor**. # - Use **Monte Carlo (MC) Dropout** for **Bayesian inference** and **uncertainty estimation**. -# +# # This example highlights how PINA can be used for classic regression tasks with probabilistic modeling capabilities. Let's first import useful modules! # In[ ]: @@ -99,21 +99,21 @@ warnings.filterwarnings("ignore") from pina import Condition, LabelTensor from pina.problem import AbstractProblem -from pina.geometry import CartesianDomain +from pina.domain import CartesianDomain # #### ***Problem & Data*** -# +# # We'll start by defining a `BayesianProblem` inheriting from `AbstractProblem` to handle input/output data. This is suitable when data is available. For other cases like PDEs without data, use: -# +# # - `SpatialProblem` – for spatial variables # - `TimeDependentProblem` – for temporal variables # - `ParametricProblem` – for parametric inputs # - `InverseProblem` – for parameter estimation from observations -# +# # but we will see this more in depth in a while! -# In[21]: +# In[ ]: # (a) Data generation and plot @@ -127,7 +127,7 @@ class BayesianProblem(AbstractProblem): output_variables = ["y"] input_variables = ["x"] - conditions = {"data": Condition(input_points=x, output_points=y)} + conditions = {"data": Condition(input=x, target=y)} problem = BayesianProblem() @@ -140,17 +140,17 @@ problem = BayesianProblem() # We highlight two very important features of PINA -# -# 1. **`LabelTensor` Structure** -# - Alongside the standard `torch.Tensor`, PINA introduces the `LabelTensor` structure, which allows **string-based indexing**. -# - Ideal for managing and stacking tensors with different labels (e.g., `"x"`, `"t"`, `"u"`) for improved clarity and organization. +# +# 1. **`LabelTensor` Structure** +# - Alongside the standard `torch.Tensor`, PINA introduces the `LabelTensor` structure, which allows **string-based indexing**. +# - Ideal for managing and stacking tensors with different labels (e.g., `"x"`, `"t"`, `"u"`) for improved clarity and organization. # - You can still use standard PyTorch tensors if needed. -# -# 2. **`Condition` Object** -# - The `Condition` object enforces the **constraints** that the model $\mathcal{M}_{\theta}$ must satisfy, such as boundary or initial conditions. +# +# 2. **`Condition` Object** +# - The `Condition` object enforces the **constraints** that the model $\mathcal{M}_{\theta}$ must satisfy, such as boundary or initial conditions. # - It ensures that the model adheres to the specific requirements of the problem, making constraint handling more intuitive and streamlined. -# In[63]: +# In[ ]: # EXTRA - on the use of LabelTensor @@ -168,14 +168,14 @@ print(f"Similarly to: \n {label_tensor[:, 0]=}") # #### ***Model Design*** -# -# We will now solve the problem using a **simple PyTorch Neural Network** with **Dropout**, which we will implement from scratch following [2]. +# +# We will now solve the problem using a **simple PyTorch Neural Network** with **Dropout**, which we will implement from scratch following [2]. # It's important to note that PINA provides a wide range of **state-of-the-art (SOTA)** architectures in the `pina.model` module, which you can explore further [here](https://mathlab.github.io/PINA/_rst/_code.html#models). -# +# # #### ***Solver Selection*** -# -# For this task, we will use a straightforward **supervised learning** approach by importing the `SupervisedSolver` from `pina.solvers`. The solver is responsible for defining the training strategy. -# +# +# For this task, we will use a straightforward **supervised learning** approach by importing the `SupervisedSolver` from `pina.solvers`. The solver is responsible for defining the training strategy. +# # The `SupervisedSolver` is designed to handle typical regression tasks effectively by minimizing the following loss function: # $$ # \mathcal{L}_{\rm{problem}} = \frac{1}{N}\sum_{i=1}^N @@ -185,17 +185,17 @@ print(f"Similarly to: \n {label_tensor[:, 0]=}") # $$ # \mathcal{L}(v) = \| v \|^2_2. # $$ -# +# # #### **Training** -# +# # Next, we will use the `Trainer` class to train the model. The `Trainer` class, based on **PyTorch Lightning**, offers many features that help: # - **Improve model accuracy** # - **Reduce training time and memory usage** -# - **Facilitate logging and visualization** -# +# - **Facilitate logging and visualization** +# # The great work done by the PyTorch Lightning team ensures a streamlined training process. -# In[64]: +# In[ ]: from pina.solver import SupervisedSolver @@ -231,18 +231,18 @@ trainer.train() # #### ***Model Training Complete! Now Visualize the Solutions*** -# +# # The model has been trained! Since we used **Dropout** during training, the model is probabilistic (Bayesian) [3]. This means that each time we evaluate the forward pass on the input points $x_i$, the results will differ due to the stochastic nature of Dropout. -# +# # To visualize the model's predictions and uncertainty, we will: -# +# # 1. **Evaluate the Forward Pass**: Perform multiple forward passes to get different predictions for each input $x_i$. # 2. **Compute the Mean**: Calculate the average prediction $\mu_\theta$ across all forward passes. # 3. **Compute the Standard Deviation**: Calculate the variability of the predictions $\sigma_\theta$, which indicates the model's uncertainty. -# +# # This allows us to understand not only the predicted values but also the confidence in those predictions. -# In[65]: +# In[ ]: x_test = LabelTensor(torch.linspace(-4, 4, 100).reshape(-1, 1), "x") @@ -267,34 +267,34 @@ plt.show() # ## PINA for Physics-Informed Machine Learning -# +# # In the previous section, we used PINA for **supervised learning**. However, one of its main strengths lies in **Physics-Informed Machine Learning (PIML)**, specifically through **Physics-Informed Neural Networks (PINNs)**. -# +# # ### What Are PINNs? -# +# # PINNs are deep learning models that integrate the laws of physics directly into the training process. By incorporating **differential equations** and **boundary conditions** into the loss function, PINNs allow the modeling of complex physical systems while ensuring the predictions remain consistent with scientific laws. -# +# # ### Solving a 2D Poisson Problem -# +# # In this section, we will solve a **2D Poisson problem** with **Dirichlet boundary conditions** on an **hourglass-shaped domain** using a simple PINN [4]. You can explore other PINN variants, e.g. [5] or [6] in PINA by visiting the [PINA solvers documentation](https://mathlab.github.io/PINA/_rst/_code.html#solvers). We aim to solve the following 2D Poisson problem: -# +# # $$ # \begin{cases} # \Delta u(x, y) = \sin{(\pi x)} \sin{(\pi y)} & \text{in } D, \\ -# u(x, y) = 0 & \text{on } \partial D +# u(x, y) = 0 & \text{on } \partial D # \end{cases} # $$ -# +# # where $D$ is an **hourglass-shaped domain** defined as the difference between a **Cartesian domain** and two intersecting **ellipsoids**, and $\partial D$ is the boundary of the domain. -# +# # ### Building Complex Domains -# +# # PINA allows you to build complex geometries easily. It provides many built-in domain shapes and Boolean operators for combining them. For this problem, we will define the hourglass-shaped domain using the existing `CartesianDomain` and `EllipsoidDomain` classes, with Boolean operators like `Difference` and `Union`. -# +# # > **👉 If you are interested in exploring the `domain` module in more detail, check out [this tutorial](https://mathlab.github.io/PINA/_rst/tutorials/tutorial6/tutorial.html).** -# +# -# In[66]: +# In[ ]: from pina.domain import EllipsoidDomain, Difference, CartesianDomain, Union @@ -333,10 +333,10 @@ border_samples = border.sample(n=1000, mode="random") # #### Plotting the domain -# +# # Nice! Now that we have built the domain, let's try to plot it -# In[67]: +# In[ ]: plt.figure(figsize=(8, 4)) @@ -360,14 +360,14 @@ plt.show() # #### Writing the Poisson Problem Class -# -# Very good! Now we will implement the problem class for the 2D Poisson problem. Unlike the previous examples, where we inherited from `AbstractProblem`, for this problem, we will inherit from the `SpatialProblem` class. -# +# +# Very good! Now we will implement the problem class for the 2D Poisson problem. Unlike the previous examples, where we inherited from `AbstractProblem`, for this problem, we will inherit from the `SpatialProblem` class. +# # The reason for this is that the Poisson problem involves **spatial variables** as input, so we use `SpatialProblem` to handle such cases. -# +# # This will allow us to define the problem with spatial dependencies and set up the neural network model accordingly. -# In[69]: +# In[ ]: from pina.problem import SpatialProblem @@ -402,15 +402,15 @@ poisson_problem = Poisson() # As you can see, writing the problem class for a differential equation in PINA is straightforward! The main differences are: -# +# # - We inherit from **`SpatialProblem`** instead of `AbstractProblem` to account for spatial variables. # - We use **`domain`** and **`equation`** inside the `Condition` to define the problem. -# +# # The `Equation` class can be very useful for creating modular problem classes. If you're interested, check out [this tutorial](https://mathlab.github.io/PINA/_rst/tutorial12/tutorial.html) for more details. There's also a dedicated [tutorial](https://mathlab.github.io/PINA/_rst/tutorial16/tutorial.html) for building custom problems! -# +# # Once the problem class is set, we need to **sample the domain** to obtain the data. PINA will automatically handle this, and if you forget to sample, an error will be raised before training begins 😉. -# In[70]: +# In[ ]: print("Points are not automatically sampled, you can see this by:") @@ -422,16 +422,16 @@ print(f" {poisson_problem.are_all_domains_discretised=}") # ### Building the Model -# +# # After setting the problem and sampling the domain, the next step is to **build the model** $\mathcal{M}_{\theta}$. -# +# # For this, we will use the custom PINA models available [here](https://mathlab.github.io/PINA/_rst/_code.html#models). Specifically, we will use a **feed-forward neural network** by importing the `FeedForward` class. -# -# This neural network takes the **coordinates** (in this case `['x', 'y']`) as input and outputs the unknown field of the Poisson problem. -# +# +# This neural network takes the **coordinates** (in this case `['x', 'y']`) as input and outputs the unknown field of the Poisson problem. +# # In this tutorial, the neural network is composed of 2 hidden layers, each with 120 neurons and tanh activation. -# In[72]: +# In[ ]: from pina.model import FeedForward @@ -445,33 +445,33 @@ model = FeedForward( # ### Solver Selection -# +# # The thir part of the PINA pipeline involves using a **Solver**. -# +# # In this tutorial, we will use the **classical PINN** solver. However, many other variants are also available and we invite to try them! -# +# # #### Loss Function in PINA -# +# # The loss function in the **classical PINN** is defined as follows: -# +# # $$\theta_{\rm{best}}=\min_{\theta}\mathcal{L}_{\rm{problem}}(\theta), \quad \mathcal{L}_{\rm{problem}}(\theta)= \frac{1}{N_{D}}\sum_{i=1}^N # \mathcal{L}(\Delta\mathcal{M}_{\theta}(\mathbf{x}_i, \mathbf{y}_i) - \sin(\pi x_i)\sin(\pi y_i)) + # \frac{1}{N}\sum_{i=1}^N # \mathcal{L}(\mathcal{M}_{\theta}(\mathbf{x}_i, \mathbf{y}_i))$$ -# +# # This loss consists of: # 1. The **differential equation residual**: Ensures the model satisfies the Poisson equation. # 2. The **boundary condition**: Ensures the model satisfies the Dirichlet boundary condition. -# +# # ### Training -# +# # For the last part of the pipeline we need a `Trainer`. We will train the model for **1000 epochs** using the default optimizer parameters. These parameters can be adjusted as needed. For more details, check the solvers documentation [here](https://mathlab.github.io/PINA/_rst/_code.html#solvers). -# +# # To track metrics during training, we use the **`MetricTracker`** class. -# +# # > **👉 Want to know more about `Trainer` and how to boost PINA performance, check out [this tutorial](https://mathlab.github.io/PINA/_rst/tutorials/tutorial11/tutorial.html).** -# In[73]: +# In[ ]: from pina.solver import PINN @@ -495,7 +495,7 @@ trainer.train() # Done! We can plot the solution and its residual -# In[75]: +# In[ ]: # sample points in the domain. remember to set requires_grad! @@ -527,28 +527,28 @@ with torch.no_grad(): # ## What's Next? -# +# # Congratulations on completing the introductory tutorial of **PINA**! Now that you have a solid foundation, here are a few directions you can explore: -# +# # 1. **Explore Advanced Solvers**: Dive into more advanced solvers like **SAPINN** or **RBAPINN** and experiment with different variations of Physics-Informed Neural Networks. # 2. **Apply PINA to New Problems**: Try solving other types of differential equations or explore inverse problems and parametric problems using the PINA framework. # 3. **Optimize Model Performance**: Use the `Trainer` class to enhance model performance by exploring features like dynamic learning rates, early stopping, and model checkpoints. -# +# # 4. **...and many more!** — There are countless directions to further explore, from testing on different problems to refining the model architecture! -# +# # For more resources and tutorials, check out the [PINA Documentation](https://mathlab.github.io/PINA/). -# -# +# +# # ### References -# +# # [1] *Coscia, Dario, et al. "Physics-informed neural networks for advanced modeling." Journal of Open Source Software, 2023.* -# +# # [2] *Hernández-Lobato, José Miguel, and Ryan Adams. "Probabilistic backpropagation for scalable learning of bayesian neural networks." International conference on machine learning, 2015.* -# +# # [3] *Gal, Yarin, and Zoubin Ghahramani. "Dropout as a bayesian approximation: Representing model uncertainty in deep learning." International conference on machine learning, 2016.* -# +# # [4] *Raissi, Maziar, Paris Perdikaris, and George E. Karniadakis. "Physics-informed neural networks: A deep learning framework for solving forward and inverse problems involving nonlinear partial differential equations." Journal of Computational Physics, 2019.* -# +# # [5] *McClenny, Levi D., and Ulisses M. Braga-Neto. "Self-adaptive physics-informed neural networks." Journal of Computational Physics, 2023.* -# +# # [6] *Anagnostopoulos, Sokratis J., et al. "Residual-based attention in physics-informed neural networks." Computer Methods in Applied Mechanics and Engineering, 2024.* diff --git a/tutorials/tutorial23/tutorial.py b/tutorials/tutorial23/tutorial.py index 54422b8..0fdd0b9 100644 --- a/tutorials/tutorial23/tutorial.py +++ b/tutorials/tutorial23/tutorial.py @@ -2,15 +2,15 @@ # coding: utf-8 # # Tutorial: Data-driven System Identification with SINDy -# +# # [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/mathLab/PINA/blob/master/tutorials/tutorial23/tutorial.ipynb) -# -# +# +# # > ##### ⚠️ ***Before starting:*** # > 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) tutorial. If not, we strongly recommend reviewing them before exploring this advanced topic. -# +# # In this tutorial, we will demonstrate a typical use case of **PINA** for Data-driven system identification using SINDy. The tutorial is largely inspired by the paper [Discovering governing equations from data by sparse identification of nonlinear dynamical systems](dx.doi.org/10.1073/pnas.1517384113). -# +# # Let's start by importing the useful modules: # In[ ]: @@ -48,16 +48,16 @@ from pina.model import SINDy # $$\dot{\boldsymbol{x}}(t)=\boldsymbol{f}(\boldsymbol{x}(t)),$$ # along with suitable initial conditions. # For simplicity, we'll omit the argument of $\boldsymbol{x}$ from this point onward. -# +# # Since $\boldsymbol{f}$ is unknown, we want to model it. # While neural networks could be used to find an expression for $\boldsymbol{f}$, in certain contexts - for instance, to perform long-horizon forecasting - it might be useful to have an **explicit** set of equations describing it, which would also allow for a better degree of **interpretability** of our model. -# +# # As a result, we use SINDy (introduced in [this paper](https://www.pnas.org/doi/full/10.1073/pnas.1517384113)), which we'll describe later on. # Now, instead, we describe the system that is going to be considered in this tutorial: the **Lorenz** system. -# +# # The Lorenz system is a set of three ordinary differential equations and is a simplified model of atmospheric convection. # It is well-known because it can exhibit chaotic behavior, _i.e._, for given values of the parameters solutions are highly sensitive to small perturbations in the initial conditions, making forecasting extremely challenging. -# +# # Mathematically speaking, we can write the Lorenz equations as # $$ # \begin{cases} @@ -67,9 +67,9 @@ from pina.model import SINDy # \end{cases} # $$ # With $\sigma = 10,\, \rho = 28$, and $\beta=8/3$, the solutions trace out the famous butterfly-shaped Lorenz attractor. -# +# # With the following lines of code, we just generate the dataset for SINDy and plot some trajectories. -# +# # **Disclaimer**: of course, here we use the equations defining the Lorenz system just to generate the data. # If we had access to the dynamical term $\boldsymbol{f}$, there would be no need to use SINDy. @@ -126,24 +126,24 @@ plot_n_conditions(X, n_ic_s) # \dot{x}_i = f_i(\boldsymbol{x}) = \sum_{k}\Theta(\boldsymbol{x})_{k}\xi_{k,i}, # $$ # with $\boldsymbol{\xi}_i\in\mathbb{R}^r$ a vector of **coefficients** telling us which terms are active in the expression of $f_i$. -# +# # Since we are in a supervised setting, we assume that we have at our disposal the snapshot matrix $\boldsymbol{X}$ and a matrix $\dot{\boldsymbol{X}}$ containing time **derivatives** at the corresponding time instances. # Then, we can just impose that the previous relation holds on the data at our disposal. # That is, our optimization problem will read as follows: # $$ # \min_{\boldsymbol{\Xi}}\|\dot{\boldsymbol{X}}-\Theta(\boldsymbol{X})\boldsymbol{\Xi}\|_2^2. # $$ -# +# # Notice, however, that the solution to the previous equation might not be **sparse**, as there might be many non-zero terms in it. # In practice, many physical systems are described by a parsimonious and **interpretable** set of equations. # Thus, we also impose a $L^1$ **penalization** on the model weights, encouraging them to be small in magnitude and trying to enforce sparsity. # The final loss is then expressed as -# +# # $$ # \min_{\boldsymbol{\Xi}}\bigl(\|\dot{\boldsymbol{X}}-\Theta(\boldsymbol{X})\boldsymbol{\Xi}\|_2^2 + \lambda\|\boldsymbol{\Xi}\|_1\bigr), # $$ # with $\lambda\in\mathbb{R}^+$ a hyperparameter. -# +# # Let us begin by computing the time derivatives of the data. # Of course, usually we do not have access to the exact time derivatives of the system, meaning that $\dot{\boldsymbol{X}}$ needs to be **approximated**. # Here we do it using a simple Finite Difference (FD) scheme, but [more sophisticated ideas](https://arxiv.org/abs/2505.16058) could be considered. @@ -195,9 +195,9 @@ function_library = [ # ## Training with PINA # We are now ready to train our model! We can use **PINA** to train the model, following the workflow from previous tutorials. -# First, we need to define the problem. In this case, we will use the [`SupervisedProblem`](https://mathlab.github.io/PINA/_rst/problem/zoo/supervised_problem.html#module-pina.problem.zoo.supervised_problem), which expects: -# -# - **Input**: the state variables tensor $\boldsymbol{X}$ containing all the collected snapshots. +# First, we need to define the problem. In this case, we will use the [`SupervisedProblem`](https://mathlab.github.io/PINA/_rst/problem/zoo/supervised_problem.html#module-pina.problem.zoo.supervised_problem), which expects: +# +# - **Input**: the state variables tensor $\boldsymbol{X}$ containing all the collected snapshots. # - **Output**: the corresponding time derivatives $\dot{\boldsymbol{X}}$. # In[ ]: @@ -210,7 +210,7 @@ problem = SupervisedProblem(X_torch, dXdt_torch) # Finally, we will use the `SupervisedSolver` to perform the training as we're dealing with a supervised problem. -# +# # Recall that we should use $L^1$-regularization on the model's weights to ensure sparsity. For the ease of implementation, we adopt $L^2$ regularization, which is less common in SINDy literature but will suffice in our case. # Additionally, more refined strategies could be used, for instance pruning coefficients below a certain **threshold** at every fixed number of epochs, but here we avoid further complications. @@ -246,11 +246,11 @@ trainer.train() # Now we'll print the identified equations and compare them with the original ones. -# +# # Before going on, we underline that after training there might be many coefficients that are small, yet still non-zero. # It is common for SINDy practitioners to interpret these coefficients as noise in the model and prune them. # This is typically done by fixing a threshold $\tau\in\mathbb{R}^+$ and setting to $0$ all those $\xi_{i,j}$ such that $|\xi_{i,j}|<\tau$. -# +# # In the following cell, we also define a function to print the identified model. # In[ ]: @@ -299,9 +299,9 @@ with torch.no_grad(): # prune coefficients # \dot{z}=-\frac{8}{3} z+xy. # \end{cases} # $$ -# +# # That's a good result, especially considering that we did not perform tuning on the weight decay hyperparameter $\lambda$ and did not really care much about other optimization parameters. -# +# # Let's plot a few trajectories! # In[ ]: @@ -328,14 +328,14 @@ plot_n_conditions(X_sim, n_ic_s_test) # Great! We can see that the qualitative behavior of the system is really close to the real one. -# +# # ## What's next? # Congratulations on completing the introductory tutorial on **Data-driven System Identification with SINDy**! Now that you have a solid foundation, here are a few directions to explore: -# -# 1. **Experiment with Dimensionality Reduction techniques** — Try to combine SINDy with different reductions techniques such as POD or autoencoders - or both of them, as done [here](https://www.sciencedirect.com/science/article/abs/pii/S0045793025003019). -# +# +# 1. **Experiment with Dimensionality Reduction techniques** — Try to combine SINDy with different reductions techniques such as POD or autoencoders - or both of them, as done [here](https://www.sciencedirect.com/science/article/abs/pii/S0045793025003019). +# # 2. **Study Parameterized Systems** — Write your own SINDy model for parameterized problems. -# +# # 3. **...and many more!** — The possibilities are vast! Continue experimenting with advanced configurations, solvers, and features in PINA. -# +# # For more resources and tutorials, check out the [PINA Documentation](https://mathlab.github.io/PINA/).