code: def _op_to_matrix(self,
                        op: Optional[ops.Operation],
                        qubits: Tuple[ops.Qid, ...]
                        ) -> Optional[np.ndarray]:
          
          q1, q2 = qubits
  
          matrix = protocols.unitary(op, None)
          if matrix is None:
              return None
  
          assert op is not None
          if op.qubits == qubits:
              return matrix
          if op.qubits == (q2, q1):
              return MergeInteractions._flip_kron_order(matrix)
          if op.qubits == (q1,):
              return np.kron(matrix, np.eye(2))
          if op.qubits == (q2,):
              return np.kron(np.eye(2), matrix)
  
          return None
docs: Determines the effect of an operation on the given qubits.
  
          If the operation is a 1-qubit operation on one of the given qubits,
          or a 2-qubit operation on both of the given qubits, and also the
          operation has a known matrix, then a matrix is returned. Otherwise None
          is returned.
  
          Args:
              op: The operation to understand.
              qubits: The qubits we care about. Order determines matrix tensor
                  order.
  
          Returns:
              None, or else a matrix equivalent to the effect of the operation.