動的回路による長距離エンタングルメント
使用量の目安: Heron r2プロセッサで約4分。(注意: これはあくまで目安です。実際の実行時間は異なる場合があります。)
背景
限られた接続性を持つデバイス上で、離れた量子ビット間の長距離エンタングルメントを実現することは困難です。本チュートリアルでは、測定ベースのプロ トコルを用いた長距離制御X(LRCX)ゲートの実装を通じて、動的回路がそのようなエンタングルメントをどのように生成できるかを示します。
Elisa Bäumer らによる 1 のアプローチに従い、この手法は回路中間測定とフィードフォワードを使用して、量子ビット間の距離に関係なく一定深度のゲートを実現します。中間ベルペアを生成し、各ペアから1つの量子ビットを測定し、古典的に条件付けされたゲートを適用して、デバイス全体にエンタングルメントを伝播させます。これにより、長いSWAPチェーンを回避し、回路深度と2量子ビットゲートエラーへの露出の両方を削減できます。
本ノートブックでは、IBM Quantum® ハードウェア向けにプロトコルを適応させ、さらに複数のLRCX操作を並列実行するように拡張します。これにより、同時条件付き操作の数に応じて性能がどのようにスケールするかを調べることができます。
要件
本チュートリアルを開始する前に、以下がインストールされていることを確認してください:
- Qiskit SDK v2.0以降(visualization サポート付き)
- Qiskit Runtime(
pip install qiskit-ibm-runtime)v0.37以降
セットアップ
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-aer qiskit-ibm-runtime
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.classical import expr
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.visualization import plot_circuit_layout
from qiskit_ibm_runtime import (
QiskitRuntimeService,
Batch,
SamplerV2 as Sampler,
)
import matplotlib.pyplot as plt
import numpy as np
ステップ1: 古典的な入力を量子問題にマッピングする
次に、以下に示す動的回路の構成(参考文献 1 の図1aから改変)に従って、2つの離れた量子ビット間の長距離CNOTゲートを実装します。重要なアイデアは、 に初期化されたアンシラ量子ビットの「バス」を使用し て、長距離ゲートテレポーテーションを仲介することです。

図に示されているように、プロセスは以下の通りです:
- 中間アンシラを介して制御量子ビットとターゲット量子ビットを接続するベルペアのチェーンを準備します。
- エンタングルされていない隣接量子ビット間でベル測定を実行し、制御ビットとターゲットビットがベルペアを共有するまで、エンタングルメントをステップごとにスワップします。
- このベルペアをゲートテレポーテーションに使用し、局所的なCNOTを一定深度で決定論的な長距離CNOTに変換します。
このアプローチは、長いSWAPチェーンを一定深度のプロトコルに置き換え、2量子ビットゲートエラーへの露出を減らし、デバイスサイズに対してスケーラブルな操作を実現します。
以下では、まずLRCX回路の動的回路による実装を順を追って説明します。最後に、動的回路の利点を強調するために、ユニタリベースの実装も比較用に提供します。
(i) 回路の初期化
比較の基礎となる簡単な量子問題から始めます。具体的には、インデックス0の制御量子ビットを持つ回路を初期化し、アダマールゲートを適用します。これにより重ね合わせ状態が生成され、制御X操作が続くことで、制御量子ビットとターゲット量子ビットの間にベル状態 が生成されます。
この段階では、まだ長距離制御X(LRCX)自体を構築していません。代わりに、LRCXの役割を明確にするための、明快で最小限の初期回路を定義することが目標です。ステップ2では、動的回路を用いた最適化としてLRCXをどのように実装できるかを示し、ユニタリ等価物との性能比較を行います。重要なのは、LRCXプロトコルはどのような初期回路にも適用可能であるということです。ここでは、デモンストレーションの明確さのために、この簡単なアダマールのセットアップを使用します。
distance = 6 # The distance of the CNOT gate, with the convention that a distance of zero is a nearest-neighbor CNOT.
def initialize_circuit(distance):
assert distance >= 0
control = 0 # control qubit
n = distance # number of qubits between target and control
qr = QuantumRegister(
n + 2, name="q"
) # Circuit with n qubits between control and target
cr = ClassicalRegister(
2, name="cr"
) # Classical register for measuring control and target qubits
k = int(n / 2) # Number of Bell States to be used
allcr = [cr]
if (
distance > 1
): # This classical register will be used to store ZZ measurements. It is only used for long-range CX gates with distance > 1
c1 = ClassicalRegister(
k, name="c1"
) # Classical register needed for post processing
allcr.append(c1)
if (
distance > 0
): # This classical register will be used to store XX measurements. It is only used if distance > 0
c2 = ClassicalRegister(
n - k, name="c2"
) # Classical register needed for post processing
allcr.append(c2)
qc = QuantumCircuit(qr, *allcr, name="CNOT")
# Apply a Hadamard gate to the control qubit such that the long-range CNOT gate will prepare a Bell state (|00> + |11>)/sqrt(2)
qc.h(control)
return qc
qc = initialize_circuit(distance)
qc.draw(fold=-1, output="mpl", scale=0.5)
ステップ2: 量子ハードウェア実行のための問題の最適化
このステップでは、動的回路を使用してLRCX回路を構築する方法を示します。目標は、純粋にユニタリな実装と比較して深度を削減することで、ハードウェア上での実行に向けて回路を最適化することです。利点を説明するために、動的LRCX構成とそのユニタリ等価物の両方を表示し、トランスパイル後の性能を比較します。重要なのは、ここではLRCXを簡単なアダマール初期化問題に適用していますが、このプロトコルは長距離CNOTが必要なあらゆる回路に適用可能であるということです。
(ii) ベルペアの準備
制御量子ビットとターゲット量子ビットの間のパスに沿って、ベルペアのチェーンを作成することから始めます。距離が奇数の場合は、まず制御ビットからその隣接ビットへのCNOTを適用します。これがテレポートされるCNOTです。偶数距離の場合、このCNOTはベルペア準備ステップの後に適用されます。次に、ベルペアチェーンが連続するペアの量子ビットをエンタングルし、制御情報をデ バイス全体に伝搬するために必要なリソースを確立します。
# Determine where to start the Bell pair chain and add an extra CNOT when n is odd
def check_even(n: int) -> int:
"""Return 1 if n is even, else 2."""
return 1 if n % 2 == 0 else 2
def prepare_bell_pairs(qc, add_barriers=True):
n = qc.num_qubits - 2 # number of qubits between target and control
k = int(n / 2)
if add_barriers:
qc.barrier()
x0 = check_even(n)
if n % 2 != 0:
qc.cx(0, 1)
# Create k Bell pairs
for i in range(k):
qc.h(x0 + 2 * i)
qc.cx(x0 + 2 * i, x0 + 2 * i + 1)
return qc
qc = prepare_bell_pairs(qc)
qc.draw(output="mpl", fold=-1, scale=0.5)
(iii) 隣接量子ビットペアをベル基底で測定する
次に、エンタングルされていない隣接量子ビットをベル基底( と の2量子ビット測定)で測定します。これにより、ターゲット量子ビットと制御ビットに隣接する量子ビットの間に長距離ベルペアが生成されます(パウリ補正が必要であり、次のステップでフィードフォワードにより実装されます)。並行して、CNOTゲートを目的のターゲット量子ビットにテレポートするエンタングル測定を実装します。
def measure_bell_basis(qc, add_barriers=True):
n = qc.num_qubits - 2 # number of qubits between target and control
k = int(n / 2)
if n > 1:
_, c1, c2 = qc.cregs
elif n > 0:
_, c2 = qc.cregs
# Determine where to start the Bell pair chain and add an extra CNOT when n is odd
x0 = 1 if n % 2 == 0 else 2
# Entangling layer that implements the Bell measurement (and additionally adds the CNOT to be teleported, if n is even)
for i in range(k + 1):
qc.cx(x0 - 1 + 2 * i, x0 + 2 * i)
for i in range(1, k + x0):
if i == 1:
qc.h(2 * i + 1 - x0)
else:
qc.h(2 * i + 1 - x0)
if add_barriers:
qc.barrier()
# Map the ZZ measurements onto classical register c1
for i in range(k):
if i == 0:
qc.measure(2 * i + x0, c1[i])
else:
qc.measure(2 * i + x0, c1[i])
# Map the XX measurements onto classical register c2
for i in range(1, k + x0):
if i == 1:
qc.measure(2 * i + 1 - x0, c2[i - 1])
else:
qc.measure(2 * i + 1 - x0, c2[i - 1])
return qc
qc = measure_bell_basis(qc)
qc.draw(output="mpl", fold=-1, scale=0.5)
(iv) 次に、パウリ副産物演算子を補正するためのフィードフォワード補正を適用する
ベル基底測定はパウリ副産物を導入するため、記録された測定結果を使用してこれらを補正する必要があります。これは2つのステップで行われます。まず、すべての 測定のパリティを計算し、その結果を使用して条件付きで ゲートをターゲット量子ビットに適用します。同様に、 測定のパリティを計算し、条件付きで ゲートを制御量子ビットに適用します。
Qiskitの新しい古典式フレームワークを使用すると、これらのパリティを回路の古典処理層で直接計算できます。個々の測定ビットごとに一連の条件付きゲートを適用する代わりに、すべての関連する測定結果のXOR(パリティ)を表す単一の古典式を構築できます。この式は単一の if_test ブロックの条件として使用され、補正ゲートを一定深度で適用できます。このアプローチは回路を簡素化するとともに、フィードフォワード補正が不要な追加レイテンシを導入しないことを保証します。
def apply_ffwd_corrections(qc):
control = 0 # control qubit
target = qc.num_qubits - 1 # target qubit
n = qc.num_qubits - 2 # number of qubits between target and control
k = int(n / 2)
x0 = check_even(n)
if n > 1:
_, c1, c2 = qc.cregs
elif n > 0:
_, c2 = qc.cregs
# First, let's compute the parity of all ZZ measurements
for i in range(k):
if i == 0:
parity_ZZ = expr.lift(
c1[i]
) # Store the value of the first ZZ measurement in parity_ZZ
else:
parity_ZZ = expr.bit_xor(
c1[i], parity_ZZ
) # Successively compute the parity via XOR operations
for i in range(1, k + x0):
if i == 1:
parity_XX = expr.lift(
c2[i - 1]
) # Store the value of the first XX measurement in parity_XX
else:
parity_XX = expr.bit_xor(
c2[i - 1], parity_XX
) # Successively compute the parity via XOR operations
if n > 0:
with qc.if_test(parity_XX):
qc.z(control)
if n > 1:
with qc.if_test(parity_ZZ):
qc.x(target)
return qc
qc = apply_ffwd_corrections(qc)
qc.draw(output="mpl", fold=-1, scale=0.5)
(v) 最後に、制御量子ビットとターゲット量子ビットを測定する
制御量子ビットとターゲット量子ビットを 、、または 基底で測定するためのヘルパー関数を定義します。ベル状態 を検証するには、 と の期待値がともに であるべきです。これらはこの状態のスタビライザだからです。 測定もここでサポートされており、以下でフィデリティを計算する際に使用されます。
def measure_in_basis(qc, basis="XX", add_barrier=True):
control = 0 # control qubit
target = qc.num_qubits - 1 # target qubit
assert basis in ["XX", "YY", "ZZ"]
qc = (
qc.copy()
) # We copy the circuit because we want to measure in different bases
cr = qc.cregs[0]
if add_barrier:
qc.barrier()
if basis == "XX":
qc.h(control)
qc.h(target)
elif basis == "YY":
qc.sdg(control)
qc.sdg(target)
qc.h(control)
qc.h(target)
qc.measure(control, cr[0])
qc.measure(target, cr[1])
return qc
qc_YY = measure_in_basis(qc.copy(), basis="YY")
qc_YY.draw(
output="mpl", fold=-1, scale=0.5
) # Circuit for measuring in the YY basis
すべてをまとめる
上記で定義した各ステップを組み合わせて、1D線上の両端に長距離CXゲートを作成します。ステップは以下の通りです:
- 制御量子ビットを に初期化する
- ベルペアを準備する
- 隣接量子ビットペアを測定する
- MCMに依存するフィードフォワード補正を適用する
def lrcx(distance, prep_barrier=True, pre_measure_barrier=True):
qc = initialize_circuit(distance)
qc = prepare_bell_pairs(qc, prep_barrier)
qc = measure_bell_basis(qc, pre_measure_barrier)
qc = apply_ffwd_corrections(qc)
return qc
qc = lrcx(distance)
# Apply the measurement in the XX, YY, and ZZ bases
qc_XX, qc_YY, qc_ZZ = [
measure_in_basis(qc, basis=basis) for basis in ["XX", "YY", "ZZ"]
]
qc_YY.draw(
output="mpl", fold=-1, scale=0.5
) # Circuit for measuring in the YY basis
さまざまな距離の回路を生成する
次に、さまざまな量子ビット間距離に対する長距離CX回路を生成します。各距離に対して、、、 基底で測定する回路を構築し、後にフィデリティの計算に使用します。
距離のリストには、短距離と長距離の両方の分離が含まれてお り、distance = 0 は最近接CXに対応します。これらの同じ距離は、後で比較用の対応するユニタリ回路の生成にも使用されます。
def cnot_unitary(distance):
"""Generate a long range CNOT gate using local CNOTs on a 1D chain of qubits subject to n
nearest-neighbor connections only.
Args:
distance (int) : The distance of the CNOT gate, with the convention that a distance of 0 is a nearest-neighbor CNOT.
Returns:
QuantumCircuit: A Quantum Circuit implementing a long-range CNOT gate between qubit 0 and qubit distance+1
"""
assert distance >= 0
n = distance # number of qubits between target and control
qr = QuantumRegister(
n + 2, name="q"
) # Circuit with n qubits between control and target
cr = ClassicalRegister(
2, name="cr"
) # Classical register for measuring control and target qubits
qc = QuantumCircuit(qr, cr, name="CNOT_unitary")
control_qubit = 0
qc.h(control_qubit) # Prepare the control qubit in the |+> state
k = int(n / 2)
qc.barrier()
for i in range(control_qubit, control_qubit + k):
qc.cx(i, i + 1)
qc.cx(i + 1, i)
qc.cx(-i - 1, -i - 2)
qc.cx(-i - 2, -i - 1)
if n % 2 == 1:
qc.cx(k + 2, k + 1)
qc.cx(k + 1, k + 2)
qc.barrier()
qc.cx(k, k + 1)
for i in range(control_qubit, control_qubit + k):
qc.cx(k - i, k - 1 - i)
qc.cx(k - 1 - i, k - i)
qc.cx(k + i + 1, k + i + 2)
qc.cx(k + i + 2, k + i + 1)
if n % 2 == 1:
qc.cx(-2, -1)
qc.cx(-1, -2)
return qc
qc_uni = cnot_unitary(distance)
# Apply the measurement in the XX, YY, and ZZ bases
qc_uni_XX, qc_uni_YY, qc_uni_ZZ = [
measure_in_basis(qc_uni, basis=basis) for basis in ["XX", "YY", "ZZ"]
]
qc_uni_YY.draw(
output="mpl", fold=-1, scale=0.5
) # Circuit for measuring in the YY basis

ユニタリベースの実装:量子ビットを中央にスワップする方法
比較のために、まず最近接接続とユニタリゲートを使用して長距離CNOTゲートを実装する場合を検討します。以下の図で、左側は最近接接続のみを持つn量子ビットの1Dチェーン上の長距離CNOTゲートの回路です。中央は回路深度 の局所CNOTゲートで実装可能な等価なユニタリ分解です。

中央の回路は以下のように実装できます:
from qiskit_aer import AerSimulator
aer_backend = AerSimulator()
pm_sim = generate_preset_pass_manager(
optimization_level=0, backend=aer_backend
)
# Dynamic circuits
isa_sim_dyn = pm_sim.run([qc_XX, qc_YY, qc_ZZ])
# Unitary circuits
isa_sim_uni = pm_sim.run([qc_uni_XX, qc_uni_YY, qc_uni_ZZ])