def __init__(self, omega_g, omega_r, energies=(1, ), graph: Graph = None, IS_subspace=True, code=qubit): # Just need to define self.hamiltonian assert IS_subspace self.energies = energies self.IS_subspace = IS_subspace self.graph = graph self.omega_r = omega_r self.omega_g = omega_g self.code = code assert self.code is qubit if self.IS_subspace: # Generate sparse mixing Hamiltonian assert graph is not None assert isinstance(graph, Graph) if code is not qubit: IS, num_IS = graph.independent_sets_qudit(self.code) else: # We have already solved for this information IS, num_IS = graph.independent_sets, graph.num_independent_sets self.transition = (0, 1) self._hamiltonian_rr = np.zeros((num_IS, num_IS)) self._hamiltonian_gg = np.zeros((num_IS, num_IS)) self._hamiltonian_cross_terms = np.zeros((num_IS, num_IS)) for k in range(IS.shape[0]): self._hamiltonian_rr[k, k] = np.sum( IS[k][1] == self.transition[0]) self._hamiltonian_gg[k, k] = np.sum( IS[k][1] == self.transition[1]) self._csc_hamiltonian_rr = sparse.csc_matrix(self._hamiltonian_rr) self._csc_hamiltonian_gg = sparse.csc_matrix(self._hamiltonian_gg) # For each IS, look at spin flips generated by the laser # Over-allocate space rows = np.zeros(graph.n * num_IS, dtype=int) columns = np.zeros(graph.n * num_IS, dtype=int) entries = np.zeros(graph.n * num_IS, dtype=float) num_terms = 0 for i in range(graph.num_independent_sets): for j in range(graph.n): if IS[i, j] == self.transition[1]: # Flip spin at this location # Get binary representation temp = IS[i].copy() temp[j] = self.transition[0] where_matched = (np.argwhere( np.sum(np.abs(IS - temp), axis=1) == 0).flatten()) if len(where_matched) > 0: # This is a valid spin flip rows[num_terms] = where_matched[0] columns[num_terms] = i entries[num_terms] = 1 num_terms += 1 # Cut off the excess in the arrays columns = columns[:2 * num_terms] rows = rows[:2 * num_terms] entries = entries[:2 * num_terms] # Populate the second half of the entries according to self.pauli columns[num_terms:2 * num_terms] = rows[:num_terms] rows[num_terms:2 * num_terms] = columns[:num_terms] entries[num_terms:2 * num_terms] = entries[:num_terms] # Now, construct the Hamiltonian self._csc_hamiltonian_cross_terms = sparse.csc_matrix( (entries, (rows, columns)), shape=(num_IS, num_IS)) self._hamiltonian_cross_terms = self._csc_hamiltonian_cross_terms else: # We are not in the IS subspace pass
def __init__(self, omega_g, omega_r, rates=(1, ), graph: Graph = None, IS_subspace=True, code=qubit): self.omega_g = omega_g self.omega_r = omega_r self.IS_subspace = IS_subspace self.transition = (0, 1) self.graph = graph # Construct jump operators if self.IS_subspace: # Generate sparse mixing Hamiltonian assert graph is not None assert isinstance(graph, Graph) if code is not qubit: IS, num_IS = graph.independent_sets_qudit(self.code) else: # We have already solved for this information IS, num_IS = graph.independent_sets, graph.num_independent_sets self._jump_operators_rg = [] self._jump_operators_gg = [] # For each atom, consider the states spontaneous emission can generate transitions between # Over-allocate space for j in range(graph.n): rows_rg = np.zeros(num_IS, dtype=int) columns_rg = np.zeros(num_IS, dtype=int) entries_rg = np.zeros(num_IS, dtype=int) rows_gg = np.zeros(num_IS, dtype=int) columns_gg = np.zeros(num_IS, dtype=int) entries_gg = np.zeros(num_IS, dtype=int) num_terms_gg = 0 num_terms_rg = 0 for i in range(IS.shape[0]): if IS[i, j] == self.transition[0]: # Flip spin at this location # Get binary representation temp = IS[i].copy() temp[j] = self.transition[1] where_matched = (np.argwhere( np.sum(np.abs(IS - temp), axis=1) == 0).flatten()) if len(where_matched) > 0: # This is a valid spin flip rows_rg[num_terms_rg] = where_matched[0] columns_rg[num_terms_rg] = i entries_rg[num_terms_rg] = 1 num_terms_rg += 1 elif IS[i, j] == self.transition[1]: rows_gg[num_terms_gg] = i columns_gg[num_terms_gg] = i entries_gg[num_terms_gg] = 1 num_terms_gg += 1 # Cut off the excess in the arrays columns_rg = columns_rg[:num_terms_rg] rows_rg = rows_rg[:num_terms_rg] entries_rg = entries_rg[:num_terms_rg] columns_gg = columns_gg[:num_terms_gg] rows_gg = rows_gg[:num_terms_gg] entries_gg = entries_gg[:num_terms_gg] # Now, append the jump operator jump_operator_rg = sparse.csc_matrix( (entries_rg, (rows_rg, columns_rg)), shape=(num_IS, num_IS)) jump_operator_gg = sparse.csc_matrix( (entries_gg, (rows_gg, columns_gg)), shape=(num_IS, num_IS)) self._jump_operators_rg.append(jump_operator_rg) self._jump_operators_gg.append(jump_operator_gg) self._jump_operators_rg = np.asarray(self._jump_operators_rg) self._jump_operators_gg = np.asarray(self._jump_operators_gg) else: # self._jump_operators_rg = [] # self._jump_operators_gg = [] op_rg = np.array([[[0, 0], [1, 0]]]) op_gg = np.array([[[0, 0], [0, 1]]]) self._jump_operators_rg = op_rg self._jump_operators_gg = op_gg super().__init__(None, rates=rates, graph=graph, IS_subspace=IS_subspace, code=code)