def test_get_classical_addresses_from_quil_program(): """ Tests that can get_classical_addresses_from_program can handle both MEASURE quil instructions with and without explicit classical registers. """ p = Program('\n'.join(['X 0', 'MEASURE 0'])) assert get_classical_addresses_from_program(p) == {} p = Program('\n'.join(['X 0', 'MEASURE 0 ro[1]'])) assert get_classical_addresses_from_program(p) == {"ro": [1]}
def run(self, quil_program, classical_addresses=None, trials=1, needs_compilation=False, isa=None): """ Run a Quil program multiple times, accumulating the values deposited in a list of classical addresses. :param Program quil_program: A Quil program. :param list|range classical_addresses: A list of addresses. :param int trials: Number of shots to collect. :param bool needs_compilation: If True, preprocesses the job with the compiler. :param ISA isa: If set, compiles to this target ISA. :return: A list of lists of bits. Each sublist corresponds to the values in `classical_addresses`. :rtype: list """ if not classical_addresses: classical_addresses = get_classical_addresses_from_program( quil_program) return self._connection._qvm_run(quil_program, classical_addresses, trials, needs_compilation, isa, self.measurement_noise, self.gate_noise, self.random_seed).tolist()
def run_async(self, quil_program, classical_addresses=None, trials=1, needs_compilation=True, isa=None): """ Similar to run except that it returns a job id and doesn't wait for the program to be executed. See https://go.rigetti.com/connections for reasons to use this method. """ if not classical_addresses: classical_addresses = get_classical_addresses_from_program( quil_program) payload = self._run_payload(quil_program, classical_addresses, trials, needs_compilation=needs_compilation, isa=isa) response = None while response is None: try: response = post_json(self.session, self.async_endpoint + "/job", self._wrap_program(payload)) except errors.DeviceRetuningError: print( "QPU is retuning. Will try to reconnect in 10 seconds...") time.sleep(10) return get_job_id(response)
def run(self, quil_program, classical_addresses=None, trials=1, needs_compilation=True, isa=None): """ Run a pyQuil program on the QPU and return the values stored in the classical registers designated by the classical_addresses parameter. The program is repeated according to the number of trials provided to the run method. This functionality is in beta. It is important to note that our QPUs currently only allow a single set of simultaneous readout pulses on all qubits in the QPU at the end of the program. This means that missing or duplicate MEASURE instructions do not change the pulse program, but instead only contribute to making a less rich or richer mapping, respectively, between classical and qubit addresses. :param Program quil_program: Pyquil program to run on the QPU :param list|range classical_addresses: Classical register addresses to return :param int trials: Number of times to run the program (a.k.a. number of shots) :param bool needs_compilation: If True, preprocesses the job with the compiler. :param ISA isa: If set, specifies a custom ISA to compile to. If left unset, Forest uses the default ISA associated to this QPU device. :return: A list of a list of classical registers (each register contains a bit) :rtype: list """ if not classical_addresses: classical_addresses = get_classical_addresses_from_program( quil_program) job = self.wait_for_job( self.run_async(quil_program, classical_addresses, trials, needs_compilation, isa)) return job.result()
def run(self, quil_program, classical_addresses=None, trials=1, needs_compilation=False, isa=None): """ Run a Quil program multiple times, accumulating the values deposited in a list of classical addresses. :param Program quil_program: A Quil program. :param list|range classical_addresses: A list of addresses. :param int trials: Number of shots to collect. :param bool needs_compilation: If True, preprocesses the job with the compiler. :param ISA isa: If set, compiles to this target ISA. :return: A list of lists of bits. Each sublist corresponds to the values in `classical_addresses`. :rtype: list """ if not classical_addresses: classical_addresses = get_classical_addresses_from_program(quil_program) payload = self._run_payload(quil_program, classical_addresses, trials, needs_compilation, isa) if self.use_queue or needs_compilation: if needs_compilation and not self.use_queue: warnings.warn('Synchronous QVM connection does not support compilation preprocessing. Running this job over the asynchronous endpoint, as if use_queue were set to True.') response = post_json(self.session, self.async_endpoint + "/job", {"machine": "QVM", "program": payload}) job = self.wait_for_job(get_job_id(response)) return job.result() else: response = post_json(self.session, self.sync_endpoint + "/qvm", payload) return response.json()
def run(self): """ Run a Quil program on the QVM multiple times and return the values stored in the classical registers designated by the classical_addresses parameter. :return: An array of bitstrings of shape ``(trials, len(classical_addresses))`` """ super().run() if isinstance(self._executable, PyQuilExecutableResponse): quil_program = _extract_program_from_pyquil_executable_response(self._executable) elif isinstance(self._executable, Program): quil_program = self._executable else: raise TypeError("quil_binary argument must be a PyQuilExecutableResponse or a Program." "This error is typically triggered by forgetting to pass (nativized)" "Quil to native_quil_to_executable or by using a compiler meant to be" "used for jobs bound for a QPU.") trials = quil_program.num_shots classical_addresses = get_classical_addresses_from_program(quil_program) if self.noise_model is not None: quil_program = apply_noise_model(quil_program, self.noise_model) quil_program = self.augment_program_with_memory_values(quil_program) self._bitstrings = self.connection._qvm_run(quil_program=quil_program, classical_addresses=classical_addresses, trials=trials, measurement_noise=self.measurement_noise, gate_noise=self.gate_noise, random_seed=self.random_seed)['ro'] return self
def run(self, program, classical_addresses, trials, symmetrize_readout=None) -> np.ndarray: """ Run a quil program. :param program: The program to run. You probably want to put MEASURE instructions in your program somewhere (like at the end) because qubits are not automatically measured :param classical_addresses: The addresses of the classical bits to return. These don't necessarily correspond to qubit indices; rather they are the second argument to any MEASURE instructions you've added to your program :param trials: The number of times to run the program. :param symmetrize_readout: Whether to apply readout error symmetrization. If not specified, the instance attribute ``symmetrize_readout`` will be used. See :py:func:`run_symmetrized_readout` for a complete description. :return: A numpy array of shape (trials, len(classical_addresses)) that contains 0s and 1s """ if symmetrize_readout is None: symmetrize_readout = self.symmetrize_readout if not classical_addresses: classical_addresses = get_classical_addresses_from_program(program) if symmetrize_readout: return self.run_symmetrized_readout(program, classical_addresses, trials) return self.qam.run(program, classical_addresses, trials)
def run(self, quil_program, classical_addresses: List[int] = None, trials=1): """ Run a Quil program multiple times, accumulating the values deposited in a list of classical addresses. :param Program quil_program: A Quil program. :param classical_addresses: The classical memory to retrieve. Specified as a list of integers that index into a readout register named ``ro``. This function--and particularly this argument--are included for backwards compatibility and will be removed in the future. :param int trials: Number of shots to collect. :return: A list of dictionaries of bits. Each dictionary corresponds to the values in `classical_addresses`. :rtype: list """ if classical_addresses is None: caddresses = get_classical_addresses_from_program(quil_program) else: caddresses = {'ro': classical_addresses} buffers = self._connection._qvm_run(quil_program, caddresses, trials, self.measurement_noise, self.gate_noise, self.random_seed) if len(buffers) == 0: return [] if 'ro' in buffers: return buffers['ro'].tolist() raise ValueError("You are using QVMConnection.run with multiple readout registers not " "named `ro`. Please use the new `QuantumComputer` abstraction.")
def run(self) -> "QVM": """ Run a Quil program on the QVM multiple times and return the values stored in the classical registers designated by the classical_addresses parameter. :return: An array of bitstrings of shape ``(trials, len(classical_addresses))`` """ super().run() if not isinstance(self._executable, Program): # This should really never happen # unless a user monkeys with `self.status` and `self._executable`. raise ValueError("Please `load` an appropriate executable.") quil_program = self._executable trials = quil_program.num_shots classical_addresses = get_classical_addresses_from_program(quil_program) if self.noise_model is not None: quil_program = apply_noise_model(quil_program, self.noise_model) quil_program = self.augment_program_with_memory_values(quil_program) results = self.connection._qvm_run( quil_program=quil_program, classical_addresses=classical_addresses, trials=trials, measurement_noise=self.measurement_noise, gate_noise=self.gate_noise, random_seed=self.random_seed, ) self._memory_results.update(results) return self
def run_async(self, program, classical_addresses, trials, symmetrize_readout=None) -> str: """ Queue a quil program for running, but return immediately with a job id. Use :py:func:`QuantumComputer.wait_for_job` to get the actual job results, probably after queueing up a whole batch of jobs. See :py:func:`run` for this function's parameter descriptions. :returns: a job id """ if symmetrize_readout is None: symmetrize_readout = self.symmetrize_readout if not classical_addresses: classical_addresses = get_classical_addresses_from_program(program) if symmetrize_readout: raise NotImplementedError( "Async symmetrized readout isn't supported") return self.qam.run_async(program, classical_addresses, trials)
def run_async(self, quil_program, classical_addresses=None, trials=1, needs_compilation=True, isa=None): """ Similar to run except that it returns a job id and doesn't wait for the program to be executed. See https://go.rigetti.com/connections for reasons to use this method. """ if not classical_addresses: classical_addresses = get_classical_addresses_from_program(quil_program) return self._connection._qpu_run_async(quil_program, classical_addresses, trials, needs_compilation, isa, device_name=self.device_name)
def run_async(self, quil_program, classical_addresses=None, trials=1, needs_compilation=False, isa=None): """ Similar to run except that it returns a job id and doesn't wait for the program to be executed. See https://go.rigetti.com/connections for reasons to use this method. """ if not classical_addresses: classical_addresses = get_classical_addresses_from_program(quil_program) payload = self._run_payload(quil_program, classical_addresses, trials, needs_compilation, isa) response = post_json(self.session, self.async_endpoint + "/job", {"machine": "QVM", "program": payload}) return get_job_id(response)
def run_async(self, quil_program, classical_addresses=None, trials=1, needs_compilation=False, isa=None): """ Similar to run except that it returns a job id and doesn't wait for the program to be executed. See https://go.rigetti.com/connections for reasons to use this method. """ if not classical_addresses: classical_addresses = get_classical_addresses_from_program( quil_program) return self._connection._qvm_run_async( quil_program, classical_addresses, trials, needs_compilation, isa, self.measurement_noise, self.gate_noise, self.random_seed)
def run(self): """ Run a Quil program on the QVM multiple times and return the values stored in the classical registers designated by the classical_addresses parameter. :return: An array of bitstrings of shape ``(trials, len(classical_addresses))`` """ super().run() if not isinstance(self._executable, Program): # This should really never happen # unless a user monkeys with `self.status` and `self._executable`. raise ValueError("Please `load` an appropriate executable.") quil_program = self._executable trials = quil_program.num_shots classical_addresses = get_classical_addresses_from_program( quil_program) if self.noise_model is not None: quil_program = apply_noise_model(quil_program, self.noise_model) quil_program = self.augment_program_with_memory_values(quil_program) try: self._bitstrings = self.connection._qvm_run( quil_program=quil_program, classical_addresses=classical_addresses, trials=trials, measurement_noise=self.measurement_noise, gate_noise=self.gate_noise, random_seed=self.random_seed)['ro'] except KeyError: warnings.warn( "You are running a QVM program with no MEASURE instructions. " "The result of this program will always be an empty array. Are " "you sure you didn't mean to measure some of your qubits?") self._bitstrings = np.zeros((trials, 0), dtype=np.int64) return self
def execute(self, executable: QuantumExecutable) -> QVMExecuteResponse: """ Synchronously execute the input program to completion. """ executable = executable.copy() if not isinstance(executable, Program): raise TypeError( f"`QVM#executable` argument must be a `Program`; got {type(executable)}" ) result_memory = {} for region in executable.declarations.keys(): result_memory[region] = np.ndarray((executable.num_shots, 0), dtype=np.int64) trials = executable.num_shots classical_addresses = get_classical_addresses_from_program(executable) if self.noise_model is not None: executable = apply_noise_model(executable, self.noise_model) executable._set_parameter_values_at_runtime() request = qvm_run_request( executable, classical_addresses, trials, self.measurement_noise, self.gate_noise, self.random_seed, ) response = self._qvm_client.run_program(request) ram = {key: np.array(val) for key, val in response.results.items()} result_memory.update(ram) return QVMExecuteResponse(executable=executable, memory=result_memory)
def run(self, pyquil_program, classical_addresses=None, trials=1): """ Run pyquil program and return the results Loads and checks program if all gates are within the stabilizer set. Then executes program. If measurements are requested then the measured results are returned :param Program pyquil_program: a pyquil Program containing only CNOT-H-S-MEASUREMENT operations :param classical_addresses: classical addresses to return :param trials: number of times to repeat the execution of the program :return: list of lists of classical memory after each run """ self.load_program(pyquil_program) if classical_addresses is None: classical_addresses = get_classical_addresses_from_program( pyquil_program) results = [] for _ in range(trials): # set up stabilizers self.tableau = self._n_qubit_tableau(self.num_qubits) self.kernel() results.append( list(map(int, self.classical_memory[classical_addresses]))) # results.append([int(b) for b in self.classical_memory[classical_addresses]]) assert results[-1] == [ int(b) for b in self.classical_memory[classical_addresses] ] # reset qvm self.memory_reset() self.program_counter = 0 return results
def test_get_classical_addresses_from_program(): p = Program(Declare("ro", "BIT", 4), [H(i) for i in range(4)]) assert get_classical_addresses_from_program(p) == {} p += [MEASURE(i, MemoryReference("ro", i)) for i in [0, 3, 1]] assert get_classical_addresses_from_program(p) == {"ro": [0, 1, 3]}
def test_get_classical_addresses_from_program(): p = Program([H(i) for i in range(4)]) assert get_classical_addresses_from_program(p) == {} p += [MEASURE(i, i) for i in [0, 3, 1]] assert get_classical_addresses_from_program(p) == {"ro": [0, 1, 3]}