//  [python]
def create_wikimovie_model(bert_config,
                           qa_config,
                           mips_config,
                           is_training,
                           features,
                           ent2ment_ind,
                           ent2ment_val,
                           ment2ent_map,
                           entity_ids,
                           entity_mask,
                           use_one_hot_embeddings,
                           summary_obj,
                           num_hops=1):
  """Creates a classification model."""
  qas_ids = features["qas_ids"]
  qry_input_ids = features["qry_input_ids"]
  qry_input_mask = features["qry_input_mask"]
  qry_entity_ids = features["qry_entity_id"]
  batch_size = tf.shape(qry_input_ids)[0]

  answer_entities = None
  bridge_entities = None
  layer_answers = []
  if is_training:
    answer_entities = features["answer_entities"]
    answer_index = tf.sparse_to_dense(
        sparse_indices=tf.concat([
            answer_entities.indices[:, 0:1],
            tf.cast(tf.expand_dims(answer_entities.values, 1), tf.int64)
        ],
                                 axis=1),
        output_shape=[batch_size, qa_config.num_entities],
        sparse_values=tf.ones_like(answer_entities.values, dtype=tf.float32),
        validate_indices=False)
    answer_index = answer_index / tf.reduce_sum(
        answer_index, axis=1, keepdims=True)
    layer_answers.insert(0, answer_index)
    if num_hops > 1 and "bridge_entities_0" in features:
      for ii in range(num_hops - 2, -1, -1):
        bridge_entities = features["bridge_entities_%d" % ii]
        bridge_index = tf.sparse_to_dense(
            sparse_indices=tf.concat([
                bridge_entities.indices[:, 0:1],
                tf.cast(tf.expand_dims(bridge_entities.values, 1), tf.int64)
            ],
                                     axis=1),
            output_shape=[batch_size, qa_config.num_entities],
            sparse_values=tf.ones_like(
                bridge_entities.values, dtype=tf.float32),
            validate_indices=False)
        bridge_index = bridge_index / tf.reduce_sum(
            bridge_index, axis=1, keepdims=True)
        layer_answers.insert(0, bridge_index)

  layer_entities, layer_mentions, layer_dense, layer_sp, _, _ = multi_hop(
      qry_input_ids,
      qry_input_mask,
      qry_entity_ids,
      entity_ids,
      entity_mask,
      ent2ment_ind,
      ent2ment_val,
      ment2ent_map,
      is_training,
      use_one_hot_embeddings,
      bert_config,
      qa_config,
      mips_config,
      num_hops=num_hops,
      exclude_set=qry_entity_ids)

  if is_training:
    nrows = qa_config.train_batch_size
  else:
    nrows = qa_config.predict_batch_size

  def _to_ragged(sp_tensor):
    r_ind = tf.RaggedTensor.from_value_rowids(
        value_rowids=sp_tensor.indices[:, 0],
        values=sp_tensor.indices[:, 1],
        nrows=nrows)
    r_val = tf.RaggedTensor.from_value_rowids(
        value_rowids=sp_tensor.indices[:, 0],
        values=sp_tensor.values,
        nrows=nrows)
    return r_ind, r_val

  total_loss = []
  predictions = {"qas_ids": qas_ids}
  for hop in range(len(layer_mentions)):
    if qa_config.light:
      if hop == len(layer_mentions) - 1:
        logits = layer_entities[hop]
        uniq_entity_ids, uniq_entity_scs = aggregate_sparse_indices(
            logits.indices, logits.values, logits.dense_shape,
            qa_config.entity_score_aggregation_fn)
        logits = tf.SparseTensor(uniq_entity_ids, uniq_entity_scs,
                                 logits.dense_shape)
        logits = tf.sparse.to_dense(
            logits, default_value=DEFAULT_VALUE, validate_indices=False)
        answer_preds = tf.argmax(logits, axis=1)
    else:
      logits = layer_entities[hop]
      uniq_entity_ids, uniq_entity_scs = aggregate_sparse_indices(
          logits.indices, logits.values, logits.dense_shape,
          qa_config.entity_score_aggregation_fn)
      uniq_entity_scs /= qa_config.softmax_temperature
      logits = tf.SparseTensor(uniq_entity_ids, uniq_entity_scs,
                               logits.dense_shape)
      logits_mask = tf.sparse.to_dense(
          tf.SparseTensor(uniq_entity_ids, tf.ones_like(uniq_entity_scs),
                          logits.dense_shape),
          validate_indices=False)
      logits = tf.sparse.to_dense(
          logits, default_value=DEFAULT_VALUE, validate_indices=False)
      answer_preds = tf.argmax(logits, axis=1)

      sparse_ind, sparse_val = _to_ragged(layer_sp[hop])
      dense_ind, dense_val = _to_ragged(layer_dense[hop])
      mention_ind, mention_val = _to_ragged(layer_mentions[hop])
      entity_ind, entity_val = _to_ragged(layer_entities[hop])
      predictions.update({
          "sparse_%d" % hop: sparse_ind.to_tensor(default_value=-1),
          "dense_%d" % hop: dense_ind.to_tensor(default_value=-1),
          "mention_%d" % hop: mention_ind.to_tensor(default_value=-1),
          "entity_%d" % hop: entity_ind.to_tensor(default_value=-1),
          "sparse_scores_%d" % hop: sparse_val.to_tensor(default_value=-1),
          "dense_scores_%d" % hop: dense_val.to_tensor(default_value=-1),
          "mention_scores_%d" % hop: mention_val.to_tensor(default_value=-1),
          "entity_scores_%d" % hop: entity_val.to_tensor(default_value=-1),
      })

    if is_training:
      layer_loss, weights = compute_loss(
          logits, layer_answers[hop], weights=logits_mask)
      total_loss.append(layer_loss)
      if summary_obj is not None:
        nnz_logits = tf.reduce_sum(
            tf.cast(tf.greater(logits, DEFAULT_VALUE), tf.float32))
        summary_obj.scalar(
            "train/nnz_logits_%d" % hop,
            tf.expand_dims(nnz_logits / tf.cast(batch_size, tf.float32), 0))
        pred_answer_index = batch_gather(layer_answers[hop],
                                         tf.expand_dims(answer_preds, 1))
        correct = tf.reduce_sum(
            tf.cast(tf.greater(pred_answer_index, 0.), tf.int32))
        summary_obj.scalar("train/num_correct_%d" % hop,
                           tf.expand_dims(correct, 0))
        summary_obj.scalar("train/total_loss_%d" % hop,
                           tf.expand_dims(layer_loss, 0))
        summary_obj.scalar("train/ans_in_ret_%d" % hop,
                           tf.expand_dims(tf.reduce_sum(weights), 0))

  if total_loss and qa_config.intermediate_loss:
    total_loss = tf.add_n(total_loss)
  elif total_loss:
    total_loss = total_loss[-1]
  else:
    total_loss = None

  predictions.update({
      "logits": logits,
      "predictions": answer_preds,
  })

  return total_loss, predictions