def test_random_unitaries_first_moment(): # the first moment should be proportional to P_21/D N_avg = 50000 D2 = 2 D3 = 3 perm_swap = [1, 0] # Permutation operators # SWAP aka P_21 which maps H_1 \otimes H_2 to H_2 \otimes H_1 D2_SWAP = rand_ops.permute_tensor_factors(D2, perm_swap) D3_SWAP = rand_ops.permute_tensor_factors(D3, perm_swap) X = rand_ops.haar_rand_unitary(D2) Id2 = np.matmul(X, np.conjugate(X.T)) Y = rand_ops.haar_rand_unitary(D3) Id3 = np.matmul(Y, np.conjugate(Y.T)) D2_avg = np.zeros_like(np.kron(Id2, Id2)) D3_avg = np.zeros_like(np.kron(Id3, Id3)) for n in range(0, N_avg): U2 = rand_ops.haar_rand_unitary(D2) U2d = np.conjugate(U2.T) D2_avg += np.kron(U2, U2d) / N_avg U3 = rand_ops.haar_rand_unitary(D3) U3d = np.conjugate(U3.T) D3_avg += np.kron(U3, U3d) / N_avg # Compute the Frobenius norm of the different between the estimated operator and the answer assert np.real(la.norm((D2_avg - D2_SWAP / D2), 'fro')) <= 0.01 assert np.real(la.norm((D2_avg - D2_SWAP / D2), 'fro')) >= 0.00 assert np.real(la.norm((D3_avg - D3_SWAP / D3), 'fro')) <= 0.02 assert np.real(la.norm((D3_avg - D3_SWAP / D3), 'fro')) >= 0.00
def generate_abstract_qv_circuit( depth: int) -> Tuple[List[np.ndarray], np.ndarray]: """ Produces an abstract description of the square model circuit of given depth=width used in a quantum volume measurement. The description remains abstract as it does not directly reference qubits in a circuit; rather, the circuit is specified as a list of depth many permutations and depth many layers of two qubit gates specified as a depth by depth//2 numpy array whose entries are each a haar random four by four matrix (a single 2 qubit gate). Each permutation is simply a list of the numbers 0 through depth-1, where the number x at index i indicates the qubit in position i should be moved to position x. The 4 by 4 matrix at gates[i, j] is the gate acting on the qubits at positions 2j, 2j+1 after the i^th permutation has occurred. :param depth: the depth, and also width, of the model circuit :return: the random depth-many permutations and depth by depth//2 many 2q-gates which comprise the model quantum circuit of [QVol] for a given depth. """ # generate a simple list representation for each permutation of the depth many qubits permutations = [np.random.permutation(range(depth)) for _ in range(depth)] # generate a matrix representation of each 2q gate in the circuit num_gates_per_layer = depth // 2 # if odd number of qubits, don't do anything to last qubit gates = np.asarray( [[haar_rand_unitary(4) for _ in range(num_gates_per_layer)] for _ in range(depth)]) return permutations, gates
def single_q_process(request): if request.param == 'X': return Program(X(0)), mat.X elif request.param == 'haar': u_rand = haar_rand_unitary(2 ** 1, rs=np.random.RandomState(52)) process = Program().defgate("RandUnitary", u_rand) process += ("RandUnitary", 0) return process, u_rand
def test_HS_obeys_cauchy_schwarz(): D = 2 Ur = rand_ops.haar_rand_unitary(D) U = Ur + np.conj(Ur.T) A = np.eye(D) B = A / 3 assert dm.hilbert_schmidt_ip(U, U) * dm.hilbert_schmidt_ip(A, A) >= abs(dm.hilbert_schmidt_ip(A, U))**2 assert dm.hilbert_schmidt_ip(U, U) * dm.hilbert_schmidt_ip(B, B) >= abs(dm.hilbert_schmidt_ip(B, U))**2
def two_q_process(request): if request.param == 'CNOT': return Program(CNOT(0, 1)), mat.CNOT elif request.param == 'haar': u_rand = haar_rand_unitary(2 ** 2, rs=np.random.RandomState(52)) process = Program().defgate("RandUnitary", u_rand) process += ("RandUnitary", 0, 1) return process, u_rand else: raise ValueError()
def test_random_unitaries_are_unitary(): N_avg = 30 D2 = 2 D3 = 3 avg_trace2 = 0 avg_det2 = 0 avg_trace3 = 0 avg_det3 = 0 for idx in range(0, N_avg): U2 = rand_ops.haar_rand_unitary(D2) U3 = rand_ops.haar_rand_unitary(D3) avg_trace2 += np.trace(np.matmul(U2, np.conjugate(U2.T))) / N_avg avg_det2 += np.abs(la.det(U2)) / N_avg avg_trace3 += np.trace(np.matmul(U3, np.conjugate(U3.T))) / N_avg avg_det3 += np.abs(la.det(U3)) / N_avg assert np.allclose(avg_trace2, 2.0) assert np.allclose(avg_det2, 1.0) assert np.allclose(avg_trace3, 3.0) assert np.allclose(avg_det3, 1.0)
def test_HS_obeys_linearity(): D = 2 Ur = rand_ops.haar_rand_unitary(D) U = Ur + np.conj(Ur.T) A = np.eye(D) B = A / 3 alpha = 0.17 beta = 0.6713 LHS = alpha * dm.hilbert_schmidt_ip(U, A) + beta * dm.hilbert_schmidt_ip(U, B) RHS = dm.hilbert_schmidt_ip(U, alpha * A + beta * B) assert np.allclose(LHS, RHS)
def single_q_tomo_fixture(): qubits = [0] qc = get_test_qc(n_qubits=len(qubits)) # Generate random unitary u_rand = haar_rand_unitary(2**1, rs=qc.qam.rs) state_prep = Program().defgate("RandUnitary", u_rand) state_prep.inst([("RandUnitary", qubits[0])]) # True state wfn = NumpyWavefunctionSimulator(n_qubits=1) psi = wfn.do_gate_matrix(u_rand, qubits=[0]).wf.reshape(-1) rho_true = np.outer(psi, psi.T.conj()) # Get data from QVM tomo_expt = generate_state_tomography_experiment(state_prep, qubits) results = list( measure_observables(qc=qc, tomo_experiment=tomo_expt, n_shots=100_000)) return results, rho_true
def test_random_unitaries_second_moment(): # the second moment should be proportional to # # P_13_24 + P_14_23 P_1423 + P_1324 # ------------------ - ------------------- # ( d^2 -1) d ( d^2 -1) # N_avg = 80000 D2 = 2 X = rand_ops.haar_rand_unitary(D2) Id2 = np.matmul(X, np.conjugate(X.T)) # lists representing the permutations p_3412 = Permutation([2, 3, 0, 1]).list() p_4321 = Permutation([3, 2, 1, 0]).list() p_4312 = Permutation([3, 2, 0, 1]).list() p_3421 = Permutation([2, 3, 1, 0]).list() # Permutation operators P_3412 = rand_ops.permute_tensor_factors(D2, p_3412) P_4321 = rand_ops.permute_tensor_factors(D2, p_4321) P_4312 = rand_ops.permute_tensor_factors(D2, p_4312) P_3421 = rand_ops.permute_tensor_factors(D2, p_3421) # ^^ convention in https://arxiv.org/pdf/0711.1017.pdf ## For archaeological reasons I will leave this code for those who come next.. ## lists representing the permutations # p_14_23 = Permutation(0, 3)(1, 2).list() # p_13_24 = Permutation(0, 2)(1, 3).list() # p_1423 = Permutation([0, 3, 1, 2]).list() # p_1324 = Permutation([0, 2, 1, 3]).list() ## Permutation operators # P_14_23 = rand_ops.permute_tensor_factors(D2, p_14_23) # P_13_24 = rand_ops.permute_tensor_factors(D2, p_13_24) # P_1423 = rand_ops.permute_tensor_factors(D2, p_1423) # P_1324 = rand_ops.permute_tensor_factors(D2, p_1324) ## ^^ convention in https://arxiv.org/pdf/0809.3813.pdf # initalize array D2_var = np.zeros_like(np.kron(np.kron(np.kron(Id2, Id2), Id2), Id2)) for n in range(0, N_avg): U2 = rand_ops.haar_rand_unitary(D2) U2d = np.conjugate(U2.T) D2_var += np.kron(np.kron(np.kron(U2, U2), U2d), U2d) / N_avg alpha = 1 / (D2**2 - 1) # 0.3333 beta = 1 / (D2 * (D2**2 - 1)) # 0.1666 theanswer1 = alpha * (P_3412 + P_4321) - beta * (P_4312 + P_3421) # ^^ Equation 5.17 in https://arxiv.org/pdf/0711.1017.pdf ## For archaeological reasons I will leave this code for those who come next.. # theanswer2 = alpha * (P_13_24 + P_14_23) - beta * (P_1423 + P_1324) ## ^^ Equation at the bottom of page 2 in https://arxiv.org/pdf/0809.3813.pdf # round and remove tiny imaginary parts truth = np.around(theanswer1, 2) estimate = np.around(np.real(D2_var), 2) # are the estimated operator and the answer close? print(truth) print(estimate) assert np.allclose(truth, estimate)