Skip to content

adding graph timeseries#807

Open
ndem0 wants to merge 7 commits into
0.3from
0.3-ts-graph
Open

adding graph timeseries#807
ndem0 wants to merge 7 commits into
0.3from
0.3-ts-graph

Conversation

@ndem0

@ndem0 ndem0 commented Jun 11, 2026

Copy link
Copy Markdown
Member

Description

Added the graph time series condition.

Checklist

  • Code follows the project’s Code Style Guidelines
  • Tests have been added or updated
  • Documentation has been updated if necessary
  • Pull request is linked to an open issue

@ndem0 ndem0 requested review from a team and dario-coscia as code owners June 11, 2026 15:40

@dario-coscia dario-coscia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor comment on an extra feature, but overall looks great. Be aware that tests are failing

residuals = []

# Iterate over the time steps
for step in range(1, batch["input"].shape[2]):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GiovanniCanali what do you think to add the pushforward trick here as well? Is it a condition thing or solver thing? Having it is very easy (we just need the no grad option)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely a condition-level concern, since gradient computation must be enabled or disabled when evaluating the residual between the model prediction and the target. As you noted, the implementation should be straightforward. Since the forward method is inherited from TimeSeriesCondition, we should consider implementing it directly there.

@GiovanniCanali GiovanniCanali added enhancement New feature or request pr-to-fix Label for PR that needs modification 0.3 Related to 0.3 release labels Jun 18, 2026

@GiovanniCanali GiovanniCanali left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is still a work in progress, but I have added a few comments below.

@ndem0, please remember to add the appropriate mapping to the Condition factory so that the software automatically dispatches graph time-series conditions and standard time-series conditions to the correct classes.


return _DataManager(input=graph)

def evaluate(self, batch, solver):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method appears to be identical to the one defined in TimeSeriesCondition and should therefore be inherited without modification.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now some small differences in accessing the node data, I would keep it otherwise several if are needed in the TimeSerieCondition

return torch.stack(residuals).as_subclass(torch.Tensor)

@property
def input(self):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method appears to be identical to the one defined in TimeSeriesCondition and should therefore be inherited without modification.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

residuals = []

# Iterate over the time steps
for step in range(1, batch["input"].shape[2]):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely a condition-level concern, since gradient computation must be enabled or disabled when evaluating the residual between the model prediction and the target. As you noted, the implementation should be straightforward. Since the forward method is inherited from TimeSeriesCondition, we should consider implementing it directly there.

@ndem0 ndem0 linked an issue Jun 18, 2026 that may be closed by this pull request
@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

badge

Code Coverage Summary

Filename                                                                       Stmts    Miss  Cover    Missing
---------------------------------------------------------------------------  -------  ------  -------  ----------------------------------------------------------------------------------------------
__init__.py                                                                       12       3  75.00%   28-37
graph.py                                                                           2       0  100.00%
label_tensor.py                                                                    2       0  100.00%
operator.py                                                                        2       0  100.00%
trainer.py                                                                         2       0  100.00%
type_checker.py                                                                    2       0  100.00%
utils.py                                                                           2       0  100.00%
_src/__init__.py                                                                   0       0  100.00%
_src/adaptive_function/__init__.py                                                 0       0  100.00%
_src/adaptive_function/adaptive_celu.py                                            6       0  100.00%
_src/adaptive_function/adaptive_elu.py                                             6       0  100.00%
_src/adaptive_function/adaptive_exp.py                                             6       0  100.00%
_src/adaptive_function/adaptive_function_interface.py                             13       0  100.00%
_src/adaptive_function/adaptive_gelu.py                                            6       0  100.00%
_src/adaptive_function/adaptive_mish.py                                            6       0  100.00%
_src/adaptive_function/adaptive_relu.py                                            6       0  100.00%
_src/adaptive_function/adaptive_sigmoid.py                                         6       0  100.00%
_src/adaptive_function/adaptive_silu.py                                            6       0  100.00%
_src/adaptive_function/adaptive_siren.py                                           6       0  100.00%
_src/adaptive_function/adaptive_softmax.py                                         6       0  100.00%
_src/adaptive_function/adaptive_softmin.py                                         6       0  100.00%
_src/adaptive_function/adaptive_tanh.py                                            6       0  100.00%
_src/adaptive_function/base_adaptive_function.py                                  43       2  95.35%   142, 146
_src/callback/__init__.py                                                          0       0  100.00%
_src/callback/optim/__init__.py                                                    0       0  100.00%
_src/callback/optim/switch_optimizer.py                                           20       0  100.00%
_src/callback/optim/switch_scheduler.py                                           20       0  100.00%
_src/callback/processing/__init__.py                                               0       0  100.00%
_src/callback/processing/data_normalizer.py                                       61       0  100.00%
_src/callback/processing/metric_tracker.py                                        29       1  96.55%   96
_src/callback/processing/pina_progress_bar.py                                     26       1  96.15%   105
_src/callback/refinement/__init__.py                                               0       0  100.00%
_src/callback/refinement/base_refinement.py                                       41       3  92.68%   71, 78, 96
_src/callback/refinement/r3_refinement.py                                         25       0  100.00%
_src/callback/refinement/refinement_interface.py                                  14       0  100.00%
_src/condition/__init__.py                                                         0       0  100.00%
_src/condition/base_condition.py                                                  43       4  90.70%   102-103, 116, 142
_src/condition/condition.py                                                       18       3  83.33%   143, 159-164
_src/condition/condition_interface.py                                             26       0  100.00%
_src/condition/data_condition.py                                                  34       0  100.00%
_src/condition/domain_equation_condition.py                                       31       1  96.77%   44
_src/condition/graph_time_series_condition.py                                     44       2  95.45%   76, 117
_src/condition/input_equation_condition.py                                        31       0  100.00%
_src/condition/input_target_condition.py                                          29       0  100.00%
_src/condition/time_series_condition.py                                           63       1  98.41%   207
_src/core/__init__.py                                                              0       0  100.00%
_src/core/graph.py                                                               113      16  85.84%   99-100, 112, 124, 126, 142, 144, 163-169, 182, 271
_src/core/label_tensor.py                                                        251      26  89.64%   81, 121, 148, 165, 177, 182, 188-193, 273, 280, 332, 348, 490, 537, 629, 664-673, 684-689, 710
_src/core/operator.py                                                             72       2  97.22%   268, 464
_src/core/trainer.py                                                              75       7  90.67%   154-161, 165-172, 180-188, 311
_src/core/type_checker.py                                                         22       0  100.00%
_src/core/utils.py                                                                73       5  93.15%   141, 178, 181, 184, 268
_src/data/__init__.py                                                              0       0  100.00%
_src/data/aggregator.py                                                           21       1  95.24%   53
_src/data/condition_subset.py                                                     26       3  88.46%   91-99
_src/data/creator.py                                                              57       8  85.96%   118, 224-238
_src/data/data_module.py                                                          55       5  90.91%   91-92, 96-97, 179
_src/data/single_batch_data_loader.py                                             25       0  100.00%
_src/data/manager/batch_manager.py                                                15       2  86.67%   44-45
_src/data/manager/data_manager.py                                                 12       1  91.67%   39
_src/data/manager/data_manager_interface.py                                       11       0  100.00%
_src/data/manager/graph_data_manager.py                                           79       4  94.94%   75, 165, 185, 207
_src/data/manager/tensor_data_manager.py                                          34       2  94.12%   82, 108
_src/domain/__init__.py                                                            0       0  100.00%
_src/domain/base_domain.py                                                        68       0  100.00%
_src/domain/base_operation.py                                                     45       4  91.11%   63, 106, 129, 139
_src/domain/cartesian_domain.py                                                   54       1  98.15%   68
_src/domain/difference.py                                                         27       0  100.00%
_src/domain/domain_interface.py                                                   25       0  100.00%
_src/domain/ellipsoid_domain.py                                                   81       4  95.06%   98, 108-109, 253
_src/domain/exclusion.py                                                          35       1  97.14%   107
_src/domain/intersection.py                                                       34       1  97.06%   97
_src/domain/operation_interface.py                                                 9       0  100.00%
_src/domain/simplex_domain.py                                                     83       5  93.98%   212, 226, 237, 249, 288
_src/domain/union.py                                                              24       0  100.00%
_src/equation/__init__.py                                                          0       0  100.00%
_src/equation/base_equation.py                                                    14       1  92.86%   52
_src/equation/equation.py                                                         16       1  93.75%   62
_src/equation/equation_interface.py                                                6       0  100.00%
_src/equation/system_equation.py                                                  23       0  100.00%
_src/equation/zoo/__init__.py                                                      0       0  100.00%
_src/equation/zoo/acoustic_wave_equation.py                                       14       0  100.00%
_src/equation/zoo/advection_equation.py                                           30       0  100.00%
_src/equation/zoo/allen_cahn_equation.py                                          16       0  100.00%
_src/equation/zoo/burgers_equation.py                                             24       0  100.00%
_src/equation/zoo/diffusion_reaction_equation.py                                  17       0  100.00%
_src/equation/zoo/fixed_flux.py                                                   13       0  100.00%
_src/equation/zoo/fixed_gradient.py                                               13       0  100.00%
_src/equation/zoo/fixed_laplacian.py                                              18       2  88.89%   63-68
_src/equation/zoo/fixed_value.py                                                  12       0  100.00%
_src/equation/zoo/helmholtz_equation.py                                           14       0  100.00%
_src/equation/zoo/poisson_equation.py                                             12       0  100.00%
_src/loss/__init__.py                                                              0       0  100.00%
_src/loss/base_dual_loss.py                                                       10       0  100.00%
_src/loss/dual_loss_interface.py                                                   7       0  100.00%
_src/loss/lp_loss.py                                                              19       0  100.00%
_src/loss/power_loss.py                                                           15       0  100.00%
_src/loss/sinkhorn_loss.py                                                        28       0  100.00%
_src/model/__init__.py                                                             0       0  100.00%
_src/model/average_neural_operator.py                                             31       2  93.55%   73, 82
_src/model/deeponet.py                                                            85      13  84.71%   179-182, 201, 232, 271, 281, 291, 301, 311, 321, 476, 486
_src/model/equivariant_graph_neural_operator.py                                   51       1  98.04%   219
_src/model/feed_forward.py                                                        89      11  87.64%   58, 195, 200, 278-292
_src/model/fourier_neural_operator.py                                             78      10  87.18%   100-104, 114, 159-163, 222, 224, 246, 346
_src/model/graph_neural_operator.py                                               40       2  95.00%   58, 60
_src/model/kernel_neural_operator.py                                              34       6  82.35%   83-84, 103-104, 123-124
_src/model/kolmogorov_arnold_network.py                                           14       0  100.00%
_src/model/low_rank_neural_operator.py                                            27       2  92.59%   89, 98
_src/model/multi_feed_forward.py                                                  12       5  58.33%   25-31
_src/model/pirate_network.py                                                      28       1  96.43%   119
_src/model/sindy.py                                                               21       0  100.00%
_src/model/spline.py                                                             124       6  95.16%   133, 156, 164, 236, 450, 475
_src/model/spline_surface.py                                                      68      12  82.35%   151-152, 187-190, 200, 213-218, 258
_src/model/vectorized_spline.py                                                  153      15  90.20%   208, 311, 317, 347-348, 408, 410, 446, 448, 450, 529, 542, 615, 630, 647
_src/model/block/__init__.py                                                       0       0  100.00%
_src/model/block/average_neural_operator_block.py                                 12       0  100.00%
_src/model/block/convolution.py                                                   64      13  79.69%   77, 81, 85, 91, 97, 111, 114, 151, 161, 171, 181, 191, 201
_src/model/block/convolution_2d.py                                               146      27  81.51%   155, 162, 282, 314, 379-433, 456
_src/model/block/embedding.py                                                     48       7  85.42%   93, 143-146, 155, 168
_src/model/block/fourier_block.py                                                 31       0  100.00%
_src/model/block/gno_block.py                                                     22       4  81.82%   73-77, 87
_src/model/block/integral.py                                                      18       4  77.78%   22-25, 71
_src/model/block/kan_block.py                                                     43       0  100.00%
_src/model/block/low_rank_block.py                                                24       0  100.00%
_src/model/block/orthogonal.py                                                    37       0  100.00%
_src/model/block/pirate_network_block.py                                          25       1  96.00%   89
_src/model/block/pod_block.py                                                     75      10  86.67%   56-59, 71, 84, 114, 151-156, 191, 216
_src/model/block/rbf_block.py                                                    179      25  86.03%   18, 42, 53, 64, 75, 86, 97, 223, 280, 282, 298, 301, 329, 335, 363, 367, 511-524
_src/model/block/residual.py                                                      46       0  100.00%
_src/model/block/spectral.py                                                      83       4  95.18%   132, 140, 262, 270
_src/model/block/stride.py                                                        28       7  75.00%   55, 58, 61, 67, 72-74
_src/model/block/utils_convolution.py                                             22       3  86.36%   58-60
_src/model/block/message_passing/__init__.py                                       0       0  100.00%
_src/model/block/message_passing/deep_tensor_network_block.py                     21       0  100.00%
_src/model/block/message_passing/en_equivariant_network_block.py                  47       1  97.87%   164
_src/model/block/message_passing/equivariant_graph_neural_operator_block.py       36       0  100.00%
_src/model/block/message_passing/interaction_network_block.py                     23       0  100.00%
_src/model/block/message_passing/radial_field_network_block.py                    20       0  100.00%
_src/optim/__init__.py                                                             0       0  100.00%
_src/optim/optimizer_interface.py                                                  7       0  100.00%
_src/optim/scheduler_interface.py                                                  7       0  100.00%
_src/optim/torch_optimizer.py                                                     14       0  100.00%
_src/optim/torch_scheduler.py                                                     16       0  100.00%
_src/problem/__init__.py                                                           0       0  100.00%
_src/problem/base_problem.py                                                      86       7  91.86%   52-53, 65-72
_src/problem/inverse_problem.py                                                   23       0  100.00%
_src/problem/parametric_problem.py                                                 9       0  100.00%
_src/problem/problem_interface.py                                                 25       0  100.00%
_src/problem/spatial_problem.py                                                    9       0  100.00%
_src/problem/time_dependent_problem.py                                             9       0  100.00%
_src/problem/zoo/__init__.py                                                       0       0  100.00%
_src/problem/zoo/acoustic_wave_problem.py                                         33       2  93.94%   25-26
_src/problem/zoo/advection_problem.py                                             25       1  96.00%   22
_src/problem/zoo/allen_cahn_problem.py                                            25       0  100.00%
_src/problem/zoo/burgers_problem.py                                               23       1  95.65%   23
_src/problem/zoo/diffusion_reaction_problem.py                                    35       3  91.43%   25-33
_src/problem/zoo/helmholtz_problem.py                                             30       0  100.00%
_src/problem/zoo/inverse_poisson_problem.py                                       52       3  94.23%   48-54
_src/problem/zoo/poisson_problem.py                                               17       0  100.00%
_src/problem/zoo/supervised_problem.py                                            11       0  100.00%
_src/solver/autoregressive_ensemble_solver.py                                      8       0  100.00%
_src/solver/autoregressive_single_model_solver.py                                  8       0  100.00%
_src/solver/base_solver.py                                                       113       4  96.46%   200, 207, 331, 388
_src/solver/causal_physics_informed_single_model_solver.py                        68       5  92.65%   140, 159, 218, 295, 320
_src/solver/competitive_physics_informed_solver.py                                59       0  100.00%
_src/solver/ensemble_solver.py                                                    10       0  100.00%
_src/solver/gradient_physics_informed_single_model_solver.py                      11       0  100.00%
_src/solver/multi_model_solver.py                                                 10       0  100.00%
_src/solver/physics_informed_ensemble_solver.py                                    9       0  100.00%
_src/solver/physics_informed_single_model_solver.py                                9       0  100.00%
_src/solver/rba_physics_informed_single_model_solver.py                           11       0  100.00%
_src/solver/self_adaptive_physics_informed_solver.py                              72       1  98.61%   126
_src/solver/single_model_solver.py                                                 8       0  100.00%
_src/solver/solver_interface.py                                                   21       0  100.00%
_src/solver/supervised_ensemble_solver.py                                          6       0  100.00%
_src/solver/supervised_single_model_solver.py                                      6       0  100.00%
_src/solver/mixin/autoregressive_mixin.py                                         50      11  78.00%   159-186
_src/solver/mixin/condition_aggregator_mixin.py                                   11       0  100.00%
_src/solver/mixin/ensemble_mixin.py                                                5       0  100.00%
_src/solver/mixin/gradient_enhanced_mixin.py                                      39       2  94.87%   67, 76
_src/solver/mixin/manual_optimization_mixin.py                                    16       0  100.00%
_src/solver/mixin/multi_model_mixin.py                                            24       1  95.83%   27
_src/solver/mixin/physics_informed_mixin.py                                        8       0  100.00%
_src/solver/mixin/residual_based_attention_mixin.py                               53       1  98.11%   89
_src/solver/mixin/single_model_mixin.py                                           19       0  100.00%
_src/weighting/__init__.py                                                         0       0  100.00%
_src/weighting/base_weighting.py                                                  28       1  96.43%   49
_src/weighting/linear_weighting.py                                                16       0  100.00%
_src/weighting/no_weighting.py                                                     4       0  100.00%
_src/weighting/ntk_weighting.py                                                   18       0  100.00%
_src/weighting/scalar_weighting.py                                                13       0  100.00%
_src/weighting/self_adaptive_weighting.py                                         12       0  100.00%
_src/weighting/weighting_interface.py                                             11       0  100.00%
adaptive_function/__init__.py                                                     15       0  100.00%
callback/__init__.py                                                              15       3  80.00%   37-46
condition/__init__.py                                                             10       0  100.00%
data/__init__.py                                                                  12       3  75.00%   24-33
data/manager.py                                                                    6       0  100.00%
domain/__init__.py                                                                11       0  100.00%
equation/__init__.py                                                              13       4  69.23%   44-54
equation/zoo.py                                                                   12       0  100.00%
loss/__init__.py                                                                  14       4  71.43%   31-41
model/__init__.py                                                                 16       0  100.00%
model/block/__init__.py                                                           14       0  100.00%
model/block/message_passing.py                                                     6       0  100.00%
optim/__init__.py                                                                 11       3  72.73%   25-34
problem/__init__.py                                                               13       3  76.92%   26-35
problem/zoo.py                                                                    10       0  100.00%
solver/__init__.py                                                                27       6  77.78%   95-109
solver/mixin.py                                                                   10      10  0.00%    3-29
weighting/__init__.py                                                              8       0  100.00%
TOTAL                                                                           5794     394  93.20%

Results for commit: 07b8b60

Minimum allowed coverage is 80.123%

♻️ This comment has been updated with latest results

@ndem0 ndem0 added pr-to-review Label for PR that are ready to been reviewed and removed pr-to-fix Label for PR that needs modification labels Jun 22, 2026

@GiovanniCanali GiovanniCanali left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything looks great! My only remaining doubt concerns the bypassed check in core/graph.py. Once that is addressed, you have the green light from my side.

Comment thread pina/_src/core/graph.py
if "x" in kwargs:
x = kwargs["x"]
self._check_x_consistency(x, pos)
# self._check_x_consistency(x, pos)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this check bypassed?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For time-series, you typically have 3D tensor: n_nodes, n_timestep, n_features (even more sometimes...)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we delete the _check_x_consistency method then?

expected_shape = torch.Size([n_nodes, n_windows, unroll_length, 2])
assert item.input.x.shape == expected_shape

# TODO: Why this test?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this if it is no longer tested. This was originally added to test_time_series_condition to verify that temporal sequentiality is preserved when sequences are not randomized.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would keep them, but I was not able to find where the batches are actually built. For the moment I just commented it.

Condition <condition/condition.rst>
Data Condition <condition/data_condition.rst>
Domain Equation Condition <condition/domain_equation_condition.rst>
Graph Time Series Condition <condition/graph_time_series_condition.rst>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed there is neither a rst file for Time Series Condition nor a reference in the _code.rst file. Is it possibile to add it in this PR, instead of opening a new one?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

@GiovanniCanali

Copy link
Copy Markdown
Collaborator

Also, remember to run the black formatter before merging! Two files to be fixed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

0.3 Related to 0.3 release enhancement New feature or request pr-to-review Label for PR that are ready to been reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TimeSeries condition for graph data

3 participants