code: def modal_dot(a, b, transpose_a=False, transpose_b=False):
      """
      Computes the matrix multiplication of a and b, handling the data modes
      automatically.
  
      This is a wrapper to standard matmul operations, for a and b with rank 2
      or 3, that:
  
      - Supports automatic broadcasting of the "batch" dimension if the two inputs
      have different ranks.
      - Supports any combination of dense and sparse inputs.
  
      This op is useful for multiplying matrices that represent batches of graphs
      in the different modes, for which the adjacency matrices may or may not be
      sparse and have different ranks from the node attributes.
  
      Additionally, it can also support the case where we have many adjacency
      matrices and only one graph signal (which is uncommon, but may still happen).
  
      If you know a-priori the type and shape of the inputs, it may be faster to
      use the built-in functions of TensorFlow directly instead.
  
      Examples:
  
          - `a` rank 2, `b` rank 2 -> `a @ b`
          - `a` rank 3, `b` rank 3 -> `[a[i] @ b[i] for i in range(len(a))]`
          - `a` rank 2, `b` rank 3 -> `[a @ b[i] for i in range(len(b))]`
          - `a` rank 3, `b` rank 2 -> `[a[i] @ b for i in range(len(a))]`
  
      :param a: Tensor or SparseTensor with rank 2 or 3;
      :param b: Tensor or SparseTensor with rank 2 or 3;
      :param transpose_a: transpose the innermost 2 dimensions of `a`;
      :param transpose_b: transpose the innermost 2 dimensions of `b`;
      :return: Tensor or SparseTensor with rank = max(rank(a), rank(b)).
      """
      a_ndim = K.ndim(a)
      b_ndim = K.ndim(b)
      assert a_ndim in (
          2,
          3,
      ), "Expected a of rank 2 or 3, got {}".format(a_ndim)
      assert b_ndim in (
          2,
          3,
      ), "Expected b of rank 2 or 3, got {}".format(b_ndim)
  
      if transpose_a:
          perm = None if a_ndim == 2 else (0, 2, 1)
          a = ops.transpose(a, perm)
      if transpose_b:
          perm = None if b_ndim == 2 else (0, 2, 1)
          b = ops.transpose(b, perm)
  
      if a_ndim == b_ndim:
          # ...ij,...jk->...ik
          return dot(a, b)
      elif a_ndim == 2:
          # ij,bjk->bik
          return mixed_mode_dot(a, b)
      else:  # a_ndim == 3
          # bij,jk->bik
          if not K.is_sparse(a) and not K.is_sparse(b):
              # Immediately fallback to standard dense matmul, no need to reshape
              return tf.matmul(a, b)
  
          # If either input is sparse, we use dot(a, b)
          # This implementation is faster than using rank 3 sparse matmul with tfsp
          a_shape = tf.shape(a)
          b_shape = tf.shape(b)
          a_flat = ops.reshape(a, (-1, a_shape[2]))
          output = dot(a_flat, b)
          return ops.reshape(output, (-1, a_shape[1], b_shape[1]))
  
file_path: spektral/spektral/layers/ops/matmul.py
imports: import tensorflow
  from tensorflow.keras import backend
  from tensorflow.python.ops.linalg.sparse import sparse
  from .None import ops
label_desc: Use image node_id 3 for calling a global function with example usage: modal_dot(a, b, transpose_a, transpose_b) and returns: dot, mixed_mode_dot, ops, tf