Skip to content

v1.0.0: PINN with one output and normalized loss terms, missing splits, no legacy code - #79

Merged
PatrickHenkel1 merged 1 commit into
refactoring_v1.0.0from
v1/pinn-bugfixes-legacy
Nov 6, 2026
Merged

PatrickHenkel1 merged 1 commit into
refactoring_v1.0.0from
v1/pinn-bugfixes-legacy

Conversation

@PatrickHenkel1

Copy link
Copy Markdown
Collaborator

This PR is stacked on #78 (CI/Sphinx): until #78 is merged, the diff also contains its changes. It reworks the PINN, fixes training on data without a test split, and removes the 0.x compatibility code.

PINN: one output, normalized loss terms

The ideas come from branch 67-pinns (normalizing the terms, per-term metrics, early stopping on the data loss) and were implemented anew.

prep = PreprocessingSingleStep(inputs=[...], output='reaPHeaPum_y', columns={'pinn': physics})
m = PINNModel(physics_targets={'pinn': 0.5})
td = prep.pipeline(path, model=m)   # the model adds its physics targets as loss_columns
m.pipeline(td)

Physics targets are loss columns, not outputs. PreprocessingData(loss_columns=...) holds columns that are used only in the loss:

  • 'y:name' is the value at the predicted time, 'name' the value at the time of the inputs.
  • Models declare them via required_loss_columns.
  • The keras model has one output, so a PINN can now be exported to AgentLib-MPC; before, it had two outputs and was rejected.

Loss:

L = mse(ŷ, y)/s + Σ w_i · mse(ŷ, p_i)/s,   s = var(y_train)
  • No averaging over the terms anymore. Before, a physics term silently halved the weight of the data.
  • The normalization makes the loss dimensionless: the data term is 1 − R², and w_i is the relative importance of term i.
  • With terms in the same unit, the normalization does not change the ratio between the terms.
  • The weights stay fixed and are chosen by validation. Adaptive schemes (GradNorm, learning rate annealing, ReLoBRaLo) target PDE residuals of very different magnitudes; here the weight encodes how much the physics is trusted.

Training and evaluation:

  • Loss and metrics are registered keras classes (physXAI.models.ann.pinn.physics).
  • The history holds data_loss, <target>_loss and rmse.
  • Early stopping monitors val_data_loss by default (early_stopping_on='total' is the alternative).
  • td.metrics.physics holds the disagreement with each physics target per split.

Alignment (documented and tested): physics targets are taken at the predicted time, like the output. For a step model with Change(T), the column is shifted with .lag(1). On the simulated room, the exact physics then equals the target.

P_hp_pinn.py (50 epochs): R² train/val/test = 0.84 / 0.83 / 0.80.

Bugs fixed

  • PINN loss arguments swapped: multi_y_loss called loss(y_pred, y_true), which is wrong for asymmetric losses.
  • PINN RMSE metric: it accumulated over the data and physics terms, so the value was meaningless.
  • PINN rescaling: the output rescaling of the CMNN also averaged over the physics column. This is resolved by the single output.
  • test_size=0 (from branch 14): X_test/y_test were empty arrays instead of None, and the evaluation predicted on an empty set. Now every model skips a missing split (single step, horizon, PINN, RNN, report, Run.save, ModelComparison), tested with (test, val) ∈ {(0, .2), (.2, 0), (0, 0)}.
  • px.Linear(bias=False) with normalized inputs now warns: the centring acts as a bias.
  • nominal_range must now be ordered (min, max).

Legacy removed (major release)

removed replacement
recursive_feature_elimination_pipeline, save_result FeatureSelection(...).run(path), result.save(run)
train_kpis / val_kpis / test_kpis, get_metrics() by_output, td.metrics.kpi('test', 'rmse')
td.columns, td.output td.inputs, td.outputs
PINNModel(pinn_weights), multi_y_loss, output=['y', 'pinn'] physics_targets, output='y'
init_layer, init_features, warmup_columns_*, PreprocessingMultiStep(batch_size) — (unknown arguments of the preprocessing now raise a TypeError)

Kept on purpose:

  • The keras custom object names, because AgentLib loads old .keras files.
  • Reading 0.x runs in ModelComparison (requested).
  • split='random_rows' as a comparison option.

The migration guide and the CHANGELOG are updated.

Tests

  • Unit tests: 145 passed, 1 skipped. New in unittests/test_models/test_pinn.py:
    • loss against a hand computation, serialization;
    • scale from the train split only;
    • history keys and early stopping monitor;
    • physics metrics;
    • export to AgentLib with one output, and the saved model gives the same predictions;
    • alignment of the physics targets;
    • missing splits for LinReg, ANN, ModularANN with RecursiveTraining, PINN, RNN and the comparison.
  • Docs and lint: strict Sphinx build and ruff are clean.
  • Executables: the affected executables pass the smoke run.

🤖 Generated with Claude Code

…gacy code

- PINNModel(physics_targets={'pinn': w}): physics targets are loss columns (PreprocessingData
  loss_columns, from model.required_loss_columns), the model has one output and is exportable;
  loss mse/s + sum w_i mse_i/s with s = var(y_train), not averaged; per-term metrics and history;
  early stopping on the validation data loss; MetricsPINN with a physics block
- fixes: swapped loss arguments, stateful RMSE inside the loss, halved data weight, rescaling over
  physics columns, test_size=0 for single step data, px.Linear(bias=False) warning, nominal_range check
- removed 0.x compatibility: recursive_feature_elimination_pipeline/save_result (-> result.save),
  flat metrics (-> by_output, kpi()), td.columns/td.output, pinn_weights/multi_y_loss, init_layer,
  init_features, warmup_columns_*, batch_size of PreprocessingMultiStep; unknown arguments raise

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings November 6, 2026 18:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

Base automatically changed from v1/ci-template-sphinx to refactoring_v1.0.0 November 6, 2026 18:14
@PatrickHenkel1
PatrickHenkel1 merged commit 793eedb into refactoring_v1.0.0 Nov 6, 2026
23 checks passed
@PatrickHenkel1
PatrickHenkel1 deleted the v1/pinn-bugfixes-legacy branch November 6, 2026 18:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants