class_docstr: pytorch nn.Module implementation for gan.network.DoppelGANgerGenerator
code: def forward(
      self,
      attribute_input_noise,
      addi_attribute_input_noise,
      feature_input_noise,
      feature_input_data,
      attribute=None,
  ):
      # attribute_input_noise.shape = (batch_size, 5)
      # addi_attribute_input_noise.shape = (batch_size, 5)
      # feature_input_noise.shape = (batch_size, time, *) time is sample_time
      # feature_input_data.shape = (batch_size, time, *) or 2 dim
      # attribute.shape = (batch_size, real_attribute_out_dim)
  
      all_attribute_input_noise_dim = self.all_attribute_input_noise_dim
      all_attribute_outputs = self.all_attribute_outputs
      all_attribute_out_dim = self.all_attribute_out_dim
  
      all_attribute = []
      all_discrete_attribute = []
      if attribute is not None:
          all_attribute.append(attribute)
          all_discrete_attribute.append(attribute)
  
      attribute_output = None
      additional_attribute_output = None
      part_real_attribute = None  # post-processed attribute_output
      part_real_discrete_attribute = (
          None  # post-processed attribute_output(discrete)
      )
      part_additional_attribute = (
          None  # post-processed additional_attribute_output
      )
      part_additional_discrete_attribute = None
      # post-processed additional_attribute_output(discrete)
      # real_attribute_out_dim = sum of all real attribute dim
      # addi_attribute_out_dim = sum of all additional attribute dim
      from bigdl.nano.utils.common import invalidInputError
  
      if self.mlp_1 is not None and self.mlp_2 is not None:
          # there are real attr and fake attr
          invalidInputError(
              len(all_attribute_outputs) == 2,
              "len of all_attribute_outputs expect to be 2",
          )
  
          # generate real attr
          attribute_output = self.mlp_1(attribute_input_noise)
          # (batch_size, real_attribute_out_dim)
          (
              part_real_attribute,
              part_real_discrete_attribute,
          ) = self._post_process_generated_attribute(
              attribute_output, all_attribute_outputs[0]
          )
  
          # generate addi attr
          addi_attribute_generator_input = torch.cat(
              [
                  part_real_discrete_attribute,
                  addi_attribute_input_noise,
              ],
              dim=1,
          )
          additional_attribute_output = self.mlp_2(
              addi_attribute_generator_input
          )
          # (batch_size, addi_attribute_out_dim)
          (
              part_additional_attribute,
              part_additional_discrete_attribute,
          ) = self._post_process_generated_attribute(
              additional_attribute_output, all_attribute_outputs[1]
          )
  
      elif self.mlp_1 is not None and self.mlp_2 is None:
          additional_attribute_output = self.mlp_1(
              attribute_input_noise
          )
          # (batch_size, addi_attribute_out_dim)
          invalidInputError(
              len(all_attribute_outputs) == 1,
              "len of all_attribute_outputs expect to be 1",
          )
          (
              part_additional_attribute,
              part_additional_discrete_attribute,
          ) = self._post_process_generated_attribute(
              additional_attribute_output, all_attribute_outputs[0]
          )
  
      if part_real_attribute is not None:
          all_attribute.append(part_real_attribute)
      if part_additional_attribute is not None:
          all_attribute.append(part_additional_attribute)
      if part_real_discrete_attribute is not None:
          all_discrete_attribute.append(part_real_discrete_attribute)
      if part_additional_discrete_attribute is not None:
          all_discrete_attribute.append(
              part_additional_discrete_attribute
          )
  
      all_attribute = torch.cat(all_attribute, dim=1)
      # will be used in Auxiliary Discriminator
      all_discrete_attribute = torch.cat(all_discrete_attribute, dim=1)
      # will be send to RNN, should with out grad
      all_discrete_attribute = all_discrete_attribute.detach()
      # just to make sure it is detached, maybe not needed
  
      # initial_state preparation
      if self.initial_state == RNNInitialStateType.ZERO:
          initial_state_c = torch.zeros(
              self.feature_num_layers,  # L (layer)
              feature_input_data.shape[0],  # N (batch_size)
              self.feature_num_units,
          )  # H (hidden dim)
          initial_state_h = torch.zeros(
              self.feature_num_layers,  # L (layer)
              feature_input_data.shape[0],  # N (batch_size)
              self.feature_num_units,
          )  # H (hidden dim)
      elif self.initial_state == RNNInitialStateType.RANDOM:
          initial_state_c = torch.randn(
              self.feature_num_layers,  # L (layer)
              feature_input_data.shape[0],  # N (batch_size)
              self.feature_num_units,
          )  # H (hidden dim)
          initial_state_h = torch.randn(
              self.feature_num_layers,  # L (layer)
              feature_input_data.shape[0],  # N (batch_size)
              self.feature_num_units,
          )  # H (hidden dim)
      elif self.initial_state == RNNInitialStateType.VARIABLE:
          invalidInputError(
              False,
              "RNNInitialStateType.VARIABLE has not been implemented!",
          )
      else:
          invalidInputError(False, "not implemented")
  
      state = (initial_state_h, initial_state_c)
      batch_size = feature_input_data.shape[0]
      # time = feature_input_data.shape[1]//self.sample_len # max time length
      time = feature_input_noise.shape[1]  # sample_time
      # all_gen_flag.shape = (feature_out_dim, (batch_size, 1)) 1 means to geneate
      all_gen_flag = []
      # all_cur_argmax.shape = (feature_out_dim, (batch_size, 1)) 0 means to generate
      all_cur_argmax = []
      # gen_flag.shape = (batch_size, 1) 1 means to generate
      gen_flag = torch.ones(batch_size, 1)
  
      time_used = 0
      all_output = []
      for i in range(time):
          time_used = i + 1
          input_all = [
              all_discrete_attribute
          ]  # (batch_size, attribute_out_dim)
          if self.noise:
              input_all.append(
                  feature_input_noise[:, i]
              )  # (batch_size, feature_out_dim)
          if self.feed_back:
              if feature_input_data.ndim == 3:
                  input_all.append(
                      feature_input_data[:, i]
                  )  # (batch_size, feature_out_dim)
              else:
                  input_all.append(new_output)
          input_all = torch.cat(input_all, dim=1)
          # (batch_size, attribute_out_dim + feature_noise_dim)
          input_all = torch.unsqueeze(input_all, dim=1)
          # (batch_size, 1, attribute_out_dim + feature_noise_dim)
          out_rnn, (h_n, c_n) = self.rnn_network(input_all, state)
          state = (h_n, c_n)
  
          # out.shape = (batch_size, length=1, out_dim)
          # output_feature = self.mlp_rnn(out_rnn)
          # output_feature.shape = (batch_size, feature_out_dim)
          new_output_all = []
          current_idx = 0
          output_feature = self.mlp_rnn(out_rnn)
          for j in range(self.sample_len):
              for k in range(len(self.feature_outputs)):
                  output = self.feature_outputs[k]
                  sub_output = output_feature[
                      :, current_idx : current_idx + output.dim
                  ]
                  if output.type_ == OutputType.DISCRETE:
                      sub_output = F.softmax(sub_output, dim=1)
                  elif output.type_ == OutputType.CONTINUOUS:
                      if output.normalization == Normalization.ZERO_ONE:
                          sub_output = torch.sigmoid(sub_output)
                      elif (
                          output.normalization
                          == Normalization.MINUSONE_ONE
                      ):
                          sub_output = torch.tanh(sub_output)
                      else:
                          invalidInputError(
                              False, "unknown normalization type"
                          )
                  else:
                      invalidInputError(False, "unknown output type")
                  new_output_all.append(sub_output)
                  current_idx += output.dim
          new_output = torch.cat(new_output_all, dim=1)
          all_output.append(new_output)
  
          for j in range(self.sample_len):
              cur_gen_flag = None  # (batch_size, 1) 1 means to generate
              all_gen_flag.append(gen_flag)
              cur_gen_flag = (
                  (
                      torch.argmax(
                          new_output_all[
                              (
                                  j * len(self.feature_outputs)
                                  + self.gen_flag_id
                              )
                          ],
                          dim=1,
                      )
                      == 0
                  )
                  .float()
                  .view(-1, 1)
              )
              all_cur_argmax.append(
                  torch.argmax(
                      new_output_all[
                          (
                              j * len(self.feature_outputs)
                              + self.gen_flag_id
                          )
                      ],
                      dim=1,
                  )
              )
              gen_flag = gen_flag * cur_gen_flag
          if torch.max(gen_flag) == 0:
              break
  
      for i in range(time_used, time):
          all_output.append(
              torch.zeros(batch_size, self.feature_out_dim)
          )
          for j in range(self.sample_len):
              all_gen_flag.append(torch.zeros(batch_size, 1))
              all_cur_argmax.append(torch.zeros(batch_size))
      feature = torch.stack(all_output, axis=1)
      # batch_size * time * (dim * sample_len)
      gen_flag = torch.stack(all_gen_flag, axis=1)
      # batch_size * (time * sample_len) * 1
      cur_argmax = torch.stack(all_cur_argmax, axis=1)
      # batch_size * (time * sample_len)
      length = torch.sum(gen_flag, dim=(1, 2))
      # batch_size
      gen_flag_t = gen_flag.view(batch_size, time, self.sample_len)
      # batch_size * time * sample_len
      gen_flag_t = torch.sum(gen_flag_t, dim=2)
      # batch_size * time * sample_len
      gen_flag_t = (gen_flag_t > 0.5).float()
      gen_flag_t = torch.unsqueeze(gen_flag_t, dim=2)
      # torch >= 1.8
      # gen_flag_t = torch.tile(gen_flag_t, [1, 1, self.feature_out_dim])
      # print("gen_flag_t old shape2:", gen_flag_t.shape)
      # torch < 1.8
      gen_flag_t = gen_flag_t.repeat(1, 1, self.feature_out_dim)
      # batch_size * time * (dim * sample_len)
      feature = feature * gen_flag_t
      feature = feature.view(
          batch_size,
          time * self.sample_len,
          int(self.feature_out_dim / self.sample_len),
      )
      # batch_size * (time * sample_len) * dim
  
      return feature, all_attribute, gen_flag, length, cur_argmax
  
imports: import torch
  from torch import nn
  import torch.nn.functional
  import numpy
  from enum import Enum
  from .op import linear, flatten
  from .output import OutputType, Normalization
label_desc: Use image node_id 3 for calling the DoppelGANgerGenerator obj's underlying member method code with example usage: obj.forward(attribute_input_noise, addi_attribute_input_noise, feature_input_noise, feature_input_data, attribute) and returns: feature,  all_attribute,  gen_flag,  length,  cur_argmax