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

Qiskit Runtime V2 プリミティブへの移行

警告

オリジナルのプリミティブ(V1 プリミティブとも呼ばれます)である V1 Sampler および V1 Estimator は、qiskit-ibm-runtime 0.23 で非推奨となりました。 これらのサポートは 2024 年 8 月 15 日に終了しました。

V1 プリミティブの非推奨化に伴い、すべてのコードを V2 インターフェースを使用するよう移行する必要があります。このガイドでは、Qiskit Runtime V2 プリミティブ(qiskit-ibm-runtime 0.21.0 以降で利用可能)の変更点とその理由を説明し、新しい各プリミティブの詳細を解説するとともに、レガシープリミティブから V2 プリミティブへのコード移行を支援するサンプルを提供します。このガイドのサンプルはすべて Qiskit Runtime プリミティブを使用していますが、一般的に同じ変更が他のプリミティブ実装にも適用されます。エラー軽減など Qiskit Runtime 固有の機能は、引き続き Qiskit Runtime に固有のものとなります。

Qiskit リファレンスプリミティブ(現在は statevector プリミティブと呼ばれます)への変更については、Qiskit 1.0 機能変更ページの qiskit.primitives セクションをご覧ください。V2 プリミティブのリファレンス実装については、StatevectorSampler および StatevectorEstimator を参照してください。

概要

プリミティブのバージョン 2 は、Sampler と Estimator の両方に対する新しい基底クラス(BaseSamplerV2 および BaseEstimatorV2)と、入出力の新しい型とともに導入されました。

新しいインターフェースでは、1 つの Circuit と複数のオブザーバブル(Estimator を使用する場合)および当該 Circuit のパラメータ値セットを指定できるため、パラメータ値セットとオブザーバブルのスイープを効率的に指定できます。以前は、結合するデータのサイズに合わせて同じ Circuit を複数回指定する必要がありました。また、引き続き resilience_level(Estimator を使用する場合)をシンプルなノブとして使用できますが、V2 プリミティブでは個別のエラー軽減・抑制メソッドをオン/オフして、ニーズに合わせてカスタマイズする柔軟性も提供されます。

ジョブの総実行時間を削減するため、V2 プリミティブは対象の QPU(量子処理ユニット)がサポートする命令を使用した Circuit とオブザーバブルのみを受け付けます。このような Circuit とオブザーバブルは、命令セットアーキテクチャ(ISA)Circuit およびオブザーバブルと呼ばれます。V2 プリミティブはレイアウト、ルーティング、変換の各操作を実行しません。Circuit を変換する手順については、トランスパイルのドキュメントを参照してください。

Sampler V2 は、量子 Circuit の実行から出力レジスタをサンプリングするというコアタスクに集中するよう簡素化されました。プログラムで定義された型のサンプルを、重みなしで返します。出力データは、プログラムで定義された出力レジスタ名ごとに分離されています。この変更により、古典的な制御フローを持つ Circuit の将来的なサポートが可能となります。

詳細については、EstimatorV2 API リファレンスおよび SamplerV2 API リファレンスを参照してください。

主な変更点

インポート

後方互換性のため、V2 プリミティブは明示的にインポートする必要があります。import <primitive>V2 as <primitive> と指定することは必須ではありませんが、コードを V2 へ移行しやすくなります。

警告

V1 プリミティブのサポートが終了した後は、import <primitive> と記述すると指定したプリミティブの V2 バージョンがインポートされます。

from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_runtime import SamplerV2 as Sampler

入出力

入力

SamplerV2EstimatorV2 はどちらも、1 つ以上の プリミティブ統合ブロック(PUB)を入力として受け取ります。各 PUB は、1 つの Circuit と、その Circuit にブロードキャストされるデータ(複数のオブザーバブルとパラメータ)を含むタプルです。各 PUB は 1 つの結果を返します。

  • Sampler V2 の PUB フォーマット:(<circuit><parameter values><shots>)。<parameter values><shots> は省略可能です。
  • Estimator V2 の PUB フォーマット:(<circuit><observables><parameter values><precision>)。<parameter values><precision> は省略可能です。 オブザーバブルとパラメータ値を組み合わせる際は、Numpy のブロードキャストルールが使用されます。

また、以下の変更も行われています。

  • Estimator V2 では、期待値推定の目標精度を指定する precision 引数が run() メソッドに追加されました。
  • Sampler V2 では、run() メソッドに shots 引数が追加されました。
サンプル

run() で precision を使用する Estimator V2 のサンプル:

# Estimate expectation values for two PUBs, both with 0.05 precision.
estimator.run([(circuit1, obs_array1), (circuit2, obs_array_2)], precision=0.05)

run() で shots を使用する Sampler V2 のサンプル:

# Sample two circuits at 128 shots each.
sampler.run([circuit1, circuit2], shots=128)

# Sample two circuits at different amounts of shots.
# The "None"s are necessary as placeholders
# for the lack of parameter values in this example.
sampler.run([
(circuit1, None, 123),
(circuit2, None, 456),
])

出力

出力は PubResult フォーマットになりました。PubResult は、単一の PUB の実行から得られるデータとメタデータです。

  • Estimator V2 は引き続き期待値を返します。

  • Estimator V2 の PubResult の data 部分には、期待値と標準誤差(stds)の両方が含まれます。V1 ではメタデータに分散が返されていました。

  • Sampler V2 は、V1 インターフェースの準確率分布の代わりに、ビット列の形式でショットごとの測定値を返します。ビット列は測定結果を示し、測定されたショットの順序を保持します。

  • Sampler V2 には、移行を支援する get_counts() などの便利なメソッドが追加されました。

  • Sampler V2 の結果オブジェクトは、動的 Circuit との互換性のため、入力 Circuit の古典レジスタ名ごとにデータを整理します。デフォルトでは、以下の例に示すように古典レジスタ名は meas です。Circuit を定義する際に、デフォルトでない名前の古典レジスタを 1 つ以上作成した場合は、その名前を使用して結果を取得します。古典レジスタ名は <circuit_name>.cregs を実行することで確認できます(例:qc.cregs)。

    # Define a quantum circuit with 2 qubits
    circuit = QuantumCircuit(2)
    circuit.h(0)
    circuit.cx(0, 1)
    circuit.measure_all()
    circuit.draw()
            ┌───┐      ░ ┌─┐
    q_0: ┤ H ├──■───░─┤M├───
    └───┘┌─┴─┐ ░ └╥┘┌─┐
    q_1: ─────┤ X ├─░──╫─┤M├
    └───┘ ░ ║ └╥┘
    meas: 2/══════════════╩══╩═
    0 1

Estimator のサンプル(入出力)

# Estimator V1: Execute 1 circuit with 4 observables
job = estimator_v1.run([circuit] * 4, [obs1, obs2, obs3, obs4])
evs = job.result().values

# Estimator V2: Execute 1 circuit with 4 observables
job = estimator_v2.run([(circuit, [obs1, obs2, obs3, obs4])])
evs = job.result()[0].data.evs

Sampler のサンプル(入出力)

  # Sampler V1: Execute 1 circuit with 3 parameter sets
job = sampler_v1.run([circuit] * 3, [vals1, vals2, vals3])
dists = job.result().quasi_dists

# Sampler V2: Executing 1 circuit with 3 parameter sets
job = sampler_v2.run([(circuit, [vals1, vals2, vals3])])
counts = job.result()[0].data.meas.get_counts()

異なる出力レジスタを使用するサンプル

from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler

alpha = ClassicalRegister(5, "alpha")
beta = ClassicalRegister(7, "beta")
qreg = QuantumRegister(12)

circuit = QuantumCircuit(qreg, alpha, beta)
circuit.h(0)
circuit.measure(qreg[:5], alpha)
circuit.measure(qreg[5:], beta)

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=12)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)

sampler = Sampler(backend)
job = sampler.run([isa_circuit])
result = job.result()
# Get results for the first (and only) PUB
pub_result = result[0]
print(f" >> Counts for the alpha output register: {pub_result.data.alpha.get_counts()}")
print(f" >> Counts for the beta output register: {pub_result.data.beta.get_counts()}")

オプション

V2 プリミティブでは、オプションの指定方法が以下のように変更されています。

  • SamplerV2EstimatorV2 はそれぞれ独立したオプションクラスを持つようになりました。プリミティブの初期化時および初期化後に、利用可能なオプションを確認してオプション値を更新できます。
  • set_options() メソッドの代わりに、V2 プリミティブのオプションには options 属性への変更を適用する update() メソッドが用意されています。
  • オプションの値を指定しない場合、そのオプションには特別な値 Unset が設定され、サーバーのデフォルト値が使用されます。
  • V2 プリミティブでは、options 属性は Python の dataclass 型です。組み込みの asdict メソッドを使用して辞書に変換できます。

利用可能なオプションの一覧については、API リファレンスを参照してください。

from dataclasses import asdict
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# Setting options during primitive initialization
estimator = Estimator(backend, options={"resilience_level": 2})

# Setting options after primitive initialization
# This uses auto complete.
estimator.options.default_shots = 4000
# This does bulk update.
estimator.options.update(default_shots=4000, resilience_level=2)

# Print the dictionary format.
# Server defaults are used for unset options.
print(asdict(estimator.options))
from dataclasses import asdict
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2 as Sampler

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# Setting options during primitive initialization
sampler = Sampler(backend, options={"default_shots": 4096})

# Setting options after primitive initialization
# This uses auto complete.
sampler.options.dynamical_decoupling.enable = True
# Turn on gate twirling. Requires qiskit_ibm_runtime 0.23.0 or later.
sampler.options.twirling.enable_gates = True

# This does bulk update. The value for default_shots is overridden if you specify shots with run() or in the PUB.
sampler.options.update(default_shots=1024, dynamical_decoupling={"sequence_type": "XpXm"})

# Print the dictionary format.
# Server defaults are used for unset options.
print(asdict(sampler.options))

エラー軽減と抑制

  • Sampler V2 は後処理なしでサンプルを返すため、レジリエンスレベルをサポートしません。

  • Sampler V2 は optimization_level をサポートしません。

  • Estimator V2 は 2024 年 9 月 30 日前後に optimization_level のサポートを終了する予定です。

  • Estimator V2 はレジリエンスレベル 3 をサポートしません。これは、V1 Estimator のレジリエンスレベル 3 が確率的エラーキャンセル(PEC)を使用しており、指数関数的な処理時間を代償として偏りのない結果を提供することが証明されているためです。レベル 3 はこのトレードオフに注意を促すために削除されました。ただし、pec_mitigation オプションを指定することで、引き続き PEC をエラー軽減手法として使用できます。

  • Estimator V2 は、以下の表に示すように resilience_level 0~2 をサポートしています。これらのオプションは V1 の対応するオプションよりも高度です。個別のエラー軽減・抑制メソッドを明示的にオン/オフすることもできます。

    レベル 1レベル 2
    測定 Twirling測定 Twirling
    読み出しエラー軽減読み出しエラー軽減
    ZNE
from dataclasses import asdict
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# Setting options during primitive initialization
estimator = Estimator(backend)

# Set resilience_level to 0
estimator.options.resilience_level = 0

# Turn on measurement error mitigation
estimator.options.resilience.measure_mitigation = True
from qiskit_ibm_runtime import SamplerV2 as Sampler

sampler = Sampler(backend)
# Turn on dynamical decoupling with sequence XpXm.
sampler.options.dynamical_decoupling.enable = True
sampler.options.dynamical_decoupling.sequence_type = "XpXm"

print(f">> dynamical decoupling sequence to use: {sampler.options.dynamical_decoupling.sequence_type}")

トランスパイル

V2 プリミティブは、特定の Backend の命令セットアーキテクチャ(ISA)に準拠した Circuit のみをサポートします。プリミティブはレイアウト、ルーティング、変換の各操作を実行しないため、V1 の対応するトランスパイルオプションはサポートされていません。

ジョブのステータス

V2 プリミティブには、BasePrimitiveJob を継承した新しい RuntimeJobV2 クラスが追加されました。この新しいクラスの status() メソッドは、Qiskit の JobStatus 列挙型の代わりに文字列を返します。詳細については、RuntimeJobV2 API リファレンスを参照してください。

job = estimator.run(...)

# check if a job is still running
print(f"Job {job.job_id()} is still running: {job.status() == "RUNNING"}")

Estimator V2 への移行手順

  1. from qiskit_ibm_runtime import Estimatorfrom qiskit_ibm_runtime import EstimatorV2 as Estimator に置き換えます。

  2. from qiskit_ibm_runtime import Options の記述をすべて削除します。Options クラスは V2 プリミティブでは使用されないためです。代わりに、EstimatorV2 クラスの初期化時にオプションを辞書として渡す(例:estimator = Estimator(backend, options={"dynamical_decoupling": {"enable": True}}))か、初期化後に設定することができます。

    estimator = Estimator(backend)
    estimator.options.dynamical_decoupling.enable = True
  3. サポートされているオプションをすべて確認し、必要に応じて更新します。

  4. 実行したい各 Circuit を、そのCircuit に適用したいオブザーバブルとパラメータ値とともにタプル(PUB)にまとめます。たとえば、circuit1observable1parameter_set1 で実行したい場合は (circuit1, observable1, parameter_set1) を使用します。

  5. 外積を適用したい場合は、オブザーバブルの配列やパラメータセットの配列を整形し直す必要があるかもしれません。たとえば、形状 (4, 1) のオブザーバブル配列と形状 (1, 6) のパラメータセット配列を組み合わせると、(4, 6) の期待値が得られます。詳しくは Numpy のブロードキャストルールを参照してください。

  6. 任意で、その特定の PUB に対して必要な精度を指定することもできます。

  7. Estimator の run() メソッドを更新し、PUB のリストを渡すようにします。たとえば run([(circuit1, observable1, parameter_set1)]) のようにします。 任意で、すべての PUB に適用される precision をここで指定することもできます。

  8. Estimator V2 のジョブ結果は PUB ごとにグループ化されています。インデックスを指定することで、各 PUB の期待値と標準誤差を確認できます。たとえば:

pub_result = job.result()[0]
print(f">>> Expectation values: {pub_result.data.evs}")
print(f">>> Standard errors: {pub_result.data.stds}")

Estimator の完全な使用例

単一の実験を実行する

Estimator を使って、単一の Circuit とオブザーバブルのペアの期待値を求めます。

import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import SparsePauliOp, random_hermitian
from qiskit_ibm_runtime import EstimatorV2 as Estimator, QiskitRuntimeService

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
estimator = Estimator(backend)

n_qubits = 127

mat = np.real(random_hermitian(n_qubits, seed=1234))
circuit = IQP(mat)
observable = SparsePauliOp("Z" * n_qubits)

pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)

job = estimator.run([(isa_circuit, isa_observable)])
result = job.result()

print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")

単一のジョブで複数の実験を実行する

Estimator を使って、複数の Circuit とオブザーバブルのペアの期待値を求めます。

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)

n_qubits = 3
rng = np.random.default_rng()
mats = [np.real(random_hermitian(n_qubits, seed=rng)) for _ in range(3)]
circuits = [IQP(mat) for mat in mats]
observables = [
SparsePauliOp("X" * n_qubits),
SparsePauliOp("Y" * n_qubits),
SparsePauliOp("Z" * n_qubits),
]

isa_circuits = pm.run(circuits)
isa_observables = [ob.apply_layout(isa_circuits[0].layout) for ob in observables]

estimator = Estimator(backend)
job = estimator.run([(isa_circuits[0], isa_observables[0]),(isa_circuits[1], isa_observables[1]),(isa_circuits[2], isa_observables[2])])
job_result = job.result()
for idx in range(len(job_result)):
pub_result = job_result[idx]
print(f">>> Expectation values for PUB {idx}: {pub_result.data.evs}")
print(f">>> Standard errors for PUB {idx}: {pub_result.data.stds}")

パラメータ化された Circuit を実行する

Estimator を使って、パラメータ値を活用して Circuit の再利用性を高めながら、単一のジョブで複数の実験を実行します。以下の例では、ステップ 1 と 2 が V1 と V2 で同じであることに注目してください。

import numpy as np

from qiskit.circuit import QuantumCircuit, Parameter
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService

# Step 1: Map classical inputs to a quantum problem

theta = Parameter("θ")

chsh_circuit = QuantumCircuit(2)
chsh_circuit.h(0)
chsh_circuit.cx(0, 1)
chsh_circuit.ry(theta, 0)

number_of_phases = 21
phases = np.linspace(0, 2 * np.pi, number_of_phases)
individual_phases = [[ph] for ph in phases]

ZZ = SparsePauliOp.from_list([("ZZ", 1)])
ZX = SparsePauliOp.from_list([("ZX", 1)])
XZ = SparsePauliOp.from_list([("XZ", 1)])
XX = SparsePauliOp.from_list([("XX", 1)])
ops = [ZZ, ZX, XZ, XX]

# Step 2: Optimize problem for quantum execution.

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
chsh_isa_circuit = pm.run(chsh_circuit)
isa_observables = [operator.apply_layout(chsh_isa_circuit.layout) for operator in ops]

from qiskit_ibm_runtime import EstimatorV2 as Estimator

# Step 3: Execute using Qiskit primitives.

# Reshape observable array for broadcasting
reshaped_ops = np.fromiter(isa_observables, dtype=object)
reshaped_ops = reshaped_ops.reshape((4, 1))

estimator = Estimator(backend, options={"default_shots": int(1e4)})
job = estimator.run([(chsh_isa_circuit, reshaped_ops, individual_phases)])
# Get results for the first (and only) PUB
pub_result = job.result()[0]
print(f">>> Expectation values: {pub_result.data.evs}")
print(f">>> Standard errors: {pub_result.data.stds}")
print(f">>> Metadata: {pub_result.metadata}")

セッションと高度なオプションを使用する

セッションと高度なオプションを活用して、QPU 上でのCircuitのパフォーマンスを最適化します。

注意

以下のコードブロックは、セッションを使用しているため、Open Plan のユーザーにはエラーが返されます。Open Plan のワークロードは、ジョブモードまたはバッチモードでのみ実行できます。

import numpy as np
from qiskit.circuit.library import IQP
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.quantum_info import SparsePauliOp, random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, Session, EstimatorV2 as Estimator

n_qubits = 127

rng = np.random.default_rng(1234)
mat = np.real(random_hermitian(n_qubits, seed=rng))
circuit = IQP(mat)
mat = np.real(random_hermitian(n_qubits, seed=rng))
another_circuit = IQP(mat)
observable = SparsePauliOp("X" * n_qubits)
another_observable = SparsePauliOp("Y" * n_qubits)

pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)
another_isa_circuit = pm.run(another_circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
another_isa_observable = another_observable.apply_layout(another_isa_circuit.layout)

service = QiskitRuntimeService()

backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)

with Session(backend=backend) as session:
estimator = Estimator()

estimator.options.resilience_level = 1

job = estimator.run([(isa_circuit, isa_observable)])
another_job = estimator.run([(another_isa_circuit, another_isa_observable)])
result = job.result()
another_result = another_job.result()

# first job
print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")

# second job
print(f" > Another Expectation value: {another_result[0].data.evs}")
print(f" > More Metadata: {another_result[0].metadata}")

Sampler V2 への移行手順

  1. from qiskit_ibm_runtime import Samplerfrom qiskit_ibm_runtime import SamplerV2 as Sampler に置き換えます。
  2. Options クラスは V2 プリミティブでは使用しないため、from qiskit_ibm_runtime import Options の文をすべて削除します。代わりに、SamplerV2 クラスの初期化時にオプションをディクショナリとして渡すか(例:sampler = Sampler(backend, options={"default_shots": 1024}))、初期化後に設定します:
    sampler = Sampler(backend)
    sampler.options.default_shots = 1024
  3. サポートされているオプションをすべて確認し、必要に応じて更新します。
  4. 実行したい各 Circuit を、適用したいオブザーバブルとパラメーター値とともにタプル(PUB)にまとめます。たとえば、circuit1parameter_set1 で実行したい場合は (circuit1, parameter_set1) を使用します。 その PUB に対して指定したいショット数を任意で設定することもできます。
  5. PUB のリストを渡すように Sampler の run() メソッドを更新します。たとえば run([(circuit1, parameter_set1)]) のようにします。 ここで shots を任意で指定することもでき、これはすべての PUB に適用されます。
  6. Sampler V2 のジョブ結果は PUB ごとにグループ化されます。インデックスを指定することで各 PUB の出力データを参照できます。Sampler V2 は重みなしのサンプルを返しますが、結果クラスにはカウントを取得する便利なメソッドがあります。たとえば:
pub_result = job.result()[0]
print(f">>> Counts: {pub_result.data.meas.get_counts()}")
print(f">>> Per-shot measurement: {pub_result.data.meas.get_counts()}")
備考

結果を取得するには古典レジスタ名が必要です。measure_all() を使用した場合、デフォルトでは meas という名前になります。Circuit を定義する際に、デフォルト以外の名前で古典レジスタを 1 つ以上作成した場合は、その名前を使用して結果を取得してください。古典レジスタ名は <circuit_name>.cregs を実行することで確認できます(例:qc.cregs)。

Sampler の完全なサンプル

単一の実験を実行する

Sampler を使用して、単一の Circuit のカウントまたは準確率分布を求めます。

import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

service = QiskitRuntimeService()

backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)

n_qubits = 127

mat = np.real(random_hermitian(n_qubits, seed=1234))
circuit = IQP(mat)
circuit.measure_all()

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)

sampler = Sampler(backend)
job = sampler.run([isa_circuit])
result = job.result()

1 つのジョブで複数の実験を実行する

Sampler を使用して、1 つのジョブで複数の Circuit のカウントまたは準確率分布を求めます。

import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler

service = QiskitRuntimeService()

backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)

n_qubits = 127

rng = np.random.default_rng()
mats = [np.real(random_hermitian(n_qubits, seed=rng)) for _ in range(3)]
circuits = [IQP(mat) for mat in mats]
for circuit in circuits:
circuit.measure_all()

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuits = pm.run(circuits)

sampler = Sampler(backend)
job = sampler.run(isa_circuits)
result = job.result()

for idx, pub_result in enumerate(result):
print(f" > Counts for pub {idx}: {pub_result.data.meas.get_counts()}")

パラメーター化された Circuit を実行する

パラメーター値を活用して Circuit の再利用性を高め、1 つのジョブで複数の実験を実行します。

import numpy as np
from qiskit.circuit.library import RealAmplitudes
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService

# Step 1: Map classical inputs to a quantum problem
num_qubits = 127
circuit = RealAmplitudes(num_qubits=num_qubits, reps=2)
circuit.measure_all()

# Define three sets of parameters for the circuit
rng = np.random.default_rng(1234)
parameter_values = [
rng.uniform(-np.pi, np.pi, size=circuit.num_parameters) for _ in range(3)
]

# Step 2: Optimize problem for quantum execution.

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=num_qubits)

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)

# Step 3: Execute using Qiskit primitives.

from qiskit_ibm_runtime import SamplerV2 as Sampler

sampler = Sampler(backend)
job = sampler.run([(isa_circuit, parameter_values)])
result = job.result()
# Get results for the first (and only) PUB
pub_result = result[0]
# Get counts from the classical register "meas".
print(f" >> Counts for the meas output register: {pub_result.data.meas.get_counts()}")

セッションと高度なオプションを使用する

セッションと高度なオプションを活用して、QPU 上での Circuit のパフォーマンスを最適化します。

注意

以下のコードブロックは、セッションを使用しているため、Open Plan のユーザーにはエラーが返されます。Open Plan のワークロードはジョブモードまたはバッチモードでのみ実行できます。

import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler, Session

n_qubits = 127

rng = np.random.default_rng(1234)
mat = np.real(random_hermitian(n_qubits, seed=rng))
circuit = IQP(mat)
circuit.measure_all()
mat = np.real(random_hermitian(n_qubits, seed=rng))
another_circuit = IQP(mat)
another_circuit.measure_all()

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
another_isa_circuit = pm.run(another_circuit)

service = QiskitRuntimeService()

# Turn on dynamical decoupling with sequence XpXm.
sampler.options.dynamical_decoupling.enable = True
sampler.options.dynamical_decoupling.sequence_type = "XpXm"

backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)

with Session(backend=backend) as session:
sampler = Sampler()
job = sampler.run([isa_circuit])
another_job = sampler.run([another_isa_circuit])
result = job.result()
another_result = another_job.result()

# first job
print(f" > Counts for job 1: {result[0].data.meas.get_counts()}")

# second job
print(f" > Counts for job 2: {another_result[0].data.meas.get_counts()}")

次のステップ

推奨事項