Externe Quantenressourcen mit Qiskit integrieren
Die Website Qiskit SDK wurde entwickelt, um Dritte bei der Schaffung externer Anbieter von Quantenressourcen zu unterstützen.
Das bedeutet, dass jede Organisation, die Quantencomputer-Ressourcen entwickelt oder einsetzt, ihre Dienste in Qiskit integrieren und die Benutzerbasis nutzen kann.
Dazu muss ein Paket erstellt werden, das Anfragen nach Quantencomputerressourcen unterstützt und diese an den Benutzer zurückgibt.
Darüber hinaus muss das Paket Nutzern die Möglichkeit bieten, Aufträge zu übermitteln und deren Ergebnisse über eine Implementierung der qiskit.primitives Objekte abzurufen.
Zugriff auf Backends bereitstellen
Damit Benutzer QuantumCircuit Objekte mit externen Ressourcen transpilieren und ausführen können, müssen sie ein Objekt instanziieren, das eine Target das Informationen über die Beschränkungen einer QPU enthält, wie z. B. ihre Konnektivität, Basisgatter und Anzahl der Qubits. Dies kann über eine Schnittstelle erfolgen, die der QiskitRuntimeService über die ein Benutzer Anfragen für eine QPU stellen kann. Dieses Objekt sollte mindestens eine Target enthalten, aber ein einfacherer Ansatz wäre die Rückgabe einer BackendV2 instanz.
Eine Beispielimplementierung könnte etwa so aussehen:
from qiskit.transpiler import Target
from qiskit.providers import BackendV2
class ProviderService:
""" Class for interacting with a provider's service"""
def __init__(
self,
#Receive arguments for authentication/instantiation
):
""" Initiate a connection with the provider service, given some method
of authentication """
def return_target(name: str) -> Target:
""" Interact with the service and return a Target object """
return target
def return_backend(name: str) -> BackendV2:
""" Interact with the service and return a BackendV2 object """
return backend
Bereitstellung einer Schnittstelle für die Ausführung
Neben einem Dienst, der Hardwarekonfigurationen zurückgibt, könnte ein Dienst, der Zugriff auf externe QPU-Ressourcen bietet, auch die Ausführung von Quanten-Workloads unterstützen. Diese Funktionalität lässt sich durch die Erstellung von Implementierungen der Basisklassen der Qiskit-Primitiven bereitstellen; dazu gehören beispielsweise die Klassen BasePrimitiveJob, BaseEstimatorV2 und BaseSamplerV2 sowie weitere. Diese Schnittstellen sollten zumindest eine Methode zur Ausführung, zur Abfrage des Auftragsstatus und zur Rückgabe der Auftragsergebnisse bereitstellen können.
Um den Auftragsstatus und die Ergebnisse zu verwalten, bietet die Qiskit SDK eine DataBin, PubResult, PrimitiveResult, und BasePrimitiveJob objekte verwendet werden.
Siehe die qiskit.primitives API-Dokumentation sowie die Referenzimplementierungen BackendEstimatorV2 und BackendSampleV2 für weitere Informationen.
Eine Beispielimplementierung des Estimator-Primitivs könnte wie folgt aussehen:
from qiskit.primitives import BaseEstimatorV2, BaseSamplerV2, EstimatorPubLike
from qiskit.primitives import DataBin, PubResult, PrimitiveResult, BasePrimitiveJob
from qiskit.providers import BackendV2
class EstimatorImplementation(BaseEstimatorV2):
""" Class for interacting with the provider's Estimator service """
def __init__(
self,
*,
backend: BackendV2,
options: dict
# Receive other arguments to instantiate an Estimator primitive with the service
):
self._backend = backend
self._options = options
self._default_precision = 0.01
@property
def backend(self) -> BackendV2:
""" Return the backend """
return self._backend
def run(
self, pubs: Iterable[EstimatorPubLike], *, precision: float | None = None
) -> BasePrimitiveJob[PrimitiveResult[PubResult]]:
""" Steps to implement:
1. Define a default precision if none is given
2. Validate pub format
3. Instantiate an object which inherits from BasePrimitiveJob
containing pub and runtime information
4. Send the job to the execution service of the provider
"""
job = BasePrimitiveJob(pubs, precision)
job_with_results = job.submit()
return job_with_resultsUnd eine Implementierung des Sampler-Primitivs könnte wie folgt aussehen:
class SamplerImplementation(BaseSamplerV2):
""" Class for interacting with the provider's Sampler service """
def __init__(
self,
*,
backend: BackendV2,
options: dict
# Receive other arguments to instantiate an Estimator primitive with the service
):
self._backend = backend
self._options = options
self._default_shots = 1024
@property
def backend(self) -> BackendV2:
""" Return the Sampler's backend """
return self._backend
def run(
self, pubs: Iterable[SamplerPubLike], *, shots: int | None = None
) -> BasePrimitiveJob[PrimitiveResult[SamplerPubResult]]:
""" Steps to implement:
1. Define a default number of shots if none is given
2. Validate pub format
3. Instantiate an object which inherits from BasePrimitiveJob
containing pub and runtime information
4. Send the job to the execution service of the provider
5. Return the data in some format
"""
job = BasePrimitiveJob(pubs, shots)
job_with_results = job.submit()
return job_with_results