code: def rank_classification(
      targets: Sequence[Tuple[Sequence[int], bool, float, int]],
      scores: Sequence[float],
      num_classes: Optional[int] = None,
      normalize_by_target_length: bool = False,
      idx_len: int = 2,
  ) -> Dict[str, Union[float, int]]:
      """Computes standard metrics classification based on log likelihood ranking.
  
      This metric is intended to be used along with the `rank_classification`
      preprocessor and postprocessor. Each example is scored (by log likelihood)
      for every possible label, and the label with the best score is selected as the
      prediction.
  
      In the case of multiple labels, a prediction matching any will be considered
      correct.
  
      For problems with two labels, AUC-pr and AUC-roc retrieval metrics will be
      reported for the positive class, which is assumed to have an 'idx' of 1. If
      more labels are present, only accuracy and F-1 will be reported.
  
      Args:
        targets: list of tuples, the 'idx', 'is_correct', 'weight' fields, and
          length of target tokens from ground truth examples.
        scores: list of float, a flat list of log likelihood scores for each
          possible label for each example.
        num_classes: int or None, the number of possible classes for the label or
          None if the number of classes vary.
        normalize_by_target_length: bool, if True the scores are normalized by the
          target token lengths.
        idx_len: int, The number of elems in the idx field in the targets. This is
          generally 2 (input_id, target_id).
  
      Returns:
        Accuracy, f1, and AUC scores.
  
      Raises:
        ValueError: if `targets` is not a sequence of 4-tuples.
      """
      assert len(targets) == len(scores)
      if len(targets[0]) != 4:
          raise ValueError(
              f"`targets` should contain 4 elements but has {len(targets[0])}."
          )
  
      normalized_scores = []
      if normalize_by_target_length:
          for target, score in zip(targets, scores):
              _, _, _, target_length = target
              score = score / target_length
              normalized_scores.append(score)
  
          scores = normalized_scores
  
      idx_0 = targets[0][0]
      if not hasattr(idx_0, "__len__") or len(idx_0) != idx_len:
          raise ValueError(
              "The first element of `targets` ('idx') should be "
              f"{idx_len}-dimensional. Got {idx_0}."
          )
  
      # Sort by 'idx' since the function relies on this assumption.
      # ((idx, is_correct, weight, target_length), score)
      get_idx = lambda x: x[0][0]
      targets, scores = zip(*sorted(zip(targets, scores), key=get_idx))
  
      def all_unique(indices):
          seen = set()
          for idx in indices:
              if idx in seen:
                  return False
              seen.add(idx)
          return True
  
      indices = (t[0] for t in targets)
      if not all_unique(indices):
          err_msg = (
              "rank_classification metric function received targets list with"
              " non-unique indices. There's no way to distinguish the items, so the"
              " metric can't be computed. Most likely, this is caused by using SeqIO"
              " rank_classification preprocessor (or a similar one) in a multi-host"
              " experiment. To fix this, either use Pathways, or make the ids random"
              " with big enough range to make them unique."
          )
          raise ValueError(err_msg)
  
      if not num_classes:
          # Assuming variable classes. Can only compute accuracy.
          num_correct = 0
          total = 0
  
          # (((input idx, output idx), is_correct, weight, target_length), score)
          get_grp = lambda x: x[0][0][0]
  
          for _, grp in itertools.groupby(
              zip(targets, scores), get_grp
          ):
              exs, log_likelihoods = zip(*grp)
              prediction = np.argmax(log_likelihoods)
              weights = exs[prediction][2]
              num_correct += exs[prediction][1] * weights
              total += weights
          return {"accuracy": 100 * num_correct / total}
  
      assert (
          len(targets) % num_classes == 0
      ), f"{len(targets)} % {num_classes} != 0"
  
      labels_indicator = np.array(
          [is_correct for _, is_correct, _, _ in targets]
      ).reshape((-1, num_classes))
      weights = np.array(
          [weight for _, _, weight, _ in targets]
      ).reshape((-1, num_classes))[:, 0]
      log_likelihoods = np.array(scores, np.float32).reshape(
          (-1, num_classes)
      )
      predictions = log_likelihoods.argmax(-1)
  
      if np.any(labels_indicator.sum(axis=-1) > 1):
          # multiple-answer case
          logging.info(
              "Multiple labels detected. Predictions matching any label will be "
              "considered correct."
          )
          num_examples = len(labels_indicator)
          return {
              "accuracy": (
                  100
                  * np.average(
                      labels_indicator[
                          np.arange(num_examples), predictions
                      ],
                      weights=weights,
                  )
              )
          }
  
      predictions_indicator = np.eye(num_classes)[predictions]
  
      def exp_normalize(x):
          b = x.max(-1)[:, np.newaxis]
          y = np.exp(x - b)
          return y / y.sum(-1)[:, np.newaxis]
  
      probs = exp_normalize(log_likelihoods)
  
      metrics = {
          "accuracy": 100
          * sklearn.metrics.accuracy_score(
              labels_indicator,
              predictions_indicator,
              sample_weight=weights,
          ),
      }
  
      if num_classes > 2:
          metrics.update(
              mean_multiclass_f1(num_classes, sample_weight=weights)(
                  labels_indicator, predictions_indicator
              )
          )
          logging.warning(
              "AUC-pr and AUC-roc are not supported when num_classes > 2"
          )
      else:
          metrics.update(
              {
                  "f1": 100
                  * sklearn.metrics.f1_score(
                      labels_indicator.argmax(-1),
                      predictions,
                      sample_weight=weights,
                  )
              }
          )
          labels_indicator = labels_indicator[:, 1]
          probs = probs[:, 1]
  
          metrics.update(
              {
                  "auc-roc": 100
                  * sklearn.metrics.roc_auc_score(
                      labels_indicator,
                      probs,
                      multi_class="ovr",
                      sample_weight=weights,
                      average="macro",
                  ),
                  "auc-pr": 100
                  * sklearn.metrics.average_precision_score(
                      labels_indicator,
                      probs,
                      sample_weight=weights,
                      average="macro",
                  ),
              }
          )
  
      return metrics
  
imports: import collections
  import itertools
  import re
  import string
  from typing import Any, Dict, Mapping, Optional, Sequence, Tuple, Union
  from absl import logging
  import editdistance
  import flax
  import jax.numpy
  import numpy
  import sacrebleu
  import scipy.stats
  import seqio
  import sklearn.metrics
  from t5.evaluation import qa_utils
  import tensorflow.compat.v2
  from rouge_score import rouge_scorer
  from rouge_score import scoring
label_desc: Use image node_id 21 for calling a global function with example usage: rank_classification(targets, scores, num_classes, normalize_by_target_length, idx_len) and returns: metrics, True, dict, dict, unknown, False