NEATでジョブをデバッグする
Package versions
このページのコードは、以下の要件を使用して開発されました。 これらのバージョン以降を使用することを推奨します。
qiskit[all]~=2.5.2
qiskit-ibm-runtime~=0.47.0
qiskit-aer~=0.17
Neatクラスを使用してEstimatorワークロードへのノイズの影響を分析できます。構文の確認には、ローカルテストモードを使用してください。
Neatクラスの使用方法
リソース集約型のワークロードをハードウェアで実行する前に、IBM Quantum Compute のNEAT(Noisy Estimator Analyzer Tool)クラスを使用して、Estimatorワークロードのセットアップが正しいか、正確な結果を返す可能性があるか、指定された問題に最適なオプションを使用しているかなどを確認できます。
Neatは入力Circuitをクリフォード化(Cliffordize)して効率的なシミュレーションを実現しながら、その構造と深さを維持します。クリフォードCircuitは同程度のノイズの影響を受けるため、元の対象Circuitを研究するための優れた代替手段となります。
まず、関連するパッケージをインポートし、IBM Quantum Compute Serviceへの認証を行います。
環境の準備
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_aer": "qiskit-aer", "qiskit_ibm_runtime": "qiskit-ibm-runtime"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
import numpy as np
import random
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.quantum_info import SparsePauliOp
from qiskit_ibm_runtime import QiskitRuntimeService, NoiseLearnerV3
# Legacy server-side primitive before qiskit-ibm-runtime 0.50.0
# from qiskit_ibm_runtime import Estimator
# Client-side primitive introduced in qiskit-ibm-runtime 0.50.0
from qiskit_ibm_runtime.executor_estimator import Estimator
from qiskit_ibm_runtime.debug_tools import Neat
from qiskit_aer.noise import NoiseModel, depolarizing_error
# Choose the least busy backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Generate a preset pass manager
# This will be used to convert the abstract circuit to an equivalent
# Instruction Set Architecture (ISA) circuit.
pm = generate_preset_pass_manager(backend=backend, optimization_level=0)
# Set the random seed
random.seed(10)
対象Circuitの初期化
以下の特性を持つ6量子ビット回路を考えます:
-
ランダムな
RZ回転とCNOTゲートのレイヤーを交互に繰り返します。 -
ミラー構造を持ちます。つまり、ユニタリー
Uを適用した後、その逆演算を適用します。
def generate_circuit(n_qubits, n_layers):
r"""
A function to generate a pseudo-random a circuit with ``n_qubits`` qubits
and ``2*n_layers`` entangling layers of the type used in this notebook.
"""
# An array of random angles
angles = [
[random.random() for q in range(n_qubits)] for s in range(n_layers)
]
qc = QuantumCircuit(n_qubits)
qubits = list(range(n_qubits))
# do random circuit
for layer in range(n_layers):
# rotations
for q_idx, qubit in enumerate(qubits):
qc.rz(angles[layer][q_idx], qubit)
# cx gates
control_qubits = (
qubits[::2] if layer % 2 == 0 else qubits[1 : n_qubits - 1 : 2]
)
for qubit in control_qubits:
qc.cx(qubit, qubit + 1)
# undo random circuit
for layer in range(n_layers)[::-1]:
# cx gates
control_qubits = (
qubits[::2] if layer % 2 == 0 else qubits[1 : n_qubits - 1 : 2]
)
for qubit in control_qubits:
qc.cx(qubit, qubit + 1)
# rotations
for q_idx, qubit in enumerate(qubits):
qc.rz(-angles[layer][q_idx], qubit)
return qc
# Generate a random circuit
qc = generate_circuit(6, 3)
# Convert the abstract circuit to an equivalent ISA circuit.
isa_qc = pm.run(qc)
qc.draw("mpl", idle_wires=0)
単一パウリZ演算子をオブザーバブルとして選択し、それらを使用してプリミティブ統合ブロック(PUB)を初期化します。
# Initialize the observables
obs = ["ZIIIII", "IZIIII", "IIZIII", "IIIZII", "IIIIZI", "IIIIIZ"]
print(f"Observables: {obs}")
# Map the observables to the backend's layout
isa_obs = [SparsePauliOp(o).apply_layout(isa_qc.layout) for o in obs]
# Initialize the PUBs, which consist of six-qubit circuits
# with `n_layers` 1, ..., 6
all_n_layers = [1, 2, 3, 4, 5, 6]
pubs = [(pm.run(generate_circuit(6, n)), isa_obs) for n in all_n_layers]
Observables: ['ZIIIII', 'IZIIII', 'IIZIII', 'IIIZII', 'IIIIZI', 'IIIIIZ']
Circuitのクリフォード化
先に定義したPUBのCircuitはクリフォード(Clifford)ではないため、古典的なシミュレーションが困難です。しかし、Neatのto_cliffordメソッドを使用して、より効率的なシミュレーションのためにクリフォードCircuitにマッピングできます。to_cliffordメソッドは、独立して使用することもできるConvertISAToClifford Transpilerパスのラッパーです。特に、元のCircuitの非クリフォード単一Qubit Gateをクリフォード単一Qubit Gateに置き換えますが、2 qubit Gate、Qubit数、またはCircuitの深さは変更しません。
クリフォードCircuitシミュレーションの詳細については、Qiskit AerプリミティブによるStabilizerCircuitの効率的なシミュレーションを参照してください。
まず、Neatを初期化します。
# You could specify a custom `NoiseModel` here. If `None`, `Neat`
# pulls the noise model from the given backend
noise_model = None
# Initialize `Neat`
analyzer = Neat(backend, noise_model)
次に、PUBをクリフォード化します。
clifford_pubs = analyzer.to_clifford(pubs)
clifford_pubs[0].circuit.draw("mpl", idle_wires=0)
アプリケーション1:Circuit出力に対するノイズの影響を分析する
この例では、Neatを使用して、理想的な条件(ideal_sim)とノイズがある条件(noisy_sim)の両方でシミュレーションを実行することにより、Circuit深さの関数としてさまざまなノイズモデルがPUBに与える影響を研究する方法を示します。これは、QPU上でジョブを実行する前に、実験結果の品質をあらかじめ見積もっておくのに役立ちます。ノイズモデルの詳細については、Qiskit Aerプリミティブによる厳密でノイズのあるシミュレーションを参照してください。
シミュレーション結果は数学的演算をサポートしているため、性能指標を計算するために互いに(または実験結果と)比較できます。
QPUはさまざまな種類のノイズの影響を受ける可能性があります。ここで使用されるQiskit Aerノイズモデルはその一部のみをシミュレートするため、実際のQPU上のノイズよりも軽度である可能性があります。
QPUからノイズモデルを初期化するときにどのエラーが含まれるかについての詳細は、AerのNoiseModel APIリファレンスを参照してください。
まず、理想的なシミュレーションとノイズのある古典的なシミュレーションを実行します。
# Perform a noiseless simulation
ideal_results = analyzer.ideal_sim(clifford_pubs)
print(f"Ideal results:\n {ideal_results}\n")
# Perform a noisy simulation with the backend's noise model
noisy_results = analyzer.noisy_sim(clifford_pubs)
print(f"Noisy results:\n {noisy_results}\n")
Ideal results:
NeatResult([NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.]))])
Noisy results:
NeatResult([NeatPubResult(vals=array([0.99609375, 0.99414062, 0.98632812, 0.9921875 , 0.97460938,
0.97851562])), NeatPubResult(vals=array([0.984375 , 0.98828125, 0.98046875, 0.98632812, 0.96679688,
0.98828125])), NeatPubResult(vals=array([0.9609375 , 0.9765625 , 0.953125 , 0.95898438, 0.95703125,
0.96679688])), NeatPubResult(vals=array([0.96875 , 0.97070312, 0.93164062, 0.94335938, 0.93164062,
0.97460938])), NeatPubResult(vals=array([0.9140625 , 0.9296875 , 0.91796875, 0.92382812, 0.90429688,
0.91992188])), NeatPubResult(vals=array([0.89648438, 0.9140625 , 0.921875 , 0.94921875, 0.92773438,
0.95117188]))])
次に、絶対差を計算するために数学的演算を適用します。このガイドの残りの部分では、理想的な結果とノイズのある結果または実験的な結果を比較するための性能指標として絶対差を使用しますが、同様の性能指標を設定できます。
絶対差は、Circuitのサイズが大きくなるにつれてノイズの影響が増加することを示しています。
# Figure of merit: Absolute difference
def rdiff(res1, res2):
r"""The absolute difference between `res1` and res2`.
--> The closer to `0`, the better.
"""
d = abs(res1 - res2)
return np.round(d.vals * 100, 2)
for idx, (ideal_res, noisy_res) in enumerate(
zip(ideal_results, noisy_results)
):
vals = rdiff(ideal_res, noisy_res)
# Print the mean absolute difference for the observables
mean_vals = np.round(np.mean(vals), 2)
print(
f"Mean absolute difference between ideal and noisy results "
f"for circuits with {all_n_layers[idx]} layers:\n {mean_vals}%\n"
)
Mean absolute difference between ideal and noisy results for circuits with 1 layers:
1.3%
Mean absolute difference between ideal and noisy results for circuits with 2 layers:
1.76%
Mean absolute difference between ideal and noisy results for circuits with 3 layers:
3.78%
Mean absolute difference between ideal and noisy results for circuits with 4 layers:
4.66%
Mean absolute difference between ideal and noisy results for circuits with 5 layers:
8.17%
Mean absolute difference between ideal and noisy results for circuits with 6 layers:
7.32%
このタイプのCircuitを改善するには、次の大まかで簡略化されたガイドラインに従います:
-
平均絶対差が90%を超える場合、エラー緩和はおそらく効果がありません。
-
平均絶対差が90%未満の場合、確率的エラー増幅(PEA)によって結果が改善される可能性があります。
-
平均絶対差が80%未満の場合、Gate折り畳みによるZNEも結果を改善できる可能性があります。
上記の絶対差はすべて90%未満であるため、元のCircuitにPEAを適用することで、その結果の品質が改善されることが期待されます。 アナライザーで異なるノイズモデルを指定できます。次の例では同じテストを実行しますが、カスタムノイズモデルを追加します。
# Set up a noise model with strength 0.02 on every two-qubit gate
noise_model = NoiseModel()
for qubits in backend.coupling_map:
noise_model.add_quantum_error(
depolarizing_error(0.02, 2), ["ecr", "cx", "cz"], qubits
)
# Update the analyzer's noise model
analyzer.noise_model = noise_model
# Perform a noiseless simulation
ideal_results = analyzer.ideal_sim(clifford_pubs)
# Perform a noisy simulation with the backend's noise model
noisy_results = analyzer.noisy_sim(clifford_pubs)
# Compare the results
for idx, (ideal_res, noisy_res) in enumerate(
zip(ideal_results, noisy_results)
):
values = rdiff(ideal_res, noisy_res)
# Print the mean absolute difference for the observables
mean_values = np.round(np.mean(values), 2)
print(
f"Mean absolute difference between ideal and noisy results "
f"for circuits with {all_n_layers[idx]} layers:\n {mean_values}%\n"
)
Mean absolute difference between ideal and noisy results for circuits with 1 layers:
4.16%
Mean absolute difference between ideal and noisy results for circuits with 2 layers:
8.92%
Mean absolute difference between ideal and noisy results for circuits with 3 layers:
12.86%
Mean absolute difference between ideal and noisy results for circuits with 4 layers:
17.45%
Mean absolute difference between ideal and noisy results for circuits with 5 layers:
25.82%
Mean absolute difference between ideal and noisy results for circuits with 6 layers:
27.96%
このように、ノイズモデルがあれば、対象のPUB(をクリフォード化したもの)をQPUで実行する前に、ノイズの影響を定量的に見積もることができます。
アプリケーション2:異なる戦略をベンチマークする
この例では、Neatを使用してPUBに最適なオプションを特定します。そのために、qiskit_aerではシミュレートできないPEAを使用した推定問題の実行を検討します。Neatを使用して、どのノイズ増幅係数が最も効果的かを判断し、QPU上で元の実験を実行する際にそれらの係数を使用できます。
# Generate a circuit with six qubits and six layers
isa_qc = pm.run(generate_circuit(6, 3))
# Use the same observables as previously
pubs = [(isa_qc, isa_obs)]
clifford_pubs = analyzer.to_clifford(pubs)
noise_factors = [
[1, 1.1],
[1, 1.1, 1.2],
[1, 1.5, 2],
[1, 1.5, 2, 2.5, 3],
[1, 4],
]
# Run the PUBs on a QPU
estimator = Estimator(backend)
estimator.options.default_shots = 100000
estimator.options.twirling.enable_gates = True
estimator.options.twirling.enable_measure = True
estimator.options.twirling.shots_per_randomization = 100
estimator.options.resilience.measure_mitigation = True
estimator.options.resilience.zne_mitigation = True
estimator.options.resilience.zne.amplifier = "pea"
# Learn noise model
layers = estimator.find_unique_layers(clifford_pubs)
learner = NoiseLearnerV3(backend)
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 uses them.
estimator.options.resilience.layer_noise_model = zip(
layers, pauli_lindblad_maps
)
jobs = []
for factors in noise_factors:
estimator.options.resilience.zne.noise_factors = factors
jobs.append(estimator.run(clifford_pubs))
results = [job.result() for job in jobs]
# Perform a noiseless simulation
ideal_results = analyzer.ideal_sim(clifford_pubs)
# Look at the mean absolute difference to quickly determine
# the best choice for your options
for factors, res in zip(noise_factors, results):
d = rdiff(ideal_results[0], res[0])
print(
f"Mean absolute difference for factors "
f"{factors}:\n {np.round(np.mean(d), 2)}%\n"
)
Mean absolute difference for factors [1, 1.1]:
10.9%
Mean absolute difference for factors [1, 1.1, 1.2]:
4.64%
Mean absolute difference for factors [1, 1.5, 2]:
4.33%
Mean absolute difference for factors [1, 1.5, 2, 2.5, 3]:
5.66%
Mean absolute difference for factors [1, 4]:
4.53%
最も小さい差を持つ結果が、どのオプションを選択すべきかを示しています。
次のステップ
- Qiskitデバッグツールの概要を読みます。
- Qiskit Aerプリミティブによる厳密でノイズのあるシミュレーションについて学びます。
- 利用可能なIBM Quantum Computeオプションについて学びます。
- エラー緩和と抑制のテクニックについて学びます。
- パスマネージャーによるTranspileトピックを参照してください。
- Qiskitパターンワークフローの一部としてCircuitをトランスパイルする方法を学びます。
- デバッグツールAPIドキュメントを確認してください。