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

Qiskit Runtimeジョブのデバッグ

パッケージバージョン

このページのコードは、以下の要件を使用して開発されました。 これらのバージョン以降を使用することを推奨します。

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
qiskit-aer~=0.17

リソース集約型のQiskit Runtimeワークロードをハードウェアで実行する前に、Qiskit RuntimeのNeat(Noisy Estimator Analyzer Tool)クラスを使用して、Estimatorワークロードのセットアップが正しいか、正確な結果を返す可能性があるか、指定された問題に最適なオプションを使用しているかなどを確認できます。

Neatは入力Circuitをクリフォード化(Cliffordize)して効率的なシミュレーションを実現しながら、その構造と深さを維持します。クリフォードCircuitは同程度のノイズの影響を受けるため、元の対象Circuitを研究するための優れた代替手段となります。

以下の例は、Neatが有用なリソースとなり得る状況を示しています。 まず、関連するパッケージをインポートし、Qiskit Runtimeサービスへの認証を行います。

環境の準備

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-aer qiskit-ibm-runtime
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, EstimatorV2 as 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 Qubit CircuitについてSchemeします:

  • ランダムなRZ回転とCNOT Gateの層を交互に配置する。
  • ミラー構造を持つ、つまりユニタリー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)ではないため、古典的なシミュレーションが困難です。しかし、Neatto_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.99023438, 0.99609375, 0.9921875 , 0.99023438, 0.99414062,
0.99414062])), NeatPubResult(vals=array([0.984375 , 0.99414062, 0.98242188, 0.98828125, 0.98632812,
0.99414062])), NeatPubResult(vals=array([0.96679688, 0.97070312, 0.95898438, 0.97851562, 0.98046875,
0.98828125])), NeatPubResult(vals=array([0.9453125 , 0.953125 , 0.97070312, 0.96875 , 0.98242188,
0.99023438])), NeatPubResult(vals=array([0.93164062, 0.9375 , 0.953125 , 0.96875 , 0.96484375,
0.98046875])), NeatPubResult(vals=array([0.92578125, 0.921875 , 0.93359375, 0.953125 , 0.95898438,
0.9765625 ]))])

次に、絶対差を計算するために数学的演算を適用します。このガイドの残りの部分では、理想的な結果とノイズのある結果または実験的な結果を比較するための性能指標として絶対差を使用しますが、同様の性能指標を設定できます。

絶対差は、Circuitのサイズが大きくなるにつれてノイズの影響が増加することを示しています。

# Figure of merit: Absolute difference
def rdiff(res1, re2):
r"""The absolute difference between `res1` and re2`.

--> The closer to `0`, the better.
"""
d = abs(res1 - re2)
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 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:
0.72%

Mean absolute difference between ideal and noisy results for circuits with 2 layers:
1.17%

Mean absolute difference between ideal and noisy results for circuits with 3 layers:
2.6%

Mean absolute difference between ideal and noisy results for circuits with 4 layers:
3.16%

Mean absolute difference between ideal and noisy results for circuits with 5 layers:
4.4%

Mean absolute difference between ideal and noisy results for circuits with 6 layers:
5.5%

このタイプの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"], 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 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:
0.0%

Mean absolute difference between ideal and noisy results for circuits with 2 layers:
0.0%

Mean absolute difference between ideal and noisy results for circuits with 3 layers:
0.0%

Mean absolute difference between ideal and noisy results for circuits with 4 layers:
0.0%

Mean absolute difference between ideal and noisy results for circuits with 5 layers:
0.0%

Mean absolute difference between ideal and noisy results for circuits with 6 layers:
0.0%

示されているように、ノイズモデルが与えられると、対象の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"

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 tell 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 {factors}:\n {np.round(np.mean(d), 2)}%\n"
)
Mean absolute difference for factors [1, 1.1]:
6.83%

Mean absolute difference for factors [1, 1.1, 1.2]:
8.76%

Mean absolute difference for factors [1, 1.5, 2]:
8.03%

Mean absolute difference for factors [1, 1.5, 2, 2.5, 3]:
10.17%

Mean absolute difference for factors [1, 4]:
8.02%

最も小さい差を持つ結果が、どのオプションを選択すべきかを示しています。

次のステップ

推奨事項