def update_backend_schema(self, input_parser): """ Updates backend schema """ if JSONSchema.BACKEND not in self._schema['properties']: return # Updates defaults provider/backend default_provider_name = None default_backend_name = None orig_backend_properties = self._original_schema.get( 'properties', {}).get(JSONSchema.BACKEND, {}).get('properties') if orig_backend_properties is not None: default_provider_name = orig_backend_properties.get( JSONSchema.PROVIDER, {}).get('default') default_backend_name = orig_backend_properties.get( JSONSchema.NAME, {}).get('default') providers = get_local_providers() if default_provider_name is None or default_provider_name not in providers: # use first provider available providers_items = providers.items() provider_tuple = next( iter(providers_items)) if len(providers_items) > 0 else ('', []) default_provider_name = provider_tuple[0] if default_backend_name is None or default_backend_name not in providers.get( default_provider_name, []): # use first backend available in provider default_backend_name = providers.get( default_provider_name)[0] if len( providers.get(default_provider_name, [])) > 0 else '' self._schema['properties'][JSONSchema.BACKEND] = { 'type': 'object', 'properties': { JSONSchema.PROVIDER: { 'type': 'string', 'default': default_provider_name }, JSONSchema.NAME: { 'type': 'string', 'default': default_backend_name }, }, 'required': [JSONSchema.PROVIDER, JSONSchema.NAME], 'additionalProperties': False, } provider_name = input_parser.get_section_property( JSONSchema.BACKEND, JSONSchema.PROVIDER, default_provider_name) backend_names = get_backends_from_provider(provider_name) backend_name = input_parser.get_section_property( JSONSchema.BACKEND, JSONSchema.NAME, default_backend_name) if backend_name not in backend_names: # use first backend available in provider backend_name = backend_names[0] if len(backend_names) > 0 else '' backend = get_backend_from_provider(provider_name, backend_name) config = backend.configuration() # Include shots in schema only if not a statevector backend. # For statevector, shots will be set to 1, in QiskitAqua if not is_statevector_backend(backend): self._schema['properties'][ JSONSchema.BACKEND]['properties']['shots'] = { 'type': 'integer', 'minimum': 1, } default_shots = 1024 # ensure default_shots <= max_shots if config.max_shots: default_shots = min(default_shots, config.max_shots) self._schema['properties'][JSONSchema.BACKEND]['properties'][ 'shots']['maximum'] = config.max_shots self._schema['properties'][JSONSchema.BACKEND]['properties'][ 'shots']['default'] = default_shots self._schema['properties'][ JSONSchema.BACKEND]['properties']['skip_transpiler'] = { 'type': 'boolean', 'default': False, } coupling_map_devices = [] noise_model_devices = [] check_coupling_map = is_simulator_backend(backend) check_noise_model = is_aer_provider( backend) and not is_aer_statevector_backend(backend) try: if (check_coupling_map or check_noise_model) and has_ibmq(): backend_names = get_backends_from_provider('qiskit.IBMQ') for backend_name in backend_names: ibmq_backend = get_backend_from_provider( 'qiskit.IBMQ', backend_name) if is_simulator_backend(ibmq_backend): continue if check_noise_model: noise_model_devices.append('qiskit.IBMQ:' + backend_name) if check_coupling_map and ibmq_backend.configuration( ).coupling_map: coupling_map_devices.append('qiskit.IBMQ:' + backend_name) except Exception as e: logger.debug("Failed to load IBMQ backends. Error {}".format( str(e))) # Includes 'coupling map' and 'coupling_map_from_device' in schema only if a simulator backend. # Actual devices have a coupling map based on the physical configuration of the device. # The user can configure the coupling map so its the same as the coupling map # of a given device in order to better simulate running on the device. # Property 'coupling_map_from_device' is a list of provider:name backends that are # real devices e.g qiskit.IBMQ:ibmqx5. # If property 'coupling_map', an array, is provided, it overrides coupling_map_from_device, # the latter defaults to 'None'. So in total no coupling map is a default, i.e. all to all coupling is possible. if is_simulator_backend(backend): self._schema['properties'][ JSONSchema.BACKEND]['properties']['coupling_map'] = { 'type': ['array', 'null'], 'default': None, } if len(coupling_map_devices) > 0: coupling_map_devices.append(None) self._schema['properties'][JSONSchema.BACKEND]['properties'][ 'coupling_map_from_device'] = { 'type': ['string', 'null'], 'default': None, 'oneOf': [{ 'enum': coupling_map_devices }], } # noise model that can be setup for Aer simulator so as to model noise of an actual device. if len(noise_model_devices) > 0: noise_model_devices.append(None) self._schema['properties'][ JSONSchema.BACKEND]['properties']['noise_model'] = { 'type': ['string', 'null'], 'default': None, 'oneOf': [{ 'enum': noise_model_devices }], } # If a noise model is supplied then the basis gates is set as per the noise model # unless basis gates is not None in which case it overrides noise model and a warning msg is logged. # as it is an advanced use case. self._schema['properties'][ JSONSchema.BACKEND]['properties']['basis_gates'] = { 'type': ['array', 'null'], 'default': None, } # TODO: Not sure if we want to continue with initial_layout in declarative form. # It requires knowledge of circuit registers etc. Perhaps its best to leave this detail to programming API. self._schema['properties'][ JSONSchema.BACKEND]['properties']['initial_layout'] = { 'type': ['object', 'null'], 'default': None, } # The same default and minimum as current RunConfig values self._schema['properties'][ JSONSchema.BACKEND]['properties']['max_credits'] = { 'type': 'integer', 'default': 10, 'minimum': 3, 'maximum': 10, } # Timeout and wait are for remote backends where we have to connect over network if not is_local_backend(backend): self._schema['properties'][ JSONSchema.BACKEND]['properties']['timeout'] = { "type": ["number", "null"], 'default': None, } self._schema['properties'][ JSONSchema.BACKEND]['properties']['wait'] = { 'type': 'number', 'default': 5.0, 'minimum': 0.0, }
def run_qobj(qobj, backend, qjob_config=None, backend_options=None, noise_config=None, skip_qobj_validation=False, job_callback=None): """ An execution wrapper with Qiskit-Terra, with job auto recover capability. The auto-recovery feature is only applied for non-simulator backend. This wrapper will try to get the result no matter how long it takes. Args: qobj (QasmQobj): qobj to execute backend (BaseBackend): backend instance qjob_config (dict, optional): configuration for quantum job object backend_options (dict, optional): configuration for simulator noise_config (dict, optional): configuration for noise model skip_qobj_validation (bool, optional): Bypass Qobj validation to decrease submission time, only works for Aer and BasicAer providers job_callback (Callable, optional): callback used in querying info of the submitted job, and providing the following arguments: job_id, job_status, queue_position, job Returns: Result: Result object Raises: ValueError: invalid backend 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, (Backend, BaseBackend)): raise ValueError( 'Backend is missing or not an instance of BaseBackend') with_autorecover = not is_simulator_backend(backend) if MAX_CIRCUITS_PER_JOB is not None: max_circuits_per_job = int(MAX_CIRCUITS_PER_JOB) else: if is_local_backend(backend): max_circuits_per_job = sys.maxsize else: max_circuits_per_job = backend.configuration().max_experiments # split qobj if it exceeds the payload of the backend qobjs = _split_qobj_to_qobjs(qobj, max_circuits_per_job) jobs = [] job_ids = [] for qob in qobjs: job, job_id = _safe_submit_qobj(qob, backend, backend_options, noise_config, skip_qobj_validation) job_ids.append(job_id) jobs.append(job) results = [] if with_autorecover: logger.info("Backend status: %s", backend.status()) logger.info("There are %s jobs are submitted.", len(jobs)) logger.info("All job ids:\n%s", job_ids) for idx, _ in enumerate(jobs): job = jobs[idx] job_id = job_ids[idx] while True: logger.info("Running %s-th qobj, job id: %s", idx, job_id) # try to get result if possible while True: job_status = _safe_get_job_status(job, job_id) queue_position = 0 if job_status in JOB_FINAL_STATES: # do callback again after the job is in the final states if job_callback is not None: job_callback(job_id, job_status, queue_position, job) break if job_status == JobStatus.QUEUED: queue_position = job.queue_position() logger.info("Job id: %s is queued at position %s", job_id, queue_position) else: logger.info("Job id: %s, status: %s", job_id, job_status) if job_callback is not None: job_callback(job_id, job_status, queue_position, job) time.sleep(qjob_config['wait']) # get result after the status is DONE if job_status == JobStatus.DONE: while True: result = job.result(**qjob_config) if result.success: results.append(result) logger.info("COMPLETED the %s-th qobj, job id: %s", idx, job_id) break logger.warning("FAILURE: Job id: %s", job_id) logger.warning( "Job (%s) is completed anyway, retrieve result " "from backend again.", job_id) job = backend.retrieve_job(job_id) break # for other cases, resubmit the qobj until the result is available. # since if there is no result returned, there is no way algorithm can do any process # get back the qobj first to avoid for job is consumed qobj = job.qobj() if job_status == JobStatus.CANCELLED: logger.warning( "FAILURE: Job id: %s is cancelled. Re-submit the Qobj.", job_id) elif job_status == JobStatus.ERROR: logger.warning( "FAILURE: Job id: %s encounters the error. " "Error is : %s. Re-submit the Qobj.", job_id, job.error_message()) else: logging.warning( "FAILURE: Job id: %s. Unknown status: %s. " "Re-submit the Qobj.", job_id, job_status) job, job_id = _safe_submit_qobj(qobj, backend, backend_options, noise_config, skip_qobj_validation) 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 results else None # If result was not successful then raise an exception with either the status msg or # extra information if this was an Aer partial result return if not result.success: msg = result.status if result.status == 'PARTIAL COMPLETED': # Aer can return partial results which Aqua algorithms cannot process and signals # using partial completed status where each returned result has a success and status. # We use the status from the first result that was not successful for res in result.results: if not res.success: msg += ', ' + res.status break raise AquaError('Circuit execution failed: {}'.format(msg)) return result
def compile_and_run_circuits(circuits, backend, backend_config=None, compile_config=None, run_config=None, 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, optional): configuration for backend compile_config (dict, optional): configuration for compilation run_config (RunConfig, optional): configuration for running a circuit qjob_config (dict, optional): configuration for quantum job object backend_options (dict, optional): configuration for simulator noise_config (dict, optional): configuration for noise model show_circuit_summary (bool, optional): showing the summary of submitted circuits. has_shared_circuits (bool, optional): use the 0-th circuits as initial state for other circuits. circuit_cache (CircuitCache, optional): A CircuitCache to use when calling compile_and_run_circuits skip_qobj_validation (bool, optional): Bypass Qobj validation to decrease submission time Returns: Result: Result object Raises: AquaError: Any error except for JobError raised by Qiskit Terra """ backend_config = backend_config or {} compile_config = compile_config or {} run_config = run_config or {} 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 is_simulator_backend(backend): 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 is_simulator_backend(backend) else True if MAX_CIRCUITS_PER_JOB is not None: max_circuits_per_job = int(MAX_CIRCUITS_PER_JOB) else: if is_local_backend(backend): 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.qobjs is None: qobj, _ = _compile_wrapper([circuits[0]], backend, backend_config, compile_config, run_config) if is_aer_provider(backend): qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs) circuit_cache.cache_circuit(qobj, [circuits[0]], 0) qobjs = [] jobs = [] job_ids = [] transpiled_circuits = [] 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: if circuit_cache.cache_transpiled_circuits: transpiled_sub_circuits = compiler.transpile( sub_circuits, backend, **backend_config, **compile_config) qobj = circuit_cache.load_qobj_from_cache( transpiled_sub_circuits, i, run_config=run_config) else: qobj = circuit_cache.load_qobj_from_cache( sub_circuits, i, run_config=run_config) if is_aer_provider(backend): qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs) # cache miss, fail gracefully except (TypeError, IndexError, FileNotFoundError, EOFError, AquaError, AttributeError) as e: circuit_cache.try_reusing_qobjs = False # Reusing Qobj didn't work if len(circuit_cache.qobjs) > 0: logger.info( 'Circuit cache miss, recompiling. Cache miss reason: ' + repr(e)) circuit_cache.misses += 1 else: logger.info( 'Circuit cache is empty, compiling from scratch.') circuit_cache.clear_cache() qobj, transpiled_sub_circuits = _compile_wrapper( sub_circuits, backend, backend_config, compile_config, run_config) transpiled_circuits.extend(transpiled_sub_circuits) if is_aer_provider(backend): qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs) try: circuit_cache.cache_circuit(qobj, sub_circuits, i) except (TypeError, IndexError, AquaError, AttributeError, KeyError) as e: try: circuit_cache.cache_transpiled_circuits = True circuit_cache.cache_circuit(qobj, transpiled_sub_circuits, i) except (TypeError, IndexError, AquaError, AttributeError, KeyError) as e: logger.info( 'Circuit could not be cached for reason: ' + repr(e)) logger.info( 'Transpilation may be too aggressive. Try skipping transpiler.' ) else: qobj, transpiled_sub_circuits = _compile_wrapper( sub_circuits, backend, backend_config, compile_config, run_config) transpiled_circuits.extend(transpiled_sub_circuits) if is_aer_provider(backend): qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs) # 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("==== Before transpiler ====") logger.debug(summarize_circuits(circuits)) logger.debug("==== After transpiler ====") logger.debug(summarize_circuits(transpiled_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 is_local(self): """Return True if backend is a local backend.""" return is_local_backend(self._backend)
def run_qobj(qobj, backend, qjob_config=None, backend_options=None, noise_config=None, skip_qobj_validation=False): """ An execution wrapper with Qiskit-Terra, with job auto recover capability. The auto-recovery feature is only applied for non-simulator backend. This wraper will try to get the result no matter how long it costs. Args: qobj (QasmQobj): qobj to execute backend (BaseBackend): backend instance qjob_config (dict, optional): configuration for quantum job object backend_options (dict, optional): configuration for simulator noise_config (dict, optional): configuration for noise model skip_qobj_validation (bool, optional): Bypass Qobj validation to decrease submission time 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') with_autorecover = False if is_simulator_backend(backend) else True if MAX_CIRCUITS_PER_JOB is not None: max_circuits_per_job = int(MAX_CIRCUITS_PER_JOB) else: if is_local_backend(backend): max_circuits_per_job = sys.maxsize else: max_circuits_per_job = backend.configuration().max_experiments # split qobj if it exceeds the payload of the backend qobjs = _split_qobj_to_qobjs(qobj, max_circuits_per_job) jobs = [] job_ids = [] for qobj in qobjs: job, job_id = _safe_submit_qobj(qobj, backend, backend_options, noise_config, skip_qobj_validation) job_ids.append(job_id) jobs.append(job) results = [] if with_autorecover: logger.info("Backend status: {}".format(backend.status())) logger.info("There are {} jobs are submitted.".format(len(jobs))) logger.info("All job ids:\n{}".format(job_ids)) for idx in range(len(jobs)): job = jobs[idx] job_id = job_ids[idx] while True: logger.info("Running {}-th qobj, 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 qobj, " "job id: {}".format(idx, job_id)) break else: logger.warning("FAILURE: the {}-th qobj, " "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 qobj, job id: {} " "Terra job error: {} ".format( idx, job_id, e)) except Exception as e: raise AquaError("FAILURE: the {}-th qobj, job id: {} " "Unknown error: {} ".format( idx, job_id, e)) from e # something wrong here if reach 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 job, job_id = _safe_submit_qobj(qobj, backend, backend_options, noise_config, skip_qobj_validation) 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_circuits(circuits, backend, backend_config=None, compile_config=None, run_config=None, show_circuit_summary=False, circuit_cache=None, **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, optional): configuration for backend compile_config (dict, optional): configuration for compilation run_config (RunConfig, optional): configuration for running a circuit show_circuit_summary (bool, optional): showing the summary of submitted circuits. circuit_cache (CircuitCache, optional): A CircuitCache to use when calling compile_and_run_circuits Returns: QasmObj: compiled qobj. Raises: AquaError: Any error except for JobError raised by Qiskit Terra """ backend_config = backend_config or {} compile_config = compile_config or {} run_config = run_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 is_simulator_backend(backend): circuits = _avoid_empty_circuits(circuits) if MAX_CIRCUITS_PER_JOB is not None: max_circuits_per_job = int(MAX_CIRCUITS_PER_JOB) else: if is_local_backend(backend): 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.qobjs is None: qobj, transpiled_circuits = _compile_wrapper([circuits[0]], backend, backend_config, compile_config, run_config) if is_aer_provider(backend): qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs) circuit_cache.cache_circuit(qobj, [circuits[0]], 0) qobjs = [] transpiled_circuits = [] 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: if circuit_cache.cache_transpiled_circuits: transpiled_sub_circuits = compiler.transpile( sub_circuits, backend, **backend_config, **compile_config) qobj = circuit_cache.load_qobj_from_cache( transpiled_sub_circuits, i, run_config=run_config) else: qobj = circuit_cache.load_qobj_from_cache( sub_circuits, i, run_config=run_config) if is_aer_provider(backend): qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs) # cache miss, fail gracefully except (TypeError, IndexError, FileNotFoundError, EOFError, AquaError, AttributeError) as e: circuit_cache.try_reusing_qobjs = False # Reusing Qobj didn't work if len(circuit_cache.qobjs) > 0: logger.info( 'Circuit cache miss, recompiling. Cache miss reason: ' + repr(e)) circuit_cache.misses += 1 else: logger.info( 'Circuit cache is empty, compiling from scratch.') circuit_cache.clear_cache() qobj, transpiled_sub_circuits = _compile_wrapper( sub_circuits, backend, backend_config, compile_config, run_config) transpiled_circuits.extend(transpiled_sub_circuits) if is_aer_provider(backend): qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs) try: circuit_cache.cache_circuit(qobj, sub_circuits, i) except (TypeError, IndexError, AquaError, AttributeError, KeyError) as e: try: circuit_cache.cache_transpiled_circuits = True circuit_cache.cache_circuit(qobj, transpiled_sub_circuits, i) except (TypeError, IndexError, AquaError, AttributeError, KeyError) as e: logger.info( 'Circuit could not be cached for reason: ' + repr(e)) logger.info( 'Transpilation may be too aggressive. Try skipping transpiler.' ) else: qobj, transpiled_sub_circuits = _compile_wrapper( sub_circuits, backend, backend_config, compile_config, run_config) transpiled_circuits.extend(transpiled_sub_circuits) if is_aer_provider(backend): qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs) qobjs.append(qobj) if logger.isEnabledFor(logging.DEBUG) and show_circuit_summary: logger.debug("==== Before transpiler ====") logger.debug(summarize_circuits(circuits)) logger.debug("==== After transpiler ====") logger.debug(summarize_circuits(transpiled_circuits)) return qobjs