メインコンテンツまでスキップ

Qiskitの実機量子コンピュータでのサンプリング

ここではQiskitバックエンドを紹介します。IBMデバイス向けに、22つのサンプリングバックエンドを提供しています:

  • QiskitSamplingBackend : IBMQ用のバックエンド
  • QiskitRuntimeSamplingBackend: QiskitRuntimeServiceによって提供されたデバイス用のバックエンド

その使い方の詳細については、以下をご覧ください。

前提条件

このセクションでは、前のセクション(Samplerサンプリング推定SamplingBackend)で説明したトピックを必要とするので、このセクションの前にそれらを読む必要があります。

このチュートリアルで使用したQURI Partsモジュール: quri-parts-circuitquri-parts-corequri-parts-qiskit

以下のようにインストールすることができます:

!pip install "quri-parts[qiskit]"

QiskitSamplingBackendとSampler

IBMQバックエンドの実デバイスを使用するには、まずアカウントを有効化する必要があり、以下に定義するall_device変数でリストされたデバイスの中から好きなものを選ぶことができます:

from qiskit import IBMQ

provider = IBMQ.enable_account("Please input your token")
all_devices = provider.backends()
print(all_devices)

次に、QiskitSamplingBackendは、デバイスを渡すことで作られます。

from quri_parts.qiskit.backend import QiskitSamplingBackend
backend = provider.get_backend("ibmq_qasm_simulator")
sampling_backend = QiskitSamplingBackend(backend=backend)

このサンプリングバックエンドを使えば、Sampling BackendチュートリアルのSampling Backend and Samplerのセクションとまったく同じコードを実行できます。

QiskitRuntimeSamplingBackendとSampler

Qiskit RuntimeはIBMが提供する、実機で実験を行うための新しいサービスです。ここでは、QURI Partsを使ってQiskit Runtimeにジョブを投入する方法を紹介します。まず、いくつかの回路を準備します。

from quri_parts.circuit import QuantumCircuit

# construct quri-parts circuit
qp_circuit1 = QuantumCircuit(2)
qp_circuit1.add_H_gate(0)
qp_circuit1.add_H_gate(1)
qp_circuit1.add_RX_gate(0, 0.23)
qp_circuit1.add_RY_gate(1, -0.99)

qp_circuit2 = QuantumCircuit(2)
qp_circuit2.add_H_gate(0)
qp_circuit2.add_H_gate(1)
qp_circuit2.add_RX_gate(0, 1.23)
qp_circuit2.add_RY_gate(1, 4.56)

qp_circuit3 = QuantumCircuit(2)
qp_circuit3.add_H_gate(0)
qp_circuit3.add_H_gate(1)
qp_circuit3.add_RX_gate(0, 0.998)
qp_circuit3.add_RY_gate(1, 1.928)

Qiskit Runtimeサービスを使用するには、qiskit.providers.backendqiskit_ibm_runtime.QiskitRuntimeServiceオブジェクトを渡してQiskitRuntimeSamplingBackendを作成します。Qiskit Runtime Serviceがサポートするすべてのデバイスのリストを見るには、次のコマンドを実行します:

from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()

# Note: all available devices can be obtained by:
print(service.backends())
#output
[<IBMBackend('ibmq_qasm_simulator')>, <IBMBackend('ibmq_lima')>, <IBMBackend('simulator_statevector')>, <IBMBackend('ibm_nairobi')>, <IBMBackend('ibm_lagos')>, <IBMBackend('ibm_perth')>, <IBMBackend('ibmq_jakarta')>, <IBMBackend('ibmq_quito')>, <IBMBackend('ibmq_belem')>, <IBMBackend('simulator_extended_stabilizer')>, <IBMBackend('simulator_mps')>, <IBMBackend('simulator_stabilizer')>, <IBMBackend('ibmq_manila')>]

qiskitのruntime sampling backendを作成する準備ができました。

from quri_parts.qiskit.backend import QiskitRuntimeSamplingBackend

backend = service.backend("ibmq_qasm_simulator")
qiskit_runtime_sampling_backend = QiskitRuntimeSamplingBackend(
backend=backend,
service=service,
)

samplerは通常通り作成されます。

from quri_parts.core.sampling import create_sampler_from_sampling_backend

sampler = create_sampler_from_sampling_backend(qiskit_runtime_sampling_backend)

sampling_cnt_1 = sampler(qp_circuit1, 1000)
sampling_cnt_2 = sampler(qp_circuit2, 2000)
sampling_cnt_3 = sampler(qp_circuit3, 3000)

複数のサンプリングジョブをセッションにグループ化する

Qiskit Runtime Serviceでは、ジョブをグループ化できるSessionオブジェクトを提供しています。上記の例では、samplerが呼び出されるたびにSessionが生成されます。QURI Partsでは、QiskitRuntimeSamplingBackendでもジョブを1つのSessionにまとめることができます。

with QiskitRuntimeSamplingBackend(backend=backend, service=service) as qiskit_runtime_sampling_backend:
sampler = create_sampler_from_sampling_backend(qiskit_runtime_sampling_backend)
sampling_cnt_1 = sampler(qp_circuit1, 1000)
sampling_cnt_2 = sampler(qp_circuit2, 2000)
sampling_cnt_3 = sampler(qp_circuit3, 3000)

課金時間トラッカー

実デバイス上でジョブを実行する場合、課金時間を追跡することでコストを追跡することが有用です。QiskitRuntimeSamplingBackendでは、total_time_limitオプションを提供し、課金時間の合計を追跡できるようにしています。課金時間の合計が制限時間を超えると、バックエンドは新しいジョブの投入を拒否し、未完了のジョブをすべてキャンセルします。実行時間制限を100100秒に設定したバックエンドを作成してみましょう:

TIME_LIMIT = 100

backend = service.backend("ibmq_qasm_simulator")

sampling_backend = QiskitRuntimeSamplingBackend(
backend=backend,
service=service,
total_time_limit=TIME_LIMIT
)

合計制限時間が設定されると、サンプリング・バックエンドとともにTrackerオブジェクトが作成されます。.tracker属性でアクセスできます。

tracker = sampling_backend.tracker

バックエンドによって投入されたジョブの合計課金時間は、自動的には追跡されません。代わりに、total_run_timerunning_jobsfinished_jobsプロパティにアクセスするたびに計算されます。作成したばかりのバックエンドでジョブを投入していないので、現時点では00になっているはずです。

tracker.total_run_time

0.00.0

バックエンドでジョブを送信すると、ジョブはトラッキングのためにトラッカーに登録されます。例えば、3つのサンプリングジョブを送信してみましょう:

sampling_job_1 = sampling_backend.sample(qp_circuit1, 10)
sampling_job_2 = sampling_backend.sample(qp_circuit2, 20)
sampling_job_3 = sampling_backend.sample(qp_circuit3, 30)

これらのジョブはトラッカー内に保存され、実機でまだ実行中であれば.running_jobsプロパティでアクセスできます。いずれかのジョブが終了していれば、.finished_jobsプロパティでアクセスできます。

print(tracker.running_jobs)
print(tracker.finished_jobs)
#output
[<quri_parts.qiskit.backend.primitive.QiskitRuntimeSamplingJob object at 0x17c66f1f0>, <quri_parts.qiskit.backend.primitive.QiskitRuntimeSamplingJob object at 0x17b24fdf0>, <quri_parts.qiskit.backend.primitive.QiskitRuntimeSamplingJob object at 0x17b24a3d0>]
[]

データの保存と再生

実機で生成された同じデータを使って別の解析を行いたい場合、実験データを保存・取り出しする方法があると便利です。ここでは、Qiskitデバイスで生成された実験データを保存・取り出しする方法を説明します。

データ保存機能は、save_data_while_samplingTrueに設定することで有効になります。QiskitSamplingBackendQiskitRuntimeSamplingBackendの両方がこの機能をサポートしています。ローカルAerシミュレータを例にしてみましょう。

from quri_parts.qiskit.backend import QiskitSamplingBackend
from qiskit_aer import AerSimulator
from quri_parts.core.sampling import create_sampler_from_sampling_backend

sampling_backend = QiskitSamplingBackend(
backend=AerSimulator(),
save_data_while_sampling=True # activate data saving feature
)

sampler = create_sampler_from_sampling_backend(sampling_backend)

cnt1 = sampler(qp_circuit1, 100)
cnt2 = sampler(qp_circuit2, 200)
cnt3 = sampler(qp_circuit3, 300)

print(cnt1)
print(cnt2)
print(cnt3)
#output
{2: 3, 3: 5, 0: 56, 1: 36}
{3: 1, 2: 1, 0: 108, 1: 90}
{1: 7, 0: 9, 2: 147, 3: 137}

上記のようなサンプリング作業を行った後、サンプリングデータをjsonファイルに保存することができます:

import json

with open('saved_sampling_job.json', 'w') as fp:
json.dump(sampling_backend.jobs_json, fp)

上記でアクセスしたjobs_jsonプロパティには、過去のサンプリングジョブが投入された順にすべてエンコードされています。では、これをメモリにロードし直して、QiskitSavedDataSamplingBackendで再生してみましょう。

from quri_parts.qiskit.backend import QiskitSavedDataSamplingBackend

with open('saved_sampling_job.json', 'r') as fp:
saved_data = json.load(fp)

replay_backend = QiskitSavedDataSamplingBackend(
backend=AerSimulator(),
saved_data=saved_data
)

replay_sampler = create_sampler_from_sampling_backend(replay_backend)

replay_cnt1 = replay_sampler(qp_circuit1, 100)
replay_cnt2 = replay_sampler(qp_circuit2, 200)
replay_cnt3 = replay_sampler(qp_circuit3, 300)

print(replay_cnt1)
print(replay_cnt2)
print(replay_cnt3)
#output
{2: 3, 3: 5, 0: 56, 1: 36}
{3: 1, 2: 1, 0: 108, 1: 90}
{1: 7, 0: 9, 2: 147, 3: 137}