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_and_measure_payload(self, quil_program, qubits, trials, needs_compilation, isa): if not isinstance(quil_program, Program): raise TypeError("quil_program must be a Quil program object") validate_run_items(qubits) if not isinstance(trials, integer_types): raise TypeError("trials must be an integer") if needs_compilation and not isa: raise TypeError( "ISA cannot be None if QVM program needs compilation preprocessing." ) if self.noise_model is not None: compiled_program = self.compiler.compile(quil_program) quil_program = apply_noise_model(compiled_program, self.noise_model) payload = { "type": TYPE_MULTISHOT_MEASURE, "qubits": list(qubits), "trials": trials } if needs_compilation: payload["uncompiled-quil"] = quil_program.out() payload["target-device"] = {"isa": isa.to_dict()} else: payload["compiled-quil"] = quil_program.out() self._maybe_add_noise_to_payload(payload) self._add_rng_seed_to_payload(payload) return payload
def _run_and_measure_payload(self, quil_program, qubits, trials): if not quil_program: raise ValueError( "You have attempted to run an empty program." " Please provide gates or measure instructions to your program." ) if not isinstance(quil_program, Program): raise TypeError("quil_program must be a Quil program object") qubits = validate_qubit_list(qubits) if not isinstance(trials, integer_types): raise TypeError("trials must be an integer") if self.noise_model is not None: compiled_program = self.compiler.quil_to_native_quil(quil_program) quil_program = apply_noise_model(compiled_program, self.noise_model) payload = { "type": TYPE_MULTISHOT_MEASURE, "qubits": list(qubits), "trials": trials, "compiled-quil": quil_program.out() } self._maybe_add_noise_to_payload(payload) self._add_rng_seed_to_payload(payload) return payload
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_payload(self, quil_program, classical_addresses, trials, needs_compilation, isa): if not quil_program: raise ValueError("You have attempted to run an empty program." " Please provide gates or measure instructions to your program.") if not isinstance(quil_program, Program): raise TypeError("quil_program must be a Quil program object") validate_run_items(classical_addresses) if not isinstance(trials, integer_types): raise TypeError("trials must be an integer") if needs_compilation and not isa: raise TypeError("ISA cannot be None if program needs compilation preprocessing.") if self.noise_model is not None: compiled_program = self.compiler.compile(quil_program) quil_program = apply_noise_model(compiled_program, self.noise_model) payload = {"type": TYPE_MULTISHOT, "addresses": list(classical_addresses), "trials": trials} if needs_compilation: payload["uncompiled-quil"] = quil_program.out() payload["target-device"] = {"isa": isa.to_dict()} else: payload["compiled-quil"] = quil_program.out() self._maybe_add_noise_to_payload(payload) self._add_rng_seed_to_payload(payload) return payload
def test_decoherence_noise(): prog = Program(RX(np.pi / 2, 0), CZ(0, 1), RZ(np.pi, 0)) gates = _get_program_gates(prog) m1 = _decoherence_noise_model(gates, T1=INFINITY, T2=INFINITY, ro_fidelity=1.) # with no readout error, assignment_probs = identity matrix assert np.allclose(m1.assignment_probs[0], np.eye(2)) assert np.allclose(m1.assignment_probs[1], np.eye(2)) for g in m1.gates: # with infinite coherence time all kraus maps should only have a single, unitary kraus op assert len(g.kraus_ops) == 1 k0, = g.kraus_ops # check unitarity k0dk0 = k0.dot(k0.conjugate().transpose()) assert np.allclose(k0dk0, np.eye(k0dk0.shape[0])) # verify that selective (by qubit) dephasing and readout infidelity is working m2 = _decoherence_noise_model(gates, T1=INFINITY, T2={0: 30e-6}, ro_fidelity={ 0: .95, 1: 1.0 }) assert np.allclose(m2.assignment_probs[0], [[.95, 0.05], [.05, .95]]) assert np.allclose(m2.assignment_probs[1], np.eye(2)) for g in m2.gates: if 0 in g.targets: # single dephasing (no damping) channel on qc 0, no noise on qc1 -> 2 Kraus ops assert len(g.kraus_ops) == 2 else: assert len(g.kraus_ops) == 1 # verify that combined T1 and T2 will lead to 4 outcome Kraus map. m3 = _decoherence_noise_model(gates, T1={0: 30e-6}, T2={0: 30e-6}) for g in m3.gates: if 0 in g.targets: # damping (implies dephasing) channel on qc 0, no noise on qc1 -> 4 Kraus ops assert len(g.kraus_ops) == 4 else: assert len(g.kraus_ops) == 1 # verify that gate names are translated new_prog = apply_noise_model(prog, m3) new_gates = _get_program_gates(new_prog) # check that headers have been embedded headers = _noise_model_program_header(m3) assert all( (isinstance(i, Pragma) and i.command in ["ADD-KRAUS", "READOUT-POVM"]) or isinstance(i, DefGate) for i in headers) assert headers.out() in new_prog.out() # verify that high-level add_decoherence_noise reproduces new_prog new_prog2 = add_decoherence_noise(prog, T1={0: 30e-6}, T2={0: 30e-6}) assert new_prog == new_prog2
def test_apply_noise_model(): p = Program(RX(np.pi / 2, 0), RX(np.pi / 2, 1), CZ(0, 1), RX(np.pi / 2, 1)) noise_model = _decoherence_noise_model(_get_program_gates(p)) pnoisy = apply_noise_model(p, noise_model) for i in pnoisy: if isinstance(i, DefGate): pass elif isinstance(i, Pragma): assert i.command in ['ADD-KRAUS', 'READOUT-POVM'] elif isinstance(i, Gate): assert i.name in NO_NOISE or not i.params
def test_apply_noise_model_perturbed_angles(): eps = 1e-15 p = Program(RX(np.pi / 2 + eps, 0), RX(np.pi / 2 - eps, 1), CZ(0, 1), RX(np.pi / 2 + eps, 1)) noise_model = _decoherence_noise_model(_get_program_gates(p)) pnoisy = apply_noise_model(p, noise_model) for i in pnoisy: if isinstance(i, DefGate): pass elif isinstance(i, Pragma): assert i.command in ["ADD-KRAUS", "READOUT-POVM"] elif isinstance(i, Gate): assert i.name in NO_NOISE or not i.params
def run_async(self, quil_program, classical_addresses, trials): """ 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 self.noise_model is not None: quil_program = apply_noise_model(quil_program, self.noise_model) return self.connection._qvm_run_async( quil_program=quil_program, classical_addresses=classical_addresses, trials=trials, needs_compilation=False, isa=None, measurement_noise=self.measurement_noise, gate_noise=self.gate_noise, random_seed=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, quil_program: Program, classical_addresses: Iterable[int], trials: int) -> np.ndarray: """ Run a Quil program on the QVM multiple times and return the values stored in the classical registers designated by the classical_addresses parameter. :param quil_program: A program to run :param classical_addresses: Classical register addresses to return :param int trials: Number of times to repeatedly run the program. This is sometimes called the number of shots. :return: An array of bitstrings of shape ``(trials, len(classical_addresses))`` """ if self.noise_model is not None: quil_program = apply_noise_model(quil_program, self.noise_model) return self.connection._qvm_run( quil_program=quil_program, classical_addresses=classical_addresses, trials=trials, needs_compilation=False, isa=None, measurement_noise=self.measurement_noise, gate_noise=self.gate_noise, random_seed=self.random_seed)