Integra recursos cuánticos externos con Qiskit
La web Qiskit SDK está pensada para que terceros puedan crear proveedores externos de recursos cuánticos.
Esto significa que cualquier organización que desarrolle o despliegue recursos de computación cuántica puede integrar sus servicios en Qiskit y aprovechar su base de usuarios.
Para ello, es necesario crear un paquete que admita solicitudes de recursos informáticos cuánticos y los devuelva al usuario.
Además, el paquete debe permitir a los usuarios enviar trabajos y recuperar sus resultados mediante una implementación de los objetos qiskit.primitives .
Proporcionar acceso a los backends
Para que los usuarios puedan transpilar y ejecutar objetos de QuantumCircuit utilizando recursos externos, necesitan instanciar un objeto que contenga un Target que proporciona información sobre las restricciones de una QPU, como su conectividad, puertas base y número de qubits. Esto puede proporcionarse a través de una interfaz similar a la QiskitRuntimeService a través de la cual un usuario puede realizar solicitudes de una QPU. Este objeto debería, como mínimo, contener un Target, pero un enfoque más sencillo sería devolver una BackendV2 instancia.
Un ejemplo de implementación puede ser algo así:
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
Proporcionar una interfaz para la ejecución
Además de proporcionar un servicio que devuelva configuraciones de hardware, un servicio que proporcione acceso a recursos QPU externos también podría soportar la ejecución de cargas de trabajo cuánticas. La exposición de esta capacidad puede realizarse mediante la creación de implementaciones de las interfaces Qiskit primitives; por ejemplo, las interfaces BasePrimitiveJob, BaseEstimatorV2 y BaseSamplerV2 entre otras. Como mínimo, estas interfaces deben ser capaces de proporcionar un método para la ejecución, la consulta del estado del trabajo y la devolución de los resultados del trabajo.
Para gestionar el estado y los resultados de los trabajos, Qiskit SDK proporciona una función DataBin, PubResult, PrimitiveResulty BasePrimitiveJob .
Consulte la documentación de la API qiskit.primitives , así como las implementaciones de referencia BackendEstimatorV2 y BackendSampleV2 para más información.
Un ejemplo de implementación de la primitiva Estimator puede tener este aspecto:
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_resultsY una implementación de la primitiva Sampler puede tener este aspecto:
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