def execute(self, circuits): """ A wrapper for all algorithms to interface with quantum backend. Args: circuits (QuantumCircuit or list[QuantumCircuit]): circuits to execute Returns: Result or [Result]: Result objects it will be a list if number of circuits exceed the maximum number (300) """ if not isinstance(circuits, list): circuits = [circuits] jobs = [] chunks = int(np.ceil(len(circuits) / self.MAX_CIRCUITS_PER_JOB)) for i in range(chunks): sub_circuits = circuits[i * self.MAX_CIRCUITS_PER_JOB:(i + 1) * self.MAX_CIRCUITS_PER_JOB] jobs.append( q_execute(sub_circuits, self._backend, **self._execute_config)) if logger.isEnabledFor(logging.DEBUG): logger.debug(summarize_circuits(circuits)) results = [] for job in jobs: results.append(job.result(**self._qjob_config)) result = functools.reduce(lambda x, y: x + y, results) return result
def run_circuits(circuits, backend, execute_config, qjob_config={}, show_circuit_summary=False, has_shared_circuits=False): """ An execution wrapper with Qiskit-Terra, with job auto recover capability. The autorecovery feature is only applied for non-simulator backend. This wraper will try to get the result no matter how long it costs. Args: circuits (QuantumCircuit or list[QuantumCircuit]): circuits to execute backend (BaseBackend): backend instance execute_config (dict): settings for qiskit execute (or compile) qjob_config (dict): settings for job object, like timeout and wait show_circuit_summary (bool): showing the summary of submitted circuits. has_shared_circuits (bool): use the 0-th circuits as initial state for other circuits. Returns: Result: Result object Raises: AlgorithmError: Any error except for JobError raised by Qiskit Terra """ if backend is None or not isinstance(backend, BaseBackend): raise AlgorithmError('Backend is missing or not an instance of BaseBackend') if not isinstance(circuits, list): circuits = [circuits] if backend.configuration().get('name', '').startswith('statevector'): circuits = _avoid_empty_circuits(circuits) if has_shared_circuits and version.parse(qiskit.__version__) > version.parse('0.6.1'): return _reuse_shared_circuits(circuits, backend, execute_config, qjob_config) with_autorecover = False if backend.configuration()['simulator'] else True max_circuits_per_job = sys.maxsize if backend.configuration()['local'] \ else MAX_CIRCUITS_PER_JOB qobjs = [] jobs = [] chunks = int(np.ceil(len(circuits) / max_circuits_per_job)) for i in range(chunks): sub_circuits = circuits[i * max_circuits_per_job:(i + 1) * max_circuits_per_job] qobj = q_compile(sub_circuits, backend, **execute_config) job = backend.run(qobj) jobs.append(job) qobjs.append(qobj) if logger.isEnabledFor(logging.DEBUG) and show_circuit_summary: logger.debug(summarize_circuits(circuits)) results = [] if with_autorecover: logger.debug("There are {} circuits and they are chunked into {} chunks, " "each with {} circutis.".format(len(circuits), chunks, max_circuits_per_job)) for idx in range(len(jobs)): job = jobs[idx] job_id = job.job_id() logger.info("Running {}-th chunk circuits, job id: {}".format(idx, job_id)) while True: try: result = job.result(**qjob_config) if result.status == 'COMPLETED': results.append(result) logger.info("COMPLETED the {}-th chunk of circuits, " "job id: {}".format(idx, job_id)) break else: logger.warning("FAILURE: the {}-th chunk of circuits, " "job id: {}".format(idx, job_id)) except JobError as e: # if terra raise any error, which means something wrong, re-run it logger.warning("FAILURE: the {}-th chunk of circuits, job id: {}, " "Terra job error: {} ".format(idx, job_id, e)) except Exception as e: raise AlgorithmError("FAILURE: the {}-th chunk of circuits, job id: {}, " "Terra unknown error: {} ".format(idx, job_id, e)) from e # keep querying the status until it is okay. while True: try: job_status = job.status() break except JobError as e: logger.warning("FAILURE: job id: {}, " "status: 'FAIL_TO_GET_STATUS' " "Terra job error: {}".format(job_id, e)) time.sleep(5) except Exception as e: raise AlgorithmError("FAILURE: job id: {}, " "status: 'FAIL_TO_GET_STATUS' " "({})".format(job_id, e)) from e logger.info("Job status: {}".format(job_status)) # when reach here, it means the job fails. let's check what kinds of failure it is. if job_status == JobStatus.DONE: logger.info("Job ({}) is completed anyway, retrieve result " "from backend.".format(job_id)) job = backend.retrieve_job(job_id) elif job_status == JobStatus.RUNNING or job_status == JobStatus.QUEUED: logger.info("Job ({}) is {}, but encounter an exception, " "recover it from backend.".format(job_id, job_status)) job = backend.retrieve_job(job_id) else: logger.info("Fail to run Job ({}), resubmit it.".format(job_id)) qobj = qobjs[idx] job = backend.run(qobj) else: results = [] for job in jobs: results.append(job.result(**qjob_config)) if len(results) != 0: result = functools.reduce(lambda x, y: x + y, results) else: result = None return result
def compile_and_run_circuits(circuits, backend, backend_config, compile_config, run_config, qjob_config=None, backend_options=None, noise_config=None, show_circuit_summary=False, has_shared_circuits=False): """ An execution wrapper with Qiskit-Terra, with job auto recover capability. The autorecovery feature is only applied for non-simulator backend. This wraper will try to get the result no matter how long it costs. Args: circuits (QuantumCircuit or list[QuantumCircuit]): circuits to execute backend (BaseBackend): backend instance backend_config (dict): configuration for backend compile_config (dict): configuration for compilation run_config (dict): configuration for running a circuit qjob_config (dict): configuration for quantum job object backend_options (dict): configuration for simulator noise_config (dict): configuration for noise model show_circuit_summary (bool): showing the summary of submitted circuits. has_shared_circuits (bool): use the 0-th circuits as initial state for other circuits. Returns: Result: Result object Raises: AquaError: Any error except for JobError raised by Qiskit Terra """ qjob_config = qjob_config or {} backend_options = backend_options or {} noise_config = noise_config or {} if backend is None or not isinstance(backend, BaseBackend): raise ValueError( 'Backend is missing or not an instance of BaseBackend') if not isinstance(circuits, list): circuits = [circuits] if 'statevector' in backend.name(): circuits = _avoid_empty_circuits(circuits) if has_shared_circuits: return _reuse_shared_circuits(circuits, backend, backend_config, compile_config, run_config, qjob_config, backend_options) with_autorecover = False if backend.configuration().simulator else True if MAX_CIRCUITS_PER_JOB is not None: max_circuits_per_job = int(MAX_CIRCUITS_PER_JOB) else: if backend.configuration().local: max_circuits_per_job = sys.maxsize else: max_circuits_per_job = backend.configuration().max_experiments qobjs = [] jobs = [] job_ids = [] chunks = int(np.ceil(len(circuits) / max_circuits_per_job)) for i in range(chunks): sub_circuits = circuits[i * max_circuits_per_job:(i + 1) * max_circuits_per_job] qobj = q_compile(sub_circuits, backend, **backend_config, **compile_config, **run_config) # assure get job ids while True: job = backend.run(qobj, **backend_options, **noise_config) try: job_id = job.job_id() break except JobError as e: logger.warning( "FAILURE: the {}-th chunk of circuits, can not get job id, " "Resubmit the qobj to get job id. " "Terra job error: {} ".format(i, e)) except Exception as e: logger.warning( "FAILURE: the {}-th chunk of circuits, can not get job id, " "Resubmit the qobj to get job id. " "Error: {} ".format(i, e)) job_ids.append(job_id) jobs.append(job) qobjs.append(qobj) if logger.isEnabledFor(logging.DEBUG) and show_circuit_summary: logger.debug(summarize_circuits(circuits)) results = [] if with_autorecover: logger.info("Backend status: {}".format(backend.status())) logger.info( "There are {} circuits and they are chunked into {} chunks, " "each with {} circutis (max.).".format(len(circuits), chunks, max_circuits_per_job)) logger.info("All job ids:\n{}".format(job_ids)) for idx in range(len(jobs)): while True: job = jobs[idx] job_id = job_ids[idx] logger.info("Running {}-th chunk circuits, job id: {}".format( idx, job_id)) # try to get result if possible try: result = job.result(**qjob_config) if result.success: results.append(result) logger.info("COMPLETED the {}-th chunk of circuits, " "job id: {}".format(idx, job_id)) break else: logger.warning("FAILURE: the {}-th chunk of circuits, " "job id: {}".format(idx, job_id)) except JobError as e: # if terra raise any error, which means something wrong, re-run it logger.warning( "FAILURE: the {}-th chunk of circuits, job id: {} " "Terra job error: {} ".format(idx, job_id, e)) except Exception as e: raise AquaError( "FAILURE: the {}-th chunk of circuits, job id: {} " "Unknown error: {} ".format(idx, job_id, e)) from e # something wrong here, querying the status to check how to handle it. # keep qeurying it until getting the status. while True: try: job_status = job.status() break except JobError as e: logger.warning("FAILURE: job id: {}, " "status: 'FAIL_TO_GET_STATUS' " "Terra job error: {}".format(job_id, e)) time.sleep(5) except Exception as e: raise AquaError("FAILURE: job id: {}, " "status: 'FAIL_TO_GET_STATUS' " "Unknown error: ({})".format( job_id, e)) from e logger.info("Job status: {}".format(job_status)) # handle the failure job based on job status if job_status == JobStatus.DONE: logger.info( "Job ({}) is completed anyway, retrieve result " "from backend.".format(job_id)) job = backend.retrieve_job(job_id) elif job_status == JobStatus.RUNNING or job_status == JobStatus.QUEUED: logger.info("Job ({}) is {}, but encounter an exception, " "recover it from backend.".format( job_id, job_status)) job = backend.retrieve_job(job_id) else: logger.info( "Fail to run Job ({}), resubmit it.".format(job_id)) qobj = qobjs[idx] # assure job get its id while True: job = backend.run(qobj, **backend_options, **noise_config) try: job_id = job.job_id() break except JobError as e: logger.warning( "FAILURE: the {}-th chunk of circuits, " "can not get job id. Resubmit the qobj to get job id. " "Terra job error: {} ".format(idx, e)) except Exception as e: logger.warning( "FAILURE: the {}-th chunk of circuits, " "can not get job id, Resubmit the qobj to get job id. " "Unknown error: {} ".format(idx, e)) jobs[idx] = job job_ids[idx] = job_id else: results = [] for job in jobs: results.append(job.result(**qjob_config)) result = _combine_result_objects(results) if len(results) != 0 else None return result
def run_circuits(circuits, backend, execute_config, qjob_config={}, max_circuits_per_job=sys.maxsize, show_circuit_summary=False): """ An execution wrapper with Qiskit-Terra, with job auto recover capability. The autorecovery feature is only applied for non-simulator backend. This wraper will try to get the result no matter how long it costs. Args: circuits (QuantumCircuit or list[QuantumCircuit]): circuits to execute backend (str): name of backend execute_config (dict): settings for qiskit execute (or compile) qjob_config (dict): settings for job object, like timeout and wait max_circuits_per_job (int): the maximum number of job, default is unlimited but 300 is limited if you submit to a remote backend show_circuit_summary (bool): showing the summary of submitted circuits. Returns: Result: Result object Raises: AlgorithmError: Any error except for JobError raised by Qiskit Terra """ if not isinstance(circuits, list): circuits = [circuits] my_backend = get_backend(backend) with_autorecover = False if my_backend.configuration( )['simulator'] else True qobjs = [] jobs = [] chunks = int(np.ceil(len(circuits) / max_circuits_per_job)) for i in range(chunks): sub_circuits = circuits[i * max_circuits_per_job:(i + 1) * max_circuits_per_job] qobj = q_compile(sub_circuits, my_backend, **execute_config) job = my_backend.run(qobj) jobs.append(job) qobjs.append(qobj) if logger.isEnabledFor(logging.DEBUG) and show_circuit_summary: logger.debug(summarize_circuits(circuits)) results = [] if with_autorecover: logger.info("There are {} circuits and they are chunked into " "{} chunks, each with {} circutis.".format( len(circuits), chunks, max_circuits_per_job)) for idx in range(len(jobs)): job = jobs[idx] job_id = job.id() logger.info("Running {}-th chunk circuits, job id: {}".format( idx, job_id)) while True: try: result = job.result(**qjob_config) if result.status == 'COMPLETED': results.append(result) logger.info( "COMPLETED the {}-th chunk of circuits, job id: {}" .format(idx, job_id)) break else: logger.warning( "FAILURE: the {}-th chunk of circuits, job id: {}". format(idx, job_id)) except JobError as e: # if terra raise any error, which means something wrong, re-run it logger.warning( "FAILURE: the {}-th chunk of circuits, job id: {}, " "Terra job error: {} ".format(idx, job_id, e)) except Exception as e: raise AlgorithmError( "FAILURE: the {}-th chunk of circuits, job id: {}, " "Terra unknown error: {} ".format(idx, job_id, e)) from e # keep querying the status until it is okay. while True: try: job_status = job.status() break except JobError as e: logger.warning( "FAILURE: job id: {}, " "status: 'FAIL_TO_GET_STATUS' Terra job error: {}". format(job_id, e)) time.sleep(5) except Exception as e: raise AlgorithmError( "FAILURE: job id: {}, " "status: 'FAIL_TO_GET_STATUS' ({})".format( job_id, e)) from e logger.info("Job status: {}".format(job_status)) # when reach here, it means the job fails. let's check what kinds of failure it is. if job_status == JobStatus.DONE: logger.info( "Job ({}) is completed anyway, retrieve result from backend." .format(job_id)) job = my_backend.retrieve_job(job_id) elif job_status == JobStatus.RUNNING or job_status == JobStatus.QUEUED: logger.info("Job ({}) is {}, but encounter an exception, " "recover it from backend.".format( job_id, job_status)) job = my_backend.retrieve_job(job_id) else: logger.info( "Fail to run Job ({}), resubmit it.".format(job_id)) qobj = qobjs[idx] job = my_backend.run(qobj) else: results = [] for job in jobs: results.append(job.result(**qjob_config)) if len(results) != 0: result = functools.reduce(lambda x, y: x + y, results) else: result = None return result