Source code for qbraid.runtime.ibm.job

# Copyright (C) 2024 qBraid
#
# This file is part of the qBraid-SDK
#
# The qBraid-SDK is free software released under the GNU General Public License v3
# or later. You can redistribute and/or modify it under the terms of the GPL v3.
# See the LICENSE file in the project root or <https://www.gnu.org/licenses/gpl-3.0.html>.
#
# THERE IS NO WARRANTY for the qBraid-SDK, as per Section 15 of the GPL v3.

"""
Module defining QiskitJob Class

"""
from __future__ import annotations

from typing import TYPE_CHECKING, Optional

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime.exceptions import RuntimeInvalidStateError

from qbraid._logging import logger
from qbraid.runtime.enums import JobStatus
from qbraid.runtime.exceptions import JobStateError, QbraidRuntimeError
from qbraid.runtime.job import QuantumJob
from qbraid.runtime.result import GateModelResultData, Result

from .result_builder import QiskitGateModelResultBuilder

if TYPE_CHECKING:
    import qiskit.result
    import qiskit_ibm_runtime


IBM_JOB_STATUS_MAP = {
    "INITIALIZING": JobStatus.INITIALIZING,
    "QUEUED": JobStatus.QUEUED,
    "VALIDATING": JobStatus.VALIDATING,
    "RUNNING": JobStatus.RUNNING,
    "CANCELLED": JobStatus.CANCELLED,
    "DONE": JobStatus.COMPLETED,
    "ERROR": JobStatus.FAILED,
}


[docs] class QiskitJob(QuantumJob): """Class for interfacing with a Qiskit IBM ``RuntimeJob``."""
[docs] def __init__( self, job_id: str, job: Optional[qiskit_ibm_runtime.RuntimeJob] = None, service: Optional[qiskit_ibm_runtime.QiskitRuntimeService] = None, **kwargs, ): """Create a ``QiskitJob`` instance.""" super().__init__(job_id, **kwargs) self._job = job or self._get_job(service=service)
def _get_job(self, service: Optional[qiskit_ibm_runtime.QiskitRuntimeService] = None): """Return the qiskit_ibm_runtime.RuntimeJob associated with instance id attribute. Attempts to retrieve a job using a specified or default service. Handles service initialization with error management for authentication issues. Raises: QbraidRuntimeError: If there is an error initializing the service or retrieving the job. """ if service is None: if self._device and getattr(self._device, "_service", None): service = self._device._service else: try: service = QiskitRuntimeService() except Exception as err: raise QbraidRuntimeError("Failed to initialize the quantum service.") from err try: return service.job(self.id) except Exception as err: raise QbraidRuntimeError(f"Error retrieving job {self.id}") from err def status(self): """Returns status from Qiskit Job object.""" job_status = self._job.status().name status = IBM_JOB_STATUS_MAP.get(job_status, JobStatus.UNKNOWN) self._cache_metadata["status"] = status return status def queue_position(self) -> Optional[int]: """Returns the position of the job in the server queue.""" return self._job.queue_position(refresh=True) def result(self): """Return the results of the job.""" if not self.is_terminal_state(): logger.info("Result will be available when job has reached final state.") runner_result: qiskit.result.Result = self._job.result() runner_data = runner_result.to_dict() job_id = runner_data.pop("job_id", self._job.job_id()) success = runner_data.pop("success", runner_result.success) builder = QiskitGateModelResultBuilder(runner_result) measurement_counts = builder.get_counts() measurements = builder.measurements() data = GateModelResultData(measurement_counts=measurement_counts, measurements=measurements) return Result( device_id=runner_result.backend_name, job_id=job_id, success=success, data=data, **runner_data, ) def cancel(self): """Attempt to cancel the job.""" if self.is_terminal_state(): raise JobStateError("Cannot cancel quantum job in non-terminal state.") try: return self._job.cancel() except RuntimeInvalidStateError as err: raise JobStateError from err