def get_hamiltonian_variables_with_edge_fields( e_qs: EvolvingQubitSystem) -> Tuple[q.qarray, q.qarray, q.qarray]: sx = q.pauli("X", sparse=True) sz = q.pauli("Z", sparse=True) qnum = (sz + q.identity(2, sparse=True)) / 2 dims = [2] * e_qs.N # noinspection PyTypeChecker time_independent_terms: q.qarray = 0 # noinspection PyTypeChecker Omega_coeff_terms: q.qarray = 0 # noinspection PyTypeChecker Delta_coeff_terms: q.qarray = 0 for i in range(e_qs.N): Omega_coeff_terms += q.ikron(sx, dims=dims, inds=i, sparse=True) / 2 n_i = q.ikron(qnum, dims=dims, inds=i, sparse=True) Delta_coeff_terms -= n_i if e_qs.N <= 8: if i == 0 or i == e_qs.N - 1: time_independent_terms += n_i * 4.5e6 * 2 * np.pi elif e_qs.N > 8: if i == 0 or i == e_qs.N - 1: time_independent_terms += n_i * 6e6 * 2 * np.pi elif i == 3 or i == e_qs.N - 4: time_independent_terms += n_i * 1.5e6 * 2 * np.pi for j in range(i): n_j = q.ikron(qnum, dims=dims, inds=j, sparse=True) time_independent_terms += e_qs.V / e_qs.geometry.get_distance( i, j)**6 * n_i * n_j return (time_independent_terms, Omega_coeff_terms, Delta_coeff_terms)
def _get_hamiltonian_variables( self) -> Tuple[q.qarray, q.qarray, q.qarray]: sx = q.pauli("X", sparse=True) sz = q.pauli("Z", sparse=True) qnum = (sz + q.identity(2, sparse=True)) / 2 dims = [2] * self.N # noinspection PyTypeChecker time_independent_terms: q.qarray = 0 # noinspection PyTypeChecker Omega_coeff_terms: q.qarray = 0 # noinspection PyTypeChecker Delta_coeff_terms: q.qarray = 0 for i in range(self.N): Omega_coeff_terms += q.ikron(sx, dims=dims, inds=i, sparse=True) / 2 n_i = q.ikron(qnum, dims=dims, inds=i, sparse=True) Delta_coeff_terms -= n_i for j in range(i): n_j = q.ikron(qnum, dims=dims, inds=j, sparse=True) time_independent_terms += self.V / self.geometry.get_distance( i, j)**6 * n_i * n_j return (time_independent_terms, Omega_coeff_terms, Delta_coeff_terms)
def test_ndarrays(self): a = qu.rand_matrix(2) i = qu.eye(2) b = qu.ikron([a], np.array([2, 2, 2]), [0, 2]) assert_allclose(b, a & i & a) b = qu.ikron([a], [2, 2, 2], np.array([0, 2])) assert_allclose(b, a & i & a)
def H_hop_boson(self, i, j): '''Hopping term *without* anti-symmetrization ''' c, cdag = annihil_op(), creation_op() ccd = qu.ikron(ops=[c, cdag], dims=self._dims, inds=[i, j]) cdc = qu.ikron(ops=[c, cdag], dims=self._dims, inds=[j, i]) return ccd + cdc
def test_ikron_ownership(self, sparse, ri, rf): dims = [7, 2, 4, 3] X = qu.rand_matrix(2, sparse=sparse) Y = qu.rand_matrix(3, sparse=sparse) X1 = qu.ikron((X, Y), dims, (1, 3))[ri:rf, :] X2 = qu.ikron((X, Y), dims, (1, 3), ownership=(ri, rf)) assert_allclose(X1.A, X2.A)
def test_sparse(self): i = qu.eye(2, sparse=True) a = qu.qu(qu.rand_matrix(2), sparse=True) b = qu.ikron(a, [2, 2, 2], 1) # infer sparse assert (qu.issparse(b)) assert_allclose(b.A, (i & a & i).A) a = qu.rand_matrix(2) b = qu.ikron(a, [2, 2, 2], 1, sparse=True) # explicit sparse assert (qu.issparse(b)) assert_allclose(b.A, (i & a & i).A)
def test_overlap(self): a = [qu.rand_matrix(4) for i in range(2)] dims1 = [2, 2, 2, 2, 2, 2] dims2 = [2, 4, 4, 2] b = qu.ikron(a, dims1, [1, 2, 3, 4]) c = qu.ikron(a, dims2, [1, 2]) assert_allclose(c, b) dims2 = [4, 2, 2, 4] b = qu.ikron(a, dims1, [0, 1, 4, 5]) c = qu.ikron(a, dims2, [0, 3]) assert_allclose(c, b)
def test_basic(self): a = qu.rand_matrix(2) i = qu.eye(2) dims = [2, 2, 2] b = qu.ikron([a], dims, [0]) assert_allclose(b, a & i & i) b = qu.ikron([a], dims, [1]) assert_allclose(b, i & a & i) b = qu.ikron([a], dims, [2]) assert_allclose(b, i & i & a) b = qu.ikron([a], dims, [0, 2]) assert_allclose(b, a & i & a) b = qu.ikron([a], dims, [0, 1, 2]) assert_allclose(b, a & a & a)
def H_hop(self, i, j, f, dir, spin): ''' Returns "hopping" term acting on sites i,j,f: (XiXjOf + YiYjOf)/2 on vertex qdits i,j, face qdit f, spin sector `spin`, where Of is {Xf, Yf, I} depending on edge. dir: {'vertical', 'horizontal'} ''' spin = { 0: 'u', 1: 'd', 'up': 'u', 'down': 'd', 'u': 'u', 'd': 'd' }[spin] X, Y, I = (qu.pauli(mu) for mu in ['x', 'y', 'i']) #`spin` says which spin sector to act on X_sigma = {'u': X & I, 'd': I & X}[spin] Y_sigma = {'u': Y & I, 'd': I & Y}[spin] Of = {'vertical': X_sigma, 'horizontal': Y_sigma}[dir] #no associated face qbit if f == None: XXO = qu.ikron(ops=[X_sigma, X_sigma], dims=self._sim_dims, inds=[i, j]) YYO = qu.ikron(ops=[Y_sigma, Y_sigma], dims=self._sim_dims, inds=[i, j]) #otherwise, let Of act on index f else: XXO = qu.ikron(ops=[X_sigma, X_sigma, Of], dims=self._sim_dims, inds=[i, j, f]) YYO = qu.ikron(ops=[Y_sigma, Y_sigma, Of], dims=self._sim_dims, inds=[i, j, f]) return 0.5 * (XXO + YYO)
def test_2d_simple(self): a = (qu.rand_matrix(2), qu.rand_matrix(2)) dims = ((2, 3), (3, 2)) inds = ((0, 0), (1, 1)) b = qu.ikron(a, dims, inds) assert b.shape == (36, 36) assert_allclose(b, a[0] & qu.eye(9) & a[1])
def stabilizer_at_face(self, fi, fj): stab_data = self.loop_stabilizer_data(fi, fj) oplist = [qu.pauli(Q) for Q in stab_data['opstring']] return qu.ikron(ops=oplist, dims=self._sim_dims, inds=stab_data['inds'])
def test_mid_multi_reverse(self): a = [qu.rand_matrix(2) for i in range(3)] i = qu.eye(2) dims = [2, 2, 2, 2, 2, 2] inds = [5, 4, 1] b = qu.ikron(a, dims, inds) assert_allclose(b, i & a[2] & i & i & a[1] & a[0])
def test_sparse_format_outputs_with_dense(self, od1, stype, pos, coo_build): x = qu.ikron(od1, [3, 3, 3], pos, sparse=True, stype=stype, coo_build=coo_build) try: default = "bsr" if (2 in pos and not coo_build) else "csr" except TypeError: default = "bsr" if (pos == 2 and not coo_build) else "csr" assert x.format == default if stype is None else stype
def test_evo_at_times(self): ham = qu.ham_heis(2, cyclic=False) p0 = qu.up() & qu.down() sim = qu.Evolution(p0, ham, method='solve') ts = np.linspace(0, 10) for t, pt in zip(ts, sim.at_times(ts)): x = cos(t) y = qu.expec(pt, qu.ikron(qu.pauli('z'), [2, 2], 0)) assert_allclose(x, y, atol=1e-15)
def test_graph_state_1d(self): n = 5 p = graph_state_1d(n, cyclic=True) for j in range(n): k = ikron( [pauli('x'), pauli('z'), pauli('z')], dims=[2] * n, inds=(j, (j - 1) % n, (j + 1) % n)) o = p.H @ k @ p np.testing.assert_allclose(o, 1)
def state_occs(self, state): Nk = [ qu.ikron(number_op(), self._dims, [site]) for site in self._V_ind.flatten() ] n_ij = np.real( np.array([qu.expec(Nk[k], state) for k in range(self._N)])).reshape(self._shape) return n_ij
def test_insert_operator(self): p = MPS_rand_state(3, 7, tags='KET') q = p.H.retag({'KET': 'BRA'}) qp = q & p sz = qu.spin_operator('z').real qp.insert_operator(sz, ('KET', 'I1'), ('BRA', 'I1'), tags='SZ', inplace=True) assert 'SZ' in qp.tags assert len(qp.tensors) == 7 x1 = qp ^ all x2 = qu.expec(p.to_dense(), qu.ikron(sz, [2, 2, 2], inds=1)) assert x1 == pytest.approx(x2)
def H_nn_int(self, i, j): ''' Nearest neighbor repulsive interaction, for vertex qbits i, j. Return (n_i)(n_j) ''' return qu.ikron(ops=[self.number_op(), self.number_op()], dims=self._sim_dims, inds=[i, j])
def H_onsite(self, i): ''' Spin-spin repulsion at site i (should only be called for vertex sites, not face sites) (n^up_i)(n^down_i) --> (1-Vi^up)(1-Vi^down)/4 Projects onto [down (x) down] for qbits at index i ''' return qu.ikron(ops=[self.q_number_op() & self.q_number_op()], dims=self._sim_dims, inds=[i])
def make_stabilizer(self): ''' DEPREC -- DON'T USE TODO: * add a projector onto codespace (see method 2) * generalize indices, rotation, reshape, etc * always need to round? check always real ''' X, Z = (qu.pauli(mu) for mu in ['x', 'z']) ## TODO: make general # ops = [Z,Z,Z,Z,X] # inds = [1,2,4,5,6] # stabilizer = qu.ikron(ops=ops, dims=self._sim_dims, inds=inds) _, Ux = qu.eigh(qu.pauli('x')) stab_data = self.loop_stabilizer_data(0, 1) oplist = [qu.pauli(Q) for Q in stab_data['opstring']] stabilizer = qu.ikron(ops=oplist, dims=self._sim_dims, inds=stab_data['inds']) #TODO: change to general rather than inds=6, Ux U = qu.ikron(Ux.copy(), dims=self._sim_dims, inds=[6]) #TODO: can this be done without rounding()/real part? Stilde = (U.H @ stabilizer @ U).real.round() assert is_diagonal(Stilde) U_plus = U[:, np.where(np.diag(Stilde) == 1.0)[0]] HamCode = U_plus.H @ self.ham_sim() @ U_plus self._stabilizer = stabilizer self._Uplus = U_plus #+1 eigenstates written in full qubit basis self._HamCode = HamCode # in +1 stabilizer eigenbasis
def test_variable_bond_ham(self): import quimb as qu HB = SpinHam(1 / 2) HB[0, 1] += 0.6, 'Z', 'Z' HB[1, 2] += 0.7, 'Z', 'Z' HB[1, 2] += 0.8, 'X', 'X' HB[2, 3] += 0.9, 'Y', 'Y' H_mpo = HB.build_mpo(4) H_sps = HB.build_sparse(4) assert H_mpo.bond_sizes() == [3, 4, 3] Sx, Sy, Sz = map(qu.spin_operator, 'xyz') H_explicit = (qu.ikron(0.6 * Sz & Sz, [2, 2, 2, 2], [0, 1]) + qu.ikron(0.7 * Sz & Sz, [2, 2, 2, 2], [1, 2]) + qu.ikron(0.8 * Sx & Sx, [2, 2, 2, 2], [1, 2]) + qu.ikron(0.9 * Sy & Sy, [2, 2, 2, 2], [2, 3])) assert_allclose(H_explicit, H_mpo.to_dense()) assert_allclose(H_explicit, H_sps.A)
def jw_creation_op(self, k): ''' Creation Jordan-Wigner qubit operator. (Z_0)x(Z_1)x ...x(Z_k-1)x |1><0|_k ''' Z = qu.pauli('z') s_plus = qu.qu([[0, 0], [1, 0]]) #|1><0| ind_list = [i for i in range(k + 1)] op_list = [Z for i in range(k)] + [s_plus] return qu.ikron(ops=op_list, dims=self._dims, inds=ind_list)
def jw_annihil_op(self, k): ''' Annihilation Jordan-Wigner qubit operator. (Z_0)x(Z_1)x ...x(Z_k-1)x |0><1|_k ''' Z = qu.pauli('z') s_minus = qu.qu([[0, 1], [0, 0]]) #|0><1| ind_list = [i for i in range(k + 1)] op_list = [Z for i in range(k)] + [s_minus] return qu.ikron(ops=op_list, dims=self._dims, inds=ind_list)
def site_number_ops(self, sites, fermi=False): ''' TODO: remove fermi? Returns: list of qarrays, local number operators acting on specified sites. param `sites`: list of ints, indices of sites param `fermi`: bool, indicates whether we are acting in the large qubit space basis (False) or in the restricted stabilizer eigenbasis (True) ''' ds = {False: self._sim_dims, True: self._fermi_dims}[fermi] return [qu.ikron(self.number_op(), ds, [site]) for site in sites] #.reshape(self._lat_shape)
def projected_ham_3(self): ''' Should be equivalent to self.ham_code() ''' #X because stabilizer acts with X on 7th qubit _, Ux = qu.eigh(qu.pauli('x')) U = qu.ikron(Ux.copy(), dims=self._sim_dims, inds=[6]) Stilde = (U.H @ self.stabilizer() @ U).real.round() #Stilde should be diagonal! U_plus = U[:, np.where(np.diag(Stilde) == 1.0)[0]] HamProj = U_plus.H @ self.ham_sim() @ U_plus return HamProj #in +1 stabilizer eigenbasis
def test_multisubsystem(self): qu.seed_rand(42) dims = [2, 2, 2] IIX = qu.ikron(qu.rand_herm(2), dims, 2) dcmp = qu.pauli_decomp(IIX, mode='c') for p, x in dcmp.items(): if x == 0j: assert (p[0] != 'I') or (p[1] != 'I') else: assert p[0] == p[1] == 'I' K = qu.rand_iso(3 * 4, 4).reshape(3, 4, 4) KIIXK = qu.kraus_op(IIX, K, dims=dims, where=[0, 2]) dcmp = qu.pauli_decomp(KIIXK, mode='c') for p, x in dcmp.items(): if x == 0j: assert p[1] != 'I' else: assert p[1] == 'I'
def state_local_occs(self, qstate=None, k=0, faces=False): ''' TODO: fix face shapes? Expectation values <k|local fermi occupation|k> in the kth excited eigenstate, at vertex sites (and faces if specified). param qstate: vector in full qubit basis param k: if `state` isn't specified, will compute for the kth excited energy eigenstate (defaults to ground state) param faces: if True, return occupations at face qubits as well Returns: local occupations `nocc_v`, (`nocc_f`) nocc_v (ndarray, shape (Lx,Ly)) nocc_f (ndarray, shape (Lx-1,Ly-1)) ''' if qstate is None: qstate = self._eigstates[:, k] #local number ops acting on each site j # Nj = self.site_number_ops(sites=self.all_sites() , fermi=False) Nj = [ qu.ikron(self.number_op(), self._sim_dims, [site]) for site in self.all_sites() ] #expectation <N> for each vertex nocc_v = np.array([ qu.expec(Nj[v], qstate) for v in self.vertex_sites() ]).reshape(self._lat_shape) if not faces: return nocc_v #expectation <Nj> for each face nocc_f = np.array([qu.expec(Nj[f], qstate) for f in self.face_sites()]) return nocc_v, nocc_f
def test_1d_vector_methods(self): X = qu.spin_operator('X', sparse=True) mera = qt.MERA.rand(16) meraX = mera.gate(X.A, 7, inplace=False) assert mera is not meraX x1 = mera.H @ meraX md = mera.to_dense() mdX = qu.ikron(X, [2] * 16, 7) @ md x2 = md.H @ mdX # check against dense assert x1 == pytest.approx(x2) # check 'outside lightcone' unaffected assert mera.select(3).H @ meraX.select(3) == pytest.approx(1.0) # check only need 'lightcone' to compute local assert mera.select(7).H @ meraX.select(7) == pytest.approx(x2)
def test_gate2split(self): psi = MPS_rand_state(10, 3) psi2 = psi.copy() G = qu.eye(2) & qu.eye(2) psi.gate2split(G, (2, 3), cutoff=0) assert psi.bond_size(2, 3) == 6 assert psi.H @ psi2 == pytest.approx(1.0) # check a unitary application G = qu.rand_uni(2**2) psi.gate2split(G, (7, 8)) psi.compress() assert psi.bond_size(2, 3) == 3 assert psi.bond_size(7, 8) > 3 assert psi.H @ psi == pytest.approx(1.0) assert abs(psi2.H @ psi) < 1.0 # check matches dense application of gate psid = psi2.to_dense() Gd = qu.ikron(G, [2] * 10, (7, 8)) assert psi.to_dense().H @ (Gd @ psid) == pytest.approx(1.0)
def jw_creation_op(self, k, spin): ''' Jordan-Wigner transformed creation operator, for site k and `spin` sector (Z_0)x(Z_1)x ...x(Z_k-1)x |1><0|_k ''' spin = {0: 0, 1: 1, 'up': 0, 'down': 1, 'u': 0, 'd': 1}[spin] Z, I = qu.pauli('z'), qu.eye(2) s_plus = qu.qu([[0, 0], [1, 0]]) N = self._Nverts Z_sig = {0: Z & I, 1: I & Z}[spin] s_plus_sig = {0: s_plus & I, 1: I & s_plus}[spin] op_list = [Z_sig for i in range(k)] + [s_plus_sig] ind_list = [i for i in range(k + 1)] return qu.ikron(ops=op_list, dims=self._dims, inds=ind_list)