def construct_circuit(self, measurement=False): """Construct circuit. Args: measurement (bool): Boolean flag to indicate if measurement should be included in the circuit. Returns: QuantumCircuit: quantum circuit. """ breakpoints = [] # Get n value used in Shor's algorithm, to know how many qubits are used self._n = math.ceil(math.log(self._N, 2)) # quantum register where the sequential QFT is performed self._up_qreg = QuantumRegister(2 * self._n, name='up') # quantum register where the multiplications are made self._down_qreg = QuantumRegister(self._n, name='down') # auxilliary quantum register used in addition and multiplication self._aux_qreg = QuantumRegister(self._n + 2, name='aux') up_cqreg = ClassicalRegister(2 * self._n, name='m_up') down_cqreg = ClassicalRegister(self._n, name='m_down') # Create Quantum Circuit circuit = QuantumCircuit(self._up_qreg, self._down_qreg, self._aux_qreg, up_cqreg, down_cqreg) # Initialize down register to 1 and create maximal superposition in top register circuit.u2(0, np.pi, self._up_qreg) circuit.u3(np.pi, 0, np.pi, self._down_qreg[0]) # validate maximal superposition in top register breakpoints.append(circuit.get_breakpoint_uniform(self._up_qreg, up_cqreg, 0.05)) # validate initialize down register to 1 breakpoints.append(circuit.get_breakpoint_classical(self._down_qreg, down_cqreg, 0.05, 1)) # Apply the multiplication gates as showed in the report in order to create the exponentiation for i in range(0, 2 * self._n): self._controlled_multiple_mod_N( circuit, self._up_qreg[i], self._down_qreg, self._aux_qreg, int(pow(self._a, pow(2, i))) ) # Apply inverse QFT ftc.construct_circuit(circuit=circuit, qubits=self._up_qreg, do_swaps=True, inverse=True) # validate uncomputation is complete and registers are in product state breakpoints.append(circuit.get_breakpoint_product(self._up_qreg[:], up_cqreg[:], self._down_qreg[:], down_cqreg[:], .05)) if measurement: circuit.measure(self._up_qreg, up_cqreg) logger.info(summarize_circuits(circuit)) return breakpoints, circuit
def construct_circuit(self, measurement: bool = False) -> QuantumCircuit: """Construct circuit. Args: measurement: Boolean flag to indicate if measurement should be included in the circuit. Returns: Quantum circuit. """ # Get n value used in Shor's algorithm, to know how many qubits are used self._n = math.ceil(math.log(self._N, 2)) self._qft.num_qubits = self._n + 1 self._iqft.num_qubits = self._n + 1 self._qft_dag = circuit_to_dag(self._qft) self._iqft_dag = circuit_to_dag(self._iqft) # quantum register where the sequential QFT is performed self._up_qreg = QuantumRegister(2 * self._n, name='up') # quantum register where the multiplications are made self._down_qreg = QuantumRegister(self._n, name='down') # auxiliary quantum register used in addition and multiplication self._aux_qreg = QuantumRegister(self._n + 2, name='aux') # Create Quantum Circuit circuit = QuantumCircuit(self._up_qreg, self._down_qreg, self._aux_qreg) # Initialize down register to 1 and create maximal superposition in top register circuit.u2(0, np.pi, self._up_qreg) circuit.u3(np.pi, 0, np.pi, self._down_qreg[0]) tdags = [] dag_self = circuit_to_dag(circuit) # Apply the multiplication gates as showed in # the report in order to create the exponentiation for i in range(0, 2 * self._n): tdags += self._controlled_multiple_mod_N_tdags( self._up_qreg[i], self._down_qreg, self._aux_qreg, int(pow(self._a, pow(2, i)))) for tdag in tdags: dag_compose_with_tagged(dag_self, tdag) composed_circuit = dag_to_circuit(dag_self) circuit.__dict__.update(composed_circuit.__dict__) # Apply inverse QFT iqft = QFT(len(self._up_qreg), inverse=True) circuit.compose(iqft, qubits=self._up_qreg) if measurement: up_cqreg = ClassicalRegister(2 * self._n, name='m') circuit.add_register(up_cqreg) circuit.measure(self._up_qreg, up_cqreg) logger.info(summarize_circuits(circuit)) return circuit
def construct_circuit(self): """Construct circuit. Returns: QuantumCircuit: quantum circuit. """ # Get n value used in Shor's algorithm, to know how many qubits are used self._n = math.ceil(math.log(self._N, 2)) # quantum register where the sequential QFT is performed self._up_qreg = QuantumRegister(2 * self._n, name='up') # quantum register where the multiplications are made self._down_qreg = QuantumRegister(self._n, name='down') # auxilliary quantum register used in addition and multiplication self._aux_qreg = QuantumRegister(self._n + 2, name='aux') # Create Quantum Circuit circuit = QuantumCircuit(self._up_qreg, self._down_qreg, self._aux_qreg) # Initialize down register to 1 and create maximal superposition in top register circuit.u2(0, np.pi, self._up_qreg) circuit.u3(np.pi, 0, np.pi, self._down_qreg[0]) # Apply the multiplication gates as showed in the report in order to create the exponentiation for i in range(0, 2 * self._n): self._controlled_multiple_mod_N(circuit, self._up_qreg[i], self._down_qreg, self._aux_qreg, int(pow(self._a, pow(2, i)))) # Apply inverse QFT ftc.construct_circuit(circuit=circuit, qubits=self._up_qreg, do_swaps=True, inverse=True) logger.info(summarize_circuits(circuit)) return circuit
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 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 wrapper 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 kwargs (optional): special aer instructions to evaluation the expectation of a hamiltonian Returns: QasmObj: compiled qobj. Raises: ValueError: backend type is wrong or not given ValueError: no circuit in the circuits """ 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 not circuits: raise ValueError("The input circuit is empty.") if is_simulator_backend(backend): circuits = _avoid_empty_circuits(circuits) 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({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) transpiled_circuits = None if circuit_cache is not None and circuit_cache.misses < circuit_cache.allowed_misses: try: if circuit_cache.cache_transpiled_circuits: transpiled_circuits = compiler.transpile(circuits, backend, **backend_config, **compile_config) qobj = circuit_cache.load_qobj_from_cache(transpiled_circuits, 0, run_config=run_config) else: qobj = circuit_cache.load_qobj_from_cache(circuits, 0, 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 circuit_cache.qobjs: logger.info('Circuit cache miss, recompiling. Cache miss reason: %s', repr(e)) circuit_cache.misses += 1 else: logger.info('Circuit cache is empty, compiling from scratch.') circuit_cache.clear_cache() qobj, transpiled_circuits = _compile_wrapper(circuits, backend, backend_config, compile_config, run_config) if is_aer_provider(backend): qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs) try: circuit_cache.cache_circuit(qobj, circuits, 0) except (TypeError, IndexError, AquaError, AttributeError, KeyError): try: circuit_cache.cache_transpiled_circuits = True circuit_cache.cache_circuit(qobj, transpiled_circuits, 0) except (TypeError, IndexError, AquaError, AttributeError, KeyError) as e: logger.info('Circuit could not be cached for reason: %s', repr(e)) logger.info('Transpilation may be too aggressive. Try skipping transpiler.') else: qobj, transpiled_circuits = _compile_wrapper(circuits, backend, backend_config, compile_config, run_config) if is_aer_provider(backend): qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs) if logger.isEnabledFor(logging.DEBUG) and show_circuit_summary: logger.debug("==== Before transpiler ====") logger.debug(summarize_circuits(circuits)) if transpiled_circuits is not None: logger.debug("==== After transpiler ====") logger.debug(summarize_circuits(transpiled_circuits)) return qobj
def construct_circuit(self, measurement: bool = False) -> QuantumCircuit: """Construct circuit. Args: measurement: Boolean flag to indicate if measurement should be included in the circuit. Returns: Quantum circuit. """ # Get n value used in Shor's algorithm, to know how many qubits are used self._n = math.ceil(math.log(self._N, 2)) self._qft.num_qubits = self._n + 1 self._iqft.num_qubits = self._n + 1 # quantum register where the sequential QFT is performed self._up_qreg = QuantumRegister(2 * self._n, name='up') # quantum register where the multiplications are made self._down_qreg = QuantumRegister(self._n, name='down') # auxiliary quantum register used in addition and multiplication self._aux_qreg = QuantumRegister(self._n + 2, name='aux') # Create Quantum Circuit circuit = QuantumCircuit(self._up_qreg, self._down_qreg, self._aux_qreg, name="Shor(N={}, a={})".format( self._N, self._a)) # Create gates to perform addition/subtraction by N in Fourier Space self._phi_add_N = self._phi_add_gate(self._aux_qreg.size - 1, self._get_angles(self._N)) self._iphi_add_N = self._phi_add_N.inverse() # Create maximal superposition in top register circuit.h(self._up_qreg) # Initialize down register to 1 circuit.x(self._down_qreg[0]) # Apply the multiplication gates as showed in # the report in order to create the exponentiation for i, ctl_up in enumerate(self._up_qreg): # type: ignore a = int(pow(self._a, pow(2, i))) controlled_multiple_mod_N = self._controlled_multiple_mod_N( len(self._down_qreg) + len(self._aux_qreg) + 1, a, ) circuit.append(controlled_multiple_mod_N, [ctl_up, *self._down_qreg, *self._aux_qreg]) # Apply inverse QFT iqft = QFT(len(self._up_qreg)).inverse().to_instruction() circuit.append(iqft, self._up_qreg) if measurement: up_cqreg = ClassicalRegister(2 * self._n, name='m') circuit.add_register(up_cqreg) circuit.measure(self._up_qreg, up_cqreg) logger.info(summarize_circuits(circuit)) return circuit
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_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