def __init__(self, qlm_batch, async_job): """ Args: qlm_batch: :class:`~qat.core.Batch` or :class:`~qat.core.Job` object. If a QLM Job object is given, it will be converted in a QLM Batch object async_job: Qiskit job instance derived from BaseJob. Result of a previous asynchronous execution of qlm_batch """ self._job_id = async_job.job_id() self._handler = async_job if isinstance(qlm_batch, Job): self._qlm_batch = Batch(jobs=[qlm_batch]) else: self._qlm_batch = qlm_batch
def submit(self, qlm_batch): """ Submits a QLM batch of jobs and returns the corresponding QiskitJob. Args: qlm_batch: :class:`~qat.core.Batch` or :class:`~qat.core.Job`. If a single job is provided, a batch is created from this job. Returns: :class:`~qat.interop.qiskit.QiskitJob` object with the same interface as a job derived from JobV1 for the user to have information on their job execution """ if self.backend is None: raise ValueError("Backend cannot be None") if isinstance(qlm_batch, Job): qlm_batch = Batch(jobs=[qlm_batch]) qiskit_circuits = [] for qlm_job in qlm_batch.jobs: qiskit_circuit = job_to_qiskit_circuit(qlm_job) qiskit_circuits.append(qiskit_circuit) async_job = execute(qiskit_circuits, self.backend, shots=qlm_batch.jobs[0].nbshots or self.backend.configuration().max_shots, coupling_map=None) return QiskitJob(qlm_batch, async_job, self.backend.configuration().max_shots)
def _submit_batch(self, qlm_batch): """ Submits a Batch object to execute on a Qiskit backend. Args: qlm_batch: Returns: A QLM BatchResult object """ if self.backend is None: raise ValueError("Backend cannot be None") if isinstance(qlm_batch, Job): qlm_batch = Batch(jobs=[qlm_batch]) qiskit_circuits = [] for qlm_job in qlm_batch.jobs: qiskit_circuit = job_to_qiskit_circuit(qlm_job) qiskit_circuits.append(qiskit_circuit) qiskit_result = execute( qiskit_circuits, self.backend, shots=qlm_batch.jobs[0].nbshots or self.backend.configuration().max_shots, coupling_map=None, optimization_level=self.optimization_level).result() results = generate_qlm_list_results(qiskit_result) new_results = [] for result in results: new_results.append(WResult.from_thrift(result)) return _wrap_results(qlm_batch, new_results, self.backend.configuration().max_shots)
def __init__(self, qlm_batch, async_job, max_shots): """ Args: qlm_batch: :class:`~qat.core.Batch` or :class:`~qat.core.Job` object. If a QLM Job object is given, it will be converted in a QLM Batch object async_job: Qiskit job instance derived from JobV1. Result of a previous asynchronous execution of qlm_batch max_shots: Maximal number of shots allowed by the Backend """ self._job_id = async_job.job_id() self._handler = async_job self._max_shots = max_shots if isinstance(qlm_batch, Job): self._qlm_batch = Batch(jobs=[qlm_batch]) else: self._qlm_batch = qlm_batch
def run(self, run_input, **kwargs): """ Convert all the circuits inside qobj into a Batch of QLM jobs before sending them into a QLM qpu. Args: run_input (list or QuantumCircuit or Schedule) kwargs: any option that can replace the default ones Returns: Returns a :class:`~qat.interop.qiskit.QLMJob` object containing the results of the QLM qpu execution after being converted into Qiskit results """ if self._qpu is None: raise NoQpuAttached("No qpu attached to the QLM connector.") circuits = run_input if isinstance(run_input, list) else [run_input] for kwarg in kwargs: if not hasattr(self.options, kwarg): raise ValueError(f"'{kwarg}' parameter not supported") nbshots = kwargs.get('shots', self.options.shots) qobj_id = kwargs.get('qobj_id', self.options.qobj_id) qobj_header = kwargs.get('qobj_header', self.options.qobj_header) # TODO: use parameter_binds for constructing the job # this involves not only iterating on the experiments # but also iterating on the parameter sets so provided qlm_task = Batch(jobs=[]) circuits_metadata = [] for circuit in circuits: qlm_circuit = qiskit_to_qlm(circuit) circuits_metadata.append({ 'n_qubits': qlm_circuit.nbqbits, 'memory_slots': qlm_circuit.nbqbits }) job = qlm_circuit.to_job(aggregate_data=False) job.nbshots = nbshots job.qubits = list(range(0, qlm_circuit.nbqbits)) qlm_task.jobs.append(job) results = self._qpu.submit(qlm_task) if not isinstance(results, (BatchResult, WResult)): results = results.join() for res in results: for sample in res.raw_data: sample.intermediate_measures = None # Creating a job that will contain the results job = QLMJob(self, str(self.id_counter)) self.id_counter += 1 job.set_results(results, qobj_id, circuits_metadata, qobj_header) return job
def retrieve_job(self, file_name): """ Retrieves a QiskitJob from a binary file in which the QLM Batch object - from which the QiskitJob has been created - has been dumped. Args: file_name: Name of the binary file Returns: :class:`~qat.interop.qiskit.providers.QiskitJob` object """ qlm_batch = Batch.load(file_name) async_job = self.backend.retrieve_job(qlm_batch.meta_data['job_id']) return QiskitJob(qlm_batch, async_job)
def test_all_to_all_topology(self) -> None: """ Checks that Sabre doesn't modify the circuit if the topology is of type ALL_TO_ALL. """ for nbqbit in range(min_nbqbit, max_nbqbit): circuit = generate_random_circuit(nbqbit) sabre = Sabre() batch = Batch(jobs=[circuit.to_job()]) hardware_specs = HardwareSpecs() batch_result = sabre.compile(batch, hardware_specs) computed_circuit = batch_result.jobs[0].circuit check_circuits_equality(circuit, computed_circuit)
def run(self, qobj): """ Convert all the circuits inside qobj into a Batch of QLM jobs before sending them into a QLM qpu. Args: qobj: Qiskit batch of circuits to run Returns: Returns a :class:`~qat.interop.qiskit.providers.QLMJob` object containing the results of the QLM qpu execution after being converted into Qiskit results """ if self._qpu is None: raise NoQpuAttached("No qpu attached to the QLM connector.") headers = [exp.header.to_dict() for exp in qobj.experiments] circuits = disassemble(qobj)[0] nbshots = qobj.config.shots qlm_task = Batch(jobs=[]) for circuit in circuits: qlm_circuit = qiskit_to_qlm(circuit) job = qlm_circuit.to_job(aggregate_data=False) job.nbshots = nbshots job.qubits = list(range(0, qlm_circuit.nbqbits)) qlm_task.jobs.append(job) results = self._qpu.submit(qlm_task) for res in results: for sample in res.raw_data: sample.intermediate_measures = None res = aggregate_data(res) # Creating a job that will contain the results job = QLMJob(self, str(self.id_counter)) self.id_counter += 1 job.set_results(results, qobj.qobj_id, headers) return job
def test_already_executable_circuit(self) -> None: """ Tests Sabre on a fully executable circuit which means all gates are applied on qubits which are connected on the hardware. """ for nbqbit in range(min_nbqbit, max_nbqbit): prog = Program() qbits = prog.qalloc(nbqbit) for i in range(len(qbits) - 1): prog.apply(H, qbits[i]) prog.apply(Z, qbits[i]) prog.apply(X.ctrl(), qbits[i + 1], qbits[i]) circuit = prog.to_circ(inline=True) sabre = Sabre() batch = Batch(jobs=[circuit.to_job()]) hardware_specs = HardwareSpecs() batch_result = sabre.compile(batch, hardware_specs) computed_circuit = batch_result.jobs[0].circuit check_circuits_equality(circuit, computed_circuit)
class QiskitJob: """ Wrapper around Qiskit's asynchronous jobs. """ def __init__(self, qlm_batch, async_job, max_shots): """ Args: qlm_batch: :class:`~qat.core.Batch` or :class:`~qat.core.Job` object. If a QLM Job object is given, it will be converted in a QLM Batch object async_job: Qiskit job instance derived from JobV1. Result of a previous asynchronous execution of qlm_batch max_shots: Maximal number of shots allowed by the Backend """ self._job_id = async_job.job_id() self._handler = async_job self._max_shots = max_shots if isinstance(qlm_batch, Job): self._qlm_batch = Batch(jobs=[qlm_batch]) else: self._qlm_batch = qlm_batch def job_id(self): """ Returns the job's ID. """ return self._job_id def status(self): """ Returns the job status. """ return self._handler.status()._name_ def result(self): """ Returns the result if available. Returns: :class:`~qat.core.Result` object or :class:`~qat.core.BatchResult` object if the batch submitted contains several jobs """ if self.status() == 'DONE': results = generate_qlm_list_results(self._handler.result()) new_results = [] for result in results: new_results.append(WResult.from_thrift(result)) batch_result = _wrap_results(self._qlm_batch, new_results, self._max_shots) if not batch_result.results or len(batch_result.results) == 1: return batch_result.results[0] return batch_result return None def cancel(self): """ Attempts to cancel the job. Returns: Boolean indicating whether the attempt was successful or not """ ret = self._handler.cancel() if ret: print("job successefully cancelled") return True print("Unable to cancel job") return False def dump(self, file_name): """ Dumps the :class:`~qat.core.Batch` object used for creating the job into a binary file. This file should later be used with AsyncBackendToQPU's :func:`~qat.interop.qiskit.AsyncBackendToQPU.retrieve_job`. Args: file_name: Name of the binary file to create """ if isinstance(self._qlm_batch.meta_data, dict): self._qlm_batch.meta_data['job_id'] = self._job_id else: self._qlm_batch.meta_data = {'job_id': self._job_id} self._qlm_batch.dump(file_name)