メインコンテンツへスキップ

Qiskitで外部量子リソースを統合する

Qiskit SDKは、サードパーティが量子リソースの外部プロバイダーを作成できるように設計されています。

つまり、量子コンピューティングリソースを開発または提供する組織は、自社のサービスをQiskitに統合し、そのユーザーベースを活用することができます。

そのためには、量子コンピューティングリソースへのリクエストをサポートし、その結果をユーザーに返すパッケージを作成する必要があります。

さらに、そのパッケージは、qiskit.primitivesオブジェクトの実装を通じて、ユーザーがジョブを送信し、結果を取得できるようにする必要があります。

Backendへのアクセスを提供する

外部リソースを使ってQuantumCircuitオブジェクトをトランスパイルおよび実行するためには、ユーザーはQPUの接続性、基底ゲート、Qubit数などの制約情報を提供するTargetを含むオブジェクトをインスタンス化する必要があります。これは、ユーザーがQPUをリクエストできるQiskitRuntimeServiceと同様のインターフェースを通じて提供できます。このオブジェクトには少なくともTargetが含まれている必要がありますが、より簡単なアプローチとしてBackendV2インスタンスを返す方法もあります。

実装例は次のようになります:

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

実行用インターフェースを提供する

ハードウェア構成を返すサービスを提供することに加えて、外部QPUリソースへのアクセスを提供するサービスは、量子ワークロードの実行もサポートする場合があります。その機能を公開するには、Qiskit primitivesインターフェースの実装を作成します。たとえば、BasePrimitiveJobBaseEstimatorV2BaseSamplerV2などがあります。これらのインターフェースは最低限、実行メソッド、ジョブステータスの照会、ジョブ結果の返却をサポートする必要があります。

ジョブのステータスと結果を処理するために、Qiskit SDKはDataBinPubResultPrimitiveResult、およびBasePrimitiveJobオブジェクトを提供しており、これらを使用する必要があります。

詳細については、qiskit.primitivesAPIドキュメントと、リファレンス実装のBackendEstimatorV2およびBackendSampleV2を参照してください。

Estimator primitiveの実装例は次のようになります:

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

Sampler primitiveの実装例は次のようになります:

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