def _set_backend(self, backend: Backend): super()._set_backend(backend) if self._cr_gate is None: # This falls into CRPulseGate which requires pulse schedule # Extract control channel index try: cr_channels = backend.configuration().control(self.physical_qubits) self._cr_channel = cr_channels[0].index except AttributeError: warnings.warn( f"{backend.name()} doesn't provide cr channel mapping. " "Cannot find proper channel index to play the cross resonance pulse.", UserWarning, ) # Extract pulse granularity try: self._granularity = backend.configuration().timing_constraints["granularity"] except (AttributeError, KeyError): # Probably no chunk size restriction on waveform memory. pass # Extract time resolution, this is anyways required for xvalue conversion try: self._dt = backend.configuration().dt except AttributeError: warnings.warn( f"{backend.name()} doesn't provide system time resolution dt. " "Cannot estimate Hamiltonian coefficients in SI units.", UserWarning, )
def __init__(self, backend: Backend) -> None: self.name = backend.name() self.n_qubits = backend.configuration().n_qubits self.operational = backend.status().operational self.simulator = backend.configuration().simulator self.pending_jobs = backend.status().pending_jobs self.active_jobs = backend.job_limit().active_jobs self.maximum_jobs = backend.job_limit().maximum_jobs self.status_msg = backend.status().status_msg # only available by real QPUs try: self.quantum_volume = backend.configuration().quantum_volume except AttributeError: pass
def from_backend(cls, backend: Backend): """Construct an :class:`InstructionDurations` object from the backend. Args: backend: backend from which durations (gate lengths) and dt are extracted. Returns: InstructionDurations: The InstructionDurations constructed from backend. Raises: TranspilerError: If dt and dtm is different in the backend. """ # All durations in seconds in gate_length instruction_durations = [] for gate, insts in backend.properties()._gates.items(): for qubits, props in insts.items(): if "gate_length" in props: gate_length = props["gate_length"][0] # Throw away datetime at index 1 instruction_durations.append((gate, qubits, gate_length, "s")) for q, props in backend.properties()._qubits.items(): if "readout_length" in props: readout_length = props["readout_length"][0] # Throw away datetime at index 1 instruction_durations.append(("measure", [q], readout_length, "s")) try: dt = backend.configuration().dt except AttributeError: dt = None return InstructionDurations(instruction_durations, dt=dt)
def round_pulse_duration(backend: Backend, duration: float) -> int: """Find the best pulse duration that meets timing constraints of the backend. Args: backend: Target backend to play pulses. duration: Duration of pulse to be formatted. Returns: Valid integer pulse duration that meets timing constraints of the backend. """ # TODO this can be moved to some common utils timing_constraints = getattr(backend.configuration(), "timing_constraints", dict()) granularity = int(timing_constraints.get("granularity", 1)) return granularity * int(duration / granularity)
def default_backend_filter(b: Backend) -> bool: """ Default backend filter Callable. PARAMETERS ---------- b: Backend A Qiskit Backend object. RETURNS ------- out: bool `True` if `b` is not a simulator and has memory enabled, `False` otherwise. """ config: BackendConfiguration = b.configuration() return config.memory and not config.simulator
def run_circuits( circuits: Union[QuantumCircuit, List[QuantumCircuit]], backend: Backend, qjob_config: Dict, backend_options: Optional[Dict] = None, noise_config: Optional[Dict] = None, run_config: Optional[Dict] = None, job_callback: Optional[Callable] = None, max_job_retries: int = 50, ) -> Result: """ 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: circuits: circuits to execute backend: backend instance qjob_config: configuration for quantum job object backend_options: backend options noise_config: configuration for noise model run_config: configuration for run job_callback: callback used in querying info of the submitted job, and providing the following arguments: job_id, job_status, queue_position, job. max_job_retries(int): positive non-zero number of trials for the job set (-1 for infinite trials) (default: 50) Returns: Result object Raises: QiskitError: Any error except for JobError raised by Qiskit Terra """ backend_interface_version = _get_backend_interface_version(backend) backend_options = backend_options or {} noise_config = noise_config or {} run_config = run_config or {} if backend_interface_version <= 1: with_autorecover = not is_simulator_backend(backend) else: with_autorecover = False if MAX_CIRCUITS_PER_JOB is not None: max_circuits_per_job = int(MAX_CIRCUITS_PER_JOB) else: if backend_interface_version <= 1: if is_local_backend(backend): max_circuits_per_job = sys.maxsize else: max_circuits_per_job = backend.configuration().max_experiments else: if backend.max_circuits is not None: max_circuits_per_job = backend.max_circuits else: max_circuits_per_job = sys.maxsize if len(circuits) > max_circuits_per_job: jobs = [] job_ids = [] split_circuits = [] count = 0 while count < len(circuits): some_circuits = circuits[count:count + max_circuits_per_job] split_circuits.append(some_circuits) job, job_id = _safe_submit_circuits( some_circuits, backend, qjob_config=qjob_config, backend_options=backend_options, noise_config=noise_config, run_config=run_config, max_job_retries=max_job_retries, ) jobs.append(job) job_ids.append(job_id) count += max_circuits_per_job else: job, job_id = _safe_submit_circuits( circuits, backend, qjob_config=qjob_config, backend_options=backend_options, noise_config=noise_config, run_config=run_config, max_job_retries=max_job_retries, ) jobs = [job] job_ids = [job_id] split_circuits = [circuits] 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): result = None logger.info("Backend status: %s", backend.status()) logger.info("There is one jobs are submitted: id: %s", job_id) job = jobs[idx] job_id = job_ids[idx] for _ in range(max_job_retries): logger.info("Running job id: %s", job_id) # try to get result if possible while True: job_status = _safe_get_job_status( job, job_id, max_job_retries, qjob_config["wait"] ) # if the status was broken, an Exception would be raised anyway 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 and hasattr( job, "queue_position"): 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: for _ in range(max_job_retries): result = job.result() if result.success: results.append(result) logger.info("COMPLETED the %s-th job, 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) else: raise QiskitError( f"Max retry limit reached. Failed to get result for job id {job_id}" ) break # for other cases, resubmit the circuit until the result is available. # since if there is no result returned, there is no way algorithm can do any process if job_status == JobStatus.CANCELLED: logger.warning( "FAILURE: Job id: %s is cancelled. Re-submit the circuits.", job_id) elif job_status == JobStatus.ERROR: logger.warning( "FAILURE: Job id: %s encounters the error. " "Error is : %s. Re-submit the circuits.", job_id, job.error_message(), ) else: logging.warning( "FAILURE: Job id: %s. Unknown status: %s. Re-submit the circuits.", job_id, job_status, ) job, job_id = _safe_submit_circuits( split_circuits[idx], backend, qjob_config=qjob_config, backend_options=backend_options, noise_config=noise_config, run_config=run_config, max_job_retries=max_job_retries, ) else: raise QiskitError( f"Max retry limit reached. Failed to get result for job with id {job_id} " ) else: results = [] for job in jobs: results.append(job.result()) 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 QiskitError(f"Circuit execution failed: {msg}") if not hasattr(result, "time_taken"): setattr(result, "time_taken", 0.0) return result