def test_wavefunction_expectation(): term1 = PauliTerm("Z", 1, .2) * PauliTerm("Z", 2) term2 = PauliTerm("X", 1, -.4) * PauliTerm("Z", 3) term3 = PauliTerm("Y", 1, 1.4) ham = PauliSum([term1, term2, term3]) ham2 = ham * ham mat = lifted_pauli(ham, [1, 3, 2]) mat2 = lifted_pauli(ham2, [1, 3, 2]) wf = np.array([0, 1.0, 2, .3, .5, -.5, .6, -.9]) hams = commuting_decomposition(ham) hams2 = commuting_decomposition(ham2) funs = [base_change_fun(ham, [1, 3, 2]) for ham in hams] funs2 = [base_change_fun(ham, [1, 3, 2]) for ham in hams2] hams = [kron_eigs(h, [1, 3, 2]) for h in hams] hams2 = [kron_eigs(h, [1, 3, 2]) for h in hams2] e1, s1 = wavefunction_expectation(hams, funs, hams2, funs2, wf) e2, s2 = wf.conj()@mat@wf, wf@mat2@wf print(e1, s1) print(e2, s2) assert np.allclose((e1, s1), (e2, s2))
def theo_Hamil(n, t): '''Difinition of a function to compute and return theoretical Hamiltonian for n qubits with (choosing-2-out-of-n) coupling terms in matrix form for evolution time t. param n: number of qubits. param t: total evolution time''' factor = 0.5 # number of combination for n qubits length = np.arange(len(comb_xx(n))) # compute the xx interaction part of the Hamiltonian part_xx = [comb_xx(n)[i] * t * factor for i in length] # compute the x field interaction part of the Hamiltonian part_x = [x * factor * t for x in comb_x(n)] # compute the z field interaction part of the Hamiltonian part_z = [x * factor * t for x in comb_z(n)] # sum up all Pauli terms in the part_xx pauli_sum_xx = PauliSum(part_xx) # sum up all Pauli terms in the part_x pauli_sum_x = PauliSum(part_x) # sum up all Pauli terms in the part_z pauli_sum_z = PauliSum(part_z) # acquire a list of qubit indices for the applied # Pauli operators list_qbt = PauliSum.get_qubits(pauli_sum_xx) # add up the final matrices of both the pauli_xx # pauli_z to obtain the Hamiltonian in its matrix # form Hamil = lifted_pauli(pauli_sum_xx, list_qbt) + lifted_pauli( pauli_sum_z, list_qbt) + lifted_pauli(pauli_sum_x, list_qbt) return Hamil
def quantumVsClassical(maximum, layers): quantum_times = [] classical_times = [] timesteps = range(2, maximum) for i in range(2, maximum): test_adjacency = matrix.undirectedAdjacencyConstruct(i, False, 0.5) Paulis = matrix.pauliBuilder(test_adjacency) print(Paulis) print( unitary_tools.lifted_pauli( Paulis, range(int(math.log(test_adjacency.shape[0], 2))))) classical_time_init = time.time() classical_time = time.time() - classical_time_init quantum_time_init = time.time() print("estimated eigenvalue: ", vqe.solveVQE(Paulis, layers)) print( "absolute difference: ", abs( min( np.linalg.eig(test_adjacency)[0] - vqe.solveVQE(Paulis, layers)))) quantum_time = time.time() - quantum_time_init quantum_times.append(quantum_time) classical_times.append(classical_time) print("quantum runtime: ", quantum_time) print("classical runtime: ", classical_time) # print(quantum_times) print(classical_times) # plt.plot(timesteps,quantum_times,'bo') plt.plot(timesteps, classical_times, 'go') plt.show()
def linear_inv_state_estimate(results: List[ExperimentResult], qubits: List[int]) -> np.ndarray: """ Estimate a quantum state using linear inversion. This is the simplest state tomography post processing. To use this function, collect state tomography data with :py:func:`generate_state_tomography_experiment` and :py:func:`~pyquil.operator_estimation.measure_observables`. For more details on this post-processing technique, see https://en.wikipedia.org/wiki/Quantum_tomography#Linear_inversion or see section 3.4 of Initialization and characterization of open quantum systems C. Wood, PhD thesis from University of Waterloo, (2015). http://hdl.handle.net/10012/9557 :param results: A tomographically complete list of results. :param qubits: All qubits that were tomographized. This specifies the order in which qubits will be kron'ed together. :return: A point estimate of the quantum state rho. """ measurement_matrix = np.vstack([ vec(lifted_pauli(result.setting.out_operator, qubits=qubits)).T.conj() for result in results ]) expectations = np.array([result.expectation for result in results]) rho = pinv(measurement_matrix) @ expectations return unvec(rho)
def test_kron_diagonal(): term1 = PauliTerm("Z", 1, .2) * PauliTerm("Z", 3) term2 = PauliTerm("Z", 1, -.4) * PauliTerm("Z", 2) term3 = PauliTerm("Z", 1, 1.4) ham = PauliSum([term1, term2, term3]) diag1 = np.diag(lifted_pauli(ham, [1, 3, 2])) diag2 = kron_eigs(ham, [1, 3, 2]) assert np.allclose(diag1, diag2)
def test_lifted_pauli(): qubits = [0, 1] xy_term = sX(0) * sY(1) # test correctness trial_matrix = lifted_pauli(xy_term, qubits) true_matrix = np.kron(mat.Y, mat.X) np.testing.assert_allclose(trial_matrix, true_matrix) x1_term = sX(1) trial_matrix = lifted_pauli(x1_term, qubits) true_matrix = np.kron(mat.X, mat.I) np.testing.assert_allclose(trial_matrix, true_matrix) zpz_term = sZ(0) + sZ(1) trial_matrix = lifted_pauli(zpz_term, qubits) true_matrix = np.zeros((4, 4)) true_matrix[0, 0] = 2 true_matrix[-1, -1] = -2 np.testing.assert_allclose(trial_matrix, true_matrix)
def _extract_from_results(results: List[ExperimentResult], qubits: List[int]): """ Construct the matrix A such that the probabilities p_ij of outcomes n_ij given an estimate E can be cast in a vectorized form. Specifically:: p = vec(p_ij) = A x vec(E) This yields convenient vectorized calculations of the cost and its gradient, in terms of A, n, and E. """ A = [] n = [] grand_total_shots = 0 for result in results: in_state_matrix = lifted_state_operator(result.setting.in_state, qubits=qubits) operator = lifted_pauli(result.setting.out_operator, qubits=qubits) proj_plus = (np.eye(2**len(qubits)) + operator) / 2 proj_minus = (np.eye(2**len(qubits)) - operator) / 2 # Constructing A per eq. (22) # TODO: figure out if we can avoid re-splitting into Pi+ and Pi- counts A += [ # vec() turns into a column vector; transpose to a row vector; index into the # 1 row to avoid an extra tensor dimension when we call np.asarray(A). vec(np.kron(in_state_matrix, proj_plus.T)).T[0], vec(np.kron(in_state_matrix, proj_minus.T)).T[0], ] expected_plus_ones = (1 + result.expectation) / 2 n += [ result.total_counts * expected_plus_ones, result.total_counts * (1 - expected_plus_ones) ] grand_total_shots += result.total_counts n_qubits = len(qubits) dimension = 2**n_qubits A = np.asarray(A) / dimension**2 n = np.asarray(n)[:, np.newaxis] / grand_total_shots return A, n
def pauli_matrix(pauli_sum: PauliSum, qubit_mapping: Dict = {}) -> np.array: """Create the matrix representation of pauli_sum. Parameters ---------- qubit_mapping: A dictionary-like object that maps from :py:class`QubitPlaceholder` to :py:class:`int` Returns ------- np.matrix: A matrix representing the PauliSum """ # get unmapped Qubits and check that all QubitPlaceholders are mapped unmapped_qubits = {*pauli_sum.get_qubits()} - qubit_mapping.keys() if not all(isinstance(q, int) for q in unmapped_qubits): raise ValueError("Not all QubitPlaceholders are mapped") # invert qubit_mapping and assert its injectivity inv_mapping = dict([v, k] for k, v in qubit_mapping.items()) if len(inv_mapping) is not len(qubit_mapping): raise ValueError("qubit_mapping must be injective") # add unmapped qubits to the inverse mapping, ensuring we don't have # a list entry twice for q in unmapped_qubits: if q not in inv_mapping.keys(): inv_mapping[q] = q else: raise ValueError("qubit_mapping maps to qubit already in use") qubit_list = [inv_mapping[k] for k in sorted(inv_mapping.keys())] matrix = lifted_pauli(pauli_sum, qubit_list) return matrix