docstr: Unrolls the RNN encoder in "imputation mode" which will fill imputed
  values with samples from the DeepAR model.
docstr_tok: ["Unrolls","the","RNN","encoder","in","``","imputation","mode","''","which","will","fill","imputed","values","with","samples","from","the","DeepAR","model","."]
code: def unroll_encoder_imputation(
      self,
      F,
      feat_static_cat: Tensor,  # (batch_size, num_features)
      feat_static_real: Tensor,  # (batch_size, num_features)
      past_time_feat: Tensor,  # (batch_size, history_length, num_features)
      past_target: Tensor,  # (batch_size, history_length, *target_shape)
      # (batch_size, history_length, *target_shape)
      past_observed_values: Tensor,
      past_is_pad: Tensor,  # (batch_size, history_length, *target_shape)
      future_observed_values: Optional[
          Tensor
      ],  # (batch_size, history_length, *target_shape)
      future_time_feat: Optional[
          Tensor
      ],  # (batch_size, prediction_length, num_features)
      future_target: Optional[
          Tensor
      ],  # (batch_size, prediction_length, *target_shape)
  ) -> Tuple[Tensor, List, Tensor, Tensor, Tensor]:
      """
      Unrolls the RNN encoder in "imputation mode" which will fill imputed
      values with samples from the DeepAR model.
      """
  
      if future_time_feat is None or future_target is None:
          time_feat = past_time_feat.slice_axis(
              axis=1,
              begin=self.history_length - self.context_length,
              end=None,
          )
  
          is_padded_indicator = past_is_pad.slice_axis(
              axis=1,
              begin=self.history_length - self.context_length,
              end=None,
          )
          target = past_target.slice_axis(
              axis=1,
              begin=self.history_length - self.context_length,
              end=None,
          )
          target_observed_values = past_observed_values.slice_axis(
              axis=1,
              begin=self.history_length - self.context_length,
              end=None,
          )
          sequence = past_target
          sequence_length = self.history_length
          subsequences_length = self.context_length
      else:
          time_feat = F.concat(
              past_time_feat.slice_axis(
                  axis=1,
                  begin=self.history_length - self.context_length,
                  end=None,
              ),
              future_time_feat,
              dim=1,
          )
  
          is_padded_indicator = F.concat(
              past_is_pad.slice_axis(
                  axis=1,
                  begin=self.history_length - self.context_length,
                  end=None,
              ),
              F.zeros_like(future_observed_values),
              dim=1,
          )
  
          target = F.concat(
              past_target.slice_axis(
                  axis=1,
                  begin=self.history_length - self.context_length,
                  end=None,
              ),
              future_target,
              dim=1,
          )
  
          target_observed_values = F.concat(
              past_observed_values.slice_axis(
                  axis=1,
                  begin=self.history_length - self.context_length,
                  end=None,
              ),
              future_observed_values,
              dim=1,
          )
  
          sequence = F.concat(past_target, future_target, dim=1)
          sequence_length = self.history_length + self.prediction_length
          subsequences_length = (
              self.context_length + self.prediction_length
          )
      # (batch_size, sub_seq_len, *target_shape, num_lags)
      lags = self.get_lagged_subsequences(
          F=F,
          sequence=sequence,
          sequence_length=sequence_length,
          indices=self.lags_seq,
          subsequences_length=subsequences_length,
      )
  
      # scale is computed on the context length last units of the past target
      # scale shape is (batch_size, 1, *target_shape)
      _, scale = self.scaler(
          past_target.slice_axis(
              axis=1, begin=-self.context_length, end=None
          ),
          past_observed_values.slice_axis(
              axis=1, begin=-self.context_length, end=None
          ),
      )
  
      # (batch_size, num_features)
      embedded_cat = self.embedder(feat_static_cat)
  
      # in addition to embedding features, use the log scale as it can help
      # prediction too
      # (batch_size, num_features + prod(target_shape))
      static_feat = F.concat(
          embedded_cat,
          feat_static_real,
          F.log(scale)
          if len(self.target_shape) == 0
          else F.log(scale.squeeze(axis=1)),
          dim=1,
      )
  
      # (batch_size, subsequences_length, num_features + 1)
      repeated_static_feat = static_feat.expand_dims(axis=1).repeat(
          axis=1, repeats=subsequences_length
      )
  
      # (batch_size, sub_seq_len, *target_shape, num_lags)
      lags_scaled = F.broadcast_div(lags, scale.expand_dims(axis=-1))
      # from (batch_size, sub_seq_len, *target_shape, num_lags)
      # to (batch_size, sub_seq_len, prod(target_shape) * num_lags)
      input_lags = F.reshape(
          data=lags_scaled,
          shape=(
              -1,
              subsequences_length,
              len(self.lags_seq) * prod(self.target_shape),
          ),
      )
  
      # (batch_size, sub_seq_len, input_dim)
      inputs = F.concat(
          input_lags, time_feat, repeated_static_feat, dim=-1
      )
  
      # Set initial state
      begin_state = self.rnn.begin_state(
          func=F.zeros,
          dtype=self.dtype,
          batch_size=inputs.shape[0]
          if isinstance(inputs, mx.nd.NDArray)
          else 0,
      )
  
      unroll_results = self.imputation_rnn_unroll(
          F,
          begin_state=begin_state,
          sequence=sequence,
          sequence_length=sequence_length,
          subsequences_length=subsequences_length,
          scale=scale,
          target=target,
          target_observed_values=target_observed_values,
          time_feat=time_feat,
          repeated_static_feat=repeated_static_feat,
          is_padded_indicator=is_padded_indicator,
      )
  
      outputs, state, imputed_sequence = unroll_results
      # outputs: (batch_size, seq_len, num_cells)
      # state: list of (batch_size, num_cells) tensors
      # scale: (batch_size, 1, *target_shape)
      # static_feat: (batch_size, num_features + prod(target_shape))
      out = F.concat(*outputs, dim=1)
      return out, state, scale, static_feat, imputed_sequence
  
imports: from typing import List, Optional, Tuple, Union, Type
  import mxnet
  import numpy
  from mxnet.gluon.contrib.rnn import VariationalDropoutCell
  from mxnet.gluon.rnn import ZoneoutCell
  from gluonts.core.component import validated
  from gluonts.itertools import prod
  from gluonts.mx import Tensor
  from gluonts.mx.block.dropout import RNNZoneoutCell, VariationalZoneoutCell
  from gluonts.mx.block.feature import FeatureEmbedder
  from gluonts.mx.block.regularization import ActivationRegularizationLoss, TemporalActivationRegularizationLoss
  from gluonts.mx.block.scaler import MeanScaler, NOPScaler
  from gluonts.mx.distribution import Distribution, DistributionOutput
  from gluonts.mx.distribution.distribution import getF
  from gluonts.mx.util import weighted_average, mx_switch
  from mxnet import autograd
label_desc: Use image node_id 7 for calling the DeepARNetwork obj's underlying member method code with example usage: obj.unroll_encoder_imputation(F, feat_static_cat, feat_static_real, past_time_feat, past_target, past_observed_values, past_is_pad, future_observed_values, future_time_feat, future_target) and returns: out,  state,  scale,  static_feat,  imputed_sequence