code: def op_table(a, b):
      """Table of edit operations between a and b.
  
      Solves for the table of edit operations, which is mainly used to
      compute word error rate. The table is of size ``[|a|+1, |b|+1]``,
      and each point ``(i, j)`` in the table has an edit operation. The
      edit operations can be deterministically followed backwards to
      find the shortest edit path to from ``a[:i-1] to b[:j-1]``. Indexes
      of zero (``i=0`` or ``j=0``) correspond to an empty sequence.
  
      The algorithm itself is well known, see
  
      `Levenshtein distance <https://en.wikipedia.org/wiki/Levenshtein_distance>`_
  
      Note that in some cases there are multiple valid edit operation
      paths which lead to the same edit distance minimum.
  
      Arguments
      ---------
      a : iterable
          Sequence for which the edit operations are solved.
      b : iterable
          Sequence for which the edit operations are solved.
  
      Returns
      -------
      list
          List of lists, Matrix, Table of edit operations.
  
      Example
      -------
      >>> ref = [1,2,3]
      >>> hyp = [1,2,4]
      >>> for row in op_table(ref, hyp):
      ...     print(row)
      ['=', 'I', 'I', 'I']
      ['D', '=', 'I', 'I']
      ['D', 'D', '=', 'I']
      ['D', 'D', 'D', 'S']
      """
      # For the dynamic programming algorithm, only two rows are really needed:
      # the one currently being filled in, and the previous one
      # The following is also the right initialization
      prev_row = [j for j in range(len(b) + 1)]
      curr_row = [0] * (len(b) + 1)  # Just init to zero
      # For the edit operation table we will need the whole matrix.
      # We will initialize the table with no-ops, so that we only need to change
      # where an edit is made.
      table = [
          [EDIT_SYMBOLS["eq"] for j in range(len(b) + 1)]
          for i in range(len(a) + 1)
      ]
      # We already know the operations on the first row and column:
      for i in range(len(a) + 1):
          table[i][0] = EDIT_SYMBOLS["del"]
      for j in range(len(b) + 1):
          table[0][j] = EDIT_SYMBOLS["ins"]
      table[0][0] = EDIT_SYMBOLS["eq"]
      # The rest of the table is filled in row-wise:
      for i, a_token in enumerate(a, start=1):
          curr_row[
              0
          ] += 1  # This trick just deals with the first column.
          for j, b_token in enumerate(b, start=1):
              # The dynamic programming algorithm cost rules
              insertion_cost = curr_row[j - 1] + 1
              deletion_cost = prev_row[j] + 1
              substitution = 0 if a_token == b_token else 1
              substitution_cost = prev_row[j - 1] + substitution
              # Here copying the Kaldi compute-wer comparison order, which in
              # ties prefers:
              # insertion > deletion > substitution
              if (
                  substitution_cost < insertion_cost
                  and substitution_cost < deletion_cost
              ):
                  curr_row[j] = substitution_cost
                  # Again, note that if not substitution, the edit table already
                  # has the correct no-op symbol.
                  if substitution:
                      table[i][j] = EDIT_SYMBOLS["sub"]
              elif deletion_cost < insertion_cost:
                  curr_row[j] = deletion_cost
                  table[i][j] = EDIT_SYMBOLS["del"]
              else:
                  curr_row[j] = insertion_cost
                  table[i][j] = EDIT_SYMBOLS["ins"]
          # Move to the next row:
          prev_row[:] = curr_row[:]
      return table
  
file_path: speechbrain/speechbrain/utils/edit_distance.py
imports: import collections
label_desc: Use image node_id 3 for calling a global function with example usage: op_table(a, b) and returns: table