def __init__(
        self,
        matrix: np.ndarray,
        *,
        name: str | None = None,
        qid_shape: Iterable[int] | None = None,
        unitary_check: bool = True,
        unitary_check_rtol: float = 1e-5,
        unitary_check_atol: float = 1e-8,
    ) -> None:
        """Initializes a matrix gate.

        Args:
            matrix: The matrix that defines the gate.
            name: The optional name of the gate to be displayed.
            qid_shape: The shape of state tensor that the matrix applies to.
                If not specified, this value is inferred by assuming that the
                matrix is supposed to apply to qubits.
            unitary_check: If True, check that the supplied matrix is unitary up to the
                given tolerances. This should only be disabled if the matrix has already been
                checked for unitarity, in which case we get a slight performance improvement by
                not checking again.
            unitary_check_rtol: The relative tolerance for checking whether the supplied matrix
                is unitary. See `cirq.is_unitary`.
            unitary_check_atol: The absolute tolerance for checking whether the supplied matrix
                is unitary. See `cirq.is_unitary`.

        Raises:
            ValueError: If the matrix is not a square numpy array, if the matrix does not match
                the `qid_shape`, if `qid_shape` is not supplied and the matrix dimension is
                not a power of 2, or if the matrix not unitary (to the supplied precisions).
        """
        if len(matrix.shape) != 2 or matrix.shape[0] != matrix.shape[1]:
            raise ValueError('`matrix` must be a square 2d numpy array.')

        if qid_shape is None:
            n = int(np.round(np.log2(matrix.shape[0] or 1)))
            if 2**n != matrix.shape[0]:
                raise ValueError(
                    f'Matrix width ({matrix.shape[0]}) is not a power of 2 and '
                    f'qid_shape is not specified.'
                )
            qid_shape = (2,) * n

        self._matrix = matrix
        self._qid_shape = tuple(qid_shape)
        self._name = name
        m = int(np.prod(self._qid_shape, dtype=np.int64))
        if self._matrix.shape != (m, m):
            raise ValueError(
                'Wrong matrix shape for qid_shape.\n'
                f'Matrix shape: {self._matrix.shape}\n'
                f'qid_shape: {self._qid_shape}\n'
            )

        if unitary_check and not linalg.is_unitary(
            matrix, rtol=unitary_check_rtol, atol=unitary_check_atol
        ):
            raise ValueError(f'Not a unitary matrix: {matrix}')
Initializes a matrix gate.

        Args:
            matrix: The matrix that defines the gate.
            name: The optional name of the gate to be displayed.
            qid_shape: The shape of state tensor that the matrix applies to.
                If not specified, this value is inferred by assuming that the
                matrix is supposed to apply to qubits.
            unitary_check: If True, check that the supplied matrix is unitary up to the
                given tolerances. This should only be disabled if the matrix has already been
                checked for unitarity, in which case we get a slight performance improvement by
                not checking again.
            unitary_check_rtol: The relative tolerance for checking whether the supplied matrix
                is unitary. See `cirq.is_unitary`.
            unitary_check_atol: The absolute tolerance for checking whether the supplied matrix
                is unitary. See `cirq.is_unitary`.

        Raises:
            ValueError: If the matrix is not a square numpy array, if the matrix does not match
                the `qid_shape`, if `qid_shape` is not supplied and the matrix dimension is
                not a power of 2, or if the matrix not unitary (to the supplied precisions).
        
cirq-core/cirq/ops/matrix_gates.py