Integrează resurse cuantice externe cu Qiskit
SDK-ul Qiskit este construit pentru a sprijini terțe părți în crearea de providere externe de resurse cuantice.
Aceasta înseamnă că orice organizație care dezvoltă sau implementează resurse de calcul cuantic își poate integra serviciile în Qiskit și poate accesa baza sa de utilizatori.
Pentru a face acest lucru, este necesară crearea unui pachet care să suporte cereri pentru resurse de calcul cuantic și să le returneze utilizatorului.
În plus, pachetul trebuie să le permită utilizatorilor să trimită joburi și să recupereze rezultatele acestora printr-o implementare a obiectelor qiskit.primitives.
Oferirea accesului la Backend-uri
Pentru ca utilizatorii să poată transpila și executa obiecte QuantumCircuit folosind resurse externe, aceștia trebuie să instanțieze un obiect care conține un Target ce furnizează informații despre constrângerile unui QPU, cum ar fi conectivitatea, porțile de bază și numărul de qubiți. Aceasta poate fi oferită printr-o interfață similară cu QiskitRuntimeService, prin care un utilizator poate face cereri pentru un QPU. Acest obiect ar trebui să conțină cel puțin un Target, dar o abordare mai simplă ar fi să returneze o instanță BackendV2.
O implementare exemplu ar putea arăta cam așa:
from qiskit.transpiler import Target
from qsikit.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
Oferirea unei interfețe pentru execuție
Pe lângă furnizarea unui serviciu care returnează configurații hardware, un serviciu ce oferă acces la resurse QPU externe ar putea suporta și execuția de workload-uri cuantice. Expunerea acestei capacități se poate face prin crearea de implementări ale interfețelor primitivelor Qiskit; de exemplu BasePrimitiveJob, BaseEstimatorV2 și BaseSamplerV2, printre altele. Cel puțin, aceste interfețe ar trebui să ofere o metodă pentru execuție, interogarea statusului jobului și returnarea rezultatelor jobului.
Pentru a gestiona statusul și rezultatele joburilor, SDK-ul Qiskit furnizează obiectele DataBin, PubResult, PrimitiveResult și BasePrimitiveJob care ar trebui utilizate.
Consultă documentația API pentru qiskit.primitives, precum și implementările de referință BackendEstimatorV2 și BackendSampleV2 pentru mai multe informații.
O implementare exemplu a primitivei Estimator ar putea arăta astfel:
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_results
Și o implementare a primitivei Sampler ar putea arăta astfel:
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