def test_vs_ref_simulator(n_qubits, prog_length, include_measures): if include_measures: seed = 52 else: seed = None for _ in range(10): prog = _generate_random_program(n_qubits=n_qubits, length=prog_length, include_measures=include_measures) ref_qam = PyQVM(n_qubits=n_qubits, seed=seed, quantum_simulator_type=ReferenceWavefunctionSimulator) ref_qam.execute(prog) ref_wf = ref_qam.wf_simulator.wf es_qam = PyQVM(n_qubits=n_qubits, seed=seed, quantum_simulator_type=NumpyWavefunctionSimulator) es_qam.execute(prog) es_wf = es_qam.wf_simulator.wf # einsum has its wavefunction as a vector of shape (2, 2, 2, ...) where qubits are indexed # from left to right. We transpose then flatten. es_wf = es_wf.transpose().reshape(-1) np.testing.assert_allclose(ref_wf, es_wf, atol=1e-15)
def test_exp_circuit(): true_wf = np.array( [ 0.54030231 - 0.84147098j, 0.00000000 + 0.0j, 0.00000000 + 0.0j, 0.00000000 + 0.0j, 0.00000000 + 0.0j, 0.00000000 + 0.0j, 0.00000000 + 0.0j, 0.00000000 + 0.0j, ] ) create2kill1 = PauliTerm("X", 1, -0.25) * PauliTerm("Y", 2) create2kill1 += PauliTerm("Y", 1, 0.25) * PauliTerm("Y", 2) create2kill1 += PauliTerm("Y", 1, 0.25) * PauliTerm("X", 2) create2kill1 += PauliTerm("X", 1, 0.25) * PauliTerm("X", 2) create2kill1 += PauliTerm("I", 0, 1.0) prog = Program() for term in create2kill1.terms: single_exp_prog = exponentiate(term) prog += single_exp_prog qam = PyQVM(n_qubits=3, quantum_simulator_type=ReferenceWavefunctionSimulator) qam.execute(prog) wf = qam.wf_simulator.wf np.testing.assert_allclose(wf.dot(np.conj(wf).T), true_wf.dot(np.conj(true_wf).T))
def test_against_ref_qft_8(): p = Program(QFT_8_INSTRUCTIONS) qam = PyQVM(n_qubits=8, quantum_simulator_type=ReferenceWavefunctionSimulator) qam.execute(p) wf = qam.wf_simulator.wf np.testing.assert_allclose(QFT_8_WF_PROBS, wf)
def test_sample_bitstrings(): prog = Program(H(0), H(1)) qam = PyQVM(n_qubits=3, quantum_simulator_type=NumpyWavefunctionSimulator, seed=52) qam.execute(prog) bitstrings = qam.wf_simulator.sample_bitstrings(10000) assert bitstrings.shape == (10000, 3) np.testing.assert_allclose([0.5, 0.5, 0], np.mean(bitstrings, axis=0), rtol=1e-2)
def test_qaoa_circuit(): wf_true = [ 0.00167784 + 1.00210180e-05 * 1j, 0.50000000 - 4.99997185e-01 * 1j, 0.50000000 - 4.99997185e-01 * 1j, 0.00167784 + 1.00210180e-05 * 1j, ] prog = Program() prog.inst( [ RY(np.pi / 2, 0), RX(np.pi, 0), RY(np.pi / 2, 1), RX(np.pi, 1), CNOT(0, 1), RX(-np.pi / 2, 1), RY(4.71572463191, 1), RX(np.pi / 2, 1), CNOT(0, 1), RX(-2 * 2.74973750579, 0), RX(-2 * 2.74973750579, 1), ] ) qam = PyQVM(n_qubits=2, quantum_simulator_type=ReferenceWavefunctionSimulator) qam.execute(prog) wf = qam.wf_simulator.wf np.testing.assert_allclose(wf_true, wf, atol=1e-8)
def test_einsum_simulator_1(): prog = Program(H(0), CNOT(0, 1)) qam = PyQVM(n_qubits=2, quantum_simulator_type=NumpyWavefunctionSimulator) qam.execute(prog) wf = qam.wf_simulator.wf np.testing.assert_allclose( wf, 1 / np.sqrt(2) * np.reshape([1, 0, 0, 1], (2, 2)))
def test_defgate(): # regression test for https://github.com/rigetti/pyquil/issues/1059 theta = np.pi / 2 U = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, np.cos(theta / 2), -1j * np.sin(theta / 2)], [0, 0, -1j * np.sin(theta / 2), np.cos(theta / 2)]]) gate_definition = DefGate('U_test', U) U_test = gate_definition.get_constructor() p = Program() p += gate_definition p += X(1) p += U_test(1, 0) qam = PyQVM(n_qubits=2, quantum_simulator_type=NumpyWavefunctionSimulator) qam.execute(p) wf1 = qam.wf_simulator.wf should_be = np.zeros((2, 2), dtype=np.complex128) one_over_sqrt2 = 1 / np.sqrt(2) should_be[0, 1] = one_over_sqrt2 should_be[1, 1] = -1j * one_over_sqrt2 np.testing.assert_allclose(wf1, should_be) # Ensure the output of the custom U_test gate matches the standard RX gate. Something like # RX(theta, 0).controlled(1) would be a more faithful reproduction of U_test, but # NumpyWavefunctionSimulator doesn't (yet) support gate modifiers, so just apply the RX gate # unconditionally. p = Program() p += X(1) p += RX(theta, 0) qam = PyQVM(n_qubits=2, quantum_simulator_type=NumpyWavefunctionSimulator) qam.execute(p) wf2 = qam.wf_simulator.wf np.testing.assert_allclose(wf1, wf2)
def test_einsum_simulator_CCNOT(): prog = Program(X(2), X(0), CCNOT(2, 1, 0)) qam = PyQVM(n_qubits=3, quantum_simulator_type=NumpyWavefunctionSimulator) qam.execute(prog) wf = qam.wf_simulator.wf should_be = np.zeros((2, 2, 2)) should_be[1, 0, 1] = 1 np.testing.assert_allclose(wf, should_be)
def test_bell_state(): prog = Program().inst([H(0), CNOT(0, 1)]) qam = PyQVM(n_qubits=2, quantum_simulator_type=ReferenceWavefunctionSimulator) qam.execute(prog) wf = qam.wf_simulator.wf ref_bell = np.zeros(4) ref_bell[0] = ref_bell[-1] = 1.0 / np.sqrt(2) np.testing.assert_allclose(ref_bell, wf)
def test_occupation_basis(): prog = Program().inst([X(0), X(1), I(2), I(3)]) state = np.zeros(2 ** 4) state[3] = 1.0 qam = PyQVM(n_qubits=4, quantum_simulator_type=ReferenceWavefunctionSimulator) qam.execute(prog) np.testing.assert_allclose(state, qam.wf_simulator.wf)
def test_kraus_application_bitflip(): p = 0.372 qam = PyQVM(n_qubits=1, quantum_simulator_type=ReferenceDensitySimulator, post_gate_noise_probabilities={'bit_flip': p}) initial_density = _random_1q_density() qam.wf_simulator.density = initial_density qam.execute(Program(I(0))) final_density = (1 - p) * initial_density + p * qmats.X.dot(initial_density).dot(qmats.X) np.testing.assert_allclose(final_density, qam.wf_simulator.density)
def test_kraus_application_dephasing(): p = 0.372 qam = PyQVM(n_qubits=1, quantum_simulator_type=ReferenceDensitySimulator, post_gate_noise_probabilities={'dephasing': p}) rho = _random_1q_density() qam.wf_simulator.density = rho qam.execute(Program(I(0))) final_density = np.array([[rho[0, 0], (1 - p) * rho[0, 1]], [(1 - p) * rho[1, 0], rho[1, 1]]]) np.testing.assert_allclose(final_density, qam.wf_simulator.density)
def test_einsum_simulator_10q(): prog = Program(H(0)) for i in range(10 - 1): prog += CNOT(i, i + 1) qam = PyQVM(n_qubits=10, quantum_simulator_type=NumpyWavefunctionSimulator) qam.execute(prog) wf = qam.wf_simulator.wf should_be = np.zeros((2, ) * 10) should_be[0, 0, 0, 0, 0, 0, 0, 0, 0, 0] = 1 / np.sqrt(2) should_be[1, 1, 1, 1, 1, 1, 1, 1, 1, 1] = 1 / np.sqrt(2) np.testing.assert_allclose(wf, should_be)
def test_measure(): qam = PyQVM(n_qubits=3, quantum_simulator_type=NumpyWavefunctionSimulator) qam.execute(Program(Declare("ro", "BIT", 64), H(0), CNOT(0, 1), MEASURE(0, MemoryReference("ro", 63)))) measured_bit = qam.ram["ro"][-1] should_be = np.zeros((2, 2, 2)) if measured_bit == 1: should_be[1, 1, 0] = 1 else: should_be[0, 0, 0] = 1 np.testing.assert_allclose(qam.wf_simulator.wf, should_be)
def test_kraus_application_depolarizing(): p = 0.372 qam = PyQVM(n_qubits=1, quantum_simulator_type=ReferenceDensitySimulator, post_gate_noise_probabilities={'depolarizing': p}) rho = _random_1q_density() qam.wf_simulator.density = rho qam.execute(Program(I(0))) final_density = (1 - p) * rho + (p / 3) * (qmats.X.dot(rho).dot(qmats.X) + qmats.Y.dot(rho).dot(qmats.Y) + qmats.Z.dot(rho).dot(qmats.Z)) np.testing.assert_allclose(final_density, qam.wf_simulator.density)
def test_if_then_2(): # if FALSE creg, then measure 0 should give 1 prog = Program() creg = prog.declare("creg", "BIT") prog.inst(MOVE(creg, 0), X(0)) branch_a = Program(X(0)) branch_b = Program() prog.if_then(creg, branch_a, branch_b) prog += MEASURE(0, creg) qam = PyQVM(n_qubits=1, quantum_simulator_type=ReferenceWavefunctionSimulator) qam.execute(prog) assert qam.ram["creg"][0] == 1
def test_kraus_compound_T1T2_application(): p1 = 0.372 p2 = 0.45 qam = PyQVM(n_qubits=1, quantum_simulator_type=ReferenceDensitySimulator, post_gate_noise_probabilities={'relaxation': p1, 'dephasing': p2}) rho = _random_1q_density() qam.wf_simulator.density = rho qam.execute(Program(I(0))) final_density = np.array([[rho[0, 0] + rho[1, 1] * p1, (1 - p2) * np.sqrt(1 - p1) * rho[0, 1]], [(1 - p2) * np.sqrt(1 - p1) * rho[1, 0], (1 - p1) * rho[1, 1]]]) np.testing.assert_allclose(final_density, qam.wf_simulator.density)
def test_if_then(): # if TRUE creg, then measure 0 should give 0 prog = Program() creg = prog.declare('creg', 'BIT') prog.inst(MOVE(creg, 1), X(0)) branch_a = Program(X(0)) branch_b = Program() prog.if_then(creg, branch_a, branch_b) prog += MEASURE(0, creg) qam = PyQVM(n_qubits=1, quantum_simulator_type=ReferenceWavefunctionSimulator) qam.execute(prog) assert qam.ram['creg'][0] == 0
def test_while(): init_register = Program() classical_flag_register = init_register.declare("classical_flag_register", "BIT") init_register += MOVE(classical_flag_register, True) loop_body = Program(X(0), H(0)).measure(0, classical_flag_register) # Put it all together in a loop program: loop_prog = init_register.while_do(classical_flag_register, loop_body) qam = PyQVM(n_qubits=1, quantum_simulator_type=ReferenceWavefunctionSimulator) qam.execute(loop_prog) assert qam.ram[classical_flag_register.name][0] == 0
def test_generate_arbitrary_states(arbitrary_state): prog, v = arbitrary_state prog = Program(prog) qam = PyQVM(n_qubits=8, quantum_simulator_type=ReferenceWavefunctionSimulator) qam.execute(prog) wf = qam.wf_simulator.wf # check actual part of wavefunction np.testing.assert_allclose(v, wf[: len(v)], atol=1e-10) # check remaining zeros part of wavefunction np.testing.assert_allclose(np.zeros(wf.shape[0] - len(v)), wf[len(v) :])
def test_vs_lisp_qvm(qvm, n_qubits, prog_length): for _ in range(10): prog = _generate_random_program(n_qubits=n_qubits, length=prog_length) lisp_wf = WavefunctionSimulator() # force lisp wfs to allocate all qubits lisp_wf = lisp_wf.wavefunction(Program(I(q) for q in range(n_qubits)) + prog) lisp_wf = lisp_wf.amplitudes ref_qam = PyQVM(n_qubits=n_qubits, quantum_simulator_type=ReferenceWavefunctionSimulator) ref_qam.execute(prog) ref_wf = ref_qam.wf_simulator.wf np.testing.assert_allclose(lisp_wf, ref_wf, atol=1e-15)
def test_halt(): prog = Program(Declare("ro", "BIT"), X(0), MEASURE(0, MemoryReference("ro", 0))) prog.inst(HALT) prog.inst(X(0), MEASURE(0, MemoryReference("ro", 0))) qam = PyQVM(n_qubits=1, quantum_simulator_type=ReferenceWavefunctionSimulator) qam.execute(prog) # HALT should stop execution; measure should give 1 assert qam.ram["ro"][0] == 1 prog = Program(Declare("ro", "BIT"), X(0)).inst(X(0)).inst(MEASURE(0, MemoryReference("ro", 0))) qam = PyQVM(n_qubits=1, quantum_simulator_type=ReferenceWavefunctionSimulator) qam.execute(prog) assert qam.ram["ro"][0] == 0
def test_biased_coin(): # sample from a 75% heads and 25% tails coin prog = Program().inst(RX(np.pi / 3, 0)).measure(0, 0) results = [] qam = PyQVM(n_qubits=1, quantum_simulator_type=ReferenceWavefunctionSimulator) for _ in range(1000): qam.execute(prog) results += [qam.ram['ro'][0]] qam.execute(Program(RESET())) coin_bias = sum(results) / 1000 assert np.isclose(coin_bias, 0.25, atol=0.05, rtol=0.05)
class NumpyWavefunctionDevice(ForestDevice): r"""NumpyWavefunction simulator device for PennyLane. Args: wires (int or Iterable[Number, str]]): Number of subsystems represented by the device, or iterable that contains unique labels for the subsystems as numbers (i.e., ``[-1, 0, 2]``) or strings (``['ancilla', 'q1', 'q2']``). shots (int): Number of circuit evaluations/random samples used to estimate expectation values of observables. """ name = "pyQVM NumpyWavefunction Simulator Device" short_name = "forest.numpy_wavefunction" observables = { "PauliX", "PauliY", "PauliZ", "Hadamard", "Hermitian", "Identity" } def __init__(self, wires, *, shots=None, **kwargs): super().__init__(wires, shots, **kwargs) self.qc = PyQVM(n_qubits=len(self.wires), quantum_simulator_type=NumpyWavefunctionSimulator) self._state = None def apply(self, operations, **kwargs): self.reset() self.qc.wf_simulator.reset() super().apply(operations, **kwargs) # TODO: currently, the PyQVM considers qubit 0 as the leftmost bit and therefore # returns amplitudes in the opposite of the Rigetti Lisp QVM (which considers qubit # 0 as the rightmost bit). This may change in the future, so in the future this # might need to get udpated to be similar to the pre_measure function of # pennylane_forest/wavefunction.py self._state = self.qc.execute(self.prog).wf_simulator.wf.flatten()
class NumpyWavefunctionDevice(WavefunctionDevice): r"""NumpyWavefunction simulator device for PennyLane. Args: wires (int): the number of qubits to initialize the device in shots (int): Number of circuit evaluations/random samples used to estimate expectation values of observables. """ name = "pyQVM NumpyWavefunction Simulator Device" short_name = "forest.numpy_wavefunction" observables = {"PauliX", "PauliY", "PauliZ", "Hadamard", "Hermitian", "Identity"} def __init__(self, wires, *, shots=1000, analytic=True, **kwargs): super(WavefunctionDevice, self).__init__(wires, shots, analytic, **kwargs) self.qc = PyQVM(n_qubits=wires, quantum_simulator_type=NumpyWavefunctionSimulator) self.state = None def pre_apply(self): self.reset() self.qc.wf_simulator.reset() def pre_measure(self): # TODO: currently, the PyQVM considers qubit 0 as the leftmost bit and therefore # returns amplitudes in the opposite of the Rigetti Lisp QVM (which considers qubit # 0 as the rightmost bit). This may change in the future, so in the future this # might need to get udpated to be similar to the pre_measure function of # pennylane_forest/wavefunction.py self.state = self.qc.execute(self.prog).wf_simulator.wf.flatten()
def test_larger_qaoa_circuit(): square_qaoa_circuit = [ H(0), H(1), H(2), H(3), X(0), PHASE(0.3928244130249029, 0), X(0), PHASE(0.3928244130249029, 0), CNOT(0, 1), RZ(0.78564882604980579, 1), CNOT(0, 1), X(0), PHASE(0.3928244130249029, 0), X(0), PHASE(0.3928244130249029, 0), CNOT(0, 3), RZ(0.78564882604980579, 3), CNOT(0, 3), X(0), PHASE(0.3928244130249029, 0), X(0), PHASE(0.3928244130249029, 0), CNOT(1, 2), RZ(0.78564882604980579, 2), CNOT(1, 2), X(0), PHASE(0.3928244130249029, 0), X(0), PHASE(0.3928244130249029, 0), CNOT(2, 3), RZ(0.78564882604980579, 3), CNOT(2, 3), H(0), RZ(-0.77868204192240842, 0), H(0), H(1), RZ(-0.77868204192240842, 1), H(1), H(2), RZ(-0.77868204192240842, 2), H(2), H(3), RZ(-0.77868204192240842, 3), H(3), ] prog = Program(square_qaoa_circuit) qam = PyQVM(n_qubits=4, quantum_simulator_type=ReferenceWavefunctionSimulator) qam.execute(prog) wf = qam.wf_simulator.wf wf_true = np.array( [ 8.43771693e-05 - 0.1233845 * 1j, -1.24927731e-01 + 0.00329533 * 1j, -1.24927731e-01 + 0.00329533 * 1j, -2.50040954e-01 + 0.12661547 * 1j, -1.24927731e-01 + 0.00329533 * 1j, -4.99915497e-01 - 0.12363516 * 1j, -2.50040954e-01 + 0.12661547 * 1j, -1.24927731e-01 + 0.00329533 * 1j, -1.24927731e-01 + 0.00329533 * 1j, -2.50040954e-01 + 0.12661547 * 1j, -4.99915497e-01 - 0.12363516 * 1j, -1.24927731e-01 + 0.00329533 * 1j, -2.50040954e-01 + 0.12661547 * 1j, -1.24927731e-01 + 0.00329533 * 1j, -1.24927731e-01 + 0.00329533 * 1j, 8.43771693e-05 - 0.1233845 * 1j, ] ) np.testing.assert_allclose(wf_true, wf)
def test_against_ref_hadamard(): p = Program(H(0)) qam = PyQVM(n_qubits=1, quantum_simulator_type=ReferenceWavefunctionSimulator) qam.execute(p) np.testing.assert_allclose(HADAMARD_WF, qam.wf_simulator.wf)
def test_multiqubit_decay_bellstate(): program = Program(RY(np.pi / 3, 0), CNOT(0, 1)) # commence manually dotting the above program initial_density = np.zeros((4, 4), dtype=complex) initial_density[0, 0] = 1.0 gate_time_1q = 50e-9 T1 = 30e-6 T2 = 15e-6 p1 = 1 - np.exp(-gate_time_1q / T1) p2 = 1 - np.exp(-gate_time_1q / T2) # RY gate_1 = np.kron(np.eye(2), qmats.RY(np.pi / 3)) state = gate_1.dot(initial_density).dot(np.conj(gate_1).T) for ii in range(2): new_density = np.zeros_like(state) for kop in qmats.relaxation_operators(p1): operator = lifted_gate_matrix(matrix=kop, qubit_inds=[ii], n_qubits=2) new_density += operator.dot(state).dot(np.conj(operator).T) state = new_density for ii in range(2): new_density = np.zeros_like(state) for kop in qmats.dephasing_operators(p2): operator = lifted_gate_matrix(matrix=kop, qubit_inds=[ii], n_qubits=2) new_density += operator.dot(state).dot(np.conj(operator).T) state = new_density # CNOT # TODO: different 1q, 2q noise probabilities cnot_01 = np.kron(qmats.I, qmats.P0) + np.kron(qmats.X, qmats.P1) state = cnot_01.dot(state).dot(cnot_01.T) gate_time_2q = 150e-9 p1 = 1 - np.exp(-gate_time_2q / T1) p2 = 1 - np.exp(-gate_time_2q / T2) for ii in range(2): new_density = np.zeros_like(state) for kop in qmats.relaxation_operators(p1): operator = lifted_gate_matrix(matrix=kop, qubit_inds=[ii], n_qubits=2) new_density += operator.dot(state).dot(np.conj(operator).T) state = new_density for ii in range(2): new_density = np.zeros_like(state) for kop in qmats.dephasing_operators(p2): operator = lifted_gate_matrix(matrix=kop, qubit_inds=[ii], n_qubits=2) new_density += operator.dot(state).dot(np.conj(operator).T) state = new_density qam = PyQVM(n_qubits=2, quantum_simulator_type=ReferenceDensitySimulator, post_gate_noise_probabilities={ 'relaxation': p1, 'dephasing': p2 }) qam.execute(program) assert np.allclose(qam.wf_simulator.density, state)
def test_default_wf_simulator(): qam = PyQVM(n_qubits=2) qam.execute(Program(H(0), H(1))) assert qam.wf_simulator.wf.reshape(-1).shape == (4,)
def test_einsum_simulator_H(): prog = Program(H(0)) qam = PyQVM(n_qubits=1, quantum_simulator_type=NumpyWavefunctionSimulator) qam.execute(prog) wf = qam.wf_simulator.wf np.testing.assert_allclose(wf, 1 / np.sqrt(2) * np.ones(2))