Source code for qbraid.runtime.ibm.provider

# 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 for configuring IBM provider credentials and authentication.

"""
from __future__ import annotations

import os
from typing import TYPE_CHECKING, Optional

import qiskit
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime.accounts import ChannelType

from qbraid._caching import cached_method
from qbraid.programs import ProgramSpec
from qbraid.runtime.profile import TargetProfile
from qbraid.runtime.provider import QuantumProvider

from .device import QiskitBackend

if TYPE_CHECKING:
    import qiskit_ibm_runtime

    import qbraid.runtime.ibm


[docs] class QiskitRuntimeProvider(QuantumProvider): """ This class is responsible for managing the interactions and authentications with the IBM Quantum services. Attributes: token (str): IBM Cloud API key or IBM Quantum API token. runtime_service (qiskit_ibm_runtime.QiskitRuntimeService): IBM Quantum runtime service. """
[docs] def __init__( self, token: Optional[str] = None, channel: Optional[ChannelType] = None, **kwargs ): """ Initializes the QbraidProvider object with optional AWS and IBM Quantum credentials. Args: token (str, optional): IBM Quantum token. Defaults to None. """ self.token = token or os.getenv("QISKIT_IBM_TOKEN") self.channel = channel or os.getenv("QISKIT_IBM_CHANNEL", "ibm_quantum") self._runtime_service = QiskitRuntimeService( token=self.token, channel=self.channel, **kwargs )
@property def runtime_service(self) -> qiskit_ibm_runtime.QiskitRuntimeService: """Returns the IBM Quantum runtime service.""" return self._runtime_service def save_config( self, token: Optional[str] = None, channel: Optional[str] = None, overwrite: bool = True, **kwargs, ) -> None: """Saves IBM runtime service account to disk for future use.""" token = token or self.token channel = channel or self.channel QiskitRuntimeService.save_account( token=token, channel=channel, overwrite=overwrite, **kwargs ) def _build_runtime_profile( self, backend: qiskit_ibm_runtime.IBMBackend, program_spec: Optional[ProgramSpec] = None ) -> TargetProfile: """Builds a runtime profile from a backend.""" program_spec = program_spec or ProgramSpec(qiskit.QuantumCircuit) config = backend.configuration() local = config.local simulator = config.local or config.simulator return TargetProfile( device_id=backend.name, simulator=simulator, local=local, num_qubits=config.n_qubits, program_spec=program_spec, instance=backend._instance, max_shots=config.max_shots, provider_name="IBM", ) @cached_method def get_devices(self, operational=True, **kwargs) -> list[qbraid.runtime.ibm.QiskitBackend]: """Returns the IBM Quantum provider backends.""" backends = self.runtime_service.backends(operational=operational, **kwargs) program_spec = ProgramSpec(qiskit.QuantumCircuit) return [ QiskitBackend( profile=self._build_runtime_profile(backend, program_spec=program_spec), service=self.runtime_service, ) for backend in backends ] @cached_method def get_device( self, device_id: str, instance: Optional[str] = None ) -> qbraid.runtime.ibm.QiskitBackend: """Returns the IBM Quantum provider backends.""" backend = self.runtime_service.backend(device_id, instance=instance) return QiskitBackend( profile=self._build_runtime_profile(backend), service=self.runtime_service ) def least_busy( self, simulator=False, operational=True, **kwargs ) -> qbraid.runtime.ibm.QiskitBackend: """Return the least busy IBMQ QPU.""" backend = self.runtime_service.least_busy( simulator=simulator, operational=operational, **kwargs ) return QiskitBackend( profile=self._build_runtime_profile(backend), service=self.runtime_service ) def __hash__(self): if not hasattr(self, "_hash"): object.__setattr__(self, "_hash", hash((self.token, self.channel))) return self._hash # pylint: disable=no-member