反復符号
使用時間の推定: Heronプロセッサで10秒未満(注意: これは推定値です。実際の実行時間は異なる場合があります。)
学習成果
-
動的回路を使用してビット反転誤り訂正符号を実装する方法
-
符号化された情報を破壊することなく量子エラーを検出するためにスタビライザーを測定する方法
-
誤り訂正ありの結果と誤り訂正なしの結果を比較することで、量子誤り訂正の性能を評価する方法
前提条件
背景
リアルタイム量子誤り訂正(QEC)を可能にするには、実行中に量子プログラムフローを動的に制御できる必要があります。これにより、量子ゲートを測定結果に基づいて条件付けできます。このチュートリアルでは、非常に単純な形式のQECであるビット反転符号を実行します。これは、符号化された量子ビットを単一のビット反転エラーから保護できる動的量子回路を示し、ビット反転符号のパフォーマンスを評価します。
追加のアンシラ量子ビットとエンタングルメントを活用して、符号化された量子情報を変換しないスタビライザーを測定でき、発生した可能性のあるエラーのいくつかのクラスについて通知できます。量子スタビライザー符号は、個の論理量子ビットを個の物理量子ビットに符号化します。スタビライザー符号は、パウリ群の元からなる離散的なエラー集合の訂正に重点を置いています。
このチュートリアルでは、単純な量子メモリ実験を用いてビット反転符号を実演します。符号化された量子ビットを論理状態に準備し、その後、複数サイクルのアイドル時間(エラーを蓄積するため)に続いて誤り検出と訂正を実装します。次に、サイクル数(時間)の関数として論理エラーの確率を定量化します。ここで、論理エラー確率とは、最終測定後にを回復しない状態で量子ビットが見つかる確率です(具体的には、エラーに対応する状態は、、、です)。
エラー率と時間の関係を、個々の未符号化の量子ビットのエラー率、および回路の実行中ではなく最終測定後にのみエラーを検出・訂正する反復符号の使用と比較します。
注: 反復符号はビット反転エラーの訂正のみを可能にするため、完全な誤り訂正符号ではありません。しかし、そのシンプルさゆえに、量子コンピュータ上で誤り訂正を実装する方法を示す良い出発点となります。以下のメモリ実験は、1種類のエラー(がへ減衰すること)のみをテストしており、技術的には古典情報の保護のみを実証しています。
要件
このチュートリアルを始める前に、以下がインストールされていることを確認してください:
-
Qiskit SDK v2.0以降、visualizationサポート付き
-
Qiskit Runtime v0.40以降 (
pip install qiskit-ibm-runtime)
セットアップ
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"matplotlib": "matplotlib", "numpy": "numpy", "qiskit": "qiskit", "qiskit_ibm_runtime": "qiskit-ibm-runtime", "scipy": "scipy", "seaborn": "seaborn"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
sns.set()
plt.rc("xtick", labelsize=20)
plt.rc("ytick", labelsize=20)
plt.rc("lines", linewidth=3)
plt.rc("font", size=20)
plt.rc("legend", fontsize="large")
plt.rc("axes", labelsize=20)
plt.rcParams["figure.figsize"] = 15, 6
plt.rcParams["legend.title_fontsize"] = 25
# Qiskit imports
from qiskit import (
QuantumCircuit,
QuantumRegister,
ClassicalRegister,
)
# qiskit-ibm-runtime
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
from qiskit_ibm_runtime.circuit import MidCircuitMeasure
service = QiskitRuntimeService()
小規模シミュレータの例
この実験の目的は、アイドル遅延中の振幅減衰など、実際のハードウェア・ノイズの下での論理量子ビットの寿命を測定することであるため、このステップは省略します。ノイズのないシミュレータでは、検出・訂正すべきエラーが表示されないからです。
大規模ハードウェアの例
ステップ1. 古典的な入力を量子問題にマッピング
バックエンドを選択する
回路の実行中にエラーを検出するには、MidCircuitMeasureメソッド(詳細はドキュメントを参照)にアクセスできるバックエンドを選択する必要があります。
# You can see all backends that support mid-circuit measurements by running the following code.
service.backends(filters=lambda b: "measure_2" in b.supported_instructions)
[<IBMBackend('ibm_pittsburgh')>,
<IBMBackend('ibm_boston')>,
<IBMBackend('ibm_kingston')>]
# Choose the least busy backend that supports mid-circuit measurements (`measure_2`).
backend = service.least_busy(
filters=lambda b: "measure_2" in b.supported_instructions,
operational=True,
simulator=False,
dynamic_circuits=True,
)
# backend = service.backend(backend_name) # alternatively, you could choose a specific backend
print(backend.name)
ibm_boston
誤り検出と訂正の複数ラウンドを実装するビット反転スタビライザー回路の一連の流れを構築する
ビット反転符号は、スタビライザー符号の最も単純な例の1つです。符号化量子ビットのいずれかでの単一のビット反転(X)エラーに対して状態を保護します。ビット反転エラーの作用を考えると、これは任意の量子ビットでおよびをマップするため、となります。この符号には5つの量子ビットが必要です。3つは保護された状態(「データ量子ビット」)を符号化するために使用され、残りの2つはスタビライザー測定アンシラとして使用されます。
以下では、(1)データ量子ビットを論理状態に準備し、(2)の遅延に続いて誤り訂正(シンドローム量子ビットのリセットを含む)を行う複数サイクルを実行し、(3)データ量子ビットの状態を読み出す回路を構築します。
また、状態に準備し、アイドル状態にしてから読み出す3つの参照量子ビットを含めることで、誤り訂正を使用しない場合の状態の寿命もテストします。
def build_qc(
qreg_data,
qreg_syndrome,
creg_data,
creg_syndrome,
qreg_ref=None,
creg_ref=None,
):
"""Build a typical error correction circuit"""
if qreg_ref:
return QuantumCircuit(
qreg_data,
qreg_syndrome,
creg_data,
creg_syndrome,
qreg_ref,
creg_ref,
)
else:
return QuantumCircuit(
qreg_data, qreg_syndrome, creg_data, creg_syndrome
)
def encode_bit_flip(circuit, qreg_data, qreg_ref=None) -> QuantumCircuit:
"""Encode bit-flip. This is done by simply adding a cx"""
for q in qreg_data:
circuit.x(q)
if qreg_ref:
for q in qreg_ref:
circuit.x(q)
circuit.barrier()
return circuit
def measure_syndrome_bit(
circuit, qreg_data, qreg_syndrome, creg_syndrome, qreg_ref=None
):
"""
Measure the syndrome by measuring the parity.
We reset our ancilla qubits after measuring the stabilizer
so we can reuse them for repeated stabilizer measurements.
Because we have already observed the state of the qubit,
we can write the conditional reset protocol directly to
avoid another round of qubit measurement if we used
the `reset` instruction.
"""
circuit.cx(qreg_data[0], qreg_syndrome[0])
circuit.cx(qreg_data[1], qreg_syndrome[0])
circuit.cx(qreg_data[0], qreg_syndrome[1])
circuit.cx(qreg_data[2], qreg_syndrome[1])
circuit.barrier()
for q_measure, c_measure in zip(qreg_syndrome, creg_syndrome):
circuit.append(MidCircuitMeasure(), [q_measure], [c_measure])
with circuit.if_test((creg_syndrome[0], 1)):
circuit.x(qreg_syndrome[0])
with circuit.if_test((creg_syndrome[1], 1)):
circuit.x(qreg_syndrome[1])
circuit.barrier()
return circuit
def apply_correction_bit(circuit, qreg_data, creg_syndrome):
"""We can detect where an error occurred and correct our state"""
with circuit.if_test((creg_syndrome, 3)):
circuit.x(qreg_data[0])
with circuit.if_test((creg_syndrome, 1)):
circuit.x(qreg_data[1])
with circuit.if_test((creg_syndrome, 2)):
circuit.x(qreg_data[2])
circuit.barrier()
return circuit
def apply_final_readout(
circuit, qreg_data, creg_data, qreg_ref=None, creg_ref=None
):
"""Read out the final measurements"""
circuit.barrier()
if qreg_ref:
circuit.measure(qreg_ref, creg_ref)
circuit.measure(qreg_data, creg_data)
return circuit
def build_error_correction_sequence(
num_cycles, cycles_per_circuit, nq_ref=3, delay=None
) -> QuantumCircuit:
circuits = []
reps = []
qreg_data = QuantumRegister(3, name="qdata")
qreg_syndrome = QuantumRegister(2, name="qsyndrome")
creg_data = ClassicalRegister(3, name="cdata")
creg_syndrome = ClassicalRegister(2, name="csyndrome")
qreg_ref = QuantumRegister(nq_ref, name="qreference")
creg_ref = ClassicalRegister(nq_ref, name="creference")
circuit = build_qc(
qreg_data,
qreg_syndrome,
creg_data,
creg_syndrome,
qreg_ref=qreg_ref,
creg_ref=creg_ref,
)
circuit = encode_bit_flip(circuit, qreg_data, qreg_ref=qreg_ref)
circuit_n = circuit.copy()
circuit_n = apply_final_readout(
circuit_n, qreg_data, creg_data, qreg_ref=qreg_ref, creg_ref=creg_ref
)
circuits.append(circuit_n)
reps.append(0)
for i in range(1, num_cycles + 1):
if delay:
circuit.delay(delay, unit="us")
circuit.barrier()
circuit = measure_syndrome_bit(
circuit,
qreg_data,
qreg_syndrome,
creg_syndrome,
qreg_ref=qreg_ref,
)
circuit = apply_correction_bit(circuit, qreg_data, creg_syndrome)
circuit_n = circuit.copy()
if i % cycles_per_circuit == 0:
circuit_n = apply_final_readout(
circuit_n,
qreg_data,
creg_data,
qreg_ref=qreg_ref,
creg_ref=creg_ref,
)
circuits.append(circuit_n)
reps.append(i)
return circuits, np.array(reps)
num_cycles = 40
cycles_per_circuit = 4
nq_ref = 3
circuits, rep_array = build_error_correction_sequence(
num_cycles, cycles_per_circuit, nq_ref=3, delay=5
)
circuits[1].draw(output="mpl", cregbundle=False, fold=50)

ステップ 2: ハードウェア実行用に問題を最適化
総ジョブ実行時間を短縮するため、Qiskitプリミティブは、ターゲットシステムがサポートする命令と接続性に準拠する回路と観測可能量(命令セットアーキテクチャ(ISA)回路と観測可能量と呼ばれる)のみを受け入れます。トランスパイルの詳細を参照してください。
ISA回路を生成
まず、最適化レベル3のプリセット・パス・マネージャーを使用して、最も長い回路をトランスパイルすることで、初期レイアウト(つまり、回路をマッピングする物理量子ビットの選択)を見つけることから始めます。
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
isa_circuit_ref = pm.run(circuits[-1])
init_layout = isa_circuit_ref.layout.initial_index_layout(
filter_ancillas=True
)
print(init_layout)
[56, 44, 62, 43, 63, 22, 48, 67]
論理量子メモリを比較する参照量子ビットについては、振幅減衰コヒーレンス時間()の観点から利用可能な最良の量子ビットを選択します。
# get all qubits ordered by T1
t1_data = []
for i in range(backend.num_qubits):
try:
t1_us = backend.properties().t1(i) * 1e6
except Exception:
t1_us = 0.0
t1_data.append((i, t1_us))
t1_data_sorted = sorted(t1_data, key=lambda x: x[1], reverse=True)
# exclude the qubits we have already mapped the error correcting code to
t1_data_sorted = [
t1_data for t1_data in t1_data_sorted if t1_data[0] not in init_layout[:5]
]
# use the best qubits in terms of T1 for the reference qubits
init_layout = init_layout[:5] + [t1_data[0] for t1_data in t1_data_sorted[:3]]
print(init_layout)
[56, 44, 62, 43, 63, 143, 131, 31]
# These are the resulting T1 times
properties = backend.properties()
print("Amplitude damping decoherence times for code data qubits:")
for q in init_layout[:3]:
t1 = properties.t1(q)
print(f"qubit {q}: T1 = {t1 * 1e6:.0f} mus")
print("\nAmplitude damping decoherence times for reference qubits:")
for q in init_layout[-3:]:
t1 = properties.t1(q)
print(f"qubit {q}: T1 = {t1 * 1e6:.0f} mus")
Amplitude damping decoherence times code data qubits:
qubit 56: T1 = 322 mus
qubit 44: T1 = 263 mus
qubit 62: T1 = 290 mus
Amplitude damping decoherence times reference qubits:
qubit 143: T1 = 442 mus
qubit 131: T1 = 410 mus
qubit 31: T1 = 401 mus
# now we transpile all circuits to this initial layout; this way each circuit is run on the same qubits and we can make a fair comparison
pm = generate_preset_pass_manager(
backend=backend,
optimization_level=3,
initial_layout=init_layout,
)
isa_circuits = pm.run(circuits)
isa_circuits[1].draw("mpl", cregbundle=False, fold=50)

ステップ3. Qiskitプリミティブを使用して実行
sampler = Sampler(mode=backend)
sampler.options.environment.job_tags = ["TUT-REPCODE"]
sampler.options.max_execution_time = 600 # this workload is expected to be under 10s, but it is generally a good habit to set a max execution time (here 600s = 10m)
job = sampler.run(isa_circuits, shots=1000)
print(job.job_id())
job.status()
'DONE'
ステップ4: 後処理を行い、目的の古典形式で結果を返す
次に、3量子ビット反復符号を使用した論理メモリと、個々の未符号化の参照量子ビットとの間で、時間に対するエラー率を比較します。
results = job.result()
def correct_counts(counts_dict):
"""
Corrects the measured logical qubit encoded in the repetition code using majority vote
"""
result = {"000": 0, "111": 0}
for bitstring, count in counts_dict.items():
key = "111" if bitstring.count("1") > 1 else "000"
result[key] += count
return result
accuracy = [] # logical qubit
accuracies_ref = np.zeros(
(len(results), nq_ref)
) # accuracies on individual reference qubits
for n, pub_result in enumerate(results):
# logical accuracy (one minus error probability) for active error correction with repetition code
counts = pub_result.data.cdata.get_counts()
shots = sum(counts.values())
counts_corrected = correct_counts(counts)
accuracy.append(counts_corrected.get("111", 0) / shots)
# accuracy for individual physical reference qubits without any error correction
for i in range(nq_ref):
counts = pub_result.data.creference.slice_bits(i).get_counts()
accuracies_ref[n, i] = counts.get("1", 0) / shots
accuracy = np.array(accuracy)
def error_proba_t1(N, t1):
"""
Exponential fitting function for amplitude damping vs. number of cycles
"""
t_cycle = 7.3e-6 # approximate time per cycle = 5 mus delay + 2.3 mus for error correction
return 1 - np.exp(-t_cycle * N / t1)
fig, ax = plt.subplots(1, 1, figsize=(15, 5))
ax.plot(
rep_array, (1.0 - accuracy) * 100.0, "ko-", linewidth=3, label="rep code"
)
for i in range(nq_ref):
accuracy_1q = accuracies_ref[:, i]
if i == 0:
ax.plot(
rep_array,
(1.0 - accuracy_1q) * 100.0,
"go-",
linewidth=1,
label="1q reference",
)
else:
ax.plot(rep_array, (1.0 - accuracy_1q) * 100.0, "go-", linewidth=1)
params_bf, pcov = curve_fit(
error_proba_t1, rep_array, 1.0 - accuracy, bounds=([0, 5e-3])
)
t1_bf = params_bf[0]
print(f"Best-fit effective T1 = {t1_bf * 1e6:.0f} us")
error_prob_bf = error_proba_t1(
rep_array, t1_bf
) # np.array([1 - np.exp(-t_cycle * nt/t1) for nt in rep_array])
ax.plot(rep_array, error_prob_bf * 100.0, "k--", linewidth=1)
ax.set_xlabel("error correction rounds")
ax.set_ylabel("error [%]")
ax.set_ylim(bottom=0)
ax.legend(fontsize=15);
Best-fit effective T1 = 1397 us

最良の(未符号化の)物理量子ビットと比較しても、状態の寿命が改善されていることが明確にわかります。ただし、この実験は1種類のエラー、すなわちビット反転のみを訂正していることに留意してください。論理量子ビットの寿命を改善できますか?遅延時間の最適化や、反復符号のサイズをを超えてスケールアップするなど、探求してみてください。
次のステップ
この内容に興味を持った場合は、以下の資料にも興味があるかもしれません:
-
量子誤り訂正の基礎コース - 量子誤り訂正についてさらに学ぶ
-
時空符号を用いた低オーバーヘッド誤り検出チュートリアル - Pauliチェックを使用してエラーを検出し、サンプルを事後選択する方法を学ぶ