ノイズ学習ヘルパー
このページのコードは、以下の要件に基づいて開発された。 これらのバージョンまたは新しいバージョンの使用をお勧めします。
qiskit[all]~=2.5.2 qiskit-ibm-runtime~=0.47.0 samplomatic~=0.21.0
エラー軽減手法であるPEA とPEC は、いずれもパウリ・リンドブラッドノイズモデルに基づくノイズ学習コンポーネントを利用しており、これは通常、1つ以上のジョブを介して qiskit-ibm-runtime 送信された後、実行中に管理されるものであり、適合されたノイズモデルへのローカルアクセスは一切行われない。 ただし、 v0.27.1 時点 qiskit-ibm-runtime では、これらのノイズ学習実験の結果を取得するために、および関連する NoiseLearnerOptions クラ NoiseLearner スが作成されています。 これらの結果は、ローカルにファイル NoiseLearnerResult として保存し、後の実験で入力データとして使用することができます。 このページでは、その使用方法と利用可能なオプションの概要を説明します。
さらに、 v0.47.0 以降では qiskit-ibm-runtime 、Executor プリミティブと互換性のある新しい NoiseLearnerV3 クラスが用意されています。 この新バージョンもディレクテッド実行モデルの一部であり、学習対象とするレイヤーを明示的に指定できるようになります。
NoiseLearner サーバーサイドの EstimatorV2 でのみ動作し、また、Executorおよび新しいクライアントサイドのEstimator(qiskit-ibm-runtime v0.50.0 で導入されたもの)でのみ NoiseLearnerV3 動作します。
NoiseLearner
概要
この NoiseLearner 講義では、1つ(または複数)の回路について、パウリ・リンドブラッドのノイズモデルに基づいてノイズ過程を特徴づける実験を行います。 このクラスは、学習実験を実行するメソッド run() を備えており、回路のリストまたは PUB を入力として受け取り、学習されたノイズチャネルと、送信されたジョブに関するメタデータを含むオブジェクト NoiseLearnerResult を返します。 以下は、ヘルパープログラムの使用方法を示すコードの抜粋です。
from qiskit import QuantumCircuit
from qiskit.transpiler import CouplingMap
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2
from qiskit_ibm_runtime.noise_learner import NoiseLearner
from qiskit_ibm_runtime.options import (
NoiseLearnerOptions,
ResilienceOptionsV2,
EstimatorOptions,
)
# Build a circuit with two entangling layers
num_qubits = 27
edges = list(CouplingMap.from_line(num_qubits, bidirectional=False))
even_edges = edges[::2]
odd_edges = edges[1::2]
circuit = QuantumCircuit(num_qubits)
for pair in even_edges:
circuit.cx(pair[0], pair[1])
for pair in odd_edges:
circuit.cx(pair[0], pair[1])
# Choose a backend to run on
service = QiskitRuntimeService()
backend = service.least_busy()
# Transpile the circuit for execution
pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
circuit_to_learn = pm.run(circuit)
# Instantiate a NoiseLearner object and execute the noise learning program
learner = NoiseLearner(mode=backend)
job = learner.run([circuit_to_learn])
noise_model = job.result()その結果得られるのは、対象回路に属する個々のエンタングルメント層ごとにノイズモデルを含むオブジェクト LayerError のリスト NoiseLearnerResult.data です。 それぞれは、回路と一連の量子ビットラベルという形式のレイヤー情報に加え、そのレイヤーについて学習されたノイズモデルの PauliLindbladError 情報を格納 LayerError しています。
import numpy
print(
f"Noise learner result contains {len(noise_model.data)} entries"
f" and has the following type:\n {type(noise_model)}\n"
)
print(
f"Each element of `NoiseLearnerResult` then contains"
f" an object of type:\n {type(noise_model.data[0])}\n"
)
# Results are truncated
with numpy.printoptions(threshold=200):
print(
f"And each of these `LayerError` objects possess"
f" data on the generators for the error channel: \n"
f"{noise_model.data[0].error.generators}\n"
)
# Results are truncated
with numpy.printoptions(threshold=200):
print(
f"Along with the error rates: \n{noise_model.data[0].error.rates}\n"
)Output:
Noise learner result contains 2 entries and has the following type:
<class 'qiskit_ibm_runtime.utils.noise_learner_result.NoiseLearnerResult'>
Each element of `NoiseLearnerResult` then contains an object of type:
<class 'qiskit_ibm_runtime.utils.noise_learner_result.LayerError'>
And each of these `LayerError` objects possess data on the generators for the error channel:
['IIIIIIIIIIIIIIIIIIIIIIIIIIX', 'IIIIIIIIIIIIIIIIIIIIIIIIIIY',
'IIIIIIIIIIIIIIIIIIIIIIIIIIZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIXI',
'IIIIIIIIIIIIIIIIIIIIIIIIIXX', 'IIIIIIIIIIIIIIIIIIIIIIIIIXY',
'IIIIIIIIIIIIIIIIIIIIIIIIIXZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIYI',
'IIIIIIIIIIIIIIIIIIIIIIIIIYX', 'IIIIIIIIIIIIIIIIIIIIIIIIIYY',
'IIIIIIIIIIIIIIIIIIIIIIIIIYZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIZI',
'IIIIIIIIIIIIIIIIIIIIIIIIIZX', 'IIIIIIIIIIIIIIIIIIIIIIIIIZY',
'IIIIIIIIIIIIIIIIIIIIIIIIIZZ', 'IIIIIIIIIIIIIIIIIIIIIIIIXII',
'IIIIIIIIIIIIIIIIIIIIIIIIXXI', 'IIIIIIIIIIIIIIIIIIIIIIIIXYI',
'IIIIIIIIIIIIIIIIIIIIIIIIXZI', 'IIIIIIIIIIIIIIIIIIIIIIIIYII',
'IIIIIIIIIIIIIIIIIIIIIIIIYXI', 'IIIIIIIIIIIIIIIIIIIIIIIIYYI',
'IIIIIIIIIIIIIIIIIIIIIIIIYZI', 'IIIIIIIIIIIIIIIIIIIIIIIIZII',
'IIIIIIIIIIIIIIIIIIIIIIIIZXI', 'IIIIIIIIIIIIIIIIIIIIIIIIZYI',
'IIIIIIIIIIIIIIIIIIIIIIIIZZI', 'IIIIIIIIIIIIIIIIIIIIIIIXIII',
'IIIIIIIIIIIIIIIIIIIIIIIXXII', 'IIIIIIIIIIIIIIIIIIIIIIIXYII',
'IIIIIIIIIIIIIIIIIIIIIIIXZII', 'IIIIIIIIIIIIIIIIIIIIIIIYIII',
'IIIIIIIIIIIIIIIIIIIIIIIYXII', 'IIIIIIIIIIIIIIIIIIIIIIIYYII',
'IIIIIIIIIIIIIIIIIIIIIIIYZII', 'IIIIIIIIIIIIIIIIIIIIIIIZIII',
'IIIIIIIIIIIIIIIIIIIIIIIZXII', 'IIIIIIIIIIIIIIIIIIIIIIIZYII',
'IIIIIIIIIIIIIIIIIIIIIIIZZII', 'IIIIIIIIIIIIIIIIIIIIIIXIIII',
'IIIIIIIIIIIIIIIIIIIIIIXXIII', 'IIIIIIIIIIIIIIIIIIIIIIXYIII',
'IIIIIIIIIIIIIIIIIIIIIIXZIII', 'IIIIIIIIIIIIIIIIIIIIIIYIIII',
'IIIIIIIIIIIIIIIIIIIIIIYXIII', 'IIIIIIIIIIIIIIIIIIIIIIYYIII',
'IIIIIIIIIIIIIIIIIIIIIIYZIII', 'IIIIIIIIIIIIIIIIIIIIIIZIIII',
'IIIIIIIIIIIIIIIIIIIIIIZXIII', 'IIIIIIIIIIIIIIIIIIIIIIZYIII',
'IIIIIIIIIIIIIIIIIIIIIIZZIII', 'IIIIIIIIIIIIIIIIIIIIIXIIIII',
'IIIIIIIIIIIIIIIIIIIIIXIIIIX', 'IIIIIIIIIIIIIIIIIIIIIXIIIIY',
'IIIIIIIIIIIIIIIIIIIIIXIIIIZ', 'IIIIIIIIIIIIIIIIIIIIIYIIIII',
'IIIIIIIIIIIIIIIIIIIIIYIIIIX', 'IIIIIIIIIIIIIIIIIIIIIYIIIIY',
'IIIIIIIIIIIIIIIIIIIIIYIIIIZ', 'IIIIIIIIIIIIIIIIIIIIIZIIIII',
'IIIIIIIIIIIIIIIIIIIIIZIIIIX', 'IIIIIIIIIIIIIIIIIIIIIZIIIIY',
'IIIIIIIIIIIIIIIIIIIIIZIIIIZ', 'IIIIIIIIIIIIIIIIIIIIXIIIIII',
'IIIIIIIIIIIIIIIIIIIIXIXIIII', 'IIIIIIIIIIIIIIIIIIIIXIYIIII',
'IIIIIIIIIIIIIIIIIIIIXIZIIII', 'IIIIIIIIIIIIIIIIIIIIYIIIIII',
'IIIIIIIIIIIIIIIIIIIIYIXIIII', 'IIIIIIIIIIIIIIIIIIIIYIYIIII',
'IIIIIIIIIIIIIIIIIIIIYIZIIII', 'IIIIIIIIIIIIIIIIIIIIZIIIIII',
'IIIIIIIIIIIIIIIIIIIIZIXIIII', 'IIIIIIIIIIIIIIIIIIIIZIYIIII', ...]
Along with the error rates:
[0.00037 0.00043 0.00032 ... 0.00031 0.00035 0.00017]
ノイズ学習結果の LayerError.error 属性には、適合したパウリ・リンドブラッド・モデルの発電機とエラー・レートが含まれており、次のような形になっている
ここで、 は LayerError.rates で、 は LayerError.generators で指定されているパウリ演算子である。
ノイズ学習オプション
オブジェクト NoiseLearner をインスタンス化する際、入力する値としていくつかの選択肢から選ぶことができます。 これらのオプションは クラス qiskit_ibm_runtime.options.NoiseLearnerOptions によってカプセル化されており、学習するレイヤーの最大数、ランダム化の回数、ツイリング戦略などを指定する機能が含まれています。 詳細については、API NoiseLearnerOptions ドキュメントを参照してください。
NoiseLearnerOptions 以下は、実験 NoiseLearner で を使用する方法を示す簡単な例です:
# Build a GHZ circuit
circuit = QuantumCircuit(10)
circuit.h(0)
circuit.cx(range(0, 9), range(1, 10))
# Choose a backend to run on
service = QiskitRuntimeService()
backend = service.least_busy()
# Transpile the circuit for execution
pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
circuit_to_run = pm.run(circuit_to_learn)
# Instantiate a NoiseLearnerOptions object
learner_options = NoiseLearnerOptions(
max_layers_to_learn=3, num_randomizations=32, twirling_strategy="all"
)
# Instantiate a NoiseLearner object and execute the noise learning program
learner = NoiseLearner(mode=backend, options=learner_options)
job = learner.run([circuit_to_run])
noise_model = job.result()原始体にノイズモデルを入力する
回路で学習されたノイズモデルは、 IBM QuantumEstimatorV2 プリミティブへの入力としても使用できます。 これは、いくつかの異なる方法でプリミティブに渡すことができます。 次の 3 つの例では、Estimator プリミティブをインスタンス化する前に オブジェクト ResilienceOptionsV2 を使用する方法や、適切な形式の辞書を渡す方法など、ノイズモデルを 属性 estimator.options に直接渡す方法を示しています。
# Pass the noise model to the `estimator.options` attribute directly
estimator = EstimatorV2(mode=backend)
estimator.options.resilience.layer_noise_model = noise_model# Specify options through a ResilienceOptionsV2 object
resilience_options = ResilienceOptionsV2(layer_noise_model=noise_model)
estimator_options = EstimatorOptions(resilience=resilience_options)
estimator = EstimatorV2(mode=backend, options=estimator_options)# Specify options by using a dictionary
options_dict = {
"resilience_level": 2,
"resilience": {"layer_noise_model": noise_model},
}
estimator = EstimatorV2(mode=backend, options=options_dict)ノイズモデルがオブジェクト EstimatorV2 に渡されると、通常通りワークロードを実行し、エラーの緩和を行うことができます。
NoiseLearnerV3
概要
NoiseLearnerと同様に、この NoiseLearnerV3 クラスは、1つまたは複数の回路について、パウリ・リンドブラッドのノイズモデルに基づいてノイズ過程を特徴づける実験を行う。 その run() メソッドは命令のリストを受け取ります。各命令は、 ISA 演算を含むtwirled-annotated BoxOp でなければなりません。
ジョブ NoiseLearnerV3 の結果には、入力された各命令に対して1つずつ、 NoiseLearnerV3Result オブジェクトのリストが含まれます。 以下のコードは、ヘルパープログラムの使用方法を示しています。
from qiskit import QuantumCircuit
from qiskit.transpiler import CouplingMap
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, Executor
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
# Build a circuit with two entangling layers
num_qubits = 27
edges = list(CouplingMap.from_line(num_qubits, bidirectional=False))
even_edges = edges[::2]
odd_edges = edges[1::2]
circuit = QuantumCircuit(num_qubits)
for pair in even_edges:
circuit.cx(pair[0], pair[1])
for pair in odd_edges:
circuit.cx(pair[0], pair[1])
# Choose a backend to run on
service = QiskitRuntimeService()
backend = service.least_busy()
# Transpile the circuit for execution
pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
isa_circuit = pm.run(circuit)
# 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)
# Find unique boxed instructions
unique_box_instructions = find_unique_box_instructions(boxed_circuit.data)
print(f"Found {len(unique_box_instructions)} unique layers")
print(
f"Each instruction is of type {type(unique_box_instructions[0].operation)}"
)
print(
f"And has annotations: {unique_box_instructions[0].operation.annotations}"
)
# Instantiate a NoiseLearnerV3 object and execute the noise learning program
learner = NoiseLearnerV3(backend)
learner.options.shots_per_randomization = 128
learner.options.num_randomizations = 32
learner_job = learner.run(unique_box_instructions)
learner_result = learner_job.result()Output:
Found 3 unique layers
Each instruction is of type <class 'qiskit.circuit.controlflow.box.BoxOp'>
And has annotations: [Twirl(group='pauli', dressing='left', decomposition='rzsx'), InjectNoise(ref='r223A', modifier_ref='', site='after')]
処理の結果は、オブジェクトの NoiseLearnerV3Result リストとなり、入力された各命令セットに対して1つずつ対応します。 NoiseLearnerV3Result には、ジェネレータやエラー率などを抽出するメソッドを持つオブジェクトを PauliLindbladMap 返すメソッドがあります to_pauli_lindblad_map() 。
print(
f"The Noise learner V3 result contains {len(learner_result)} entries"
f" and each has the following type:\n {type(learner_result[0])}\n"
)
noise_map = learner_result[0].to_pauli_lindblad_map()
print(
f"After converting to PauliLindbladMap, you can extract data "
f" on the generators for the error channel "
f"(truncated to 3): \n{noise_map.generators()[:3]}\n"
)
with numpy.printoptions(threshold=20):
print(
f"Along with the error rates "
f"(truncated to 3): \n{noise_map.rates[:3]}\n"
)Output:
The Noise learner V3 result contains 3 entries and each has the following type:
<class 'qiskit_ibm_runtime.results.noise_learner_v3.NoiseLearnerV3Result'>
After converting to PauliLindbladMap, you can extract data on the generators for the error channel (truncated to 3):
<QubitSparsePauliList with 3 elements on 27 qubits: [X_5, Y_5, Z_5]>
Along with the error rates (truncated to 3):
[2.3e-04 9.0e-05 1.1e-04]
ノイズ学習オプション
NoiseLearnerV3 ランダム化の回数やレイヤーペアの深さなど、いくつかのオプションに対応しています。 プリミティブ型と同様に、オブジェクトの NoiseLearnerV3 インスタンス化中またはインスタンス化後にオプションを指定することができます。 前のコード例では、 および num_randomizations オプション shots_per_randomization の設定方法を示しました。 NoiseLearnerV3Options 詳細については、APIドキュメントを参照してください。
Executorにノイズモデルを入力する
Executorは、回路アノテーション(samplex形式)およびオプションで指定された設計意図に従います。 InjectNoise はノイズを挿入する場所を指定するための注釈であり、samplex 引 pauli_lindblad_maps 数は使用するノイズマップを指定します。
前の例の回路は、命令を注釈付きのボックスにグループ化するボクシング・パス・マネージャーを経由します。 理解しやすくするために、関連するコードをここに記載します。
inject_noise_targets=”gates”エンタングラーを含むボックスに注釈InjectNoiseを追加するように指定します。inject_noise_strategy="uniform_modification"これは、 の注釈が付いたInjectNoiseすべての同等のボックスに、同じrefとmodifier_refを割り当てることを指定します。InjectNoise.refそのボックスにノイズモデルを割り当てるために使用される一意の識別子です。InjectNoise.modifier_refボックスに割り当てられたノイズモデルを、乗数によってスケーリングできるようにします。
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=False,
inject_noise_targets="gates", # no measurement mitigation
inject_noise_strategy="uniform_modification",
)前の例の回路には3つのボックスが含まれており、そのうち2つには異なる ref 属性を持つ注釈が含まれています InjectNoise (これらは同等ではないため)。
# box_circuit comes from the example above
for idx, instruction in enumerate(boxed_circuit):
# The `InjectNoise` annotation defines which boxes to inject noise.
print(f"Annotations of box #{idx}: {instruction.operation.annotations}\n")Output:
Annotations of box #0: [Twirl(group='pauli', dressing='left', decomposition='rzsx'), InjectNoise(ref='r223A', modifier_ref='r223A', site='after')]
Annotations of box #1: [Twirl(group='pauli', dressing='left', decomposition='rzsx'), InjectNoise(ref='r621A', modifier_ref='r621A', site='after')]
Annotations of box #2: [Twirl(group='pauli', dressing='right', decomposition='rzsx')]
ジョブ NoiseLearnerV3 の結果は、Executorに渡す前に辞書に変換する必要があります。 この辞書のキーは属性 InjectNoise.ref であり、値はそれに対応するノイズマップです。 このマッピングにより、Executorはどのノイズモデルをどこに注入すべきかがわかります。
以下のコードでは、前の例で作成した回路と結果 NoiseLearnerV3 を取り込み、それらをExecutorに渡す方法を示しています。Executorは、ノイズモデルを組み込んだ回路のバリエーションを生成し、ハードウェア上で実行します。
from qiskit_ibm_runtime.quantum_program import QuantumProgram
from samplomatic import build
# Generate a quantum program
program = QuantumProgram(shots=1000)
# Build the template circuit and samplex pair
template_circuit, samplex = build(boxed_circuit)
# Convert the NoiseLearnerV3 result to a dictionary
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)クライアントサイドのEstimatorで 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()次のステップ
- 「 EstimatorOptions 」APIリファレンスおよび 「 ResilienceOptionsV2 」APIリファレンスをご確認ください。
- 利用可能なエラーの軽減および抑制手法について、詳しくご覧ください。
- Estimatorのノイズ管理の実装方法について学びましょう。