10 Commits

Author SHA1 Message Date
Dario Coscia
f07e59b69b Logo update (#696)
* update logo

* bump version from 0.2.4 to 0.2.5
2025-11-06 17:09:52 +01:00
Giovanni Canali
d4fa3ea9df move to method to the interface (#694) 2025-11-04 10:56:27 +01:00
Giovanni Canali
fca3db7926 fix bugs for helmholtz and advection (#686) 2025-10-30 10:15:38 +01:00
Dario Coscia
64930c431f disable compilation py>=3.14 2025-10-29 17:34:21 +01:00
GiovanniCanali
24d806b262 move equation attributes to correct device 2025-10-29 16:05:50 +01:00
Dario Coscia
9c3e55da91 fix tester workflow
Revert change in #646, from on_pr_target -> on_pr (#692)
2025-10-29 16:04:39 +01:00
GiovanniCanali
256ac9d025 fix python-version error 2025-10-27 13:50:26 +01:00
Giovanni Canali
6935e0d58a Merge pull request #674 from mathLab/dev
Dev updates 0.2.4
2025-10-23 11:43:03 +02:00
Dario Coscia
4860a2db84 update version
Update version due to py3.9 dropping
2025-10-23 11:29:11 +02:00
GiovanniCanali
cb629cc554 remove 3.9 add 3.14 2025-10-23 10:36:47 +02:00
20 changed files with 109 additions and 58 deletions

View File

@@ -11,7 +11,7 @@ jobs:
strategy:
matrix:
os: [windows-latest, macos-latest, ubuntu-latest]
python-version: [3.9, '3.10', '3.11', '3.12', '3.13']
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
steps:
- uses: actions/checkout@v2
- name: Set up Python

View File

@@ -1,7 +1,7 @@
name: "Testing Pull Request"
on:
pull_request_target:
pull_request:
branches:
- "master"
- "dev"
@@ -13,7 +13,7 @@ jobs:
fail-fast: false
matrix:
os: [windows-latest, macos-latest, ubuntu-latest]
python-version: [3.9, '3.10', '3.11', '3.12', '3.13']
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
steps:
- uses: actions/checkout@v4
@@ -75,4 +75,4 @@ jobs:
threshold: 80.123
fail: true
publish: true
coverage-summary-title: "Code Coverage Summary"
coverage-summary-title: "Code Coverage Summary"

View File

@@ -23,7 +23,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.9
python-version: '3.10'
- name: Install dependencies
run: |
@@ -91,7 +91,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.9
python-version: '3.10'
- name: Install dependencies
run: |

View File

@@ -7,8 +7,8 @@ SPDX-License-Identifier: Apache-2.0
<table>
<tr>
<td>
<a href="https://github.com/mathLab/PINA/raw/master/readme/pina_logo.png">
<img src="https://github.com/mathLab/PINA/raw/master/readme/pina_logo.png"
<a href="readme/pina_logo.png">
<img src="readme/pina_logo.png"
alt="PINA logo"
style="width: 220px; aspect-ratio: 1 / 1; object-fit: contain;">
</a>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 KiB

After

Width:  |  Height:  |  Size: 411 KiB

View File

@@ -1,7 +1,6 @@
"""Module for the Equation."""
import inspect
from .equation_interface import EquationInterface
@@ -49,6 +48,10 @@ class Equation(EquationInterface):
:raises RuntimeError: If the underlying equation signature length is not
2 (direct problem) or 3 (inverse problem).
"""
# Move the equation to the input_ device
self.to(input_.device)
# Call the underlying equation based on its signature length
if self.__len_sig == 2:
return self.__equation(input_, output_)
if self.__len_sig == 3:

View File

@@ -239,19 +239,19 @@ class Advection(Equation): # pylint: disable=R0903
)
# Ensure consistency of c length
if len(self.c) != (len(input_lbl) - 1) and len(self.c) > 1:
if self.c.shape[-1] != len(input_lbl) - 1 and self.c.shape[-1] > 1:
raise ValueError(
"If 'c' is passed as a list, its length must be equal to "
"the number of spatial dimensions."
)
# Repeat c to ensure consistent shape for advection
self.c = self.c.repeat(output_.shape[0], 1)
if self.c.shape[1] != (len(input_lbl) - 1):
self.c = self.c.repeat(1, len(input_lbl) - 1)
c = self.c.repeat(output_.shape[0], 1)
if c.shape[1] != (len(input_lbl) - 1):
c = c.repeat(1, len(input_lbl) - 1)
# Add a dimension to c for the following operations
self.c = self.c.unsqueeze(-1)
c = c.unsqueeze(-1)
# Compute the time derivative and the spatial gradient
time_der = grad(output_, input_, components=None, d="t")
@@ -262,7 +262,7 @@ class Advection(Equation): # pylint: disable=R0903
tmp = tmp.transpose(-1, -2)
# Compute advection term
adv = (tmp * self.c).sum(dim=tmp.tensor.ndim - 2)
adv = (tmp * c).sum(dim=tmp.tensor.ndim - 2)
return time_der + adv

View File

@@ -1,6 +1,7 @@
"""Module for the Equation Interface."""
from abc import ABCMeta, abstractmethod
import torch
class EquationInterface(metaclass=ABCMeta):
@@ -33,3 +34,33 @@ class EquationInterface(metaclass=ABCMeta):
:return: The computed residual of the equation.
:rtype: LabelTensor
"""
def to(self, device):
"""
Move all tensor attributes to the specified device.
:param torch.device device: The target device to move the tensors to.
:return: The instance moved to the specified device.
:rtype: EquationInterface
"""
# Iterate over all attributes of the Equation
for key, val in self.__dict__.items():
# Move tensors in dictionaries to the specified device
if isinstance(val, dict):
self.__dict__[key] = {
k: v.to(device) if torch.is_tensor(v) else v
for k, v in val.items()
}
# Move tensors in lists to the specified device
elif isinstance(val, list):
self.__dict__[key] = [
v.to(device) if torch.is_tensor(v) else v for v in val
]
# Move tensor attributes to the specified device
elif torch.is_tensor(val):
self.__dict__[key] = val.to(device)
return self

View File

@@ -101,6 +101,10 @@ class SystemEquation(EquationInterface):
:return: The aggregated residuals of the system of equations.
:rtype: LabelTensor
"""
# Move the equation to the input_ device
self.to(input_.device)
# Compute the residual for each equation
residual = torch.hstack(
[
equation.residual(input_, output_, params_)
@@ -108,6 +112,7 @@ class SystemEquation(EquationInterface):
]
)
# Skip reduction if not specified
if self.reduction is None:
return residual

View File

@@ -41,7 +41,7 @@ class EnEquivariantNetworkBlock(MessagePassing):
DOI: `<https://doi.org/10.48550/arXiv.2102.09844>`_.
"""
def __init__( # pylint: disable=R0913, R0917
def __init__(
self,
node_feature_dim,
edge_feature_dim,
@@ -143,9 +143,7 @@ class EnEquivariantNetworkBlock(MessagePassing):
func=activation,
)
def forward(
self, x, pos, edge_index, edge_attr=None, vel=None
): # pylint: disable=R0917
def forward(self, x, pos, edge_index, edge_attr=None, vel=None):
"""
Forward pass of the block, triggering the message-passing routine.
@@ -171,9 +169,7 @@ class EnEquivariantNetworkBlock(MessagePassing):
edge_index=edge_index, x=x, pos=pos, edge_attr=edge_attr, vel=vel
)
def message(
self, x_i, x_j, pos_i, pos_j, edge_attr
): # pylint: disable=R0917
def message(self, x_i, x_j, pos_i, pos_j, edge_attr):
"""
Compute the message to be passed between nodes and edges.
@@ -238,9 +234,7 @@ class EnEquivariantNetworkBlock(MessagePassing):
return agg_message, agg_m_ij
def update(
self, aggregated_inputs, x, pos, edge_index, vel
): # pylint: disable=R0917
def update(self, aggregated_inputs, x, pos, edge_index, vel):
"""
Update node features, positions, and optionally velocities.

View File

@@ -23,7 +23,7 @@ class EquivariantGraphNeuralOperatorBlock(torch.nn.Module):
<https://arxiv.org/abs/2401.11037>`_
"""
def __init__( # pylint: disable=R0913, R0917
def __init__(
self,
node_feature_dim,
edge_feature_dim,
@@ -101,9 +101,7 @@ class EquivariantGraphNeuralOperatorBlock(torch.nn.Module):
flow=flow,
)
def forward( # pylint: disable=R0917
self, x, pos, vel, edge_index, edge_attr=None
):
def forward(self, x, pos, vel, edge_index, edge_attr=None):
"""
Forward pass of the Equivariant Graph Neural Operator block.
@@ -184,11 +182,7 @@ class EquivariantGraphNeuralOperatorBlock(torch.nn.Module):
weights = torch.complex(real[..., :modes], img[..., :modes])
# Convolution in Fourier space
# torch.fft.rfftn and irfftn are callable functions, but pylint
# incorrectly flags them as E1102 (not callable).
fourier = torch.fft.rfftn(x, dim=[0])[:modes] # pylint: disable=E1102
fourier = torch.fft.rfftn(x, dim=[0])[:modes]
out = torch.einsum(einsum_idx, fourier, weights)
return torch.fft.irfftn( # pylint: disable=E1102
out, s=x.shape[0], dim=0
)
return torch.fft.irfftn(out, s=x.shape[0], dim=0)

View File

@@ -5,9 +5,7 @@ from ..utils import check_positive_integer
from .block.message_passing import EquivariantGraphNeuralOperatorBlock
# Disable pylint warnings for too few public methods (since this is a simple
# model class in a standard PyTorch style)
class EquivariantGraphNeuralOperator(torch.nn.Module): # pylint: disable=R0903
class EquivariantGraphNeuralOperator(torch.nn.Module):
"""
Equivariant Graph Neural Operator (EGNO) for modeling 3D dynamics.
@@ -34,9 +32,7 @@ class EquivariantGraphNeuralOperator(torch.nn.Module): # pylint: disable=R0903
<https://arxiv.org/abs/2401.11037>`_
"""
# Disable pylint warnings for too many arguments in init (since this is a
# model class with many configurable parameters)
def __init__( # pylint: disable=R0913, R0917, R0914
def __init__(
self,
n_egno_layers,
node_feature_dim,

View File

@@ -48,11 +48,10 @@ class HelmholtzProblem(SpatialProblem):
:type alpha: float | int
"""
super().__init__()
self.alpha = alpha
check_consistency(alpha, (int, float))
self.alpha = alpha
def forcing_term(self, input_):
def forcing_term(input_):
"""
Implementation of the forcing term.
"""

View File

@@ -71,9 +71,7 @@ class PINNInterface(SupervisedSolverInterface, metaclass=ABCMeta):
"""
# Override the compilation, compiling only for torch < 2.8, see
# related issue at https://github.com/mathLab/PINA/issues/621
if torch.__version__ < "2.8":
self.trainer.compile = True
else:
if torch.__version__ >= "2.8":
self.trainer.compile = False
warnings.warn(
"Compilation is disabled for torch >= 2.8. "

View File

@@ -174,11 +174,7 @@ class SolverInterface(lightning.pytorch.LightningModule, metaclass=ABCMeta):
:return: The result of the parent class ``setup`` method.
:rtype: Any
"""
if stage == "fit" and self.trainer.compile:
self._setup_compile()
if stage == "test" and (
self.trainer.compile and not self._is_compiled()
):
if self.trainer.compile and not self._is_compiled():
self._setup_compile()
return super().setup(stage)

View File

@@ -1,12 +1,17 @@
"""Module for the Trainer."""
import sys
import warnings
import torch
import lightning
from .utils import check_consistency
from .utils import check_consistency, custom_warning_format
from .data import PinaDataModule
from .solver import SolverInterface, PINNInterface
# set the warning for compile options
warnings.formatwarning = custom_warning_format
warnings.filterwarnings("always", category=UserWarning)
class Trainer(lightning.pytorch.Trainer):
"""
@@ -49,7 +54,8 @@ class Trainer(lightning.pytorch.Trainer):
:param float val_size: The percentage of elements to include in the
validation dataset. Default is ``0.0``.
:param bool compile: If ``True``, the model is compiled before training.
Default is ``False``. For Windows users, it is always disabled.
Default is ``False``. For Windows users, it is always disabled. Not
supported for python version greater or equal than 3.14.
:param bool repeat: Whether to repeat the dataset data in each
condition during training. For further details, see the
:class:`~pina.data.data_module.PinaDataModule` class. Default is
@@ -104,8 +110,17 @@ class Trainer(lightning.pytorch.Trainer):
super().__init__(**kwargs)
# checking compilation and automatic batching
if compile is None or sys.platform == "win32":
# compilation disabled for Windows and for Python 3.14+
if (
compile is None
or sys.platform == "win32"
or sys.version_info >= (3, 14)
):
compile = False
warnings.warn(
"Compilation is disabled for Python 3.14+ and for Windows.",
UserWarning,
)
repeat = repeat if repeat is not None else False
@@ -325,3 +340,23 @@ class Trainer(lightning.pytorch.Trainer):
if batch_size is not None:
check_consistency(batch_size, int)
return pin_memory, num_workers, shuffle, batch_size
@property
def compile(self):
"""
Whether compilation is required or not.
:return: ``True`` if compilation is required, ``False`` otherwise.
:rtype: bool
"""
return self._compile
@compile.setter
def compile(self, value):
"""
Setting the value of compile.
:param bool value: Whether compilation is required or not.
"""
check_consistency(value, bool)
self._compile = value

View File

@@ -1,6 +1,6 @@
[project]
name = "pina-mathlab"
version = "0.2.3"
version = "0.2.5"
description = "Physic Informed Neural networks for Advance modeling."
readme = "README.md"
authors = [
@@ -19,7 +19,7 @@ dependencies = [
"torch_geometric",
"matplotlib",
]
requires-python = ">=3.9"
requires-python = ">=3.10"
[project.optional-dependencies]
doc = [

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 411 KiB

View File

@@ -104,7 +104,7 @@ def test_advection_equation(c):
# Should fail if c is a list and its length != spatial dimension
with pytest.raises(ValueError):
Advection([1, 2, 3])
equation = Advection([1, 2, 3])
residual = equation.residual(pts, u)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 411 KiB