def run_and_measure(self, program: Program, qubits: List[int], trials: int, symmetrize_readout=None): """ Run the provided state preparation program and measure all qubits contained in the program. .. note:: In contrast to :py:class:`QVMConnection.run_and_measure`, this method simulates noise correctly for noisy QVMs. However, this method is slower for ``trials > 1``. For faster noise-free simulation, consider :py:class:`WavefunctionSimulator.run_and_measure`. :param program: The state preparation program to run and then measure. :param qubits: Qubit indices to measure. :param trials: The number of times to run the program. :param symmetrize_readout: Whether to apply readout error symmetrization. If not specified, the class attribute ``symmetrize_readout`` will be used. See :py:func:`run_symmetrized_readout` for a complete description. :return: A numpy array of shape (trials, len(qubits)) that contains 0s and 1s """ new_prog = Program().inst(program) # make a copy? classical_addrs = list(range(len(qubits))) for q, i in zip(qubits, classical_addrs): new_prog += MEASURE(q, i) return self.run(program=new_prog, classical_addresses=classical_addrs, trials=trials, symmetrize_readout=symmetrize_readout)
def prep_circuit_program(self, prog): #Prepare parametric gates ro = prog.declare('ro', memory_type='BIT') self.params = self.get_params(prog) params_dict = {} for i in range(len(gates)): q1 = gates[i][0] q2 = gates[i][1] for k in range(0, 3): index = i * 18 + 6 * k angles = self.params[index], self.params[index + 1], self.params[index + 2] prog.inst([g for g in self.single_qubit_unitary(angles, q1)]) index += 3 angles = self.params[index], self.params[index + 1], self.params[index + 2] prog.inst([g for g in self.single_qubit_unitary(angles, q2)]) prog.inst(CZ(q1, q2)) index = 18 * len(gates) angles = self.params[index], self.params[index + 1], self.params[index + 2] prog.inst([g for g in self.single_qubit_unitary(angles, self.n - 1)]) prog += MEASURE(15, ro) return prog
def test_construction_syntax(): p = Program().inst(X(0), Y(1), Z(0)).measure(0, 1) assert p.out() == 'X 0\nY 1\nZ 0\nMEASURE 0 [1]\n' p = Program().inst(X(0)).inst(Y(1)).measure(0, 1).inst(MEASURE(1, 2)) assert p.out() == 'X 0\nY 1\nMEASURE 0 [1]\nMEASURE 1 [2]\n' p = Program().inst(X(0)).measure(0, 1).inst(Y(1), X(0)).measure(0, 0) assert p.out() == 'X 0\nMEASURE 0 [1]\nY 1\nX 0\nMEASURE 0 [0]\n'
def add(a: int, b: int): qvm = QVMConnection() p = Program() # make sure the first number is the greater if a < b: t = a a = b b = t # Number of bits needed to store the sum of # numbers a and b for which bitlen(a) <= n and bitlen(b) <= n is n + 1 bitlen_a = max(bitlen(a), 1) + 1 bitlen_b = max(bitlen(b), 1) a_qubits = list(range(0, bitlen_a)) b_qubits = list(range(bitlen_a, bitlen_a + bitlen_b)) p += prep_qubits(a_qubits, a) p += prep_qubits(b_qubits, b) p += add_qubits(a_qubits, b_qubits) p.inst([MEASURE(j, i) for i, j in enumerate(a_qubits)]) result, = qvm.run(p, trials=1) return sum([k * 2**i for i, k in enumerate(result)])
def _measure_bitstrings(qc, programs: List[Program], meas_qubits: List[int], num_shots: int = 600) -> List[np.ndarray]: """ Wrapper for appending measure instructions onto each program, running the program, and accumulating the resulting bitarrays. :param qc: a quantum computer object on which to run each program :param programs: a list of programs to run :param meas_qubits: groups of qubits to measure for each program :param num_shots: the number of shots to run for each program :return: a len(programs) long list of num_shots by num_meas_qubits bit arrays of results for each program. """ results = [] for program in programs: # copy the program so the original is not mutated prog = program.copy() ro = prog.declare('ro', 'BIT', len(meas_qubits)) for idx, q in enumerate(meas_qubits): prog += MEASURE(q, ro[idx]) prog.wrap_in_numshots_loop(num_shots) prog = qc.compiler.quil_to_native_quil(prog) exe = qc.compiler.native_quil_to_executable(prog) shots = qc.run(exe) results.append(shots) return results
def generate_single_t1_experiment(qubits: Union[int, List[int]], time: float, n_shots: int = 1000) -> Program: """ Return a t1 program in native Quil for a single time point. :param qubits: Which qubits to measure. :param time: The decay time before measurement. :param n_shots: The number of shots to average over for the data point. :return: A T1 Program. """ program = Program() try: len(qubits) except TypeError: qubits = [qubits] ro = program.declare('ro', 'BIT', len(qubits)) for q in qubits: program += RX(np.pi, q) program += Pragma('DELAY', [q], str(time)) for i in range(len(qubits)): program += MEASURE(qubits[i], ro[i]) program.wrap_in_numshots_loop(n_shots) return program
def test_dagger(): p = Program(X(0), H(0)) assert p.dagger().out() == "DAGGER H 0\nDAGGER X 0\n" p = Program(X(0), MEASURE(0, MemoryReference("ro", 0))) with pytest.raises(ValueError): p.dagger().out() # ensure that modifiers are preserved https://github.com/rigetti/pyquil/pull/914 p = Program() control = 0 target = 1 cnot_control = 2 p += X(target).controlled(control) p += Y(target).controlled(control) p += Z(target).controlled(control) p += H(target).controlled(control) p += S(target).controlled(control) p += T(target).controlled(control) p += PHASE(pi, target).controlled(control) p += CNOT(cnot_control, target).controlled(control) for instr, instr_dagger in zip(reversed(p._instructions), p.dagger()._instructions): assert "DAGGER " + instr.out() == instr_dagger.out()
def test_majority_gate(qvm): """ Testing the majority gate with a truth table """ # a, b, c a, b, c true_truth_table = { (0, 0, 0): (0, 0, 0), (0, 0, 1): (0, 0, 1), (0, 1, 0): (0, 1, 0), (0, 1, 1): (1, 1, 1), (1, 0, 0): (0, 1, 1), (1, 0, 1): (1, 1, 0), (1, 1, 0): (1, 0, 1), (1, 1, 1): (1, 0, 0) } maj_gate_program = majority_gate(0, 1, 2) for key, value in true_truth_table.items(): state_prep_prog = Program() for qbit_idx, bit in enumerate(key): if bit == 1: state_prep_prog += X(qbit_idx) prog = state_prep_prog + maj_gate_program ro = prog.declare('ro', 'BIT', 3) for q in range(3): prog += MEASURE(q, ro[q]) exe = qvm.compiler.native_quil_to_executable(prog) result = qvm.run(exe) assert tuple(result[0]) == true_truth_table[key]
def NN_test(qubit, prep_program, N_encode, N_decode): ### qubit: qubit we want to encode (main qubit) ### prep_program: arbitrary program to put the main qubit in the state we want to ### transmit ### N_encode: number of qubits to encode the main qubit in ### N_decode: number of qubits to read out ### if N_decode < N_encode, we will always get alpha squared = beta squared = 0.5, ### no matter what we originally encoded the qubit in. ### Note that this test only gives the absolute squared values of alpha and beta pq = Program() pq += prep_program prog, code_register = NN_encode(qubit, N_encode) pq += prog progg, out = NN_decode(code_register[0:N_decode]) pq += progg ro = pq.declare('ro', 'BIT', 1) pq += MEASURE(out, ro) result = np.sum(qvm.run(address_qubits(pq), trials=1000)) / 1000.0 alpha_sqrd = 1.0 - result beta_sqrd = result print('alpha squared = {}'.format(alpha_sqrd)) print('beta squared = {}'.format(beta_sqrd)) return alpha_sqrd, beta_sqrd
def test_get_qubits(): pq = Program(X(0), CNOT(0, 4), MEASURE(5, [5])) assert pq.get_qubits() == {0, 4, 5} qq = pq.alloc() pq.inst(Y(2), X(qq)) assert pq.get_qubits() == {0, 1, 2, 4, 5} # this synthesizes the allocation
def test_CCNOT_in_X_basis(qvm): """ Testing the definition of Toffoli / CCNOT in the X basis. """ # Toffoli truth table true_truth_table = { (0, 0, 0): (0, 0, 0), (0, 0, 1): (0, 0, 1), (0, 1, 0): (0, 1, 0), (0, 1, 1): (0, 1, 1), (1, 0, 0): (1, 0, 0), (1, 0, 1): (1, 0, 1), (1, 1, 0): (1, 1, 1), (1, 1, 1): (1, 1, 0) } CCNOTX = CCNOT_X_basis(0, 1, 2) for key, value in true_truth_table.items(): state_prep_prog = Program().inst(I(2)) meas_prog = Program() for qbit_idx, bit in enumerate(key): if bit == 1: state_prep_prog += X(qbit_idx) # Hadamard to get to the X basis state_prep_prog += H(qbit_idx) # Hadamard to get back to the Z basis before measurement meas_prog += H(qbit_idx) prog = state_prep_prog + CCNOTX + meas_prog ro = prog.declare('ro', 'BIT', 3) for q in range(3): prog += MEASURE(q, ro[q]) exe = qvm.compiler.native_quil_to_executable(prog) result = qvm.run(exe) assert tuple(result[0]) == true_truth_table[key]
def get_string(self, betas: List[float], gammas: List[float], samples: int = 100): """ Compute the most probable string. The method assumes you have passed init_betas and init_gammas with your pre-computed angles or you have run the VQE loop to determine the angles. If you have not done this you will be returning the output for a random set of angles. :param betas: List of beta angles :param gammas: List of gamma angles :param samples: (Optional) number of samples to get back from the QuantumComputer. :returns: tuple representing the bitstring, Counter object from collections holding all output bitstrings and their frequency. """ if samples <= 0 and not isinstance(samples, int): raise ValueError("samples variable must be positive integer") param_prog = self.get_parameterized_program() stacked_params = np.hstack((betas, gammas)) sampling_prog = Program() ro = sampling_prog.declare('ro', 'BIT', len(self.qubits)) sampling_prog += param_prog(stacked_params) sampling_prog += [MEASURE(qubit, r) for qubit, r in zip(self.qubits, ro)] sampling_prog.wrap_in_numshots_loop(samples) executable = self.qc.compile(sampling_prog) bitstring_samples = self.qc.run(executable) bitstring_tuples = list(map(tuple, bitstring_samples)) freq = Counter(bitstring_tuples) most_frequent_bit_string = max(freq, key=lambda x: freq[x]) return most_frequent_bit_string, freq
def generate_cz_phase_ramsey_program(qb: int, other_qb: int, n_shots: int = 1000) -> Program: """ Generate a single CZ phase Ramsey experiment at a given phase. :param qb: The qubit to move around the Bloch sphere and measure the incurred RZ on. :param other_qb: The other qubit that constitutes a two-qubit pair along with `qb`. :param n_shots: The number of shots to average over for each data point. :param phase: The phase kick to supply after playing the CZ pulse on the equator. :param n_shots: The number of shots to average over for the data point. :return: A parametric Program for performing a CZ Ramsey experiment. """ program = Program() # NOTE: only need readout register for `qb` not `other_qb` since `other_qb` is only # needed to identify which CZ gate we're using ro = program.declare('ro', 'BIT', 1) theta = program.declare('theta', 'REAL') # go to the equator program += Program(RX(np.pi / 2, qb)) # apply the CZ gate - note that CZ is symmetric, so the order of qubits doesn't matter program += Program(CZ(qb, other_qb)) # go to |1> after a phase kick program += Program(RZ(theta, qb), RX(np.pi / 2, qb)) program += MEASURE(qb, ro[0]) program.wrap_in_numshots_loop(n_shots) return program
def generate_single_rabi_experiment(qubits: Union[int, List[int]], theta: float, n_shots: int = 1000) -> Program: """ Return a Rabi program in native Quil rotated through the given angle. Rabi oscillations are observed by applying successively larger rotations to the same initial state. :param qubits: Which qubits to measure. :param theta: The angle of the Rabi RX rotation. :param n_shots: The number of shots to average over for the data point. :return: A Program that rotates through a given angle about the X axis. """ program = Program() try: len(qubits) except TypeError: qubits = [qubits] ro = program.declare('ro', 'BIT', len(qubits)) for q in qubits: program += RX(theta, q) for i in range(len(qubits)): program += MEASURE(qubits[i], ro[i]) program.wrap_in_numshots_loop(n_shots) return program
def test_reset(forest): device = NxDevice(nx.complete_graph(3)) qc = QuantumComputer( name="testy!", qam=QVM(connection=forest), device=device, compiler=DummyCompiler() ) p = Program( Declare(name="theta", memory_type="REAL"), Declare(name="ro", memory_type="BIT"), RX(MemoryReference("theta"), 0), MEASURE(0, MemoryReference("ro")), ).wrap_in_numshots_loop(1000) qc.run(executable=p, memory_map={"theta": [np.pi]}) aref = ParameterAref(name="theta", index=0) assert qc.qam._variables_shim[aref] == np.pi assert qc.qam._executable == p assert qc.qam._memory_results["ro"].shape == (1000, 1) assert all([bit == 1 for bit in qc.qam._memory_results["ro"]]) assert qc.qam.status == "done" qc.reset() assert qc.qam._variables_shim == {} assert qc.qam._executable is None assert qc.qam._memory_results["ro"] is None assert qc.qam.status == "connected"
def test_validate_protoquil_with_pragma(): prog = Program( RESET(), H(1), Pragma('DELAY'), MEASURE(1) ) assert prog.is_protoquil()
def test_qvm_run_just_program(client_configuration: QCSClientConfiguration): qvm = QVM(client_configuration=client_configuration, gate_noise=(0.01, 0.01, 0.01)) p = Program(Declare("ro", "BIT"), X(0), MEASURE(0, MemoryReference("ro"))) result = qvm.run(p.wrap_in_numshots_loop(1000)) bitstrings = result.readout_data.get("ro") assert bitstrings.shape == (1000, 1) assert np.mean(bitstrings) > 0.8
def test_validate_supported_quil_measure_last(): prog = Program( MEASURE(0), H(0), ) with pytest.raises(ValueError): validate_supported_quil(prog) assert not prog.is_supported_on_qpu()
def test_validate_protoquil_measure_last(): prog = Program( MEASURE(0), H(0), ) with pytest.raises(ValueError): validate_protoquil(prog) assert not prog.is_protoquil()
def test_validate_supported_quil_with_pragma(): prog = Program( RESET(), H(1), Pragma('DELAY'), MEASURE(1, None) ) assert prog.is_supported_on_qpu()
def cxn(): # DEPRECATED try: cxn = QVMConnection(endpoint='http://localhost:5000') cxn.run(Program(I(0), MEASURE(0, 0)), [0]) return cxn except RequestException as e: return pytest.skip("This test requires a running local QVM: {}".format(e))
def test_qvm_run_region_declared_and_measured( client_configuration: QCSClientConfiguration): qvm = QVM(client_configuration=client_configuration) p = Program(Declare("reg", "BIT"), X(0), MEASURE(0, MemoryReference("reg"))) result = qvm.run(p.wrap_in_numshots_loop(100)) bitstrings = result.readout_data.get("reg") assert bitstrings.shape == (100, 1)
def test_qvm_run_region_not_declared_is_measured_non_ro( client_configuration: QCSClientConfiguration): qvm = QVM(client_configuration=client_configuration) p = Program(X(0), MEASURE(0, MemoryReference("reg"))) with pytest.raises(QVMError, match='Bad memory region name "reg" in MEASURE'): qvm.load(p).run().wait()
def test_qvm_run_region_declared_and_measured( client_configuration: QCSClientConfiguration): qvm = QVM(client_configuration=client_configuration) p = Program(Declare("reg", "BIT"), X(0), MEASURE(0, MemoryReference("reg"))) qvm.load(p.wrap_in_numshots_loop(100)).run().wait() bitstrings = qvm.read_memory(region_name="reg") assert bitstrings.shape == (100, 1)
def ghz_program(qubits: List[int]) -> Program: program = Program() program.inst(H(qubits[0])) for i in range(len(qubits) - 1): program.inst(CNOT(qubits[i], qubits[i + 1])) ro = program.declare('ro', 'BIT', len(qubits)) program.inst([MEASURE(qubit, ro[idx]) for idx, qubit in enumerate(qubits)]) return program
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 _do_tomography(target_program, nsamples, cxn, qubits, max_num_qubits, tomography_class, program_generator, settings, use_run=False): """ :param Program target_program: The program to run to generate the state or process. :param int nsamples: The number of samples to take per measurement. :param QPUConnection|QVMConnection cxn: The connection to Forest. :param lists qubits: The list of qubits to perform tomography on. :param int max_num_qubits: The maximum allowed number of qubits. :param type tomography_class: The type of tomography to perform. :param function program_generator: The function that yields the tomography experiments. :param TomographySettings settings: The settings for running the optimizer. :param bool use_run: If ``True``, use append measurements on all qubits and use ``cxn.run`` instead of ``cxn.run_and_measure``. :return: The tomography result, the assignment probabilities, and the histograms of counts measured. :rtype: tuple """ if qubits is None: qubits = sorted(target_program.get_qubits()) num_qubits = len(qubits) dimension = 2 ** num_qubits if num_qubits > max_num_qubits: raise ValueError("Too many qubits!") assignment_probs = sample_assignment_probs(qubits, nsamples, cxn) tomo_seq = list(program_generator(target_program, qubits)) histograms = np.zeros((len(tomo_seq), dimension)) jobs = [] _log.info('Submitting jobs...') for i, tomo_prog in izip(ut.TRANGE(len(tomo_seq)), tomo_seq): if use_run: jobs.append(cxn.run_async(tomo_prog + Program([MEASURE(q, q) for q in qubits]), qubits, nsamples)) else: jobs.append(cxn.run_and_measure_async(tomo_prog, qubits, nsamples)) _log.info('Waiting for results...') for i, job_id in izip(ut.TRANGE(len(jobs)), jobs): job = cxn.wait_for_job(job_id) results = job.result() idxs = list(map(bitlist_to_int, results)) histograms[i] = ut.make_histogram(idxs, dimension) povm = o_ut.make_diagonal_povm(grove.tomography.operator_utils.POVM_PI_BASIS ** num_qubits, assignment_probs) channel_ops = list(default_channel_ops(num_qubits)) # Currently the analysis pathways are slightly different, so we branch on which type of # tomography is being done. if tomography_class.__tomography_type__ == "PROCESS": histograms = histograms.reshape((len(channel_ops), len(channel_ops), dimension)) tomo_result = tomography_class.estimate_from_ssr(histograms, povm, channel_ops, channel_ops, settings) else: tomo_result = tomography_class.estimate_from_ssr(histograms, povm, channel_ops, settings) return tomo_result, assignment_probs, histograms
def pre_expval(self): """Run the QVM""" # pylint: disable=attribute-defined-outside-init for e in self.expval_queue: wire = [e.wires[0]] if e.name == 'PauliX': # X = H.Z.H self.apply('Hadamard', wire, []) elif e.name == 'PauliY': # Y = (HS^)^.Z.(HS^) and S^=SZ self.apply('PauliZ', wire, []) self.apply('S', wire, []) self.apply('Hadamard', wire, []) elif e.name == 'Hadamard': # H = Ry(-pi/4)^.Z.Ry(-pi/4) self.apply('RY', wire, [-np.pi/4]) elif e.name == 'Hermitian': # For arbitrary Hermitian matrix H, let U be the unitary matrix # that diagonalises it, and w_i be the eigenvalues. H = e.parameters[0] Hkey = tuple(H.flatten().tolist()) if Hkey in self._eigs: # retrieve eigenvectors U = self._eigs[Hkey]['eigvec'] else: # store the eigenvalues corresponding to H # in a dictionary, so that they do not need to # be calculated later w, U = np.linalg.eigh(H) self._eigs[Hkey] = {'eigval': w, 'eigvec': U} # Perform a change of basis before measuring by applying U^ to the circuit self.apply('QubitUnitary', wire, [U.conj().T]) prag = Program(Pragma('INITIAL_REWIRING', ['"PARTIAL"'])) if self.active_reset: prag += RESET() self.prog = prag + self.prog qubits = list(self.prog.get_qubits()) ro = self.prog.declare('ro', 'BIT', len(qubits)) for i, q in enumerate(qubits): self.prog.inst(MEASURE(q, ro[i])) self.prog.wrap_in_numshots_loop(self.shots) executable = self.qc.compile(self.prog) bitstring_array = self.qc.run(executable=executable) self.state = {} for i, q in enumerate(qubits): self.state[q] = bitstring_array[:, i]
def _create_circuit(self, oracle): p = Program() classical_reg = p.declare('ro', 'BIT', len(self.input_qbits)) p.defgate('oracle', oracle) p += [H(x) for x in self.input_qbits] p += tuple(['oracle'] + sorted(self.qbits, reverse=True)) p += [H(x) for x in self.input_qbits] p += [MEASURE(x, y) for x, y in zip(self.input_qbits, classical_reg)] return p
def _wrap_program(self, program): # the actions select gates. but a pyquil program needs a bit more # namely, declaration of classical memory for readout, and suitable # measurement instructions ro = program.declare("ro", "BIT", self.num_qubits) for q in range(self.num_qubits): program.inst(MEASURE(q, ro[q])) program.wrap_in_numshots_loop(NUM_SHOTS) return program