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

カットベルペアによる動的回路のベンチマーク

使用量の見積もり: Heron r2 プロセッサで22秒(注: これは見積もりです。実際の実行時間は異なる場合があります。)

背景

量子ハードウェアは通常、局所的な相互作用に限定されていますが、多くのアルゴリズムでは遠く離れた量子ビット同士、あるいは別々のプロセッサ上の量子ビットをエンタングルさせる必要があります。動的回路、すなわちミッドサーキット測定とフィードフォワードを備えた回路は、リアルタイムの古典的通信を使用して非局所的な量子操作を効果的に実装することで、これらの制約を克服する手段を提供します。このアプローチでは、回路の一部(または一つのQPU)からの測定結果が条件付きで別の部分のゲートをトリガーすることができ、エンタングルメントを長距離にわたってテレポーテーションすることが可能になります。これが**局所操作と古典通信(LOCC)**スキームの基礎であり、エンタングルされたリソース状態(ベルペア)を消費し、測定結果を古典的に通信して遠く離れた量子ビットを結合します。

LOCCの有望な用途の一つは、長距離エンタングルメントのチュートリアルで示されているように、テレポーテーションによって仮想的な長距離CNOTゲートを実現することです。直接的な長距離CNOT(ハードウェアの接続性では許可されない場合があります)の代わりに、ベルペアを生成し、テレポーテーションベースのゲート実装を行います。しかし、このような操作の忠実度はハードウェアの特性に依存します。必要な遅延(測定結果を待つ間)中の量子ビットのデコヒーレンスや古典通信のレイテンシが、エンタングルされた状態を劣化させる可能性があります。また、ミッドサーキット測定のエラーは、条件付きゲートを通じて回路の残りの部分に伝播するため、最終測定のエラーよりも修正が困難です。

参考文献の実験では、著者らはLOCCベースのエンタングルメントに最も適したデバイスの部分を特定するためのベルペア忠実度ベンチマークを導入しています。その考え方は、プロセッサ内の接続された4つの量子ビットの全てのグループに対して、小さな動的回路を実行するというものです。この4量子ビット回路は、まず中間の2つの量子ビットにベルペアを作成し、次にそれをリソースとして使用して、LOCCを用いて2つの端の量子ビットをエンタングルさせます。具体的には、量子ビット1と2を局所的に(アダマールゲートとCNOTを使用して)カットされていないベルペアに準備し、そのベルペアを消費するテレポーテーションルーチンによって量子ビット0と3をエンタングルさせます。量子ビット1と2は回路の実行中に測定され、その結果に基づいてパウリ補正(量子ビット3へのXゲートと量子ビット0へのZゲート)が適用されます。量子ビット0と3は回路の最後にベル状態に残ります。

この最終的なエンタングルペアの品質を定量化するために、そのスタビライザーを測定します。具体的には、ZZ基底でのパリティ(Z0Z3Z_0Z_3)とXX基底でのパリティ(X0X3X_0X_3)です。完全なベルペアの場合、これらの期待値はどちらも+1になります。実際には、ハードウェアノイズによってこれらの値は低下します。そのため、各量子ビットペアに対して回路を2回繰り返します。1つの回路は量子ビット0と3をZZ基底で測定し、もう1つはXX基底で測定します。結果から、その量子ビットペアのZ0Z3\langle Z_0Z_3\rangleX0X3\langle X_0X_3\rangleの推定値を得ます。これらのスタビライザーの理想値(1)に対する平均二乗誤差(MSE)を、エンタングルメント忠実度の簡単な指標として使用します。MSEが低いほど、2つの量子ビットが理想に近いベル状態(高い忠実度)を達成したことを意味し、MSEが高いほどエラーが多いことを示します。この実験をデバイス全体にわたって走査することで、異なる量子ビットグループの測定およびフィードフォワード能力をベンチマークし、LOCC操作に最適な量子ビットペアを特定することができます。

このチュートリアルでは、IBM Quantum® デバイス上でこの実験を実演し、動的回路を使用して遠く離れた量子ビット間のエンタングルメントを生成および評価する方法を示します。デバイス上の全ての4量子ビット線形チェーンをマッピングし、各チェーンでテレポーテーション回路を実行し、MSE値の分布を可視化します。このエンドツーエンドの手順は、Qiskit Runtimeと動的回路機能を活用して、回路のカッティングやモジュラーシステムへの量子アルゴリズムの分散に関するハードウェアアウェアな選択を行うための方法を示します。

前提条件

このチュートリアルを始める前に、以下がインストールされていることを確認してください。

  • Qiskit SDK v2.0以降、可視化サポート付き
  • Qiskit Runtime v0.40以降(pip install qiskit-ibm-runtime

セットアップ

from qiskit import QuantumCircuit

from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
from qiskit.transpiler import generate_preset_pass_manager

import numpy as np
import matplotlib.pyplot as plt

def create_bell_stab(initial_layouts):
"""
Create a circuit for a 1D chain of qubits (number of qubits must be a multiple of 4),
where a middle Bell pair is consumed to create a Bell at the edge.
Takes as input a list of lists, where each element of the list is a
1D chain of physical qubits that is used as the initial_layout for the transpiled circuit.
Returns a list of length-2 tuples, each tuple contains a circuit to measure the ZZ stabilizer and
a circuit to measure the XX stabilizer of the edge Bell state.
"""
bell_circuits = []
for (
initial_layout
) in initial_layouts: # Iterate over chains of physical qubits
assert (
len(initial_layout) % 4 == 0
), f"The length of the chain must be a multiple of 4, len(inital_layout)={len(initial_layout)}"
num_pairs = len(initial_layout) // 4

bell_parallel = QuantumCircuit(4 * num_pairs, 4 * num_pairs)

for pair_idx in range(num_pairs):
(q0, q1, q2, q3) = (
pair_idx * 4,
pair_idx * 4 + 1,
pair_idx * 4 + 2,
pair_idx * 4 + 3,
)
(c0, c1) = pair_idx * 4, pair_idx * 4 + 3 # edge qubits
(ca0, ca1) = pair_idx * 4 + 1, pair_idx * 4 + 2 # middle qubits

bell_parallel.h(q0)
bell_parallel.h(q1)
bell_parallel.cx(q1, q2)
bell_parallel.cx(q0, q1)
bell_parallel.cx(q2, q3)
bell_parallel.h(q2)

# add barrier BEFORE measurements and add id in conditional
bell_parallel.barrier()
for pair_idx in range(num_pairs):
(q0, q1, q2, q3) = (
pair_idx * 4,
pair_idx * 4 + 1,
pair_idx * 4 + 2,
pair_idx * 4 + 3,
)
(ca0, ca1) = pair_idx * 4 + 1, pair_idx * 4 + 2 # middle qubits

bell_parallel.measure(q1, ca0)
bell_parallel.measure(q2, ca1)
# bell_parallel.barrier() #remove barrier after measurement

for pair_idx in range(num_pairs):
(q0, q1, q2, q3) = (
pair_idx * 4,
pair_idx * 4 + 1,
pair_idx * 4 + 2,
pair_idx * 4 + 3,
)
(ca0, ca1) = pair_idx * 4 + 1, pair_idx * 4 + 2 # middle qubits
with bell_parallel.if_test((ca0, 1)):
bell_parallel.x(q3)
with bell_parallel.if_test((ca1, 1)):
bell_parallel.z(q0)
bell_parallel.id(q0) # add id here for correct alignment

bell_zz = bell_parallel.copy()
bell_zz.barrier()
bell_xx = bell_parallel.copy()
bell_xx.barrier()
for pair_idx in range(num_pairs):
(q0, q1, q2, q3) = (
pair_idx * 4,
pair_idx * 4 + 1,
pair_idx * 4 + 2,
pair_idx * 4 + 3,
)
bell_xx.h(q0)
bell_xx.h(q3)
bell_xx.barrier()
for pair_idx in range(num_pairs):
(q0, q1, q2, q3) = (
pair_idx * 4,
pair_idx * 4 + 1,
pair_idx * 4 + 2,
pair_idx * 4 + 3,
)
(c0, c1) = pair_idx * 4, pair_idx * 4 + 3 # edge qubits

bell_zz.measure(q0, c0)
bell_zz.measure(q3, c1)

bell_xx.measure(q0, c0)
bell_xx.measure(q3, c1)

bell_circuits.append(bell_zz)
bell_circuits.append(bell_xx)

return bell_circuits

def get_mse(result, initial_layouts):
"""
given a result object and the initial layouts, returns a dict of layouts and their mse
"""
layout_mse = {}
for layout_idx, initial_layout in enumerate(initial_layouts):
layout_mse[tuple(initial_layout)] = {}

num_pairs = len(initial_layout) // 4

counts_zz = result[2 * layout_idx].data.c.get_counts()
total_shots = sum(counts_zz.values())

# Get ZZ expectation value
exp_zz_list = []
for pair_idx in range(num_pairs):
exp_zz = 0
for bitstr, shots in counts_zz.items():
bitstr = bitstr[::-1] # reverse order to big endian
b1, b0 = (
bitstr[pair_idx * 4],
bitstr[pair_idx * 4 + 3],
) # parse bitstring to get edge measurements for each 4-q chain
z_val0 = 1 if b0 == "0" else -1
z_val1 = 1 if b1 == "0" else -1
exp_zz += z_val0 * z_val1 * shots
exp_zz /= total_shots
exp_zz_list.append(exp_zz)

counts_xx = result[2 * layout_idx + 1].data.c.get_counts()
total_shots = sum(counts_xx.values())

# Get XX expectation value
exp_xx_list = []
for pair_idx in range(num_pairs):
exp_xx = 0
for bitstr, shots in counts_xx.items():
bitstr = bitstr[::-1] # reverse order to big endian
b1, b0 = (
bitstr[pair_idx * 4],
bitstr[pair_idx * 4 + 3],
) # parse bitstring to get edge measurements for each 4-q chain
x_val0 = 1 if b0 == "0" else -1
x_val1 = 1 if b1 == "0" else -1
exp_xx += x_val0 * x_val1 * shots
exp_xx /= total_shots
exp_xx_list.append(exp_xx)

mse_list = [
((exp_zz - 1) ** 2 + (exp_xx - 1) ** 2) / 2
for exp_zz, exp_xx in zip(exp_zz_list, exp_xx_list)
]

print(f"layout {initial_layout}")
for idx in range(num_pairs):
layout_mse[tuple(initial_layout)][
tuple(initial_layout[4 * idx : 4 * idx + 4])
] = mse_list[idx]
print(
f"qubits: {initial_layout[4*idx:4*idx+4]}, mse:, {round(mse_list[idx],4)}"
)
# print(f'exp_zz: {round(exp_zz_list[idx],4)}, exp_xx: {round(exp_xx_list[idx],4)}')
print(" ")
return layout_mse

def plot_mse_ecdfs(layouts_mse, combine_layouts=False):
"""
Plot CDF of MSE data for multiple layouts. Optionally combine all data in a single CDF
"""

if not combine_layouts:
for initial_layout, layouts in layouts_mse.items():
sorted_layouts = dict(
sorted(layouts.items(), key=lambda item: item[1])
) # sort layouts by mse

# get layouts and mses
layout_list = list(sorted_layouts.keys())
mse_list = np.asarray(list(sorted_layouts.values()))

# convert to numpy
x = np.array(mse_list)
y = np.arange(1, len(x) + 1) / len(x)

# Prepend (x[0], 0) to start CDF at zero
x = np.insert(x, 0, x[0])
y = np.insert(y, 0, 0)

# Create the plot
plt.plot(
x,
y,
marker="x",
linestyle="-",
label=f"qubits: {initial_layout}",
)

# add qubits labels for the edge pairs
for xi, yi, q in zip(x[1:], y[1:], layout_list):
plt.annotate(
[q[0], q[3]],
(xi, yi),
textcoords="offset points",
xytext=(5, -10),
ha="left",
fontsize=8,
)

elif combine_layouts:
all_layouts = {}
all_initial_layout = []
for (
initial_layout,
layouts,
) in layouts_mse.items(): # puts together all layout information
all_layouts.update(layouts)
all_initial_layout += initial_layout

sorted_layouts = dict(
sorted(all_layouts.items(), key=lambda item: item[1])
) # sort layouts by mse

# get layouts and mses
layout_list = list(sorted_layouts.keys())
mse_list = np.asarray(list(sorted_layouts.values()))

# convert to numpy
x = np.array(mse_list)
y = np.arange(1, len(x) + 1) / len(x)

# Prepend (x[0], 0) to start CDF at zero
x = np.insert(x, 0, x[0])
y = np.insert(y, 0, 0)

# Create the plot
plt.plot(
x,
y,
marker="x",
linestyle="-",
label=f"qubits: {sorted(list(set(all_initial_layout)))}",
)

# add qubit labels for the edge pairs
for xi, yi, q in zip(x[1:], y[1:], layout_list):
plt.annotate(
[q[0], q[3]],
(xi, yi),
textcoords="offset points",
xytext=(5, -10),
ha="left",
fontsize=8,
)

plt.xscale("log")
plt.xlabel("Mean squared error of ⟨ZZ⟩ and ⟨XX⟩")
plt.ylabel("Cumulative distribution function")
plt.title("CDF for different initial layouts")
plt.grid(alpha=0.3)
plt.show()

ステップ1: 古典的入力を量子問題にマッピングする

最初のステップは、デバイスのトポロジーに合わせて、全てのベルペアリンク候補をベンチマークするための量子回路のセットを作成することです。デバイスの結合マップをプログラム的に探索し、線形に接続された4量子ビットの全てのチェーンを見つけます。そのようなチェーン(量子ビットインデックス [q0q1q2q3][q0-q1-q2-q3] でラベル付け)は、エンタングルメントスワッピング回路のテストケースとして機能します。全ての長さ4のパスを特定することで、プロトコルを実現できる量子ビットの可能なグループ分けの最大カバレッジを確保します。

service = QiskitRuntimeService()
backend = service.least_busy(operational=True)

これらのチェーンを生成するために、デバイスグラフ上で貪欲探索を実行するヘルパー関数を使用します。この関数は、4つの4量子ビットチェーンを16量子ビットグループにまとめた「ストライプ」を返します(動的回路では現在、測定レジスタのサイズが 16 量子ビットに制限されています)。バンドリングにより、チップの異なる部分で複数の4量子ビット実験を並列に実行でき、デバイス全体を効率的に使用できます。各16量子ビットストライプには4つの互いに素なチェーンが含まれており、そのグループ内で量子ビットが再利用されることはありません。例えば、1つのストライプはチェーン [0123][0-1-2-3][4567][4-5-6-7][891011][8-9-10-11][12131415][12-13-14-15] をまとめたものになります。ストライプに含まれなかった量子ビットは leftover 変数に返されます。

from itertools import chain
from collections import defaultdict

def stripes16_from_backend(backend):
"""
Creates stripes of 16 qubits, four non-overlapping four-qubit chains, that cover as much of
the coupling map as possible. Returns any unused qubits as leftovers.
"""
# get the undirected adjacency list
edges = backend.coupling_map.get_edges()
graph = defaultdict(set)
for u, v in edges:
graph[u].add(v)
graph[v].add(u)

qubits = sorted(graph) # all qubit indices that appear

# greedy search for 4-long linear chains (blocks) ────────────
used = set() # qubits already placed in a block
blocks = [] # each block is a four-qubit list

for q in qubits: # deterministic order for reproducibility
if q in used:
continue # already consumed by earlier block

# depth-first "straight" walk of length 3 without revisiting nodes
def extend(path):
if len(path) == 4:
return path
tip = path[-1]
for nbr in sorted(graph[tip]): # deterministic
if nbr not in path and nbr not in used:
maybe = extend(path + [nbr])
if maybe:
return maybe
return None

block = extend([q])
if block: # found a 4-node path
blocks.append(block)
used.update(block)

# bundle four four-qubit blocks into one 16-qubit stripe (max number of measurement compatible with if-else)
stripes = [
list(chain.from_iterable(blocks[i : i + 4]))
for i in range(0, len(blocks) // 4 * 4, 4) # full groups of four
]

leftovers = set(qubits) - set(chain.from_iterable(stripes))
return stripes, leftovers
initial_layouts, leftover = stripes16_from_backend(backend)

次に、各16量子ビットストライプに対して回路を構築します。このルーチンは各チェーンに対して以下を行います。

  • 中間ベルペアの準備: 量子ビット1にアダマールゲート、量子ビット1から量子ビット2へCNOTを適用します。これにより量子ビット1と2がエンタングルされます(Φ+=(00+11)/2|\Phi^+\rangle = (|00\rangle + |11\rangle)/\sqrt{2} ベル状態の生成)。
  • 端の量子ビットのエンタングル: 量子ビット0から量子ビット1へCNOT、量子ビット2から量子ビット3へCNOTを適用します。これにより、最初は分離していたペアが結合され、次のステップの後に量子ビット0と3がエンタングルされるようになります。量子ビット2にはアダマールゲートも適用されます(これは前のCNOTと組み合わせて、量子ビット1と2に対するベル測定の一部を構成します)。この時点では量子ビット0と3はまだエンタングルされていませんが、量子ビット1と2はより大きな4量子ビット状態の中でエンタングルされています。
  • ミッドサーキット測定とフィードフォワード: 量子ビット1と2(中間の量子ビット)が計算基底で測定され、2つの古典ビットが得られます。これらの測定結果に基づいて条件付き操作を適用します。量子ビット1の測定(このビットを m12m_{12} と呼びます)が1の場合、量子ビット3に XX ゲートを適用します。量子ビット2の測定(m21m_{21})が1の場合、量子ビット0に ZZ ゲートを適用します。これらの条件付きゲート(Qiskitの if_test/if_else コンストラクトを使用して実現)は、標準的なテレポーテーション補正を実装します。量子ビット1と2を射影することで発生するランダムなパウリフリップを「打ち消し」、測定結果にかかわらず量子ビット0と3が既知のベル状態になるようにします。このステップの後、量子ビット0と3は理想的にはベル状態 Φ+|\Phi^+\rangle にエンタングルされているはずです。
  • ベルペアスタビライザーの測定: 次に、回路を2つのバージョンに分割します。最初のバージョンでは量子ビット0と3の ZZZZ スタビライザーを測定します。2番目のバージョンでは、これらの量子ビットの XXXX スタビライザーを測定します。

各4量子ビットの初期レイアウトに対して、上記の関数は2つの回路(ZZZZ 用と XXXX スタビライザー測定用)を返します。このステップの終了時には、デバイス上の全ての4量子ビットチェーンをカバーする回路のリストが得られます。これらの回路にはミッドサーキット測定と条件付き(if/else)操作が含まれており、これらが動的回路の主要な命令です。

circuits = create_bell_stab(initial_layouts)
circuits[-1].draw("mpl", fold=-1)

前のコードセルの出力

ステップ2: 量子ハードウェア実行のための問題の最適化

実際のハードウェアで回路を実行する前に、デバイスの物理的な制約に合わせてトランスパイルする必要があります。トランスパイルにより、抽象的な回路が選択されたデバイスの物理量子ビットとゲートセットにマッピングされます。各チェーンに特定の物理量子ビットを既に選択しているため(回路生成器に initial_layout を提供することで)、その固定レイアウトでトランスパイラの optimization_level=0 を使用します。これにより、Qiskitは量子ビットの再割り当てや回路構造を変更する可能性のある重い最適化を行わないようになります。操作のシーケンス(特に条件付きゲート)を指定通りに維持したいためです。

isa_circuits = []
for ind, init_layout in enumerate(initial_layouts):
pm = generate_preset_pass_manager(
optimization_level=0, backend=backend, initial_layout=init_layout
)
isa_circ = pm.run(circuits[ind * 2 : ind * 2 + 2])
isa_circuits.extend(isa_circ)
isa_circuits[1].draw("mpl", fold=-1, idle_wires=False)

前のコードセルの出力

ステップ3: Qiskitプリミティブを使用した実行

これで量子デバイス上で実験を実行できます。Qiskit RuntimeとそのSamplerプリミティブを使用して、回路のバッチを効率的に実行します。

sampler = Sampler(mode=backend)
sampler.options.environment.job_tags = ["cut-bell-pair-test"]
job = sampler.run(isa_circuits)

ステップ4: 後処理と所望の古典形式での結果の返却

最後のステップは、テストされた各量子ビットグループの平均二乗誤差メトリック(MSE)を計算し、結果をまとめることです。各チェーンについて、測定された Z0Z3\langle Z_0Z_3\rangleX0X3\langle X_0X_3\rangle が得られています。量子ビット0と3が完全に Φ+|\Phi^+\rangle ベル状態にエンタングルされていれば、これらの両方が+1になることが期待されます。偏差をMSEを用いて定量化します。

MSE=(Z0Z31)2+(X0X31)22.\text{MSE} = \frac{( \langle Z_0Z_3\rangle - 1)^2 + (\langle X_0X_3\rangle - 1)^2}{2}.

この値は完全なベルペアの場合0であり、エンタングルされた状態がノイジーになるにつれて増加します(ランダムな結果が期待値0付近になると、MSEは1に近づきます)。コードはこのMSEを各4量子ビットグループに対して計算します。

結果は、デバイス全体でエンタングルメント品質に幅広い範囲があることを明らかにします。これは、使用する物理量子ビットによってベル状態の忠実度に1桁以上の差が生じる可能性があるという論文の知見を裏付けるものです。実用的には、チップの特定の領域やリンクが、ミッドサーキット測定やフィードフォワード操作において他よりもはるかに優れていることを意味します。量子ビットの読み出しエラー、量子ビットの寿命、クロストークなどの要因がこれらの差異に寄与していると考えられます。例えば、あるチェーンに特にノイジーな読み出し量子ビットが含まれている場合、ミッドサーキット測定が信頼性を欠き、そのエンタングルペアの忠実度が低下します(MSEが高くなります)。

layouts_mse = get_mse(job.result(), initial_layouts)
layout [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
qubits: [0, 1, 2, 3], mse:, 0.0312
qubits: [4, 5, 6, 7], mse:, 0.0491
qubits: [8, 9, 10, 11], mse:, 0.0711
qubits: [12, 13, 14, 15], mse:, 0.0436

layout [16, 23, 22, 21, 17, 27, 26, 25, 18, 31, 30, 29, 19, 35, 34, 33]
qubits: [16, 23, 22, 21], mse:, 0.0197
qubits: [17, 27, 26, 25], mse:, 0.113
qubits: [18, 31, 30, 29], mse:, 0.0287
qubits: [19, 35, 34, 33], mse:, 0.0433

layout [36, 41, 42, 43, 37, 45, 46, 47, 38, 49, 50, 51, 39, 53, 54, 55]
qubits: [36, 41, 42, 43], mse:, 0.1645
qubits: [37, 45, 46, 47], mse:, 0.0409
qubits: [38, 49, 50, 51], mse:, 0.0519
qubits: [39, 53, 54, 55], mse:, 0.0829

layout [56, 63, 62, 61, 57, 67, 66, 65, 58, 71, 70, 69, 59, 75, 74, 73]
qubits: [56, 63, 62, 61], mse:, 0.8663
qubits: [57, 67, 66, 65], mse:, 0.0375
qubits: [58, 71, 70, 69], mse:, 0.0664
qubits: [59, 75, 74, 73], mse:, 0.0291

layout [76, 81, 82, 83, 77, 85, 86, 87, 78, 89, 90, 91, 79, 93, 94, 95]
qubits: [76, 81, 82, 83], mse:, 0.0598
qubits: [77, 85, 86, 87], mse:, 0.313
qubits: [78, 89, 90, 91], mse:, 0.0679
qubits: [79, 93, 94, 95], mse:, 0.0505

layout [96, 103, 102, 101, 97, 107, 106, 105, 98, 111, 110, 109, 99, 115, 114, 113]
qubits: [96, 103, 102, 101], mse:, 0.0302
qubits: [97, 107, 106, 105], mse:, 0.0384
qubits: [98, 111, 110, 109], mse:, 0.0375
qubits: [99, 115, 114, 113], mse:, 0.1051

layout [116, 121, 122, 123, 117, 125, 126, 127, 118, 129, 130, 131, 119, 133, 134, 135]
qubits: [116, 121, 122, 123], mse:, 0.1624
qubits: [117, 125, 126, 127], mse:, 0.7246
qubits: [118, 129, 130, 131], mse:, 0.5919
qubits: [119, 133, 134, 135], mse:, 0.5277

layout [136, 143, 142, 141, 137, 147, 146, 145, 138, 151, 150, 149, 139, 155, 154, 153]
qubits: [136, 143, 142, 141], mse:, 0.0383
qubits: [137, 147, 146, 145], mse:, 1.0187
qubits: [138, 151, 150, 149], mse:, 0.1531
qubits: [139, 155, 154, 153], mse:, 0.0471

最後に、全チェーンのMSE値の累積分布関数(CDF)をプロットして、全体的なパフォーマンスを可視化します。CDFプロットでは、x軸にMSEの閾値、y軸にその閾値以下のMSEを持つ量子ビットペアの割合を示します。この曲線はゼロから始まり、閾値が全てのデータポイントを包含するにつれて1に近づきます。低いMSE付近での急激な上昇は多くのペアが高忠実度であることを示し、緩やかな上昇は多くのペアに大きなエラーがあることを意味します。CDFには最良のペアの識別情報を注釈として付けます。プロットでは、CDFの各点が1つの4量子ビットチェーンのMSEに対応し、その実験でエンタングルされた量子ビットインデックスのペア [q0,q3][q0, q3] でラベル付けされます。これにより、どの物理量子ビットペアが最も優れたパフォーマンスを示すか(CDFの最も左側の点)を容易に特定できます。

plot_mse_ecdfs(layouts_mse, combine_layouts=True)

前のコードセルの出力

参考文献

[1] Carrera Vazquez, A., Tornow, C., Ristè, D. et al. Combining quantum processors with real-time classical communication. Nature 636, 75-79 (2024).