def get_fidelity_with(self, target_state: Union[str, q.qarray] = "ghz") -> float: """ :param target_state: One of "ghz", "ghz_antisymmetric", "ground", and "excited". Can also be ghz_component_1 or ghz_component_2 :return: """ assert ( self.evo is not None), "evo attribute cannot be None (call solve method)" final_state = self.solved_states[-1] if target_state == "ghz": return q.fidelity(final_state, self.ghz_state.get_state_tensor(symmetric=True)) elif target_state == "ghz_antisymmetric": return q.fidelity(final_state, self.ghz_state.get_state_tensor(symmetric=False)) elif target_state == "ghz_component_1": return q.fidelity(final_state, self.ghz_state._get_components()[0]) elif target_state == "ghz_component_2": return q.fidelity(final_state, self.ghz_state._get_components()[1]) elif target_state == "ground": return q.fidelity(final_state, q.kron(*states_quimb.get_ground_states(self.N))) elif target_state == "excited": return q.fidelity(final_state, q.kron(*states_quimb.get_excited_states(self.N))) elif isinstance(target_state, q.qarray): return q.fidelity(final_state, target_state) else: raise ValueError( f"target_state has to be one of 'ghz', 'ground', or 'excited', not {target_state}." )
def gen_term(self, sites=None): """Generate the interaction term acting on ``sites``. """ # make sure have sites as (i, i + 1) if supplied if sites is not None: i, j = sites = tuple(sorted(sites)) if j - i != 1: raise ValueError("Only nearest neighbour interactions are " "supported for an ``NNI``.") else: i = j = None term = self.H2s.get(sites, self.H2s[None]) if term is None: raise ValueError("No term has been set for sites {}, either specif" "ically or via a default term.".format(sites)) # add single site term to left site if present H1 = self.H1s.get(i, self.H1s[None]) # but only if this site has a term set if H1 is not None: I_2 = qu.eye(H1.shape[0], dtype=H1.dtype) term = term + qu.kron(H1, I_2) # if not PBC, for the last interaction, add to right site as well if sites and (j == self.n - 1) and (not self.cyclic): H1 = self.H1s.get(j, self.H1s[None]) # but again, only if that site has a term set if H1 is not None: I_2 = qu.eye(H1.shape[0], dtype=H1.dtype) term = term + qu.kron(I_2, H1) return term
def two_qubit_eigsectors(strA='XY', strB='YX'): ''' Params: strA, strB [strings] Action of stabilizers on face-qubit subspace. Return: `eigsectors` [ndarray(shape=(2,2), dtype=object)] Each element of eigsector contains the unique vector in the face-qubit subspace that diagonalizes strA, strB with respective eigenvalues +/-1. eigsectors[0,0]: +1, +1 eigsectors[0,1]: +1, -1 eigsectors[1,0]: -1, +1 eigsectors[1,1]: -1, -1 ''' sign_dic = {0: 1.0, 1: -1.0} X, Y, Z, I = (qu.pauli(mu) for mu in ['x','y','z','i']) opmap = {'X': X, 'Y':Y, 'Z':Z, 'I':I} face_dims = [2]*2 #dimensions of face-qubit subspace # ### # strA = 'XY' # strB = 'YX' # ### SA = qu.kron(*[opmap[Q] for Q in strA]) SB = qu.kron(*[opmap[Q] for Q in strB]) # SA, SB = X&Y, Y&X eigsectors = np.ndarray(shape=face_dims, dtype=object) eva, Ua = qu.eigh(SA) for indA, signA in sign_dic.items(): #pick evectors in (signA) sector Ua_sgn = Ua[:, np.isclose(eva, signA)] #4x2 #Stabilizer B on (signA) eigspace of SA Qb = Ua_sgn.H @ SB @ Ua_sgn evb, Ub = qu.eigh(Qb) for indB, signB in sign_dic.items(): #pick evector in signB sector Ub_sgn = Ub[:,np.isclose(evb, signB)] #2x1 face_state = Ua_sgn @ Ub_sgn #4x1 assert np.allclose(SA@face_state, signA*face_state) assert np.allclose(SB@face_state, signB*face_state) eigsectors[indA,indB] = face_state return eigsectors
def _get_components(self) -> Tuple[q.qarray, q.qarray]: ghz_1 = q.kron(*[ q.ket([0, 1], sparse=True) if _ % 2 == 0 else q.ket([1, 0], sparse=True) for _ in range(self.N) ]) ghz_2 = q.kron(*[ q.ket([0, 1], sparse=True) if _ % 2 == 1 else q.ket([1, 0], sparse=True) for _ in range(self.N) ]) return ghz_1, ghz_2
def _get_components(self) -> Tuple[q.qarray, q.qarray]: ghz_1 = q.kron(*[ q.ket([0, 1], sparse=True) if self.single_component[i] else q. ket([1, 0], sparse=True) for i in range(self.N) ]) ghz_2 = q.kron(*[ q.ket([0, 1], sparse=True) if not self.single_component[i] else q. ket([1, 0], sparse=True) for i in range(self.N) ]) return ghz_1, ghz_2
def __init__(self, N: int, V: float, geometry: BaseGeometry, Omega: np.ndarray, Delta: np.ndarray, t_list: np.ndarray, ghz_state: BaseGHZState, solve_points_per_timestep: int = 1): super().__init__(N, V, geometry) self.Omega = Omega self.Delta = Delta self.t_list = t_list assert len(t_list) - 1 == len(Omega) == len(Delta), \ "Omega and Delta need to be of equal length, and of length one less than t_list" self.psi_0 = q.kron(*states_quimb.get_ground_states(N)) self.ghz_state = ghz_state self.solve_points_per_timestep = solve_points_per_timestep self.evo: Optional[q.Evolution] = None self.solved_states: List[q.qarray] = [] self.solved_t_list = []
def test_pauli_reconstruct(self): p1 = qu.rand_rho(4) names_cffs = qu.pauli_decomp(p1, mode='c') pr = sum( qu.kron(*(qu.pauli(s) for s in name)) * names_cffs["".join(name)] for name in itertools.product('IXYZ', repeat=2)) assert_allclose(pr, p1)
def test_kron_basic(self, parallel): a = qu.rand_ket(2) b = qu.rand_ket(4) c = qu.rand_ket(4) d = qu.rand_ket(5) t = qu.kron(a, b, c, d, parallel=parallel) assert_allclose(t, a & b & c & d)
def test_partial_trace_dop_product_state(self): dims = [3, 2, 4, 2, 3] ps = [qu.rand_rho(dim) for dim in dims] pt = qu.kron(*ps) for i, dim in enumerate(dims): p = qu.partial_trace(pt, dims, i) assert_allclose(p, ps[i])
def get_energies(self): omega_zero_all_energies = [] omega_non_zero_all_energies = [] state_tensors = [q.kron(*state) for state in self.states] for detuning in tqdm(self.Delta): H = self.get_hamiltonian(detuning) energies = [] for i, state in enumerate(self.states): energy = q.expec(H, state_tensors[i]).real energies.append(energy) omega_zero_all_energies.append(energies) if self.Omega != 0: eigenvalues = q.eigvalsh(H.toarray()).real omega_non_zero_all_energies.append(eigenvalues) self.Omega_zero_energies = np.array(omega_zero_all_energies) self.Omega_non_zero_energies = np.array(omega_non_zero_all_energies)
def __init__(self, N: int, V: float, geometry: BaseGeometry, t_list: np.ndarray, ghz_state: BaseGHZState, Omega_range: Tuple[float, float], Delta_range: Tuple[float, float], verbose: bool = False): self.verbose = verbose self.ti_evolving_qubit_system_kwargs = { 'N': N, 'V': V, 'geometry': geometry, 'ghz_state': ghz_state, 't_list': np.linspace(0, t_list[1], 10) } self.psi_0 = q.kron(*states_quimb.get_ground_states(N)) self.t_list = t_list self.required_steps = len(t_list) - 1 # Actions for all ts except the end. self.recorded_steps = { 'Omega': [], 'Delta': [], } self.step_number = 0 self.latest_evolving_qubit_system: Optional[ TimeIndependentEvolvingQubitSystem] = None self.total_solve_time = 0 self.action_normalisation = Omega_range, Delta_range self.action_space = gym.spaces.Box(low=np.array([0, 0], dtype=np.float32), high=np.array([1, 1], dtype=np.float32)) # Using action_normalisation as PPO policy generates actions of order 1 self.observation_space = gym.spaces.Discrete(self.required_steps) self._maximum_fidelity_achieved = 0.505
def make_stab_gate_map(self, coo_stab_map, store='gate'): '''TODO: NOW MAPS coos to (where, gate) tuples. Return ------- `gate_map`: dict[tuple : (tuple, qarray)] Maps coordinates (x,y) in the *face* array (empty faces!) to pairs (where, gate) that specify the stabilizer gate and the sites to be acted on. Param: ------ coo_stab_map: dict[tuple : dict] Maps coordinates (x,y) in the FACE array of the lattice to `loop_stab` dictionaries of the form {'inds' : (indices), 'opstring' : (string)} store: 'gate' or 'tuple', optional Whether to store a 'dense' 2**8 x 2**8 array or a tuple of 8 (ordered) 2 x 2 arrays ''' gate_map = dict() for coo, loop_stab in coo_stab_map.items(): qubits = tuple(loop_stab['inds']) #tuple e.g. (1,2,4,5,6) opstring = loop_stab['opstring'] #str e.g. 'ZZZZX' if store == 'gate': # store (where, kron(gate1,gate2,...)) gate = qu.kron(*(qu.pauli(Q) for Q in opstring)) gate *= self.multiplier gate_map[coo] = (qubits, gate) elif store == 'tuple': # store (where, (gate1, gate2, ...)) signs = [self.multiplier] + [1.0] * (len(opstring) - 1) gates = tuple(signs[k] * qu.pauli(Q) for k, Q in enumerate(opstring)) gate_map[coo] = (qubits, gates) return gate_map
def __init__(self, N: int, V: float, geometry: BaseGeometry, Omega: float, Delta: float, t_list: np.ndarray, ghz_state: BaseGHZState, psi_0: q.qarray = None): super().__init__(N, V, geometry) self.Omega = Omega self.Delta = Delta self.t_list = t_list self.psi_0 = q.kron(*states_quimb.get_ground_states( N, sparse=True)) if psi_0 is None else psi_0 self.ghz_state = ghz_state self.evo: Optional[q.Evolution] = None self.solved_states: List[q.qarray] = []
def _get_components(self) -> Tuple[q.qarray, q.qarray]: return q.kron(*get_ground_states(self.N, sparse=True)), q.kron( *get_excited_states(self.N, sparse=True))
def three_qubit_U_matrix(qlattice, qstabs=None): ''' TODO: *define SX_vert (vertices acted on by Z-stabilizer) ''' sectors = {1.0: 0, -1.0: 1} X, Y, Z, I = (qu.pauli(mu) for mu in ['x','y','z','i']) opmap = {'X': X, 'Y':Y, 'Z':Z, 'I':I} face_dims = [2]*3 #dimensions of face-qubit subspace if qstabs==None: qstab_A = loopStabOperator( vert_inds=[1,2,5,4], face_op_str='XYI', face_inds=[12,13,14]) qstab_B = loopStabOperator( vert_inds=[3,4,7,6], face_op_str='YXY', face_inds=[12,13,14]) qstab_C = loopStabOperator( vert_inds=[7,8,11,10], face_op_str='IYX', face_inds=[12,13,14]) SA = qu.kron(*[opmap[Q] for Q in qstab_A.face_op_str]) SB = qu.kron(*[opmap[Q] for Q in qstab_B.face_op_str]) SC = qu.kron(*[opmap[Q] for Q in qstab_C.face_op_str]) # SA, SB, SC = X&Y&I, Y&X&Y, I&Y&X eigfaces = np.ndarray(shape=face_dims, dtype=object) for signA, indA in sectors.items(): eva, Ua = qu.eigh(SA) Ua_sector = Ua[:,eva==signA] #8x4 Qb = Ua_sector.H @ SB @ Ua_sector # Qc = Ua_sector.H @ SC @ Ua_sector evb, Ub = qu.eigh(Qb) for signB, indB in sectors.items(): Ub_sector = Ub[:, np.isclose(evb,signB)] #4x2 #SC on b-eigenspace Qc = Ub_sector.H @ Ua_sector.H @ SC @ Ua_sector @ Ub_sector #2x2 evc, Uc = qu.eigh(Qc) for signC, indC in sectors.items(): #should be in a 1-dimensional subspace now vec = Uc[:,np.isclose(evc,signC)] #vector in 8-dim face qubit space face_vec = Ua_sector @ Ub_sector @ vec assert np.allclose(SA@face_vec, signA*face_vec) assert np.allclose(SB@face_vec, signB*face_vec) assert np.allclose(SC@face_vec, signC*face_vec) eigfaces[indA,indB,indC] = face_vec Nfermi, Nqubit = qlattice._Nfermi, qlattice._Nsites code_dims = [2]*Nfermi #vertices qub_dims = [2]*Nqubit #vertices&faces ####### qindices_A = qstab_A.vert_inds qindices_B = qstab_B.vert_inds qindices_C = qstab_C.vert_inds ####### #parts of stabilizers acting only on vertex qubits SA_vert = qu.ikron(ops=[Z,Z,Z,Z], inds=qindices_A, dims=code_dims) SB_vert = qu.ikron(ops=[Z,Z,Z,Z], inds=qindices_B, dims=code_dims) SC_vert = qu.ikron(ops=[Z,Z,Z,Z], inds=qindices_C, dims=code_dims) Uplus = np.ndarray(shape=(2**Nqubit, 2**Nfermi)) print('here') for k in range(2**Nfermi): print(k%10) #state of vertex qubits, all local z-eigenstates vertex_state = qu.basis_vec(i=k, dim=2**Nfermi) secA = sectors[qu.expec(SA_vert, vertex_state)] secB = sectors[qu.expec(SB_vert, vertex_state)] secC = sectors[qu.expec(SC_vert, vertex_state)] face_state = eigfaces[secA, secB, secC] full_state = vertex_state & face_state #"stable" eigenstate written in qubit basis Uplus[:,k] = full_state.flatten() #full_state should be +1 eigenstate of all stabilizers assert np.allclose((SA_vert&SA) @ full_state, full_state) assert np.allclose((SB_vert&SB) @ full_state, full_state) assert np.allclose((SC_vert&SC) @ full_state, full_state) return qu.qu(Uplus)
def string_to_gate(cls, string_op): X, Y, Z, I = (qu.pauli(mu) for mu in ['x','y','z','i']) opmap = {'X': X, 'Y':Y, 'Z':Z, 'I':I} return qu.kron(*[opmap[Q] for Q in string_op])
def test_kron_ownership(self, sparse, ri, rf): dims = [7, 2, 4, 3] ops = [qu.rand_matrix(d, sparse=sparse) for d in dims] X1 = qu.kron(*ops)[ri:rf, :] X2 = qu.kron(*ops, ownership=(ri, rf)) assert_allclose(X1.A, X2.A)
def get_product_basis_states_index(state: List[q.qarray]) -> int: return q.kron(*state).argmax()