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, circuit_cache=None, skip_qobj_validation=False, **kwargs): """ 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 (RunConfig): 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 if circuit_cache is not None and circuit_cache.try_reusing_qobjs: # Check if all circuits are the same length. If not, don't try to use the same qobj.experiment for all of them. if len(set([len(circ.data) for circ in circuits])) > 1: circuit_cache.try_reusing_qobjs = False else: # Try setting up the reusable qobj # Compile and cache first circuit if cache is empty. The load method will try to reuse it if circuit_cache.try_reusing_qobjs and circuit_cache.qobjs is None: qobj = q_compile([circuits[0]], backend, **execute_config) circuit_cache.cache_circuit(qobj, [circuits[0]], 0) 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] if circuit_cache is not None and circuit_cache.misses < circuit_cache.allowed_misses: try: qobj = circuit_cache.load_qobj_from_cache( sub_circuits, i, run_config=run_config) # cache miss, fail gracefully except (TypeError, IndexError, FileNotFoundError, EOFError, AquaError, AttributeError) as e: circuit_cache.try_reusing_qobjs = False # Reusing Qobj didn't work circuit_cache.clear_cache() logger.debug( 'Circuit cache miss, recompiling. Cache miss reason: ' + repr(e)) qobj = q_compile(sub_circuits, backend, **backend_config, **compile_config, **run_config.to_dict()) circuit_cache.cache_circuit(qobj, sub_circuits, i) circuit_cache.misses += 1 else: qobj = q_compile(sub_circuits, backend, **backend_config, **compile_config, **run_config.to_dict()) if 'expectation' in kwargs: from qiskit.providers.aer.utils.qobj_utils import snapshot_instr, append_instr # add others, how to derive the correct used number of qubits? # the compiled qobj could be wrong if coupling map is used. # if mulitple params are provided, we assume that each circuit is corresponding one param # otherwise, params are used for all circuits. params = kwargs['expectation']['params'] num_qubits = kwargs['expectation']['num_qubits'] if len(params) == 1: new_ins = snapshot_instr('expectation_value_pauli', 'test', range(num_qubits), params=params[0]) for ii in range(len(sub_circuits)): qobj = append_instr(qobj, ii, new_ins) else: for ii in range(len(sub_circuits)): new_ins = snapshot_instr('expectation_value_pauli', 'test', range(num_qubits), params=params[ii]) qobj = append_instr(qobj, ii, new_ins) # assure get job ids while True: job = run_on_backend(backend, qobj, backend_options=backend_options, noise_config=noise_config, skip_qobj_validation=skip_qobj_validation) 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 = run_on_backend( backend, qobj, backend_options=backend_options, noise_config=noise_config, skip_qobj_validation=skip_qobj_validation) 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 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