docstr: Computes the loss for training COPDeepAR, all inputs tensors representing
  time series have NTC layout.
  
  Parameters
  ----------
  F
  feat_static_cat : (batch_size, num_features)
  feat_static_real : (batch_size, num_features)
  past_time_feat : (batch_size, history_length, num_features)
  past_target : (batch_size, history_length, *target_shape)
  past_observed_values : (batch_size, history_length, *target_shape, seq_len)
  future_time_feat : (batch_size, prediction_length, num_features)
  future_target : (batch_size, prediction_length, *target_shape)
  future_observed_values : (batch_size, prediction_length, *target_shape)
  agg_features_dict: Dictionary of features for aggregated levels
  
  Returns loss with shape (batch_size, context + prediction_length, 1)
  -------
docstr_tok: ["Computes","the","loss","for","training","COPDeepAR",",","all","inputs","tensors","representing","time","series","have","NTC","layout",".","Parameters","--","--","--","--","--","F","feat_static_cat",":","(","batch_size",",","num_features",")","feat_static_real",":","(","batch_size",",","num_features",")","past_time_feat",":","(","batch_size",",","history_length",",","num_features",")","past_target",":","(","batch_size",",","history_length",",","*","target_shape",")","past_observed_values",":","(","batch_size",",","history_length",",","*","target_shape",",","seq_len",")","future_time_feat",":","(","batch_size",",","prediction_length",",","num_features",")","future_target",":","(","batch_size",",","prediction_length",",","*","target_shape",")","future_observed_values",":","(","batch_size",",","prediction_length",",","*","target_shape",")","agg_features_dict",":","Dictionary","of","features","for","aggregated","levels","Returns","loss","with","shape","(","batch_size",",","context","+","prediction_length",",","1",")","--","--","--","-"]
code: def hybrid_forward(
      self,
      F,
      feat_static_cat: Tensor,
      feat_static_real: Tensor,
      past_time_feat: Tensor,
      past_target: Tensor,
      past_observed_values: Tensor,
      past_is_pad: Optional[Tensor],
      future_time_feat: Tensor,
      future_target: Tensor,
      future_observed_values: Tensor,
      agg_features_dict: Dict,
  ) -> Tensor:
      """
      Computes the loss for training COPDeepAR, all inputs tensors representing
      time series have NTC layout.
  
      Parameters
      ----------
      F
      feat_static_cat : (batch_size, num_features)
      feat_static_real : (batch_size, num_features)
      past_time_feat : (batch_size, history_length, num_features)
      past_target : (batch_size, history_length, *target_shape)
      past_observed_values : (batch_size, history_length, *target_shape, seq_len)
      future_time_feat : (batch_size, prediction_length, num_features)
      future_target : (batch_size, prediction_length, *target_shape)
      future_observed_values : (batch_size, prediction_length, *target_shape)
      agg_features_dict: Dictionary of features for aggregated levels
  
      Returns loss with shape (batch_size, context + prediction_length, 1)
      -------
  
      """
  
      embeddings_at_all_levels_ls = []
      target_at_all_levels_ls = []
      scale_ls = []
  
      for i, agg_multiple in enumerate(
          self.temporal_hierarchy.agg_multiples
      ):
          if agg_multiple != 1:
              past_time_feat_agg = agg_features_dict[f"level_{i}"][
                  "past_time_feat_agg"
              ]
              future_time_feat_agg = agg_features_dict[f"level_{i}"][
                  "future_time_feat_agg"
              ]
          else:
              past_time_feat_agg = past_time_feat
              future_time_feat_agg = future_time_feat
  
          target_related_feat_agg = (
              self.get_target_related_feat_at_agg_level(
                  agg_level=i,
                  past_target=past_target,
                  past_is_pad=past_is_pad,
                  past_observed_values=past_observed_values,
                  future_target=future_target,
                  future_observed_values=future_observed_values,
              )
          )
  
          rnn_outputs, _, scale, _, _ = self.models[i].unroll_encoder(
              F=F,
              feat_static_cat=feat_static_cat,
              feat_static_real=feat_static_real,
              past_time_feat=past_time_feat_agg,
              future_time_feat=future_time_feat_agg,
              **target_related_feat_agg,
          )
          scale_ls.append(scale.expand_dims(axis=-1))
  
          # put together target sequence
          # (batch_size, seq_len, *target_shape)
          target = F.concat(
              target_related_feat_agg["past_target"].slice_axis(
                  axis=1,
                  begin=self.models[i].history_length
                  - self.models[i].context_length,
                  end=None,
              ),
              target_related_feat_agg["future_target"],
              dim=1,
          )
  
          # We reconcile blocks/windows of time steps: e.g., if we have 28 values of daily data, then we
          # reconcile 4 windows where each window has a length of 7 if number of leaves in the hierarchy is 7.
          window_size = (
              self.temporal_hierarchy.num_leaves // agg_multiple
          )
          num_windows = (
              self.models[i].context_length
              + self.models[i].prediction_length
          ) // window_size
  
          embeddings_at_all_levels_ls.append(
              rnn_outputs.reshape(
                  (
                      rnn_outputs.shape[0],
                      num_windows,
                      -1,
                      rnn_outputs.shape[-1],
                  )
              )
          )
  
          target_at_all_levels_ls.append(
              target.reshape((target.shape[0], num_windows, -1))
          )
  
      # Last dimension contains embeddings at all time-levels and message passing/aggregation can be done on it.
      # Shape: (bs, num_windows, total_num_time_steps_of_hierarchy, embedding_dim)
      embeddings_at_all_levels = F.concat(
          *embeddings_at_all_levels_ls, dim=-2
      )
  
      if self.use_gnn:
          embeddings_at_all_levels = self.gnn(embeddings_at_all_levels)
  
      distr_at_all_levels = self._embeddings_to_distr(
          F,
          embeddings_at_all_levels,
          scale_ls,
      )
  
      target_at_all_levels = F.concat(*target_at_all_levels_ls, dim=-1)
  
      if self.loss_function == "nll":
          loss = distr_at_all_levels.loss(x=target_at_all_levels)
  
          # Determine which epoch we are currently in.
          self.batch_no += 1
          epoch_no = self.batch_no // self.num_batches_per_epoch + 1
          epoch_frac = epoch_no / self.epochs
  
          if epoch_frac > self.warmstart_epoch_frac:
              print(
                  f"epoch_frac: {epoch_frac}. Switching the loss function to CRPS"
              )
              self.loss_function = "crps_univariate"
  
      else:
          samples_at_all_levels = self._distr_to_samples(
              distr_at_all_levels,
              num_samples=self.num_samples_for_loss,
          )
  
          if self.do_reconciliation:
              reconciled_samples_at_all_levels = reconcile_samples(
                  reconciliation_mat=self.M,
                  samples=samples_at_all_levels,
                  non_negative=self.non_negative,
              )
          else:
              reconciled_samples_at_all_levels = samples_at_all_levels
  
          loss = (
              EmpiricalDistribution(
                  samples=reconciled_samples_at_all_levels, event_dim=1
              )
              .loss(x=target_at_all_levels)
              .expand_dims(axis=-1)
          )
  
      return loss
  
imports: from typing import Dict, List, Optional, Tuple
  import mxnet
  import numpy
  from gluonts.core.component import Type, validated
  from gluonts.itertools import prod
  from gluonts.mx.model.deepar import DeepAREstimator
  from gluonts.mx.model.deepar._network import DeepARPredictionNetwork
  from gluonts.mx.model.deepvar_hierarchical._estimator import projection_mat
  from gluonts.mx.model.deepvar_hierarchical._network import coherency_error
  from gluonts.mx.distribution import Distribution, EmpiricalDistribution
  from gluonts.mx import Tensor
  from gluonts.mx.distribution import TransformedPiecewiseLinear
  from gluonts.nursery.temporal_hierarchical_forecasting.utils import utils
  from gluonts.nursery.temporal_hierarchical_forecasting.model.cop_deepar import gluonts_fixes, gnn
label_desc: Use image node_id 2 for calling the COPDeepARTrainingNetwork obj's underlying member method code with example usage: obj.hybrid_forward(F, feat_static_cat, feat_static_real, past_time_feat, past_target, past_observed_values, past_is_pad, future_time_feat, future_target, future_observed_values, agg_features_dict) and returns: loss