def Graph_t(self, step): """ return a state which CZ gates have been applied #step on all plus state """ MPS_t = [] chi = 2 # this particular graph state has bond dimension of 2 U1 = np.reshape( np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]]), (2, 2, 2, 2)) # ctlr z plus = (1.0 / np.sqrt(2.0)) * np.ones(2) # first qubit temp = ncon((plus, plus, U1), ([1], [2], [-1, -2, 1, 2])) X, Y, Z = np.linalg.svd(temp, full_matrices=0) # truncation chi2 = np.min([np.sum(Y > 10.**(-10)), chi]) piv = np.zeros(len(Y), np.bool) piv[(np.argsort(Y)[::-1])[:chi2]] = True Y = Y[piv] invsq = np.sqrt(sum(Y**2)) X = X[:, piv] Z = Z[piv, :] MPS_t.append(np.matmul(X, np.diag(Y))) for i in range(step - 1): temp = ncon((Z, plus, U1), ([-1, 1], [2], [-2, -3, 1, 2])) s0 = temp.shape[0] s1 = temp.shape[1] s2 = temp.shape[2] temp = np.reshape(temp, (s0 * s1, s2)) X, Y, Z = np.linalg.svd(temp, full_matrices=0) # truncation chi2 = np.min([np.sum(Y > 10.**(-10)), chi]) piv = np.zeros(len(Y), np.bool) piv[(np.argsort(Y)[::-1])[:chi2]] = True Y = Y[piv] invsq = np.sqrt(sum(Y**2)) X = X[:, piv] Z = Z[piv, :] MPS_t.append(np.reshape(np.matmul(X, np.diag(Y)), (s0, s1, chi2))) MPS_t.append( np.transpose(Z) ) # transpose is to keep the indexing order consistent with my poor choice #MPS_t.append(np.expand_dim(np.transpose(Z), axis=1)) # transpose is to keep the indexing order consistent with my poor choice for i in range(step + 1, self.N - 1): MPS_t.append(np.reshape(plus, [1, 2, 1])) #if step != self.N -1: # MPS_t.append(np.reshape(plus, [2, 1])) MPS_t.append(np.reshape(plus, [2, 1])) return MPS_t
def construct_Nframes(self): # Nqubit tensor product of frame Mn and dual frame Ntn self.Ntn = self.Nt.copy() self.Mn = self.M.copy() for i in range(self.N - 1): self.Ntn = ncon((self.Ntn, self.Nt), ([-1, -3, -5], [-2, -4, -6])) self.Mn = ncon((self.Mn, self.M), ([-1, -3, -5], [-2, -4, -6])) self.Ntn = np.reshape(self.Ntn, (4**(i + 2), 2**(i + 2), 2**(i + 2))) self.Mn = np.reshape(self.Mn, (4**(i + 2), 2**(i + 2), 2**(i + 2)))
def P_gate(self, gate, mask=True): gate_factor = int(gate.ndim / 2) if gate_factor == 1: g = ncon((self.M, gate, self.Nt, np.transpose(np.conj(gate))), ([-1, 4, 1], [1, 2], [-2, 2, 5], [5, 4])) else: g = ncon((self.M, self.M, gate, self.Nt, self.Nt, np.conj(gate)), ([-1, 9, 1], [-2, 10, 2], [1, 2, 3, 4], [-3, 3, 7], [-4, 4, 8], [9, 10, 7, 8])) if mask: g_mask = np.abs(g.real) > 1e-15 g = np.multiply(g, g_mask) return g.real.astype('float32')
def get_initial_bias(self, initial_state, as_torch_tensor=False): # which initial product state? if initial_state == '0': s = np.array([1, 0]) elif initial_state == '1': s = np.array([0, 1]) elif initial_state == '+': s = (1.0 / np.sqrt(2.0)) * np.array([1, 1]) elif initial_state == '-': s = (1.0 / np.sqrt(2.0)) * np.array([1, -1]) elif initial_state == 'r': s = (1.0 / np.sqrt(2.0)) * np.array([1, 1j]) elif initial_state == 'l': s = (1.0 / np.sqrt(2.0)) * np.array([1, -1j]) self.P = np.real(ncon((self.M, s, np.conj(s)), ([-1, 1, 2], [1], [2]))) # solving for bias self.bias = np.zeros(self.K) self.bias = np.log(self.P) if np.sum(np.abs(self.softmax(self.bias) - self.P)) > 0.00000000001: print("initial bias not found") elif as_torch_tensor: return torch.tensor(self.bias) else: return self.bias
def N_body_gate(self, gate, mask=True): gn = ncon((self.Mn, gate, self.Ntn, np.transpose(np.conj(gate))), ([-1, 4, 1], [1, 2], [-2, 2, 5], [5, 4])) if mask: gn_mask = np.abs(gn.real) > 1e-15 gn = np.multiply(gn, gn_mask) return gn.real.astype('float32')
def one_body_gate(self, gate, mask=True): g1 = ncon((self.M, gate, self.Nt, np.transpose(np.conj(gate))), ([-1, 4, 1], [1, 2], [-2, 2, 5], [5, 4])) if mask: g1_mask = np.abs(g1.real) > 1e-15 g1 = np.multiply(g1, g1_mask) return g1.real.astype('float32')
def two_body_gate_all(self, gate, mask=True): g2 = ncon((self.M, self.M, gate, self.Nt, self.Nt, np.conj(gate)), ([-1, 9, 1], [-2, 10, 2], [1, 2, 3, 4], [-3, 3, 7], [-4, 4, 8], [9, 10, 7, 8])) #if mask: # g2_mask = np.abs(g2.real) > 1e-15 # g2 = np.multiply(g2, g2_mask) return g2
def cFidelity_t(self, S, logP, step): """ compute the classical fidelity with linear Graph state after apply CZ gates with #step """ Fidelity = 0.0 F2 = 0.0 Ns = S.shape[0] L1 = 0.0 L1_2 = 0.0 MPS_t = self.Graph_t(step) #for i in range(len(MPS_t)): # print(MPS_t[i].shape) plus = (1.0 / np.sqrt(2.0)) * np.ones(2) plus_pho = np.outer(plus, np.conjugate(plus)) prob_plus = ncon((plus_pho, self.M), ([1, 2], [-1, 2, 1])) for i in range(Ns): P = ncon((MPS_t[0], MPS_t[0], self.M[S[i, 0], :, :]), ([1, -1], [2, -2], [1, 2])) # contracting the entire TN for each sample S[i,:] for j in range(1, step): P = ncon((P, MPS_t[j], MPS_t[j], self.M[S[i, j], :, :]), ([1, 2], [1, 3, -1], [2, 4, -2], [3, 4])) P = ncon((P, MPS_t[step], MPS_t[step], self.M[S[i, step], :, :]), ([1, 2], [3, 1], [4, 2], [3, 4])) for j in range(step + 1, self.N): P *= prob_plus[S[i, j]] ratio = P / np.exp(logP[i]) ee = np.sqrt(ratio) Fidelity = Fidelity + ee F2 = F2 + ee**2 L1 = L1 + np.abs(1 - ratio) L1_2 = L1_2 + np.abs(1 - ratio)**2 F2 = F2 / float(Ns) Fidelity = np.abs(Fidelity / float(Ns)) Error = np.sqrt(np.abs(F2 - Fidelity**2) / float(Ns)) L1_2 = L1_2 / float(Ns) L1 = np.abs(L1 / float(Ns)) L1_err = np.sqrt(np.abs(L1_2 - L1**2) / float(Ns)) return np.real(Fidelity), Error, L1, L1_err
def cFidelity(self, S, logP): Fidelity = 0.0 F2 = 0.0 Ns = S.shape[0] KL = 0.0 K2 = 0.0 L1 = 0.0 L1_2 = 0.0 for i in range(Ns): P = ncon((self.MPS[0], self.MPS[0], self.M[S[i, 0], :, :]), ([1, -1], [2, -2], [1, 2])) # contracting the entire TN for each sample S[i,:] for j in range(1, self.N - 1): P = ncon((P, self.MPS[j], self.MPS[j], self.M[S[i, j], :, :]), ([1, 2], [1, 3, -1], [2, 4, -2], [3, 4])) P = ncon((P, self.MPS[self.N - 1], self.MPS[self.N - 1], self.M[S[i, self.N - 1], :, :]), ([1, 2], [3, 1], [4, 2], [3, 4])) ratio = P / np.exp(logP[i]) ee = np.sqrt(ratio) Fidelity = Fidelity + ee F2 = F2 + ee**2 L1 = L1 + np.abs(1 - ratio) L1_2 = L1_2 + np.abs(1 - ratio)**2 #KL = KL + 2 * np.log(ee + 1e-9) #K2 = K2 + 4 * (np.log(ee + 1e-9)) ** 2 F2 = F2 / float(Ns) Fidelity = np.abs(Fidelity / float(Ns)) Error = np.sqrt(np.abs(F2 - Fidelity**2) / float(Ns)) L1_2 = L1_2 / float(Ns) L1 = np.abs(L1 / float(Ns)) L1_err = np.sqrt(np.abs(L1_2 - L1**2) / float(Ns)) K2 = K2 / float(Ns) KL = np.abs(KL / float(Ns)) ErrorKL = np.sqrt(np.abs(K2 - KL**2) / float(Ns)) return np.real(Fidelity), Error, L1, L1_err
def Fidelity(self, S): Fidelity = 0.0 F2 = 0.0 Ns = S.shape[0] for i in range(Ns): # contracting the entire TN for each sample S[i,:] # eT = ncon(( self.TB[0][:,:,S[i,0]], self.TB[1][:,:,:,:,S[i,1]]) ,( [1,2],[-1,-2,1,2 ])) eT = ncon((self.it[:, S[i, 0]], self.M, self.MPS[0], self.MPS[0]), ([3], [3, 2, 1], [1, -1], [2, -2])) for j in range(1, self.N - 1): # eT = ncon((eT,self.TB[j][:,:,:,:,S[i,j]]),([ 1,2],[ -1,-2, 1,2 ])) eT = ncon( (eT, self.it[:, S[i, j]], self.M, self.MPS[j], self.MPS[j]), ([2, 4], [1], [1, 5, 3], [2, 3, -1], [4, 5, -2])) # eT = ncon((eT, self.TB[self.N-1][:,:,S[i,self.N-1]]),([1,2],[1,2 ])) j = self.N - 1 eT = ncon( (eT, self.it[:, S[i, j]], self.M, self.MPS[j], self.MPS[j]), ([2, 5], [1], [1, 4, 3], [3, 2], [4, 5])) # print i, eT Fidelity = Fidelity + eT F2 = F2 + eT**2 Fest = Fidelity / float(i + 1) F2est = F2 / float(i + 1) Error = np.sqrt(np.abs(F2est - Fest**2) / float(i + 1)) # print i,np.real(Fest),Error # disp([i,i/Ns, real(Fest), real(Error)]) # fflush(stdout) F2 = F2 / float(Ns) Fidelity = np.abs(Fidelity / float(Ns)) Error = np.sqrt(np.abs(F2 - Fidelity**2) / float(Ns)) return np.real(Fidelity), Error
def getinitialbias(self, initial_state): self.P = np.real( ncon((self.M, self.s, np.conj(self.s)), ([-1, 1, 2], [1], [2]))) # solving for bias self.bias = np.zeros(self.K) self.bias = np.log(self.P) if np.sum(np.abs(self.softmax(self.bias) - self.P)) > 0.00000000001: print("initial bias not found") else: return self.bias
def __init__(self, POVM='Trine', Number_qubits=4, MPS='GHZ'): self.N = Number_qubits # Hamiltonian for calculation of energy (TFIM in 1d) # self.Jz = Jz # self.hx = hx # POVMs and other operators # Pauli matrices self.I = np.array([[1, 0], [0, 1]]) self.X = np.array([[0, 1], [1, 0]]) self.s1 = self.X self.Z = np.array([[1, 0], [0, -1]]) self.s3 = self.Z self.Y = np.array([[0, -1j], [1j, 0]]) self.s2 = self.Y self.H = 1.0 / np.sqrt(2.0) * np.array([[1, 1], [1, -1]]) self.Sp = np.array([[1.0, 0.0], [0.0, -1j]]) self.oxo = np.array([[1.0, 0.0], [0.0, 0.0]]) self.IxI = np.array([[0.0, 0.0], [0.0, 1.0]]) self.Phase = np.array([[1.0, 0.0], [0.0, 1j]]) # =S = (Sp)^{\dag} self.T = np.array([[1.0, 0], [0, np.exp(-1j * np.pi / 4.0)]]) self.U1 = np.array([[np.exp(-1j * np.pi / 3.0), 0], [0, np.exp(1j * np.pi / 3.0)]]) # two-qubit gates self.cy = ncon((self.oxo, self.I), ([-1, -3], [-2, -4])) + ncon( (self.IxI, self.Y), ([-1, -3], [-2, -4])) self.cz = ncon((self.oxo, self.I), ([-1, -3], [-2, -4])) + ncon( (self.IxI, self.Z), ([-1, -3], [-2, -4])) # Tetra POVM if POVM == '4Pauli': self.K = 4 self.M = np.zeros((self.K, 2, 2), dtype=complex) self.M[0, :, :] = 1.0 / 3.0 * np.array([[1, 0], [0, 0]]) self.M[1, :, :] = 1.0 / 6.0 * np.array([[1, 1], [1, 1]]) self.M[2, :, :] = 1.0 / 6.0 * np.array([[1, -1j], [1j, 1]]) self.M[3, :, :] = 1.0 / 3.0 * (np.array([[0, 0], [0, 1]]) + 0.5 * np.array([[1, -1], [-1, 1]]) + 0.5 * np.array([[1, 1j], [-1j, 1]])) if POVM == 'Tetra': self.K = 4 self.M = np.zeros((self.K, 2, 2), dtype=complex) self.v1 = np.array([0, 0, 1.0]) self.M[0, :, :] = 1.0 / 4.0 * (self.I + self.v1[0] * self.s1 + self.v1[1] * self.s2 + self.v1[2] * self.s3) self.v2 = np.array([2.0 * np.sqrt(2.0) / 3.0, 0.0, -1.0 / 3.0]) self.M[1, :, :] = 1.0 / 4.0 * (self.I + self.v2[0] * self.s1 + self.v2[1] * self.s2 + self.v2[2] * self.s3) self.v3 = np.array( [-np.sqrt(2.0) / 3.0, np.sqrt(2.0 / 3.0), -1.0 / 3.0]) self.M[2, :, :] = 1.0 / 4.0 * (self.I + self.v3[0] * self.s1 + self.v3[1] * self.s2 + self.v3[2] * self.s3) self.v4 = np.array( [-np.sqrt(2.0) / 3.0, -np.sqrt(2.0 / 3.0), -1.0 / 3.0]) self.M[3, :, :] = 1.0 / 4.0 * (self.I + self.v4[0] * self.s1 + self.v4[1] * self.s2 + self.v4[2] * self.s3) if POVM == 'Tetra_pos': self.K = 4 self.M = np.zeros((self.K, 2, 2), dtype=complex) self.v1 = np.array([1.0, 1.0, 1.0]) / np.sqrt(3) self.M[0, :, :] = 1.0 / 4.0 * (self.I + self.v1[0] * self.s1 + self.v1[1] * self.s2 + self.v1[2] * self.s3) self.v2 = np.array([1.0, -1.0, -1.0]) / np.sqrt(3) self.M[1, :, :] = 1.0 / 4.0 * (self.I + self.v2[0] * self.s1 + self.v2[1] * self.s2 + self.v2[2] * self.s3) self.v3 = np.array([-1.0, 1.0, -1.0]) / np.sqrt(3) self.M[2, :, :] = 1.0 / 4.0 * (self.I + self.v3[0] * self.s1 + self.v3[1] * self.s2 + self.v3[2] * self.s3) self.v4 = np.array([-1.0, -1.0, 1.0]) / np.sqrt(3) self.M[3, :, :] = 1.0 / 4.0 * (self.I + self.v4[0] * self.s1 + self.v4[1] * self.s2 + self.v4[2] * self.s3) elif POVM == 'Trine': self.K = 3 self.M = np.zeros((self.K, 2, 2), dtype=complex) phi0 = 0.0 for k in range(self.K): phi = phi0 + k * 2 * np.pi / 3.0 self.M[k, :, :] = 0.5 * (self.I + np.cos(phi) * self.Z + np.sin(phi) * self.X) * 2 / 3.0 # % T matrix and its inverse self.t = ncon((self.M, self.M), ([-1, 1, 2], [-2, 2, 1])) self.it = np.linalg.inv(self.t) # Tensor for expectation value # self.Trsx = np.zeros((self.N,self.K),dtype=complex) # self.Trsy = np.zeros((self.N,self.K),dtype=complex) # self.Trsz = np.zeros((self.N,self.K),dtype=complex) # self.Trrho = np.zeros((self.N,self.K),dtype=complex) # self.Trrho2 = np.zeros((self.N,self.K,self.K),dtype=complex) # self.T2 = np.zeros((self.N,self.K,self.K),dtype=complex) self.Trsx = np.zeros(self.K, dtype=complex) self.Trsy = np.zeros(self.K, dtype=complex) self.Trsz = np.zeros(self.K, dtype=complex) self.Trrho = np.zeros((self.N, self.K), dtype=complex) self.Trrho2 = np.zeros((self.N, self.K, self.K), dtype=complex) self.T2 = np.zeros((self.N, self.K, self.K), dtype=complex) self.Trsx = ncon((self.M, self.it, self.X), ([3, 2, 1], [3, -1], [2, 1])) self.Trsy = ncon((self.M, self.it, self.Y), ([3, 2, 1], [3, -1], [2, 1])) self.Trsz = ncon((self.M, self.it, self.Z), ([3, 2, 1], [3, -1], [2, 1])) self.stab_ops = [self.Trsx, self.Trsz] if MPS == "GHZ": # Copy tensors used to construct GHZ as an MPS. The procedure below should work for any other MPS cc = np.zeros((2, 2)) # corner cc[0, 0] = 2**(-1.0 / (2 * self.N)) cc[1, 1] = 2**(-1.0 / (2 * self.N)) cb = np.zeros((2, 2, 2)) # bulk cb[0, 0, 0] = 2**(-1.0 / (2 * self.N)) cb[1, 1, 1] = 2**(-1.0 / (2 * self.N)) self.MPS = [] self.MPS.append(cc) for i in range(self.N - 2): self.MPS.append(cb) self.MPS.append(cc) elif MPS == "Graph": MPS = [] chi = 2 # this particular graph state has bond dimension of 2 U1 = np.reshape( np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]]), (2, 2, 2, 2)) # ctlr z plus = (1.0 / np.sqrt(2.0)) * np.ones(2) # first qubit temp = ncon((plus, plus, U1), ([1], [2], [-1, -2, 1, 2])) X, Y, Z = np.linalg.svd(temp, full_matrices=0) # truncation chi2 = np.min([np.sum(Y > 10.**(-10)), chi]) piv = np.zeros(len(Y), np.bool) piv[(np.argsort(Y)[::-1])[:chi2]] = True Y = Y[piv] invsq = np.sqrt(sum(Y**2)) X = X[:, piv] Z = Z[piv, :] MPS.append(np.matmul(X, np.diag(Y))) for i in range(1, self.N - 1): temp = ncon((Z, plus, U1), ([-1, 1], [2], [-2, -3, 1, 2])) s0 = temp.shape[0] s1 = temp.shape[1] s2 = temp.shape[2] temp = np.reshape(temp, (s0 * s1, s2)) X, Y, Z = np.linalg.svd(temp, full_matrices=0) # truncation chi2 = np.min([np.sum(Y > 10.**(-10)), chi]) piv = np.zeros(len(Y), np.bool) piv[(np.argsort(Y)[::-1])[:chi2]] = True Y = Y[piv] invsq = np.sqrt(sum(Y**2)) X = X[:, piv] Z = Z[piv, :] MPS.append(np.reshape(np.matmul(X, np.diag(Y)), (s0, s1, chi2))) # last qubit MPS.append( np.transpose(Z) ) # transpose is to keep the indexing order consistent with my poor choice # testing # GS = ncon((MPS[0],MPS[1],MPS[2],MPS[3]),([-1,1],[1,-2,2],[2,-3,3],[3,-4])) # GS = np.reshape(GS,(2**4)) self.MPS = MPS elif MPS == "plus": print(MPS, "squi") plus = (1.0 / np.sqrt(2.0)) * np.ones(2) self.MPS = [] self.MPS.append(np.reshape(plus, [2, 1])) for i in range(1, self.N - 1): self.MPS.append(np.reshape(plus, [1, 2, 1])) self.MPS.append(np.reshape(plus, [2, 1]))
def stabilizers(self, i, si, j, sj, k, sk): MPS = deepcopy(self.MPS) if i == 0: MPS[i] = ncon((MPS[i], si), ([1, -2], [-1, 1])) elif i == self.N - 1: MPS[i] = ncon((MPS[i], si), ([-2, 1], [-1, 1])) else: MPS[i] = ncon((MPS[i], si), ([-1, 1, -3], [-2, 1])) if j == 0: MPS[j] = ncon((MPS[j], sj), ([1, -2], [-1, 1])) elif j == self.N - 1: MPS[j] = ncon((MPS[j], sj), ([-2, 1], [-1, 1])) else: MPS[j] = ncon((MPS[j], sj), ([-1, 1, -3], [-2, 1])) if k == 0: MPS[k] = ncon((MPS[k], sk), ([1, -2], [-1, 1])) elif k == self.N - 1: MPS[k] = ncon((MPS[k], sk), ([-2, 1], [-1, 1])) else: MPS[k] = ncon((MPS[k], sk), ([-1, 1, -3], [-2, 1])) C = ncon((MPS[0], np.conj(self.MPS[0])), ([1, -1], [1, -2])) for ii in range(1, self.N - 1): C = ncon((C, MPS[ii], np.conj(self.MPS[ii])), ([1, 2], [1, 3, -1], [2, 3, -2])) ii = self.N - 1 C = ncon((C, MPS[ii], np.conj(self.MPS[ii])), ([1, 2], [3, 1], [3, 2])) return C
def __init__(self, POVM='4Pauli', Number_qubits=4, initial_state='+', Jz=1.0, hx=1.0, eps=1e-4): self.type = POVM self.N = Number_qubits # Hamiltonian for calculation of energy (TFIM in 1d) self.Jz = Jz self.hx = hx self.eps = eps # POVMs and other operators # Pauli matrices,gates,simple states self.I = np.array([[1, 0], [0, 1]]) self.X = np.array([[0, 1], [1, 0]]) self.s1 = self.X self.Z = np.array([[1, 0], [0, -1]]) self.s3 = self.Z self.Y = np.array([[0, -1j], [1j, 0]]) self.s2 = self.Y self.H = 1.0 / np.sqrt(2.0) * np.array([[1, 1], [1, -1]]) self.Sp = np.array([[1.0, 0.0], [0.0, -1j]]) self.oxo = np.array([[1.0, 0.0], [0.0, 0.0]]) self.IxI = np.array([[0.0, 0.0], [0.0, 1.0]]) self.Phase = np.array([[1.0, 0.0], [0.0, 1j]]) # =S = (Sp)^{\dag} aa = 8.0 self.R8 = np.array([[np.cos(np.pi / aa), -np.sin(np.pi / aa)], [np.sin(np.pi / aa), np.cos(np.pi / aa)]]) self.T = np.array([[1.0, 0], [0, np.exp(-1j * np.pi / 4.0)]]) self.U1 = np.array([[np.exp(-1j * np.pi / 3.0), 0], [0, np.exp(1j * np.pi / 3.0)]]) #two-qubit gates self.cx = ncon((self.oxo, self.I), ([-1, -3], [-2, -4])) + ncon( (self.IxI, self.X), ([-1, -3], [-2, -4])) self.cy = ncon((self.oxo, self.I), ([-1, -3], [-2, -4])) + ncon( (self.IxI, self.Y), ([-1, -3], [-2, -4])) self.cz = ncon((self.oxo, self.I), ([-1, -3], [-2, -4])) + ncon( (self.IxI, self.Z), ([-1, -3], [-2, -4])) self.cnot = ncon((self.oxo, self.I), ([-1, -3], [-2, -4])) + ncon( (self.IxI, self.X), ([-1, -3], [-2, -4])) self.cu1 = ncon((self.oxo, self.I), ([-1, -3], [-2, -4])) + ncon( (self.IxI, self.U1), ([-1, -3], [-2, -4])) self.HI = ncon((self.H, self.I), ([-1, -3], [-2, -4])) self.PhaseI = ncon((self.Phase, self.I), ([-1, -3], [-2, -4])) self.R8I = ncon((self.R8, self.I), ([-1, -3], [-2, -4])) self.TI = ncon((self.T, self.I), ([-1, -3], [-2, -4])) self.single_qubit = [self.H, self.Phase, self.T, self.U1] self.two_qubit = [ self.cnot, self.cz, self.cy, self.cu1, self.HI, self.PhaseI, self.R8I, self.TI, self.cx ] if POVM == '4Pauli': self.K = 4 self.M = np.zeros((self.K, 2, 2), dtype=complex) self.M[0, :, :] = 1.0 / 3.0 * np.array([[1, 0], [0, 0]]) self.M[1, :, :] = 1.0 / 6.0 * np.array([[1, 1], [1, 1]]) self.M[2, :, :] = 1.0 / 6.0 * np.array([[1, -1j], [1j, 1]]) self.M[3,:,:] = 1.0/3.0*(np.array([[0, 0],[0, 1]]) + \ 0.5*np.array([[1, -1],[-1, 1]]) \ + 0.5*np.array([[1, 1j],[-1j, 1]]) ) if POVM == 'Tetra': ## symmetric self.K = 4 self.M = np.zeros((self.K, 2, 2), dtype=complex) self.v1 = np.array([0, 0, 1.0]) self.M[0, :, :] = 1.0 / 4.0 * (self.I + self.v1[0] * self.s1 + self.v1[1] * self.s2 + self.v1[2] * self.s3) self.v2 = np.array([2.0 * np.sqrt(2.0) / 3.0, 0.0, -1.0 / 3.0]) self.M[1, :, :] = 1.0 / 4.0 * (self.I + self.v2[0] * self.s1 + self.v2[1] * self.s2 + self.v2[2] * self.s3) self.v3 = np.array( [-np.sqrt(2.0) / 3.0, np.sqrt(2.0 / 3.0), -1.0 / 3.0]) self.M[2, :, :] = 1.0 / 4.0 * (self.I + self.v3[0] * self.s1 + self.v3[1] * self.s2 + self.v3[2] * self.s3) self.v4 = np.array( [-np.sqrt(2.0) / 3.0, -np.sqrt(2.0 / 3.0), -1.0 / 3.0]) self.M[3, :, :] = 1.0 / 4.0 * (self.I + self.v4[0] * self.s1 + self.v4[1] * self.s2 + self.v4[2] * self.s3) if POVM == 'Tetra_pos': self.K = 4 self.M = np.zeros((self.K, 2, 2), dtype=complex) self.v1 = np.array([1.0, 1.0, 1.0]) / np.sqrt(3) self.M[0, :, :] = 1.0 / 4.0 * (self.I + self.v1[0] * self.s1 + self.v1[1] * self.s2 + self.v1[2] * self.s3) self.v2 = np.array([1.0, -1.0, -1.0]) / np.sqrt(3) self.M[1, :, :] = 1.0 / 4.0 * (self.I + self.v2[0] * self.s1 + self.v2[1] * self.s2 + self.v2[2] * self.s3) self.v3 = np.array([-1.0, 1.0, -1.0]) / np.sqrt(3) self.M[2, :, :] = 1.0 / 4.0 * (self.I + self.v3[0] * self.s1 + self.v3[1] * self.s2 + self.v3[2] * self.s3) self.v4 = np.array([-1.0, -1.0, 1.0]) / np.sqrt(3) self.M[3, :, :] = 1.0 / 4.0 * (self.I + self.v4[0] * self.s1 + self.v4[1] * self.s2 + self.v4[2] * self.s3) elif POVM == 'Trine': self.K = 3 self.M = np.zeros((self.K, 2, 2), dtype=complex) phi0 = 0.0 for k in range(self.K): phi = phi0 + (k) * 2 * np.pi / 3.0 self.M[k, :, :] = 0.5 * (self.I + np.cos(phi) * self.Z + np.sin(phi) * self.X) * 2 / 3.0 #% T matrix and its inverse self.t = ncon((self.M, self.M), ([-1, 1, 2], [-2, 2, 1])).real self.it = np.linalg.inv(self.t) # dual frame of M self.Nt = ncon((self.it, self.M), ([-1, 1], [1, -2, -3])) self.Nt_norm = [] for i in range(self.Nt.shape[0]): self.Nt_norm.append(np.linalg.norm(self.Nt[i])) self.Nt_norm = np.array(self.Nt_norm) # Tensor for expectation value self.Trsx = np.zeros((self.N, self.K), dtype=complex) self.Trsy = np.zeros((self.N, self.K), dtype=complex) self.Trsz = np.zeros((self.N, self.K), dtype=complex) self.Trrho = np.zeros((self.N, self.K), dtype=complex) self.Trrho2 = np.zeros((self.N, self.K, self.K), dtype=complex) self.T2 = np.zeros((self.N, self.K, self.K), dtype=complex) # probability gate set single qubit self.p_single_qubit = [] for i in range(len(self.single_qubit)): #mat = ncon((self.M,self.single_qubit[i],self.M,self.it,np.transpose(np.conj(self.single_qubit[i]))),([-1,4,1],[1,2],[3,2,5],[3,-2],[5,4])) mat = self.one_body_gate(self.single_qubit[i]) self.p_single_qubit.append(mat) # probability gate set two qubit self.p_two_qubit = [] for i in range(len(self.two_qubit)): #mat = ncon((self.M,self.M,self.two_qubit[i],self.M,self.M,self.it,self.it,np.conj(self.two_qubit[i])),([-1,9,1],[-2,10,2],[1,2,3,4],[5,3,7],[6,4,8],[5,-3],[6,-4],[9,10,7,8])) mat = self.two_body_gate(self.two_qubit[i]) self.p_two_qubit.append(mat) #print(np.real(np.sum(np.reshape(self.p_two_qubit[i],(16,16)),1)),np.real(np.sum(np.reshape(self.p_two_qubit[i],(16,16)),0))) # set initial wavefunction if initial_state == '0': self.s = np.array([1, 0]) elif initial_state == '1': self.s = np.array([0, 1]) elif initial_state == '+': self.s = (1.0 / np.sqrt(2.0)) * np.array([1, 1]) elif initial_state == '-': self.s = (1.0 / np.sqrt(2.0)) * np.array([1, -1]) elif initial_state == 'r': self.s = (1.0 / np.sqrt(2.0)) * np.array([1, 1j]) elif initial_state == 'l': self.s = (1.0 / np.sqrt(2.0)) * np.array([1, -1j]) # time evolution gate """ hl = ZZ + XI hlx = ZZ + XI + IX """ self.ZZ = np.kron(self.Z, self.Z) self.XI = np.kron(self.X, self.I) self.XI2 = np.kron(self.X, self.I) + np.kron(self.I, self.X) self.hl = self.Jz * np.kron(self.Z, self.Z) + self.hx * np.kron( self.X, self.I) self.hl = -np.reshape(self.hl, (2, 2, 2, 2)) self.hlx = self.Jz * np.kron(self.Z, self.Z) + self.hx * ( np.kron(self.X, self.I) + np.kron(self.I, self.X)) self.hlx = -np.reshape(self.hlx, (2, 2, 2, 2)) self.zz2 = np.reshape(self.ZZ, (2, 2, 2, 2)) #self.sx = np.reshape(np.kron(self.X,self.I)+ np.kron(self.I,self.X),(2,2,2,2)) self.sx = np.reshape(np.kron(self.X, self.I), (2, 2, 2, 2)) self.exp_hl = np.reshape(-self.eps * self.hl, (4, 4)) self.exp_hl = expm(self.exp_hl) self.exp_hl_norm = np.linalg.norm(self.exp_hl) self.exp_hl2 = self.exp_hl / self.exp_hl_norm self.mat = np.reshape(self.exp_hl, (2, 2, 2, 2)) self.mat2 = np.reshape(self.exp_hl2, (2, 2, 2, 2)) self.Up = self.two_body_gate(self.mat) self.Up2 = self.two_body_gate(self.mat2) #self.hlp = ncon((self.it,self.M,self.hl,self.it,self.M),([1,-1],[1,3,2],[2,5,3,6],[4,-2],[4,6,5])).real #self.sxp = ncon((self.it,self.M,self.sx,self.it,self.M),([1,-1],[1,3,2],[2,5,3,6],[4,-2],[4,6,5])).real # Hamiltonian observable list self.hl_ob = ncon( (self.hl, self.Nt, self.Nt), ([1, 2, 3, 4], [-1, 3, 1], [-2, 4, 2])).real.astype(np.float32) self.hlx_ob = ncon( (self.hlx, self.Nt, self.Nt), ([1, 2, 3, 4], [-1, 3, 1], [-2, 4, 2])).real.astype(np.float32) self.x_ob = ncon((-self.hx * self.X, self.Nt), ([1, 2], [-1, 2, 1])).real.astype(np.float32) self.zz_ob = ncon( (self.zz2, self.Nt, self.Nt), ([1, 2, 3, 4], [-1, 3, 1], [-2, 4, 2])).real.astype(np.float32) # commuting and anti_computing operator hl_Nt = ncon((self.hl, self.Nt, self.Nt), ([-1, -2, 1, 2], [-3, 1, -5], [-4, 2, -6])) Nt_hl = ncon((self.Nt, self.Nt, self.hl), ([-3, -1, 1], [-4, -2, 2], [1, 2, -5, -6])) hlx_Nt = ncon((self.hlx, self.Nt, self.Nt), ([-1, -2, 1, 2], [-3, 1, -5], [-4, 2, -6])) Nt_hlx = ncon((self.Nt, self.Nt, self.hlx), ([-3, -1, 1], [-4, -2, 2], [1, 2, -5, -6])) x_Nt = ncon((-self.hx * self.X, self.Nt), ([-1, 1], [-2, 1, -3])) Nt_x = ncon((self.Nt, -self.hx * self.X), ([-2, -1, 1], [1, -3])) self.hl_anti = ncon( (hl_Nt + Nt_hl, self.M, self.M), ([1, 2, -3, -4, 3, 4], [-1, 3, 1], [-2, 4, 2])).real.astype( np.float32) self.hl_com = ncon( (-hl_Nt + Nt_hl, self.M, self.M), ([1, 2, -3, -4, 3, 4], [-1, 3, 1], [-2, 4, 2])).imag.astype( np.float32) self.hlx_anti = ncon( (hlx_Nt + Nt_hlx, self.M, self.M), ([1, 2, -3, -4, 3, 4], [-1, 3, 1], [-2, 4, 2])).real.astype( np.float32) self.hlx_com = ncon( (-hlx_Nt + Nt_hlx, self.M, self.M), ([1, 2, -3, -4, 3, 4], [-1, 3, 1], [-2, 4, 2])).imag.astype( np.float32) self.x_anti = ncon((x_Nt + Nt_x, self.M), ([1, -2, 2], [-1, 2, 1])).real.astype(np.float32) self.x_com = ncon((-x_Nt + Nt_x, self.M), ([1, -2, 2], [-1, 2, 1])).imag.astype(np.float32) # MPO H self.Ham = [] mat = np.zeros((3, 3, 2, 2)) mat[0, 0] = self.I mat[1, 0] = -self.Z mat[2, 0] = -self.X * self.hx mat[2, 1] = self.Z mat[2, 2] = self.I self.Ham.append(mat[2]) for i in range(1, self.N - 1): self.Ham.append(mat) self.Ham.append(mat[:, 0, :, :]) # MPS for Hamiltonian in probability space self.Hp = [] mat = ncon((self.Ham[0], self.M, self.it), ([-2, 3, 1], [2, 1, 3], [2, -1])) self.Hp.append(mat) for i in range(1, self.N - 1): mat = ncon((self.Ham[i], self.M, self.it), ([-1, -3, 3, 1], [2, 1, 3], [2, -2])) self.Hp.append(mat) mat = ncon((self.Ham[self.N - 1], self.M, self.it), ([-1, 3, 1], [2, 1, 3], [2, -2])) self.Hp.append(mat)