From 70e4873531b1c54dfc0364ef9f54fde692e35ef7 Mon Sep 17 00:00:00 2001 From: Obaidullah Date: Thu, 27 Aug 2026 03:58:24 +0500 Subject: [PATCH 1/2] [DOC]: Add RecurrentNetwork usage example - Add doctest example showing complete workflow: data -> dataset -> model -> train -> predict - Uses generate_ar_data for self-contained example - Includes target_lags demonstration for RNN Fixes #2377 --- pytorch_forecasting/models/rnn/_rnn.py | 74 ++++++++++++++++++++------ 1 file changed, 57 insertions(+), 17 deletions(-) diff --git a/pytorch_forecasting/models/rnn/_rnn.py b/pytorch_forecasting/models/rnn/_rnn.py index 578d4ac0a..701264071 100644 --- a/pytorch_forecasting/models/rnn/_rnn.py +++ b/pytorch_forecasting/models/rnn/_rnn.py @@ -98,6 +98,49 @@ def __init__( loss (MultiHorizonMetric, optional): loss: loss function taking prediction and targets. logging_metrics (nn.ModuleList, optional): Metrics to log during training. Defaults to nn.ModuleList([SMAPE(), MAE(), RMSE(), MAPE(), MASE()]). + Example: + >>> import lightning.pytorch as pl + >>> from pytorch_forcasting import RecurrentNetwork,TimeSeriesDataSet + >>> from pytorch_forcasting.data.examples import generate_ar_data + >>> data = generate_ar_data(n_series=10, timesteps = 100, seed = 42) + >>> data["time_idx"] = data["time_idx"].astype(int) + >>> max_encoder_length = 24 + >>> max_prediction_length = 6 + training = TimeSeriesDataSet( + ... data, + ... time_idx = "time_idx", + ... target = "value", + ... group_ids = ["series"], + ... max_encoder_length = max_encoder_length, + ... max_prediction_length = max_prediction_length, + ... time_vary_unknown_reals= ["value"], + ... target_lags = {"value": [1, 2, 3, 6, 12, 24]}, + ... add_relative_time_idx = True, + ... add_target_scales = True, + ... add_encoder_length = True, + ... ) + >>> validation = TimeSeriesDataSet.from_dataset( + ... training, data, predict=True, stop_randomization=True + ... ) + >>> train_dataloader = training.to_dataloader(train=True, batch_size=32, num_workers=0) + >>> val_dataloader = validation.to_dataloader(train=False, batch_size=32, num_workers=0) + >>> rnn = RecurrentNetwork.from_dataset( + ... training, + ... cell_type = "LSTM", + ... hidden_size = 32, + ... rnn_layers = 2, + ... dropout = 0.1, + ... learning_rate = 1e-3, + ... log_interval = 10, + ...) + >>> trainer = pl.Trainer( + ... max_epochs = 1, + ... accelerator = "cpu", + ... enable_checkpointing = False, + ... logger = False + ... ) + >>> trainer.fit(rnn, train_dataloaders = train_dataloader, val_dataloaders = val_dataloader) + >>> predictions = rnn.predict(val_dataloader, trainer = trainer) """ # noqa : E501 if static_categoricals is None: static_categoricals = [] @@ -148,9 +191,9 @@ def __init__( " be the same apart from target variable" ) for targeti in to_list(target): - assert ( - targeti in time_varying_reals_encoder - ), f"target {targeti} has to be real" # todo: remove this restriction + assert targeti in time_varying_reals_encoder, ( + f"target {targeti} has to be real" + ) # todo: remove this restriction assert (isinstance(target, str) and isinstance(loss, MultiHorizonMetric)) or ( isinstance(target, tuple | list) and isinstance(loss, MultiLoss) @@ -174,9 +217,9 @@ def __init__( self.output_projector = nn.Linear( self.hparams.hidden_size, self.hparams.output_size ) - assert not isinstance( - self.loss, QuantileLoss - ), "QuantileLoss does not work with recurrent network" + assert not isinstance(self.loss, QuantileLoss), ( + "QuantileLoss does not work with recurrent network" + ) else: # multi target self.output_projector = nn.ModuleList( [ @@ -185,9 +228,9 @@ def __init__( ] ) for l in self.loss: - assert not isinstance( - l, QuantileLoss - ), "QuantileLoss does not work with recurrent network" + assert not isinstance(l, QuantileLoss), ( + "QuantileLoss does not work with recurrent network" + ) @classmethod def from_dataset( @@ -213,14 +256,11 @@ def from_dataset( dataset=dataset, kwargs=kwargs, default_loss=MAE() ) ) - assert ( - not isinstance(dataset.target_normalizer, NaNLabelEncoder) - and ( - not isinstance(dataset.target_normalizer, MultiNormalizer) - or all( - not isinstance(normalizer, NaNLabelEncoder) - for normalizer in dataset.target_normalizer - ) + assert not isinstance(dataset.target_normalizer, NaNLabelEncoder) and ( + not isinstance(dataset.target_normalizer, MultiNormalizer) + or all( + not isinstance(normalizer, NaNLabelEncoder) + for normalizer in dataset.target_normalizer ) ), ( "target(s) should be continuous - categorical targets are not supported" From 26279f80a5aca23b0c2c376ba2434581d5f68e5d Mon Sep 17 00:00:00 2001 From: Obaidullah Date: Thu, 27 Aug 2026 16:51:44 +0500 Subject: [PATCH 2/2] Code Quality check fixed --- pytorch_forecasting/models/rnn/_rnn.py | 110 ++++++++++++++----------- 1 file changed, 61 insertions(+), 49 deletions(-) diff --git a/pytorch_forecasting/models/rnn/_rnn.py b/pytorch_forecasting/models/rnn/_rnn.py index 701264071..31e99a264 100644 --- a/pytorch_forecasting/models/rnn/_rnn.py +++ b/pytorch_forecasting/models/rnn/_rnn.py @@ -98,49 +98,58 @@ def __init__( loss (MultiHorizonMetric, optional): loss: loss function taking prediction and targets. logging_metrics (nn.ModuleList, optional): Metrics to log during training. Defaults to nn.ModuleList([SMAPE(), MAE(), RMSE(), MAPE(), MASE()]). + Example: + >>> import lightning.pytorch as pl - >>> from pytorch_forcasting import RecurrentNetwork,TimeSeriesDataSet - >>> from pytorch_forcasting.data.examples import generate_ar_data - >>> data = generate_ar_data(n_series=10, timesteps = 100, seed = 42) - >>> data["time_idx"] = data["time_idx"].astype(int) - >>> max_encoder_length = 24 - >>> max_prediction_length = 6 - training = TimeSeriesDataSet( - ... data, - ... time_idx = "time_idx", - ... target = "value", - ... group_ids = ["series"], - ... max_encoder_length = max_encoder_length, - ... max_prediction_length = max_prediction_length, - ... time_vary_unknown_reals= ["value"], - ... target_lags = {"value": [1, 2, 3, 6, 12, 24]}, - ... add_relative_time_idx = True, - ... add_target_scales = True, - ... add_encoder_length = True, + >>> from pytorch_forecasting import RecurrentNetwork, TimeSeriesDataSet + >>> from pytorch_forecasting.data.examples import generate_ar_data + >>> data = generate_ar_data(n_series=10, timesteps=400, seed=42) + >>> max_encoder_length = 60 + >>> max_prediction_length = 20 + >>> training = TimeSeriesDataSet( + ... data, + ... time_idx="time_idx", + ... target="value", + ... group_ids=["series"], + ... max_encoder_length=max_encoder_length, + ... max_prediction_length=max_prediction_length, + ... time_varying_unknown_reals=["value"], + ... lags={"value": [12, 24]}, + ... add_relative_time_idx=True, + ... add_target_scales=True, + ... add_encoder_length=True, ... ) >>> validation = TimeSeriesDataSet.from_dataset( - ... training, data, predict=True, stop_randomization=True + ... training, data, predict=True, stop_randomization=True + ... ) + >>> train_dataloader = training.to_dataloader( + ... train=True, batch_size=32, num_workers=0 + ... ) + >>> val_dataloader = validation.to_dataloader( + ... train=False, batch_size=32, num_workers=0 ... ) - >>> train_dataloader = training.to_dataloader(train=True, batch_size=32, num_workers=0) - >>> val_dataloader = validation.to_dataloader(train=False, batch_size=32, num_workers=0) >>> rnn = RecurrentNetwork.from_dataset( - ... training, - ... cell_type = "LSTM", - ... hidden_size = 32, - ... rnn_layers = 2, - ... dropout = 0.1, - ... learning_rate = 1e-3, - ... log_interval = 10, - ...) + ... training, + ... cell_type="LSTM", + ... hidden_size=32, + ... rnn_layers=2, + ... dropout=0.1, + ... learning_rate=1e-3, + ... log_interval=10, + ... ) >>> trainer = pl.Trainer( - ... max_epochs = 1, - ... accelerator = "cpu", - ... enable_checkpointing = False, - ... logger = False + ... max_epochs=1, + ... accelerator="cpu", + ... enable_checkpointing=False, + ... logger=False, ... ) - >>> trainer.fit(rnn, train_dataloaders = train_dataloader, val_dataloaders = val_dataloader) - >>> predictions = rnn.predict(val_dataloader, trainer = trainer) + >>> trainer.fit( + ... rnn, + ... train_dataloaders=train_dataloader, + ... val_dataloaders=val_dataloader, + ... ) + >>> predictions = rnn.predict(val_dataloader) """ # noqa : E501 if static_categoricals is None: static_categoricals = [] @@ -191,9 +200,9 @@ def __init__( " be the same apart from target variable" ) for targeti in to_list(target): - assert targeti in time_varying_reals_encoder, ( - f"target {targeti} has to be real" - ) # todo: remove this restriction + assert ( + targeti in time_varying_reals_encoder + ), f"target {targeti} has to be real" # todo: remove this restriction assert (isinstance(target, str) and isinstance(loss, MultiHorizonMetric)) or ( isinstance(target, tuple | list) and isinstance(loss, MultiLoss) @@ -217,9 +226,9 @@ def __init__( self.output_projector = nn.Linear( self.hparams.hidden_size, self.hparams.output_size ) - assert not isinstance(self.loss, QuantileLoss), ( - "QuantileLoss does not work with recurrent network" - ) + assert not isinstance( + self.loss, QuantileLoss + ), "QuantileLoss does not work with recurrent network" else: # multi target self.output_projector = nn.ModuleList( [ @@ -228,9 +237,9 @@ def __init__( ] ) for l in self.loss: - assert not isinstance(l, QuantileLoss), ( - "QuantileLoss does not work with recurrent network" - ) + assert not isinstance( + l, QuantileLoss + ), "QuantileLoss does not work with recurrent network" @classmethod def from_dataset( @@ -256,11 +265,14 @@ def from_dataset( dataset=dataset, kwargs=kwargs, default_loss=MAE() ) ) - assert not isinstance(dataset.target_normalizer, NaNLabelEncoder) and ( - not isinstance(dataset.target_normalizer, MultiNormalizer) - or all( - not isinstance(normalizer, NaNLabelEncoder) - for normalizer in dataset.target_normalizer + assert ( + not isinstance(dataset.target_normalizer, NaNLabelEncoder) + and ( + not isinstance(dataset.target_normalizer, MultiNormalizer) + or all( + not isinstance(normalizer, NaNLabelEncoder) + for normalizer in dataset.target_normalizer + ) ) ), ( "target(s) should be continuous - categorical targets are not supported"