Source code for pyhazards.models.neuralhydrology_ealstm

from __future__ import annotations

from typing import Any

import torch
import torch.nn as nn


def _streamflow_inputs(batch: Any) -> torch.Tensor:
    x = batch["x"] if isinstance(batch, dict) else batch
    if x.ndim != 4:
        raise ValueError("EA-LSTM expects inputs shaped (batch, history, nodes, features).")
    return x


[docs] class NeuralHydrologyEALSTM(nn.Module): """Entity-aware LSTM style streamflow baseline.""" def __init__( self, input_dim: int = 2, hidden_dim: int = 64, num_layers: int = 1, out_dim: int = 1, dropout: float = 0.1, ): super().__init__() self.hidden_dim = int(hidden_dim) self.out_dim = int(out_dim) self.dynamic_encoder = nn.LSTM( input_size=input_dim, hidden_size=hidden_dim, num_layers=num_layers, batch_first=True, dropout=dropout if num_layers > 1 else 0.0, ) self.static_gate = nn.Sequential( nn.Linear(input_dim, hidden_dim), nn.Sigmoid(), ) self.head = nn.Linear(hidden_dim, self.out_dim)
[docs] def forward(self, batch: Any) -> torch.Tensor: x = _streamflow_inputs(batch) bsz, history, nodes, features = x.shape series = x.permute(0, 2, 1, 3).reshape(bsz * nodes, history, features) encoded, _ = self.dynamic_encoder(series) static_features = series.mean(dim=1) gated = encoded[:, -1] * self.static_gate(static_features) preds = self.head(gated) return preds.view(bsz, nodes, self.out_dim)
[docs] def neuralhydrology_ealstm_builder( task: str, input_dim: int = 2, hidden_dim: int = 64, num_layers: int = 1, out_dim: int = 1, dropout: float = 0.1, **kwargs, ) -> nn.Module: _ = kwargs if task.lower() != "regression": raise ValueError("NeuralHydrologyEALSTM only supports regression for streamflow forecasting.") return NeuralHydrologyEALSTM( input_dim=input_dim, hidden_dim=hidden_dim, num_layers=num_layers, out_dim=out_dim, dropout=dropout, )
__all__ = ["NeuralHydrologyEALSTM", "neuralhydrology_ealstm_builder"]