docs: def inner(X, Y, ip_B=None):
      '''Euclidean and non-Euclidean inner product.
  
      numpy.vdot only works for vectors and numpy.dot does not use the conjugate
      transpose.
  
      :param X: numpy array with ``shape==(N,m)``
      :param Y: numpy array with ``shape==(N,n)``
      :param ip_B: (optional) May be one of the following
  
          * ``None``: Euclidean inner product.
          * a self-adjoint and positive definite operator :math:`B` (as
            ``numpy.array`` or ``LinearOperator``). Then :math:`X^*B Y` is
            returned.
          * a callable which takes 2 arguments X and Y and returns
            :math:`\\langle X,Y\\rangle`.
  
      **Caution:** a callable should only be used if necessary. The choice
      potentially has an impact on the round-off behavior, e.g. of projections.
  
      :return: numpy array :math:`\\langle X,Y\\rangle` with ``shape==(m,n)``.
      '''
      if ip_B is None or isinstance(ip_B, IdentityLinearOperator):
          return numpy.dot(X.T.conj(), Y)
      (N, m) = X.shape
      (_, n) = Y.shape
      try:
          B = get_linearoperator((N, N), ip_B)
      except TypeError:
          return ip_B(X, Y)
      if m > n:
          return numpy.dot((B*X).T.conj(), Y)
      else:
          return numpy.dot(X.T.conj(), B*Y)