Skip to main content
IBM Quantum Platform

トランスパイラ設定を比較する

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

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

トランスパイラの設定によって回路への最適化の方法が異なり、多くの場合、従来の処理時間が長くなるという代償を伴います。 このガイドでは、回路の作成、トランスパイル、および提出までの全プロセスを順を追って説明し、さまざまな設定におけるパフォーマンスのテスト方法をご紹介します。

なお、同じ設定が、ある回路の性能を向上させる一方で、別の回路の性能を低下させる可能性があることに注意してください。 実際のハードウェア上で実行する前に、トランスパイルされた回路を必ず確認してください。


サンプル回路の設定と作成

# Create circuit to test transpiler on
from qiskit import QuantumCircuit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.circuit.library import grover_operator, DiagonalGate

# Use Statevector object to calculate the ideal output
from qiskit.quantum_info import Statevector
from qiskit.visualization import plot_histogram
from qiskit.transpiler import PassManager

from qiskit.circuit.library import XGate
from qiskit.quantum_info import hellinger_fidelity

トランスパイラが最適化を試みるための小さな回路を作成する。 この例は、状態をマークするオラクルを用いてグローバーの 111アルゴリズムを実行する回路を作成する。 次に、後で比較するために理想的な分布(完璧な量子コンピュータで無限回実行した場合に測定されるであろう値)をシミュレートする。

oracle = DiagonalGate([1] * 7 + [-1])
qc = QuantumCircuit(3)
qc.h([0, 1, 2])
qc = qc.compose(grover_operator(oracle))

qc.draw(output="mpl", style="iqp")

Output:

Output of the previous code cell
ideal_distribution = Statevector.from_instruction(qc).probabilities_dict()

plot_histogram(ideal_distribution)

Output:

Output of the previous code cell

トランスパイル

次に、QPU用の回路をトランスパイルします。 トランスパイラのパフォーマンスを、 optimization_level 設定値を 0 (最低) と 3 (最高) に設定した場合で比較します。 最適化の最低レベルでは、回路をデバイス上で動作させるために必要な最小限の処理のみを行います。具体的には、回路の量子ビットをデバイスの量子ビットにマッピングし、すべての2量子ビット操作を可能にするためにスワップゲートを追加します。 最高レベルの最適化ははるかに高度で、ゲート総数を削減するために多くの手法を用いる。 マルチ量子ビットゲートはエラー率が高く、量子ビットは時間の経過とともにデコヒーレンスを起こすため、回路が短いほどより良い結果が得られるはずである。

Important

この例では IBM Quantum® ハードウェアを使用していますが、Qiskit対応のQPUであればどれでも試すことができます。 実際の結果は異なる場合があります。

以下のセルは、 optimization_levelの値がどちらの場合でも qc 、2量子ビットゲート数を出力し、トランスパイルされた回路をリストに追加する。 トランスパイラのアルゴリズムの一部はランダム化されているため、再現性を確保するためにシードを設定します。

# Use IBM Quantum Compute Service to run jobs on hardware
from qiskit_ibm_runtime import (
    QiskitRuntimeService,
    SamplerV2 as Sampler,
)
# Select the backend with the fewest number of jobs in the queue
service = QiskitRuntimeService()
backend = service.least_busy(
    operational=True, simulator=False, min_num_qubits=127
)
backend.name

Output:

'ibm_fez'
# Need to add measurements to the circuit
qc.measure_all()

# Find the correct two-qubit gate
twoQ_gates = set(["ecr", "cz", "cx"])
for gate in backend.basis_gates:
    if gate in twoQ_gates:
        twoQ_gate = gate

circuits = []
for optimization_level in [0, 3]:
    pm = generate_preset_pass_manager(
        optimization_level, backend=backend, seed_transpiler=0
    )
    t_qc = pm.run(qc)
    print(
        f"Two-qubit gates (optimization_level={optimization_level}): ",
        t_qc.count_ops()[twoQ_gate],
    )
    circuits.append(t_qc)

Output:

Two-qubit gates (optimization_level=0):  21
Two-qubit gates (optimization_level=3):  12

CNOTは通常エラー率が高いため、でトランスパイルされた回路は optimization_level=3 はるかに優れた性能を発揮するはずである。

パフォーマンスを向上させるもう一つの方法は、アイドル状態の量子ビットに一連のゲートを適用する「 動的デカップリング 」です。 これにより、環境との望ましくない相互作用の一部が解消されます。 次のセルは、でトランスパイルされた回路に動 optimization_level=3 的なデカップリングを追加し、それをリストに追加します。

from qiskit_ibm_runtime.transpiler.passes.scheduling import (
    ASAPScheduleAnalysis,
    PadDynamicalDecoupling,
)

# Get gate durations so the transpiler knows how long each operation takes
durations = backend.target.durations()

# This is the sequence we'll apply to idling qubits
dd_sequence = [XGate(), XGate()]

# Run scheduling and dynamic decoupling passes on circuit
pm = PassManager(
    [
        ASAPScheduleAnalysis(durations),
        PadDynamicalDecoupling(durations, dd_sequence),
    ]
)
circ_dd = pm.run(circuits[1])

# Add this new circuit to our list
circuits.append(circ_dd)
circ_dd.draw(output="mpl", style="iqp", idle_wires=False)

Output:

Output of the previous code cell

このコースを走破する

これで、さまざまな設定でトランスパイルされた回路のリストが完成しました。 次に、Samplerプリミティブを使用してこれらの回路を実行し、結果を に保存します result

sampler = Sampler(backend)
job = sampler.run(
    [(circuit) for circuit in circuits],  # sample all three circuits
    shots=8000,
)
result = job.result()

「結果の表示」

最後に、装置の実測結果を理想分布と対比してプロットする。 ゲート数が少ないため、の optimization_level=3 結果は理想的な分布により近く、さらに動的デカップリングにより、 optimization_level=3 + dd の結果はさらに理想的な分布に近づいていることがわかります。

binary_prob = [
    {
        k: v / res.data.meas.num_shots
        for k, v in res.data.meas.get_counts().items()
    }
    for res in result
]
plot_histogram(
    binary_prob + [ideal_distribution],
    bar_labels=False,
    legend=[
        "optimization_level=0",
        "optimization_level=3",
        "optimization_level=3 + dd",
        "ideal distribution",
    ],
)

Output:

Output of the previous code cell

各結果セットと理想分布間のヘリンガー忠実度を計算することでこれを確認できます(値が高いほど良く、1が完全な忠実度です)。

for prob in binary_prob:
    print(f"{hellinger_fidelity(prob, ideal_distribution):.3f}")

Output:

0.774
0.978
0.979

次のステップ

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