Skip to main content
IBM Quantum Platform

NEAT を使ったジョブのデバッグ

  • このページのコードは、以下の要件に基づいて開発された。 これらのバージョンまたは新しいバージョンの使用をお勧めします。

    qiskit[all]~=2.5.1
    qiskit-ibm-runtime~=0.47.0
    qiskit-aer~=0.17
    

この Neat クラスを使用すると、Estimator ワークロードに対するノイズの影響を分析できます。 構文の検証には、 ローカルテストモードを使用してください。


Neat クラスの使用方法

リソースを大量に消費するワークロードをハードウェア上で実行する前に、 IBM Quantum のCompute NEAT(Noisy Estimator Analyzer Tool) クラスを使用することで、推定器ワークロードが正しく設定されているか、正確な結果が得られる見込みがあるか、指定された問題に対して最適なオプションが使用されているかなどを確認できます。

Neat 入力回路をクリフォード化することで、その構造と深さを保ちつつ、効率的なシミュレーションが可能になる。 クリフォード回路は同レベルのノイズに悩まされるため、目的のオリジナル回路を研究するための良い代用品となる。

まず、必要なパッケージをインポートし、 IBM Quantum Compute Service への認証を行います

環境の準備

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)

ターゲット回路を初期化する

以下の性質を持つ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)

Output:

Output of the previous code cell

観測値としてシングルパウリ 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]

Output:

Observables: ['ZIIIII', 'IZIIII', 'IIZIII', 'IIIZII', 'IIIIZI', 'IIIIIZ']

回路をクリフォード化

先に定義した PUB 回路はクリフォードではないため、古典的なシミュレーションが難しい。 しかし、 Neat to_clifford メソッドを使ってクリフォード回路にマッピングすることができます。 この to_clifford メソッドは ConvertISAToClifford のラッパーです。 特に、元の回路の非クリフォード・シングル量子ビット・ゲートをクリフォード・シングル量子ビット・ゲートに置き換えるが、2量子ビット・ゲート、量子ビットの数、回路の深さは変更しない。

クリフォード回路シミュレーションの詳細については、 Qiskit Aerプリミティブによるスタビライザー回路の効率的シミュレーションを参照してください。

まず、 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)

Output:

Output of the previous code cell

応用例1:ノイズが回路出力に与える影響を分析する

この例では、回路深さに対するPUBへの異なるノイズモデルの影響を調べるために、理想的な(ideal_sim)条件とノイズ Neat のある(noisy_sim)条件の両方でシミュレーションを実行する方法を示しています。 これは、QPU上でジョブを実行する前に、実験結果の品質に関する期待値を設定するのに有用である。 ノイズモデルの詳細については、 「Qiskit Aerプリミティブを用いた正確かつノイズを含むシミュレーション」 を参照してください。

シミュレーション結果は数学的な演算をサポートするため、互いに(あるいは実験結果と)比較し、数値を算出することができる。

Caution

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")

Output:

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.9921875 , 0.99023438, 0.99023438, 0.99023438, 0.97265625,
       0.97070312])), NeatPubResult(vals=array([0.98046875, 0.98632812, 0.98828125, 0.9921875 , 0.96289062,
       0.9765625 ])), NeatPubResult(vals=array([0.96289062, 0.96875   , 0.953125  , 0.953125  , 0.95507812,
       0.9609375 ])), NeatPubResult(vals=array([0.94726562, 0.95507812, 0.93945312, 0.94921875, 0.94921875,
       0.95898438])), NeatPubResult(vals=array([0.91992188, 0.92382812, 0.91015625, 0.921875  , 0.92382812,
       0.9375    ])), NeatPubResult(vals=array([0.90039062, 0.9140625 , 0.90234375, 0.92578125, 0.91601562,
       0.94335938]))])

次に、数学的演算を適用して差の絶対値を計算する。 このガイドの残りの部分では、理想的な結果とノイズの多い結果や実験的な結果を比較するために、メリットの数値として差の絶対値を使用しているが、同様のメリットの数値を設定することも可能である。

絶対的な差は、ノイズの影響が回路のサイズとともに大きくなることを示している。

# 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 "
        f"for circuits with {all_n_layers[idx]} layers:\n  {mean_vals}%\n"
    )

Output:

Mean absolute difference between ideal and noisy results for circuits with 1 layers:
  1.56%

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

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

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

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

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

この種の回路を改善するには、以下の大まかで簡略化されたガイドラインに従えばよい:

  • 平均絶対差が90%以上の場合、軽減策を講じても効果がない可能性が高い。
  • 平均絶対差が90%未満であれば、 確率的誤差増幅法(PEA )で結果を改善できる可能性が高い。
  • 平均差の絶対値が80%以下であれば、 ゲート折りたたみ式のZNEでも結果を改善できる可能性が高い。

上記の差の絶対値はすべて90%未満であるため、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 "
        f"for circuits with {all_n_layers[idx]} layers:\n  {mean_values}%\n"
    )

Output:

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%

このように、ノイズモデルがあれば、QPUで実行する前に、関心のある(クリフォード化された)PUBに対するノイズの影響を定量化してみることができる。


アプリケーション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 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"
    )

Output:

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

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

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

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

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

最も差の小さい結果が、どの選択肢を選ぶべきかを示唆する。


次のステップ

推奨事項
このページは役に立ちましたか?
バグや誤字の報告、またはコンテンツの要求はGitHubで行ってください。