Normalize the examples from different tasks so they can be merged.

  This function is specific to NLP tasks and normalizes them so that in the
  end the example only has "targets" and "task_id". For tasks that originally
  have inputs, this is done by appending task_id to the inputs and prepending
  targets, so normalized_targets = inputs task_id targets. For classification
  tasks, targets are constructed by spelling out the class.

  Args:
    task: the Problem class of the task we are normalizing.
    example: a dictionary of tensors, the example to normalize.
    is_infer: bool, whether we are performing inference or not.
    vocab_type: the type of vocabulary in use.
    vocab_offset: integer, offset index for subword vocabularies.
    max_input_length: maximum length to cut inputs to.
    max_target_length: maximum length to cut targets to.
    fixed_train_length: set length to this size if > 0.

  Returns:
    a dictionary of tensors, like example, after normalizing, which in this
    case means that it only has "targets" and "task_id" as feature.

def normalize_example_nlp(task, example, is_infer, vocab_type, vocab_offset,
                          max_input_length, max_target_length,
                          fixed_train_length):
  """Normalize the examples from different tasks so they can be merged.

  This function is specific to NLP tasks and normalizes them so that in the
  end the example only has "targets" and "task_id". For tasks that originally
  have inputs, this is done by appending task_id to the inputs and prepending
  targets, so normalized_targets = inputs task_id targets. For classification
  tasks, targets are constructed by spelling out the class.

  Args:
    task: the Problem class of the task we are normalizing.
    example: a dictionary of tensors, the example to normalize.
    is_infer: bool, whether we are performing inference or not.
    vocab_type: the type of vocabulary in use.
    vocab_offset: integer, offset index for subword vocabularies.
    max_input_length: maximum length to cut inputs to.
    max_target_length: maximum length to cut targets to.
    fixed_train_length: set length to this size if > 0.

  Returns:
    a dictionary of tensors, like example, after normalizing, which in this
    case means that it only has "targets" and "task_id" as feature.
  """
  if task.has_inputs:
    example["inputs"] = example["inputs"][:-1]  # remove EOS token

  if hasattr(task, "class_labels"):
    if vocab_type == text_problems.VocabType.CHARACTER:
      # TODO(urvashik): handle the case where num_labels > 9
      example["targets"] = tf.cast(discretization.int_to_bit(
          example["targets"], 1, base=10) + 50, tf.int64)
      example["targets"] = tf.squeeze(example["targets"], axis=[-1])
    elif vocab_type == text_problems.VocabType.SUBWORD:
      example["targets"] = vocab_offset + example["targets"]
  else:
    # sequence with inputs and targets eg: summarization
    if task.has_inputs:
      if max_input_length > 0:
        example["inputs"] = example["inputs"][:max_input_length]
      # Do not truncate targets during inference with beam decoding.
      if max_target_length > 0 and not is_infer:
        example["targets"] = example["targets"][:max_target_length]

  def make_constant_shape(x, size):
    x = x[:size]
    xlen = tf.shape(x)[0]
    x = tf.pad(x, [[0, size - xlen]])
    return tf.reshape(x, [size])

  if task.has_inputs:
    if is_infer:
      concat_list = [example["inputs"], [task.task_id]]
      example["inputs"] = tf.concat(concat_list, axis=0)
    else:
      inputs = example.pop("inputs")
      concat_list = [inputs, [task.task_id], example["targets"]]
      example["targets"] = tf.concat(concat_list, axis=0)
      if fixed_train_length > 0:
        example["targets"] = make_constant_shape(
            example["targets"], fixed_train_length)
  else:
    concat_list = [[task.task_id], example["targets"]]
    example["targets"] = tf.concat(concat_list, axis=0)
    if not is_infer and fixed_train_length > 0:
      example["targets"] = make_constant_shape(
          example["targets"], fixed_train_length)

  example["task_id"] = tf.constant([task.task_id], dtype=tf.int64)
  return example