サーバー側からクライアント側の Sampler と Estimator への移行
このガイドでは、IBM Quantum® の サーバー側 の Sampler および Estimator 実装から、qiskit-ibm-runtime の新しい クライアント側 実装への移行方法について説明します。インターフェースとオプションはほとんど変更されていないため、ほとんどのコードはそのまま実行できますが、理解しておくべきいくつかの動作の違いがあります。
背景
Sampler と Estimator は、Qiskit で定義されたプリミティブの インターフェース です。IBM Quantum Compute Service(旧 Qiskit Runtime)は、これまでランタイム環境内でこれらのプリミティブの 実装 を提供してきました。sampler.run() または estimator.run() を呼び出すと、リクエストがサービスに送信され、エラー抑制や緩和を含む すべて の計算がサーバー側で行われます。
このブラックボックス的な体験は便利で、実装の詳細を気にする必要がありません。しかし、処理中に何が起きているかを見ることができないため、プリミティブのデバッグ、カスタマイズ、または学習が難しくなります。
新たに導入された ディレクテッド実行モデル は、この逆のアプローチを取り、ホワイトボックス の体験を提供します。すべての設計意図はクライアント側で捉えられ、単一のサーバー側プリミティブである Executor が、指示された通りにその入力を正確に処理します。暗黙の判断を代わりに行うことはありません。
qiskit-ibm-runtime v0.50.0 以降、Sampler と Estimator は Executor の上に クライアント側 で再実装されています。これらは以前と同じ利便性と抽象化を提供しつつ、必要に応じて実装の詳細を確認できるようになりました。インターフェースとオプションはほぼ同じままであるため、移行はシームレスに行えるはずです。
注: IBM Quantum は、Sampler と Estimator インターフェースのバージョン 2(BaseSamplerV2 と BaseEstimatorV2)のみをサポートしています。そのため、このガイドでは単に Sampler および Estimator と呼びます。
インポートの更新
現在は、新しい実装を専用のモジュールから明示的にインポートする必要があります。
from qiskit_ibm_runtime.executor_sampler import Sampler
from qiskit_ibm_runtime.executor_estimator import Estimator
近い将来、トップレベルのインポートが新しいクライアント側の実装に解決されるようになり、コードの変更は不要になります。
# Coming soon — the following code will import the new client-side implementations.
from qiskit_ibm_runtime import Sampler, Estimator
同様に、型指定されたオプションオブジェクトを構築する場合は、代わりに qiskit_ibm_runtime.options_models からインポートするか、単純なネストされた dict を渡す必要があります。
from qiskit_ibm_runtime.options_models import SamplerOptions, EstimatorOptions
変わらない点
-
modeとoptionsを使用したプリミティブの構築。 -
run()のシグネチャと PUB フォーマット。 -
オプションツリー(
options.twirling、options.resilience、options.default_shotsなど)。 -
job.result()によって返される結果のデータ構造。
新しい Sampler における非互換な変更
| 変更 | 移行のアクション |
|---|---|
基盤となるプリミティブが Executor になりました。IBM Quantum Platform のユーザーインターフェースと job.primitive_id はどちらも、sampler の代わりに executor を表示します。 | job.primitive_id を参照するコードを更新してください。 |
新しい実装は、Sampler の入力を Executor の入力にマップするため、job.inputs は Executor の入力を返します。 | job.inputs を参照するコードを更新してください。ジョブの入力 を参照してください。 |
より多くの前処理と後処理がクライアント側で行われるようになったため、sampler.run() と job.result() は以前より時間がかかる場合があります。 | INFO ログを有効にして、クライアント側の処理の進行状況を確認してください。INFO ログを有効にする を参照してください。 |
回路のメタデータは結果のメタデータにコピーされます。結果のメタデータで許可されるデータ型は、str、float、int、bool、およびそれらの型のリストまたは辞書に限定されるようになりました。 | 他のデータ型が必要な場合は、(たとえば base64 で)文字列としてエンコードしてください。 |
オプションクラス(options_models.SamplerOptions など)は、データクラスではなく Pydantic モデルになったため、asdict() を使用して Python の辞書に変換することはできなくなりました。 | 代わりに options.model_dump() を使用してください。 |
以前は V2 サフィックスが付いていたオプションクラス(ExecutionOptionsV2 など)は、V1 プリミティブがサポートされなくなったため、サフィックスが付かなくなりました。 | これらのオプションクラスの V2 サフィックスを削除してください。ExecutionOptionsV2 を ExecutionOptions に、ResilienceOptionsV2 を ResilienceOptions に、SamplerExecutionOptionsV2 を SamplerExecutionOptions に置き換えます。 |
twirling が有効で、shots(PUB 内または run() 内)、shots_per_randomization、num_randomizations が すべて 指定されている場合、num_randomizations * shots_per_randomization が shots よりも優先されます。 | shots の値を使用したい場合は、num_randomizations と shots_per_randomization を省略してください。 |
一部の入力検証がサーバー側に移動し、IBMInputValueError の代わりに RuntimeError が送出されるようになりました。 | コードがキャッチする例外の種類を更新してください。 |
| 単一のジョブ内で複数の異なるショット値を混在させることはサポートされなくなりました。 | ショット値ごとに別々のジョブを送信してください。検討事項については、ジョブの分割 を参照してください。 |
新しい Estimator における非互換な変更
| 変更 | 移行のアクション |
|---|---|
基盤となるプリミティブが Executor になりました。IBM Quantum Platform のユーザーインターフェースと job.primitive_id はどちらも、estimator の代わりに executor を表示します。 | job.primitive_id を参照するコードを更新してください。 |
新しい実装は、Estimator の入力を Executor の入力にマップするため、job.inputs は Executor の入力を返します。 | job.inputs を参照するコードを更新してください。ジョブの入力 を参照してください。 |
より多くの前処理と後処理がクライアント側で行われるようになったため、estimator.run() と job.result() は以前より時間がかかる場合があります。 | INFO ログを有効にして、クライアント側の処理の進行状況を確認してください。INFO ログを有効にする を参照してください。 |
回路のメタデータは結果のメタデータにコピーされます。結果のメタデータで許可されるデータ型は、str、float、int、bool、およびそれらの型のリストまたは辞書に限定されるようになりました。 | 他のデータ型が必要な場合は、(たとえば base64 で)文字列としてエンコードしてください。 |
オプションクラス(options_models.EstimatorOptions など)は、データクラスではなく Pydantic モデルになったため、asdict() を使用して Python の辞書に変換することはできなくなりました。 | 代わりに options.model_dump() を使用してください。 |
以前は V2 サフィックスが付いていたオプションクラス(ExecutionOptionsV2 など)は、V1 プリミティブがサポートされなくなったため、サフィックスが付かなくなりました。 | これらのオプションクラスの V2 サフィックスを削除してください。ExecutionOptionsV2 を ExecutionOptions に、ResilienceOptionsV2 を ResilienceOptions に置き換えます。 |
| 選択されたサブセットではなく、すべての入力オプションが結果のメタデータに返されます。 | なし — これは情報提供のみです。 |
一部の入力検証がサーバー側に移動し、IBMInputValueError の代わりに RuntimeError が送出されるようになりました。 | コードがキャッチする例外の種類を更新してください。 |
| PEA と PEC の暗黙のノイズ学習は行われなくなりました。TREX の測定ノイズ学習は引き続きサポートされています。 | ノイズモデルを個別に学習し、Estimator に渡してください。PEA と PEC の明示的なノイズ学習を実行する を参照してください。 |
ResilienceOptions.layer_noise_model の入力タイプが異なり、NoiseLearnerV3 の結果から構築できます。 | ノイズモデルを NoiseLearnerV3 を使用して学習し、Estimator に渡す方法については、PEA と PEC の明示的なノイズ学習を実行する を参照してください。 |
MeasureNoiseLearningOptions.shots_per_randomization はサポートされなくなりました。 | 測定ノイズ学習回路を含む、ジョブ内のすべての回路に単一のショット値が使用されます。異なるショット値を使用する必要がある場合は、Estimator の外部で qiskit-mitigation を使用して TREX を適用してください。 |
| 単一のジョブ内で複数の異なる精度値を混在させることはサポートされなくなりました。 | 必要な精度ごとに別々のジョブを送信してください。検討事項については、ジョブの分割 を参照してください。 |
seed_estimator オプションはサポートされなくなりました。 | options.seed_estimator の代入を削除してください(設定すると ValidationError が送出されます)。クライアント側に相当するものはないため、このシードによる結果の再現性はなくなりました。 |
INFO ログを有効にする
より多くの処理がクライアント側で行われるようになったため、その処理の進行状況を確認できると便利です。qiskit_ibm_runtime ロガーの INFO レベルのログを有効にします。
import logging
logger = logging.getLogger("qiskit_ibm_runtime")
logger.setLevel(logging.INFO)
PEA と PEC の明示的なノイズ学習を実行する
新しい Estimator は、PEA または PEC エラー緩和メソッドが選択されている場合でも、暗黙的な ノイズ学習を行わなくなりました。ノイズモデルを明示的に学習し、渡す必要があります。新しい NoiseLearnerV3 を使用して、回路がどのようにレイヤーに層別化されるかを制御します。これは、ボックス化された回路命令(たとえば、一意のレイヤー)のリストを入力として受け取ります。
PEA と PEC では、この明示的なパターンが 必須 になりました。ノイズ学習のステップを省略すると、コードが失敗します。TREX の測定ノイズ学習は影響を受けず、これまでどおり動作します。
同様に、コードが NoiseLearner を使用し、結果として得られるノイズモデルをサーバー側の Estimator に渡している場合は、NoiseLearnerV3 へ移行 する必要があります。新しい Estimator と互換性のない古い NoiseLearner は使用しないでください。
サーバー側の Estimator(LayerNoiseLearningOptions)のノイズ学習オプションはすべて、max_layers_to_learn を除き、NoiseLearnerV3 のオプション(NoiseLearnerV3Options)に直接マップされます。学習するレイヤー数は、代わりに NoiseLearnerV3 に渡されたレイヤー数に基づきます。
例:
サーバー側 Estimator(PEC を有効にした場合):
from qiskit_ibm_runtime import Estimator
pubs = [...] # Your PUBs
estimator = Estimator(mode, options)
estimator.options.resilience.pec_mitigation = True # or zne_mitigation + pea amplifier
estimator.options.resilience.layer_noise_learning.num_randomizations = 64
job = estimator.run(pubs)
クライアント側 Estimator(PEC を有効にした場合):
from qiskit_ibm_runtime.executor_estimator import Estimator
from qiskit_ibm_runtime import NoiseLearnerV3
pubs = [...] # Your PUBs
estimator = Estimator(mode, options)
estimator.options.resilience.pec_mitigation = True # or zne_mitigation + pea amplifier
# Identify the unique layers to learn.
layers = estimator.find_unique_layers(pubs)
# Learn the noise model for those layers (runs as a separate job).
learner = NoiseLearnerV3(mode)
learner.options.num_randomizations = 64 # Same as layer_noise_learning.num_randomizations
learner_job = learner.run(layers)
learner_result = learner_job.result()
# Convert results to Pauli-Lindblad noise maps.
pauli_lindblad_maps = learner_result.to_pauli_lindblad_maps()
# Assign the learned noise maps so PEA/PEC uses them.
estimator.options.resilience.layer_noise_model = zip(layers, pauli_lindblad_maps)
# Now execute the target PUBs.
job = estimator.run(pubs)
NoiseLearner から NoiseLearnerV3 への移行
NoiseLearner は Estimator のサーバー側実装でのみ動作します。そのため、コードが NoiseLearner を使用してノイズモデルを学習し、Estimator に渡している場合は、NoiseLearnerV3 を使用するようにコードを更新する必要があります。
詳細については、NoiseLearner から NoiseLearnerV3 への移行 ガイドを参照してください。
ジョブの分割
単一のジョブ内で混在したショット値または精度値がサポートされなくなったため、1 つのジョブを複数に分割する必要がある場合は、以下を考慮してください。
-
PUB をターゲット値ごとにグループ化してください。PUB ごとに 1 つのジョブではなく、異なる値ごとに 1 つのジョブとします。 分割は再グループ化であるため、送信する PUB の合計数は変わりません。たとえば、
[A@0.01, B@0.05, C@0.01]が与えられた場合、precision=0.01で[A, C]、precision=0.05で[B]の 2 つのジョブを送信します。各ジョブには固定のオーバーヘッドが伴うため、AとCを別々のジョブとして送信するのは効率が悪くなります。 -
一度学習して、そのノイズモデルをすべての分割ジョブで使用してください。 すべてのレイヤーの和集合に対して、単一の
NoiseLearnerV3ジョブを実行する方が効率的です。ノイズ学習ジョブの結果には、各入力命令に対応するNoiseLearnerV3Resultオブジェクトのリストが、入力リストと同じ順序で含まれます。このノイズ学習ジョブの出力は、分割されたすべての(Estimator)ジョブで使用でき、分割ジョブの PUB に含まれないレイヤーのノイズモデルは無視されます。 -
分割したジョブをすべて先に
Batchで送信してから、結果を収集してください。Batch実行モードは、複数のジョブがある場合に効率的な並列実行を提供します。ただし、job.result()はブロッキングであるため、送信ループ内でそれを呼び出すとジョブが直列化され、Batchを使用する利点が失われます。必ず(以下に示す)全て送信してから収集するパターンを使用してください。
次の例では、pub1 と pub2 は precision=0.5 を必要とし、pub3 は precision=0.1 を必要とします。
group1_pubs = [pub1, pub2]
group2_pubs = [pub3]
with Batch(backend=backend) as batch:
estimator = Estimator(mode=batch)
estimator.options.resilience.pec_mitigation = True
# Learn once, over the union of every job's layers.
all_layers = estimator.find_unique_layers(group1_pubs + group2_pubs)
learner_job = NoiseLearnerV3(mode=batch).run(all_layers)
learner_result = learner_job.result()
pauli_lindblad_maps = learner_result.to_pauli_lindblad_maps()
# Assign the learned noise maps. Any layers not found in the input PUBs are ignored.
estimator.options.resilience.layer_noise_model = zip(all_layers, pauli_lindblad_maps)
# Submit every split job with different precision values.
jobs = []
jobs.append(estimator.run(group1_pubs, precision=0.5))
jobs.append(estimator.run(group2_pubs, precision=0.1))
# Block once, at the end — the jobs run in parallel.
results = [job.result() for job in jobs]
ジョブ入力の構造
新しい実装は、Sampler または Estimator の入力を Executor の入力にマップするため、job.inputs は Executor の入力を含む辞書を返します。この辞書には次のキーがあります。
-
options: 入力のExecutorOption。 -
quantum_program: 入力のQuantumProgram -
schema_version: 使用されたサーバー側のスキーマバージョン。
コードがジョブに指定されたオプションを見つけるために job.inputs['options'] を使用していた場合、代わりに job.result().metadata['options'] を使用できるようになりました。
フェイク・バックエンドを使用してローカルでテストする
ハードウェアに送信する前に、Fake* バックエンドに対して移行後のコードを検証し、構文エラーを早期に検出できます。ローカルテストモードに関する以下の詳細に注意してください。
-
ハードウェアの結果は再現されません。 ローカルのノイズありシミュレーションは、実際のデバイスのノイズを完全には再現しないため、出力が異なる場合があります。実行することで、オプションのパスと値の型が正しいことを検証できます。
-
NoiseLearnerV3には ローカルテストモードがありません。そのmodeは実際のBackend、Session、またはBatchのみを受け付けるため、フェイク・バックエンドに対してノイズ学習ステップを実行することはできません。代わりに、NoiseLearnerV3API リファレンス に対してコードのその部分を検証してください。コンストラクター、run(instructions)の入力形式、および(一意のレイヤー・ヘルパーなどの)ヘルパーが文書通りに使用されていることを確認してください。
効率的なローカルシミュレーションのために回路をクリフォード化する
フェイク・バックエンドは状態ベクトル(ノイズあり)シミュレーターを使用しており、そのコストは量子ビット数と深さに対して指数関数的に増大します。そのため、現実的なワークロード回路は、ハングしたりメモリを使い果たしたりする可能性があります。ローカルテストではオプションのパスを実行するだけでよく(物理的な結果を再現する必要はないため)、まず ConvertISAToClifford を使用して回路をクリフォード回路に縮小してください。これは、各 RZ/RZZ/RX 角度を最も近い π/2 の倍数に丸めます。クリフォード回路は、サイズに関わらず効率的にシミュレート(スタビライザー・シミュレーション)できます。
from qiskit.transpiler import PassManager
from qiskit_ibm_runtime.transpiler.passes import ConvertISAToClifford
clifford = PassManager([ConvertISAToClifford()]).run(isa_circuit)
# run `clifford` (not the original) through the fake-backend primitive
ConvertISAToClifford は、入力として ISA 回路が必要です(バックエンドを対象とした generate_preset_pass_manager(...).run(...) の出力)。ローカル PUB を構築する際には、以下の結果を考慮する必要があります。
-
.layout属性は失われます。 クリフォード化された回路は同じ量子ビット数を保持しますが、clifford.layoutはNoneになるため、observable.apply_layout(clifford.layout)は失敗します。代わりに、クリフォード化前の ISA 回路から観測量をレイアウトしてください。isa_obs = observable.apply_layout(isa_circuit.layout)としてから、(clifford, isa_obs)を実行します。 -
パラメーターは束縛されます。 回転角度を丸めることで、パラメトリックな ISA 回路が具体的なクリフォード回路になるため、
clifford.num_parametersは0になります。パラメーター値の配列をまだ保持している PUB は、変換に失敗します。ローカル実行では、PUB からパラメーター配列を削除してください。ハードウェア実行では、元のパラメトリック回路とその値がそのまま保持されます。