text: from __future__ import division
  
  import six
  
  import chainer
  from chainer import backend
  from chainer import function
  from chainer.utils import type_check
  
  
  def _fbeta_score(precision, recall, beta):
      beta_square = beta * beta
      return (
          (1 + beta_square) * precision * recall
          / (beta_square * precision + recall)
      ).astype(precision.dtype, copy=False)
  
  
  class ClassificationSummary(function.Function):
  
      def __init__(self, label_num, beta, ignore_label):
          self.label_num = label_num
          self.beta = beta
          self.ignore_label = ignore_label
  
      def check_type_forward(self, in_types):
          type_check._argname(in_types, ('x', 't'))
          x_type, t_type = in_types
  
          type_check.expect(
              x_type.dtype.kind == 'f',
              t_type.dtype.kind == 'i'
          )
  
          t_ndim = type_check.eval(t_type.ndim)
          type_check.expect(
              x_type.ndim >= t_type.ndim,
              x_type.shape[0] == t_type.shape[0],
              x_type.shape[2: t_ndim + 1] == t_type.shape[1:]
          )
          for i in six.moves.range(t_ndim + 1, type_check.eval(x_type.ndim)):
              type_check.expect(x_type.shape[i] == 1)
  
      def forward(self, inputs):
          xp = backend.get_array_module(*inputs)
          y, t = inputs
          # numpy.bincount requires int32 on Windows
          t = t.astype('i', copy=False)
  
          if self.label_num is None:
              label_num = xp.amax(t) + 1
          else:
              label_num = self.label_num
              if chainer.is_debug():
                  assert (t < label_num).all()
  
          mask = (t == self.ignore_label).ravel()
          pred = xp.where(mask, label_num, y.argmax(axis=1).ravel())
          true = xp.where(mask, label_num, t.ravel())
          support = xp.bincount(true, minlength=label_num + 1)[:label_num]
          relevant = xp.bincount(pred, minlength=label_num + 1)[:label_num]
          tp_mask = xp.where(pred == true, true, label_num)
          tp = xp.bincount(tp_mask, minlength=label_num + 1)[:label_num]
  
          precision = tp / relevant
          recall = tp / support
          fbeta = _fbeta_score(precision, recall, self.beta)
  
          return precision, recall, fbeta, support
  
  
  def classification_summary(y, t, label_num=None, beta=1.0, ignore_label=-1):
      """Calculates Precision, Recall, F beta Score, and support.
  
      This function calculates the following quantities for each class.
  
      - Precision: :math:`\\frac{\\mathrm{tp}}{\\mathrm{tp} + \\mathrm{fp}}`
      - Recall: :math:`\\frac{\\mathrm{tp}}{\\mathrm{tp} + \\mathrm{tn}}`
      - F beta Score: The weighted harmonic average of Precision and Recall.
      - Support: The number of instances of each ground truth label.
  
      Here, ``tp``, ``fp``, and ``tn`` stand for the number of true positives,
      false positives, and true negative, respectively.
  
      ``label_num`` specifies the number of classes, that is,
      each value in ``t`` must be an integer in the range of
      ``[0, label_num)``.
      If ``label_num`` is ``None``, this function regards
      ``label_num`` as a maximum of in ``t`` plus one.
  
      ``ignore_label`` determines which instances should be ignored.
      Specifically, instances with the given label are not taken
      into account for calculating the above quantities.
      By default, it is set to -1 so that all instances are taken
      into consideration, as labels are supposed to be non-negative integers.
      Setting ``ignore_label`` to a non-negative integer less than ``label_num``
      is illegal and yields undefined behavior. In the current implementation,
      it arises ``RuntimeWarning`` and ``ignore_label``-th entries in output
      arrays do not contain correct quantities.
  
      Args:
          y (:class:`~chainer.Variable` or :ref:`ndarray`):
              Variable holding a vector of scores.
          t (:class:`~chainer.Variable` or :ref:`ndarray`):
              Variable holding a vector of ground truth labels.
          label_num (int): The number of classes.
          beta (float): The parameter which determines the weight of
              precision in the F-beta score.
          ignore_label (int): Instances with this label are ignored.
  
      Returns:
          4-tuple of ~chainer.Variable of size ``(label_num,)``.
          Each element represents precision, recall, F beta score,
          and support of this minibatch.
  
      """
      return ClassificationSummary(label_num, beta, ignore_label)(y, t)
  
  
  def precision(y, t, label_num=None, ignore_label=-1):
      ret = ClassificationSummary(label_num, 1.0, ignore_label)(y, t)
      return ret[0], ret[-1]
  
  
  def recall(y, t, label_num=None, ignore_label=-1):
      ret = ClassificationSummary(label_num, 1.0, ignore_label)(y, t)
      return ret[1], ret[-1]
  
  
  def fbeta_score(y, t, label_num=None, beta=1.0, ignore_label=-1):
      ret = ClassificationSummary(label_num, beta, ignore_label)(y, t)
      return ret[2], ret[-1]
  
  
  def f1_score(y, t, label_num=None, ignore_label=-1):
      ret = ClassificationSummary(label_num, 1.0, ignore_label)(y, t)
      return ret[2], ret[-1]
  