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

NoiseLearner から NoiseLearnerV3 への移行

このガイドでは、IBM Quantum® の NoiseLearner から NoiseLearnerV3 への移行手順を説明します。両方のクラスは、Pauli-Lindblad ノイズモデル に基づいてノイズプロセスを特徴付ける実験を実行しますが、入力と出力は若干異なります。

背景​

NoiseLearner クラスは、ユーザーが明示的なノイズ学習を実行できるように作成されました。結果として得られるノイズモデルは、PEA や PEC などのエラー緩和技術を適用するために IBM Quantum の Estimator に渡すことができます。

NoiseLearner は Estimator と連携して動作するように設計されているため、Estimator と同じレイヤー検出戦略を暗黙的に採用しています。この戦略は変更できません。変更すると、後続の緩和ステップが正しく機能しなくなります。

qiskit-ibm-runtime v0.47.0 以降、Executor プリミティブと ディレクテッド実行モデル に対応した新しい NoiseLearnerV3 クラスが追加されました。この新しいモデルは、設計意図をクライアント側で捉えるための部品を提供することで ホワイトボックス の体験を実現し、単一のサーバー側プリミティブ(Executor)が、指示された通りにその入力を正確に処理します。暗黙の判断を代わりに行うことはありません。元の NoiseLearner とは異なり、NoiseLearnerV3 を使用する際は回路をどのように層別化するかを自分で制御でき、このクラスはボックス化された回路命令(たとえば、一意のレイヤー)のリストを単に入力として受け取ります。

NoiseLearnerV3 は、測定ノイズ学習もサポートしています。入力リストの各命令について、ボックスに 1 量子ビットおよび 2 量子ビットゲートが含まれる場合は Pauli-Lindblad 学習プロトコルを、ボックスに測定が含まれる場合は TREX プロトコルを実行します。

移行すべきか?​

NoiseLearner はレガシーなサーバー側の Estimator でのみ動作し、NoiseLearnerV3 は Executor とクライアント側の Estimator でのみ動作します。Executor またはクライアント側の Estimator を使用している場合は、NoiseLearnerV3 に移行する必要があります。レガシーなサーバー側の Estimator は非推奨となり、qiskit-ibm-runtime v0.50.0 でクライアント側の同等品に置き換えられます。

備考

qiskit-ibm-runtime v0.50.0 以降を使用している場合は、まず サーバー側からクライアント側の Sampler と Estimator への移行 ガイドを読んで、クライアント側プリミティブに移行してください。

移行手順​

ステップ1: インポートの変更​

NoiseLearner:

from qiskit_ibm_runtime.noise_learner import NoiseLearner

NoiseLearnerV3:

from qiskit_ibm_runtime import NoiseLearnerV3

ステップ2: 入力の更新​

NoiseLearner の run() メソッドは回路または PUB のリストを取りますが、NoiseLearnerV3 の run() メソッドは命令のリストを取り、その各命令は ISA 操作を含む twirling 注釈付きの BoxOp である必要があります。使用予定のプリミティブに応じて、注釈付きボックスを生成するための便利なメソッドが用意されています。

NoiseLearner:

from qiskit_ibm_runtime.noise_learner import NoiseLearner

learner = NoiseLearner(mode=backend)
# `circuits_to_learn` is a list of ISA QuantumCircuit
learner_job = learner.run(circuits_to_learn)

クライアント側 Estimator と連携する場合の NoiseLearnerV3:

回路の実行にクライアント側 Estimator を使用する予定がある場合は、Estimator の find_unique_layers メソッドを使用して、注釈付きボックス(レイヤー)を作成できます。

from qiskit_ibm_runtime.executor_estimator import Estimator
from qiskit_ibm_runtime import NoiseLearnerV3

pubs = [...] # Your PUBs
estimator = Estimator(backend)
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(backend)
learner_job = learner.run(layers)

Executor と連携する場合の NoiseLearnerV3:

回路の実行に Executor を使用する予定がある場合は、Samplomatic の generate_boxing_pass_manager 関数を使用して、注釈付きボックスを作成することを検討してください。

from qiskit_ibm_runtime.noise_learner_v3 import NoiseLearnerV3
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic.utils import find_unique_box_instructions

# Run the boxing pass manager to group instructions into annotated boxes.
# `isa_circuit` is an ISA QuantumCircuit.
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=False,
inject_noise_targets="gates", # no measurement mitigation
inject_noise_strategy="uniform_modification",
)
boxed_circuit = boxing_pm.run(isa_circuit)

# Find unique boxed instructions.
unique_box_instructions = find_unique_box_instructions(boxed_circuit.data)

# Instantiate a NoiseLearnerV3 object and execute the noise learning program.
learner = NoiseLearnerV3(backend)
learner_job = learner.run(unique_box_instructions)

ステップ3: オプションの変換​

ほとんどの NoiseLearnerOptions フィールドは、以下を除いて NoiseLearnerV3Options に直接マップされます。

  • max_layers_to_learn: NoiseLearnerV3 では、学習するレイヤー数は渡されたレイヤー数に基づきます。

  • twirling_strategy: NoiseLearnerV3 では、twirling 戦略は(generate_boxing_pass_manager() を使用する場合などに)命令がどのようにボックス化され注釈付けされるかによって定義されます。

NoiseLearner:

from qiskit_ibm_runtime.noise_learner import NoiseLearner
from qiskit_ibm_runtime.options import NoiseLearnerOptions

# Instantiate a NoiseLearnerOptions object
learner_options = NoiseLearnerOptions(
max_layers_to_learn=3, num_randomizations=32, twirling_strategy="all"
)

learner = NoiseLearner(mode=backend, options=learner_options)
learner_job = learner.run(circuits_to_learn)

クライアント側 Estimator と連携する場合の NoiseLearnerV3:

回路の実行にクライアント側 Estimator を使用する予定がある場合は、Estimator の twirling.strategy オプションを設定できます。

from qiskit_ibm_runtime.executor_estimator import Estimator
from qiskit_ibm_runtime import NoiseLearnerV3
from qiskit_ibm_runtime.options_models import NoiseLearnerV3Options

pubs = [...] # Your PUBs
estimator = Estimator(backend)
estimator.options.resilience.pec_mitigation = True # or zne_mitigation + pea amplifier
estimator.options.twirling.strategy = "all" # set twirling strategy here

# Identify the unique layers to learn.
layers = estimator.find_unique_layers(pubs)

# Instantiate a NoiseLearnerV3 object and execute the noise learning program
learner_options = NoiseLearnerV3Options(num_randomizations=32)
learner = NoiseLearnerV3(backend, options=learner_options)

# Learn just the first 3 layers.
learner_job = learner.run(layers[:3])

Executor と連携する場合の NoiseLearnerV3:

回路の実行に Executor を使用する予定がある場合は、generate_boxing_pass_manager 関数に twirling_strategy オプションを渡すことができます。

generate_boxing_pass_manager() では、twirling_strategy の値にアンダースコア("active_accum"、"active_circuit")が使用される一方、NoiseLearnerOptions.twirling_strategy の値にはハイフン("active-accum"、"active-circuit")が使用されることに注意してください。

from qiskit_ibm_runtime.noise_learner_v3 import NoiseLearnerV3
from qiskit_ibm_runtime.options_models import NoiseLearnerV3Options
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic.utils import find_unique_box_instructions

# Run the boxing pass manager to group instructions into annotated boxes
# `isa_circuit` is an ISA QuantumCircuit
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=False,
twirling_strategy="all", # twirling strategy can be specified here
inject_noise_targets="gates",
inject_noise_strategy="uniform_modification",
)
boxed_circuit = boxing_pm.run(isa_circuit)

# Find unique boxed instructions
unique_box_instructions = find_unique_box_instructions(boxed_circuit.data)

learner_options = NoiseLearnerV3Options(num_randomizations=32)

# Instantiate a NoiseLearnerV3 object and execute the noise learning program
learner = NoiseLearnerV3(backend, options=learner_options)
# Learn just the first 3 layers.
learner_job = learner.run(unique_box_instructions[:3])

ステップ4: 結果の確認​

NoiseLearner と NoiseLearnerV3 の出力には類似した情報が含まれますが、フォーマットが異なります。コードが出力を明示的に検査している場合は更新してください。

結果属性のマッピング:

(learner_result はラーナージョブの出力です)

属性NoiseLearnerNoiseLearnerV3
結果の型NoiseLearnerResultNoiseLearnerV3Results(NoiseLearnerV3Result のシーケンス風コンテナ)
学習したレイヤー数len(learner_result.data)len(learner_result)
最初のレイヤーのデータlayer_error = learner_result.data[0]noise_map = learner_result[0].to_pauli_lindblad_map()
各レイヤーの結果の型LayerError(type(layer_error))PauliLindbladMap(type(noise_map))
エラーチャネルのジェネレーターlayer_error.error.generatorsnoise_map.generators()
エラー率layer_error.error.ratesnoise_map.rates

ステップ5: プリミティブへのノイズモデルの入力​

NoiseLearner はレガシーなサーバー側の Estimator でのみ動作し、NoiseLearnerV3 は Executor とクライアント側の Estimator でのみ動作します。ノイズモデルの指定方法は、使用するプリミティブによって若干異なります。

レガシーなサーバー側 Estimator と連携する場合の NoiseLearner:

from qiskit_ibm_runtime import Estimator as LegacyEstimator

learner_result = learner_job.result()

# Pass the noise model to the `estimator.options` attribute directly
estimator = LegacyEstimator(mode=backend)
estimator.options.resilience.layer_noise_model = learner_result
job = estimator.run(pubs)

クライアント側 Estimator と連携する場合の NoiseLearnerV3:

ステップ2で layers を生成したのと同じ Estimator を再利用してください。ラーナーが返すノイズマップはそれらのレイヤーと位置的に対応しているため、由来する Estimator に割り当てる必要があります。PEA/PEC は、ステップ2ですでにその Estimator で有効化されています。

NoiseLearnerV3 は Pauli-Lindblad と TREX の両方のプロトコルをサポートしていますが、Estimator は Pauli-Lindblad プロトコルで学習した 2 量子ビットレイヤーのノイズモデルのみを受け付けることに注意してください。

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)

Executor と連携する場合の NoiseLearnerV3:

from qiskit_ibm_runtime import Executor
from qiskit_ibm_runtime.quantum_program import QuantumProgram

# Generate a quantum program
program = QuantumProgram(shots=1000)

# Convert the NoiseLearnerV3 result to a dictionary
learner_result = learner_job.result()
noise_maps = learner_result.to_dict(
instructions=unique_box_instructions, require_refs=False
)

# Append the samplex item and execute
program.append_samplex_item(
template_circuit,
samplex=samplex,
samplex_arguments={
"pauli_lindblad_maps": noise_maps,
},
)

executor = Executor(backend)
executor_job = executor.run(program)

完全な例​

NoiseLearnerV3とクライアントサイドEstimator​

from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

from qiskit_ibm_runtime import QiskitRuntimeService, NoiseLearnerV3
from qiskit_ibm_runtime.executor_estimator import Estimator

# 1. Account + backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# 2. Circuit + observable
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)

observable = SparsePauliOp("ZZ")

# 3. Transpile to ISA
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
pubs = [(isa_circuit, isa_observable)]

# 4. Initialize Estimator with options
estimator = Estimator(backend)
estimator.options.resilience.pec_mitigation = True

# 5. Extract the unique boxed layers from PUBs
layers = estimator.find_unique_layers(pubs)

# 6. Learn the noise model for those layers
learner = NoiseLearnerV3(backend)
learner_job = learner.run(layers)
learner_result = learner_job.result()

# 7. Convert the result to Pauli-Lindblad maps and pass them to Estimator
pauli_lindblad_maps = learner_result.to_pauli_lindblad_maps()
estimator.options.resilience.layer_noise_model = zip(layers, pauli_lindblad_maps)

# 8. Execute the target PUBs
job = estimator.run(pubs)
result = job.result()

NoiseLearnerV3とExecutor​

from qiskit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager

from qiskit_ibm_runtime import QiskitRuntimeService, Executor, NoiseLearnerV3
from qiskit_ibm_runtime.quantum_program import QuantumProgram

from samplomatic import build
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic.utils import find_unique_box_instructions

# 1. Account + backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# 2. Circuit + observable
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()

# 3. Transpile to ISA
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)

# 4. Run the boxing pass manager to group instructions into annotated boxes
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=False,
inject_noise_targets="gates", # no measurement mitigation
inject_noise_strategy="uniform_modification",
)
boxed_circuit = boxing_pm.run(isa_circuit)

# 5. Find unique boxed instructions (layers)
unique_box_instructions = find_unique_box_instructions(boxed_circuit.data)

# 6. Learn the noise model for those layers
learner = NoiseLearnerV3(backend)
learner_job = learner.run(unique_box_instructions)
learner_result = learner_job.result()

# 7. Convert the NoiseLearnerV3 result to a dictionary
noise_maps = learner_result.to_dict(
instructions=unique_box_instructions, require_refs=False
)

# 8. Build the template circuit and samplex pair
template_circuit, samplex = build(boxed_circuit)

# 9. Prepare a quantum program
program = QuantumProgram(shots=1000)
program.append_samplex_item(
template_circuit,
samplex=samplex,
samplex_arguments={
"pauli_lindblad_maps": noise_maps,
},
)

executor = Executor(backend)
job = executor.run(program)
result = job.result()

参考文献​