def test_aer_result_handle() -> None: c = Circuit(2, 2).H(0).CX(0, 1).measure_all() b = AerBackend() handles = b.process_circuits([c, c.copy()], n_shots=2) ids, indices = zip(*(han for han in handles)) assert all(isinstance(idval, str) for idval in ids) assert indices == (0, 1) assert len(b.get_result(handles[0]).get_shots()) == 2 with pytest.raises(TypeError) as errorinfo: _ = b.get_result(ResultHandle("43")) assert "ResultHandle('43',) does not match expected identifier types" in str( errorinfo.value) wronghandle = ResultHandle("asdf", 3) with pytest.raises(CircuitNotRunError) as errorinfoCirc: _ = b.get_result(wronghandle) assert "Circuit corresponding to {0!r} ".format( wronghandle) + "has not been run by this backend instance." in str( errorinfoCirc.value)
def process_circuits( self, circuits: Iterable[Circuit], n_shots: Optional[int] = None, valid_check: bool = True, **kwargs: KwargTypes, ) -> List[ResultHandle]: """ See :py:meth:`pytket.backends.Backend.process_circuits`. Supported kwargs: none. """ if n_shots is None or n_shots < 1: raise ValueError("Parameter n_shots is required for this backend") if valid_check: self._check_all_circuits(circuits) basebody = { "lang": "json", "body": None, "target": self._device_name, "shots": n_shots, } handles = [] for i, circ in enumerate(circuits): result = dict() bodycopy = basebody.copy() (bodycopy["body"], meas) = tk_to_ionq(circ) # type: ignore if len(meas) == 0: result["result"] = self.empty_result(circ, n_shots=n_shots) measures = json.dumps(meas) bodycopy["name"] = circ.name if circ.name else f"{self._label}_{i}" if self._MACHINE_DEBUG: handle = ResultHandle( _DEBUG_HANDLE_PREFIX + str(circ.n_qubits), n_shots, measures, ) handles.append(handle) else: header = self._header.copy() header["Content-Type"] = "application/json" try: # post job resp = post(self._url, json.dumps(bodycopy), headers=header).json() if "error" in resp: raise RuntimeError(resp["error"]) if resp["status"] == "failed": raise RuntimeError("Unknown error while submitting job.") except ConnectionError: raise ConnectionError( f"{self._label} Connection Error: Error during submit..." ) # extract job ID from response job_id = resp["id"] handle = ResultHandle(job_id, n_shots, measures) handles.append(handle) for handle in handles: self._cache[handle] = result return handles
def process_circuits( self, circuits: Iterable[Circuit], n_shots: Optional[int] = None, valid_check: bool = True, **kwargs: KwargTypes, ) -> List[ResultHandle]: """ See :py:meth:`pytket.backends.Backend.process_circuits`. Supported kwargs: none. """ if n_shots is None or n_shots < 1: raise ValueError("Parameter n_shots is required for this backend") if valid_check: self._check_all_circuits(circuits) basebody = { "machine": self._device_name, "language": "OPENQASM 2.0", "priority": "normal", "count": n_shots, "options": None, } handle_list = [] for i, circ in enumerate(circuits): honeywell_circ = circuit_to_qasm_str(circ, header="hqslib1") body = basebody.copy() body["name"] = circ.name if circ.name else f"{self._label}_{i}" body["program"] = honeywell_circ if self._api_handler is None: handle_list.append( ResultHandle(_DEBUG_HANDLE_PREFIX + str((circ.n_qubits, n_shots)))) else: try: res = _submit_job(self._api_handler, body) if res.status_code != HTTPStatus.OK: self.relogin() res = _submit_job(self._api_handler, body) jobdict = res.json() if res.status_code != HTTPStatus.OK: raise HQSAPIError( f'HTTP error submitting job, {jobdict["error"]["text"]}' ) except ConnectionError: raise ConnectionError( f"{self._label} Connection Error: Error during submit..." ) # extract job ID from response handle = ResultHandle(jobdict["job"]) handle_list.append(handle) self._cache[handle] = dict() return handle_list
def process_circuits( self, circuits: Iterable[Circuit], n_shots: Optional[int] = None, valid_check: bool = True, **kwargs: KwargTypes, ) -> List[ResultHandle]: """ Supported `kwargs`: none """ if not self.supports_shots and not self.supports_state: raise RuntimeError("Backend does not support shots or state") if n_shots is None: n_shots = 0 want_state = n_shots == 0 if (not want_state) and (n_shots < self._sample_min_shots or n_shots > self._sample_max_shots): raise ValueError( "For sampling, n_shots must be between " f"{self._sample_min_shots} and {self._sample_max_shots}. " "For statevector simulation, omit this parameter.") if valid_check: self._check_all_circuits(circuits, nomeasure_warn=False) handles = [] for circ in circuits: bkcirc = self._to_bkcirc(circ) if want_state: bkcirc.add_result_type(ResultType.StateVector()) if not bkcirc.instructions and len(circ.bits) == 0: task = None else: task = self._run(bkcirc, n_shots=n_shots) if self._device_type == _DeviceType.LOCAL: # Results are available now. Put them in the cache. if task is not None: assert task.state() == "COMPLETED" results = _get_result(task, want_state) else: results = { "result": self.empty_result(circ, n_shots=n_shots) } else: # Task is asynchronous. Must wait for results. results = {} if task is not None: handle = ResultHandle(task.id, want_state) else: handle = ResultHandle(str(uuid4()), False) self._cache[handle] = results handles.append(handle) return handles
def process_circuits( self, circuits: Iterable[Circuit], n_shots: Optional[int] = None, valid_check: bool = True, **kwargs: KwargTypes, ) -> List[ResultHandle]: circuit_list = list(circuits) if valid_check: self._check_all_circuits(circuit_list) qcs = [tk_to_qiskit(tkc) for tkc in circuit_list] seed = cast(Optional[int], kwargs.get("seed")) qobj = assemble(qcs, shots=n_shots, memory=self._memory, seed_simulator=seed) job = self._backend.run(qobj, noise_model=self._noise_model) jobid = job.job_id() handle_list = [ ResultHandle(jobid, i) for i in range(len(circuit_list)) ] for handle in handle_list: self._cache[handle] = {"job": job} return handle_list
def process_circuits( self, circuits: Iterable[Circuit], n_shots: Optional[int] = None, valid_check: bool = True, **kwargs: KwargTypes, ) -> List[ResultHandle]: circuit_list = list(circuits) if valid_check: self._check_all_circuits(circuit_list, nomeasure_warn=(n_shots is not None)) handle_list = [] for circuit in circuit_list: qulacs_state = self._sim(circuit.n_qubits) qulacs_state.set_zero_state() qulacs_circ = tk_to_qulacs(circuit) qulacs_circ.update_quantum_state(qulacs_state) state = qulacs_state.get_vector() qubits = sorted(circuit.qubits, reverse=True) shots = None bits = None if n_shots: bits2index = list((com.bits[0], qubits.index(com.qubits[0])) for com in circuit if com.op.type == OpType.Measure) if len(bits2index) == 0: bits = circuit.bits shots = OutcomeArray.from_ints([0] * n_shots, len(bits)) else: bits, choose_indices = zip(*bits2index) samples = qulacs_state.sampling(n_shots) shots = OutcomeArray.from_ints(samples, circuit.n_qubits) shots = shots.choose_indices(choose_indices) try: phase = float(circuit.phase) coeff = np.exp(phase * np.pi * 1j) state *= coeff except TypeError: warning( "Global phase is dependent on a symbolic parameter, so cannot " "adjust for phase") implicit_perm = circuit.implicit_qubit_permutation() qubits = [implicit_perm[qb] for qb in qubits] handle = ResultHandle(str(uuid4())) self._cache[handle] = { "result": BackendResult(state=state, shots=shots, c_bits=bits, q_bits=qubits) } handle_list.append(handle) del qulacs_state del qulacs_circ return handle_list
def process_circuits( self, circuits: Iterable[Circuit], n_shots: Optional[int] = None, valid_check: bool = True, **kwargs: KwargTypes, ) -> List[ResultHandle]: """ Submit circuits to the backend for running. The results will be stored in the backend's result cache to be retrieved by the corresponding get_<data> method. Use keyword arguments to specify parameters to be used in submitting circuits See specific Backend derived class for available parameters, from the following list: * `seed`: RNG seed for simulators :param circuits: Circuits to process on the backend. :type circuits: Iterable[Circuit] :param n_shots: Number of shots to run per circuit. None is to be used for state/unitary simulators. Defaults to None. :type n_shots: Optional[int], optional :param valid_check: Explicitly check that all circuits satisfy all required predicates to run on the backend. Defaults to True :type valid_check: bool, optional :return: Handles to results for each input circuit, as an interable in the same order as the circuits. :rtype: List[ResultHandle] """ circuit_list = list(circuits) if valid_check: self._check_all_circuits(circuit_list) handle_list = [] for circuit in circuit_list: handle = ResultHandle(str(uuid4())) mycirc, measure_map = tk_to_mymeasures(circuit) qubit_list, bit_list = zip(*measure_map.items()) qubit_shots = sample_mycircuit(mycirc, set(qubit_list), n_shots, kwargs.get("seed")) # Pad shot table with 0 columns for unused bits all_shots = np.zeros((n_shots, len(circuit.bits)), dtype=int) all_shots[:, :len(qubit_list)] = qubit_shots res_bits = [ measure_map[q] for q in sorted(qubit_list, reverse=True) ] for b in circuit.bits: if b not in bit_list: res_bits.append(b) res = BackendResult(c_bits=res_bits, shots=OutcomeArray.from_readouts(all_shots)) self._cache[handle] = {"result": res} handle_list.append(handle) return handle_list
def process_circuits( self, circuits: Iterable[Circuit], n_shots: Optional[int] = None, valid_check: bool = True, **kwargs: KwargTypes, ) -> List[ResultHandle]: """ See :py:meth:`pytket.backends.Backend.process_circuits`. Supported kwargs: none. """ if n_shots is None or n_shots < 1: raise ValueError("Parameter n_shots is required for this backend") if valid_check: self._check_all_circuits(circuits) handles = [] for i, c in enumerate(circuits): (aqt_circ, measures) = _translate_aqt(c) if self._MACHINE_DEBUG: handles.append( ResultHandle( _DEBUG_HANDLE_PREFIX + str((c.n_qubits, n_shots)), measures)) else: resp = put( self._url, data={ "data": json.dumps(aqt_circ), "repetitions": n_shots, "no_qubits": c.n_qubits, "label": c.name if c.name else f"{self._label}_{i}", }, headers=self._header, ).json() if "status" not in resp: raise RuntimeError(resp["message"]) if resp["status"] == "error": raise RuntimeError(resp["ERROR"]) handles.append(ResultHandle(resp["id"], measures)) for handle in handles: self._cache[handle] = dict() return handles
def process_circuits( self, circuits: Iterable[Circuit], n_shots: Optional[int] = None, valid_check: bool = True, **kwargs: KwargTypes, ) -> List[ResultHandle]: """ See :py:meth:`pytket.backends.Backend.process_circuits`. Supported kwargs: `seed`. """ circuit_list = list(circuits) if valid_check: self._check_all_circuits(circuit_list) handle_list = [] for circuit in circuit_list: sim = Simulator(rnd_seed=kwargs.get("seed")) fwd = ForwarderEngine(sim) eng = MainEngine(backend=sim, engine_list=[fwd]) qureg = eng.allocate_qureg(circuit.n_qubits) tk_to_projectq(eng, qureg, circuit, True) eng.flush() state = np.array( eng.backend.cheat()[1], dtype=complex ) # `cheat()` returns tuple:(a dictionary of qubit indices, statevector) handle = ResultHandle(str(uuid4())) try: phase = float(circuit.phase) coeff = np.exp(phase * np.pi * 1j) state *= coeff except ValueError: warning( "Global phase is dependent on a symbolic parameter, so cannot " "adjust for phase") implicit_perm = circuit.implicit_qubit_permutation() # reverse qubits as projectq state is dlo res_qubits = [ implicit_perm[qb] for qb in sorted(circuit.qubits, reverse=True) ] measures = circuit.n_gates_of_type(OpType.Measure) if measures == 0 and n_shots is not None: backres = self.empty_result(circuit, n_shots=n_shots) else: backres = BackendResult(q_bits=res_qubits, state=state) self._cache[handle] = {"result": backres} handle_list.append(handle) return handle_list
def process_circuits( self, circuits: Iterable[Circuit], n_shots: Optional[int] = None, valid_check: bool = True, **kwargs: KwargTypes, ) -> List[ResultHandle]: """ Submit circuits to the backend for running. The results will be stored in the backend's result cache to be retrieved by the corresponding get_<data> method. Use keyword arguments to specify parameters to be used in submitting circuits See specific Backend derived class for available parameters, from the following list: * `seed`: RNG seed for simulators :param circuits: Circuits to process on the backend. :type circuits: Iterable[Circuit] :param n_shots: Number of shots to run per circuit. None is to be used for state/unitary simulators. Defaults to None. :type n_shots: Optional[int], optional :param valid_check: Explicitly check that all circuits satisfy all required predicates to run on the backend. Defaults to True :type valid_check: bool, optional :return: Handles to results for each input circuit, as an interable in the same order as the circuits. :rtype: List[ResultHandle] """ circuit_list = list(circuits) if valid_check: self._check_all_circuits(circuit_list) handle_list = [] for circuit in circuit_list: handle = ResultHandle(str(uuid4())) mycirc = tk_to_mycircuit(circuit) state = run_mycircuit(mycirc) state *= np.exp(1j * np.pi * circuit.phase) implicit_perm = circuit.implicit_qubit_permutation() res_qubits = [ implicit_perm[qb] for qb in sorted(circuit.qubits, reverse=True) ] res = BackendResult(q_bits=res_qubits, state=state) self._cache[handle] = {"result": res} handle_list.append(handle) return handle_list
def process_circuits( self, circuits: Iterable[Circuit], n_shots: Optional[int] = None, valid_check: bool = True, **kwargs: KwargTypes, ) -> List[ResultHandle]: """ See :py:meth:`pytket.backends.Backend.process_circuits`. Supported kwargs: none. """ if n_shots is None or n_shots < 1: raise ValueError("Parameter n_shots is required for this backend") handle_list = [] for chunk in itertools.zip_longest(*([iter(circuits)] * self._max_per_job)): filtchunk = list(filter(lambda x: x is not None, chunk)) if valid_check: self._check_all_circuits(filtchunk) qcs = [tk_to_qiskit(tkc) for tkc in filtchunk] qobj = assemble(qcs, shots=n_shots, memory=self._config.memory) if self._MACHINE_DEBUG: handle_list += [ ResultHandle( _DEBUG_HANDLE_PREFIX + str((c.n_qubits, n_shots)), i) for i, c in enumerate(filtchunk) ] else: job = self._backend.run(qobj) jobid = job.job_id() handle_list += [ ResultHandle(jobid, i) for i in range(len(filtchunk)) ] for handle in handle_list: self._cache[handle] = dict() return handle_list
def get_result(self, handle: ResultHandle, **kwargs: KwargTypes) -> BackendResult: try: return super().get_result(handle) except CircuitNotRunError: jobid, _ = handle try: job: "AerJob" = self._cache[handle]["job"] except KeyError: raise CircuitNotRunError(handle) res = job.result() backresults = qiskit_result_to_backendresult(res) for circ_index, backres in enumerate(backresults): self._cache[ResultHandle(jobid, circ_index)]["result"] = backres return cast(BackendResult, self._cache[handle]["result"])
def get_result(self, handle: ResultHandle, **kwargs: KwargTypes) -> BackendResult: if handle in self._cache: if "result" in self._cache[handle]: return cast(BackendResult, self._cache[handle]["result"]) self._check_handle_type(handle) try: job: "AerJob" = self._cache[handle]["job"] except KeyError: raise CircuitNotRunError(handle) res = job.result() backresults = qiskit_result_to_backendresult(res) for circ_index, backres in enumerate(backresults): newhandle = ResultHandle(handle[0], circ_index) if "implicit_perm_qubits" in self._cache[newhandle]: permed_qbit_map: Dict[ Qubit, Qubit] = self._cache[newhandle]["implicit_perm_qubits"] original_indexmap = backres.q_bits.copy() assert original_indexmap # Simultaneous permutation of inputs and outputs of process # Handles implicit permutation of outputs for statevector backres.q_bits = { permed_qbit_map[qb]: index for qb, index in original_indexmap.items() } if backres._unitary is not None: # For unitaries, the implicit permutation # should only be applied to inputs # The above relabelling will permute both inputs and outputs # Correct by applying the inverse # permutation on the inputs (i.e. a column permutation) permutation = [0] * len(original_indexmap) for qb, index in original_indexmap.items(): permutation[index] = original_indexmap[ permed_qbit_map[qb]] backres._unitary = permute_basis_indexing( backres._unitary.T, tuple(permutation)).T self._cache[newhandle]["result"] = backres return cast(BackendResult, self._cache[handle]["result"])
def process_circuits( self, circuits: Iterable[Circuit], n_shots: Optional[int] = None, valid_check: bool = True, **kwargs: KwargTypes, ) -> List[ResultHandle]: self._simulator = self.set_simulator(seed=kwargs.get("seed")) circuit_list = list(circuits) if valid_check: self._check_all_circuits(circuit_list) handle_list = [] for i, circuit in enumerate(circuit_list): handle = ResultHandle(str(uuid4()), i) handle_list.append(handle) backres = self.run_circuit(circuit, n_shots=n_shots) self._cache[handle] = {"result": backres} return handle_list
def process_circuits_moments( self, circuits: Iterable[Circuit], valid_check: bool = True, **kwargs: KwargTypes, ) -> List[ResultHandle]: """ Submit circuits to the backend for running. The results will be stored in the backend's result cache to be retrieved by the corresponding get_<data> method. The get_<data> method will return List[BackendResult] corresponding to each moment. Use keyword arguments to specify parameters to be used in submitting circuits: * `seed`: RNG seed for simulators :param circuits: Circuits to process on the backend. :type circuits: Iterable[Circuit] :param valid_check: Explicitly check that all circuits satisfy all required predicates to run on the backend. Defaults to True :type valid_check: bool, optional :return: Handles to results for each input circuit, as an interable in the same order as the circuits. :rtype: List[ResultHandle] """ self._simulator = self.set_simulator(seed=kwargs.get("seed")) circuit_list = list(circuits) if valid_check: self._check_all_circuits(circuit_list) handle_list = [] for i, circuit in enumerate(circuit_list): circuit.remove_blank_wires() handle = ResultHandle(str(uuid4()), i) handle_list.append(handle) backres = self.run_circuit_moments(circuit) self._cache[handle] = {"result": backres} # type: ignore return handle_list
def process_circuits( self, circuits: Iterable[Circuit], n_shots: Optional[int] = None, valid_check: bool = True, **kwargs: KwargTypes, ) -> List[ResultHandle]: """ See :py:meth:`pytket.backends.Backend.process_circuits`. Supported kwargs: `seed`. """ if n_shots is None or n_shots < 1: raise ValueError( "Parameter n_shots is required for this backend for ForestBackend" ) if valid_check: self._check_all_circuits(circuits) handle_list = [] for circuit in circuits: p, bits = tk_to_pyquil(circuit, return_used_bits=True) p.wrap_in_numshots_loop(n_shots) ex = self._qc.compiler.native_quil_to_executable(p) qam = copy(self._qc.qam) qam.load(ex) qam.random_seed = kwargs.get("seed") # type: ignore qam.run() handle = ResultHandle(uuid4().int) measures = circuit.n_gates_of_type(OpType.Measure) if measures == 0: self._cache[handle] = { "qam": qam, "c_bits": sorted(bits), "result": self.empty_result(circuit, n_shots=n_shots), } else: self._cache[handle] = {"qam": qam, "c_bits": sorted(bits)} handle_list.append(handle) return handle_list
def get_result(self, handle: ResultHandle, **kwargs: KwargTypes) -> BackendResult: """ See :py:meth:`pytket.backends.Backend.get_result`. Supported kwargs: `timeout`, `wait`. """ try: return super().get_result(handle) except CircuitNotRunError: jobid = cast(str, handle[0]) index = cast(int, handle[1]) if self._MACHINE_DEBUG or jobid.startswith(_DEBUG_HANDLE_PREFIX): shots: int n_qubits: int n_qubits, shots = literal_eval( jobid[len(_DEBUG_HANDLE_PREFIX):]) res = _gen_debug_results(n_qubits, shots, index) else: try: job = self._retrieve_job(jobid) except IBMQBackendApiError: raise CircuitNotRunError(handle) if self._monitor and job: job_monitor(job) newkwargs = { key: kwargs[key] for key in ("wait", "timeout") if key in kwargs } res = job.result(**newkwargs) backresults = list(qiskit_result_to_backendresult(res)) self._cache.update((ResultHandle(jobid, circ_index), { "result": backres }) for circ_index, backres in enumerate(backresults)) return cast(BackendResult, self._cache[handle]["result"])
def process_circuits( self, circuits: Iterable[Circuit], n_shots: Optional[int] = None, valid_check: bool = True, **kwargs: KwargTypes, ) -> List[ResultHandle]: handle_list = [] if valid_check: self._check_all_circuits(circuits) for circuit in circuits: p = tk_to_pyquil(circuit) for qb in circuit.qubits: # Qubits with no gates will not be included in the Program # Add identities to ensure all qubits are present and dimension # is as expected p += I(Qubit_(qb.index[0])) handle = ResultHandle(uuid4().int) state = np.array(self._sim.wavefunction(p).amplitudes) try: phase = float(circuit.phase) coeff = np.exp(phase * np.pi * 1j) state *= coeff except ValueError: warning( "Global phase is dependent on a symbolic parameter, so cannot " "adjust for phase") implicit_perm = circuit.implicit_qubit_permutation() res_qubits = [ implicit_perm[qb] for qb in sorted(circuit.qubits, reverse=True) ] res = BackendResult(q_bits=res_qubits, state=state) self._cache[handle] = {"result": res} handle_list.append(handle) return handle_list
def process_circuits( self, circuits: Iterable[Circuit], n_shots: Optional[int] = None, valid_check: bool = True, **kwargs: KwargTypes, ) -> List[ResultHandle]: """ See :py:meth:`pytket.backends.Backend.process_circuits`. Supported kwargs: none. """ if valid_check: self._check_all_circuits(circuits, nomeasure_warn=False) handles = [] for c in circuits: qs = tk_to_qsharp(c) qc = qscompile(qs) results = self._calculate_results(qc, n_shots) handle = ResultHandle(str(uuid4())) key = "result" if isinstance(results, BackendResult) else "resource" self._cache.update({handle: {key: results}}) handles.append(handle) return handles