Pengesanan ralat overhed rendah menggunakan kod ruang-masa
Dalam tutorial ini, kita akan melaksanakan aliran kerja untuk mensampel daripada litar Clifford dan menggunakan pakej qiskit-paulice untuk meningkatkan fidelity taburan yang diukur dengan menerapkan semakan Pauli ruang-masa di seluruh litar. Semakan Pauli ruang-masa serupa dengan semakan Pauli koheren (CPC) kerana litar "muatan" Clifford dikusutkan dengan beberapa qubit ancilla untuk "menyemak" invarian tertentu dalam litar. Pengukuran ancilla menghasilkan sindrom yang digunakan untuk menentukan sama ada ralat logik dikesan semasa pelaksanaan litar. Pemilihan-pasca (postselection) hanya sampel yang tiada ralat dikesan boleh meningkatkan fidelity taburan yang disampel dengan mengorbankan kadar pemilihan-pasca yang menurun. Perbezaan utama antara CPC dan semakan ruang-masa ialah pengukuran sindrom CPC memerlukan pengukuran operator berwajaran tinggi yang setempat dalam masa. Pada topologi qubit dengan kesambungan terhad, seperti heavy hex, pelaksanaan semakan ini memerlukan banyak gate SWAP, yang sering menjadikan litar terlalu dalam untuk dijalankan secara praktikal. Pelaksanaan semakan Pauli sebagai kod ruang-masa membolehkannya diagihkan di seluruh litar muatan, membolehkan pengekodan qubit yang cekap perkakasan yang kekal berkesan dalam mengesan ralat logik semasa pelaksanaan litar. Peranan utama pakej qiskit-paulice ialah mencari dan menyisipkan semakan Pauli ruang-masa yang sah ke lokasi dalam litar yang memaksimumkan keberkesanan pengesanan ralat dan meminimumkan overhed qubit.
Tutorial ini melaksanakan aliran kerja untuk meningkatkan fidelity taburan yang disampel daripada litar kuantum menggunakan semakan ruang-masa. Memandangkan kita akan menggunakan litar Clifford, kita boleh menganggar fidelity secara langsung daripada purata nilai jangkaan penstabil litar. Kita akan mengikuti langkah-langkah berikut:
-
Cipta litar: Litar Clifford rawak brickwork 1D yang cetek digunakan untuk menggambarkan semakan.
-
Kenal pasti qubit sebagai sasaran semakan: Petakan litar kepada qubit fizikal pada QPU Heron
ibm_bostondan kenal pasti qubit yang mempunyai ancilla jiran. -
Transpile litar ke backend: Pilih susun atur qubit dan terjemahkan gate litar ke dalam set gate natif
ibm_boston. -
Cipta model hingar daripada data backend: Jana model hingar kasar berdasarkan data penanda aras backend. Data hingar ini akan digunakan untuk menilai keberkesanan setiap semakan tambahan.
-
Visualisasikan semakan Pauli: Tambah beberapa semakan Pauli pada litar cetek, supaya kita dapat menggambarkan cara semakan ditambah.
-
Tentukan penstabil untuk diukur: Tingkatkan kedalaman litar untuk meningkatkan kesan hingar gate. Sampel secara rawak subset penstabil litar yang lebih dalam.
-
Cari semakan yang baik untuk keseluruhan keadaan: Cari satu set semakan untuk penstabil keadaan yang disediakan litar. Semakan sedemikian sah walau apa pun yang diukur selepas itu, jadi litar yang disemak yang sama digunakan untuk setiap penstabil yang disampel.
-
Sampel daripada litar yang berhingar: Ukur litar kosong dan litar yang disemak dalam asas setiap penstabil yang disampel dengan samplomatic, dan sampelkannya dengan primitif
Executorpada simulator berhingar yang menggunakan hingar depolarisasi yang sama seperti yang digunakan untuk mencari semakan. -
Pilih-pasca sampel litar yang disemak tanpa ralat dikesan: Buang sampel yang gagal sekurang-kurangnya satu semakan. Kira nilai jangkaan bagi litar berhingar kosong dan litar yang mengandungi semakan.
-
Bandingkan fidelity keadaan berhingar asal dan yang dipilih-pasca: Tunjukkan bahawa pemilihan-pasca hanya sampel tanpa ralat dikesan meningkatkan anggaran fidelity keadaan berhingar.
1. Cipta litar
Pertama, kita akan mencipta litar Clifford yang cetek dengan gate berkusutnya disusun dalam kesambungan brickwork. Kita akan menggunakan litar kecil ini untuk menggambarkan cara semakan ditambah pada litar.
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"matplotlib": "matplotlib", "numpy": "numpy", "qiskit": "qiskit", "qiskit_aer": "qiskit-aer", "qiskit_ibm_runtime": "qiskit-ibm-runtime", "qiskit_paulice": "qiskit-paulice", "samplomatic": "samplomatic"}
_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
from qiskit import QuantumCircuit
def random_clifford_circuit(
num_qubits: int, depth: int, rng: np.random.Generator
) -> QuantumCircuit:
"""Brickwork random Clifford on `num_qubits`, with `depth` CZ layers."""
qc = QuantumCircuit(num_qubits)
qc.h(range(num_qubits))
for d in range(depth):
for i in range(d % 2, num_qubits - 1, 2):
qc.cz(i, i + 1)
for q in range(num_qubits):
if rng.integers(0, 2):
qc.sx(q)
if rng.integers(0, 2):
qc.s(q)
if rng.integers(0, 2):
qc.sx(q)
return qc
num_qubits = 12
depth = 4
seed = 1764
rng = np.random.default_rng(seed)
np.random.seed(seed)
circuit = random_clifford_circuit(num_qubits, depth, rng)
circuit.draw("mpl", fold=-1, scale=0.6)

2. Kenal pasti qubit sebagai sasaran semakan
Seterusnya kita memilih susun atur qubit 1D pada QPU Heron r3, ibm_boston. Kita memilih susun atur yang berliku secara pepenjuru melalui graf gandingan dan memaksimumkan bilangan qubit ancilla yang tersedia. Untuk mencari ancilla yang tersedia, kita menggunakan fungsi get_check_qubits, yang memulangkan senarai pasangan qubit sasaran/ancilla. Semakan Pauli pada target_qubits[i] akan dilaksanakan menggunakan ancilla_qubits[i].
Dalam graf gandingan di bawah, qubit hijau ialah qubit muatan, dan qubit oren ialah ancilla yang akan kita gunakan untuk melaksanakan semakan. Qubit 68, 69, 89, 91, 111, 113 dan 133 mempunyai ancilla bersebelahan, jadi kita akan menggunakan qubit tersebut (sepadan dengan qubit maya 0, 1, 3, 5, 7, 9 dan 11) sebagai qubit sasaran untuk melaksanakan semakan. Perhatikan bahawa qubit 134 bersebelahan dengan qubit 133 dalam muatan tetapi tidak diserlahkan sebagai ancilla. Ini kerana 133 telah (secara sewenang-wenangnya) dipilih untuk melaksanakan semakan dengan ancilla 132.
from qiskit.visualization import plot_coupling_map
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_paulice.layout import get_check_qubits
service = QiskitRuntimeService()
backend = service.backend("ibm_boston")
# Choose a layout and pair each available target qubit with a neighboring ancilla
layout = [68, 69, 78, 89, 90, 91, 98, 111, 112, 113, 119, 133]
target_qubits, ancilla_qubits = get_check_qubits(backend, layout)
print(f"Target qubits: {target_qubits}")
print(f"Ancilla qubits: {ancilla_qubits}")
plot_coupling_map(
num_qubits=backend.num_qubits,
qubit_coordinates=getattr(
backend.configuration(), "qubit_coordinates", None
),
coupling_map=backend.configuration().coupling_map,
figsize=(12, 12),
qubit_color=[
"#4CAF50"
if i in set(layout)
else "#FF9800"
if i in set(ancilla_qubits)
else "#DDDDDD"
for i in backend.coupling_map.graph.node_indices()
],
qubit_size=220,
line_width=2,
font_size=90,
)
Target qubits: [68, 69, 89, 91, 111, 113, 133]
Ancilla qubits: [67, 70, 88, 92, 110, 114, 132]

3. Transpile litar ke backend
Memandangkan backend telah dipilih, kita boleh terus men-transpile litar maya kita menjadi litar ISA. Di sini, kita hanya menetapkan susun atur qubit dan menterjemahkan gate ke dalam set gate natif ibm_boston.
from qiskit.transpiler import generate_preset_pass_manager
pm = generate_preset_pass_manager(
optimization_level=0, backend=backend, initial_layout=layout
)
circuit_isa = pm.run(circuit)
circuit_isa.draw("mpl", fold=-1, scale=0.6)

4. Cipta model hingar daripada data backend
Sekarang kita tahu bagaimana litar akan disusun atur pada QPU, kita boleh memodelkan bagaimana hingar gate dan hingar bacaan pada backend akan menjejaskan pelaksanaan litar. Model hingar digunakan untuk menentukan tempat optimum dalam litar untuk meletakkan semakan Pauli. Kita mahu meletakkan semakan pada kedudukan di mana ia menangkap jumlah ralat maksimum. Walaupun model hingar yang lebih tepat boleh meningkatkan keupayaan pengesanan semakan, biasanya tidak perlu mempelajari model hingar dengan mensampel QPU. Di sini kita menyimpulkan model hingar gate depolarisasi seragam menggunakan hanya data penanda aras backend daripada qiskit-ibm-runtime, dan mengabaikan ralat bacaan yang turut dilaporkannya: semakan yang kita temui dalam langkah 7 hanya membaca ancilla mereka, jadi ralat bacaan muatan tidak kelihatan kepada mereka dan hanya akan merendahkan fidelity pemilihan-pasca dalam langkah 10. Lihat dokumentasi qiskit_paulice.NoiseModel untuk maklumat lanjut tentang menentukan hingar gate dan bacaan. Bagi pengguna yang ingin menggunakan model hingar Pauli-Lindblad yang dipelajari, lihat NoiseModel.from_pauli_lindblad_maps.
from dataclasses import replace
from qiskit_paulice.noise_models import NoiseModel
# Model the gate noise as a uniform depolarizing channel based on backend benchmark data
noise_model = replace(
NoiseModel.from_backend(backend, layout, uniform_gate_noise=True),
readout_noise=None,
)
print(noise_model)
NoiseModel(gate_noise=0.0015808382794757693, readout_noise=None, idling_noise=None)
5. Visualisasikan semakan Pauli
Sekarang mari tambah beberapa semakan pada litar dan lihat rupanya. Kita boleh menggunakan fungsi qiskit_paulice.add_pauli_checks untuk mencari set semakan sah yang baik. Kita menyediakan litar muatan Clifford sebagai input, bersama senarai qubit muatan untuk menambah semakan (qubit sasaran) dan model hingar yang kita buat dalam langkah sebelumnya. Dalam contoh ini, kita akan menambah semakan pada kesemua 7 qubit sasaran yang mempunyai ancilla tersedia. Semakan ditambah pada qubit mengikut urutan ia muncul dalam target_qubits, yang memudahkan penaakulan tentang susun atur akhir litar yang disemak: final_layout = layout + ancilla_qubits. Jika pengguna memutuskan untuk menjalankan salah satu litar output dengan semakan yang lebih sedikit (i), susun atur akhir ialah final_layout = layout + ancilla_qubits[:i+1].
Visualisasi litar menunjukkan bahawa semakan telah dilaksanakan dengan betul menggunakan pasangan sasaran/ancilla yang ditentukan.
Butiran tentang qiskit_paulice.add_pauli_checks:
- Input pertama hendaklah litar Clifford. Di sini ia tidak mempunyai pengukuran dan kita menghantar
stabilizers="all", jadi semakan dicari untuk penstabil keadaan yang disediakan litar: semakan adalah sah jika hasil darab Pauli ruang-masanya mengukur penstabil sedemikian, sindromnya ialah bit ancilla sahaja, dan ia kekal sah walau dalam asas apa pun muatan diukur selepas itu. Inilah yang diperlukan oleh anggaran fidelity, kerana setiap penstabil yang disampel diukur dalam asas yang berbeza. (Mengabaikanstabilizerssebaliknya mencari semakan untuk pengukuran terminal litar.) - Argumen kedudukan kedua ialah senarai indeks qubit sasaran. Setiap qubit sasaran akan berkongsi gate berkusut dengan qubit ancilla untuk membentuk semakan. Semakan akan dicari dan ditambah pada litar mengikut urutan ia diberikan dalam argumen ini.
- Argumen kedudukan terakhir ialah model hingar yang mewakili hingar yang dijangka menjejaskan litar semasa pelaksanaan. Model ini digunakan untuk menilai keberkesanan sesuatu semakan.
ancilla_qubits: Jika litar input mempunyai susun atur, kwarg ini mengarahkanadd_pauli_checkscara menetapkan qubit fizikal kepada ancilla yang baru ditambah.ancilla_qubitshendaklah sama panjang dengantarget_qubits(argumen kedudukan ke-2), danancilla_qubits[i]hendaklah digunakan untuk melaksanakan semakan padatarget_qubits[i].cost: Fungsi kos yang digunakan untuk menilai semakan. Boleh jadigammaatauLER(kadar ralat logik), yang kesemuanya ialah cara menerangkan berapa banyak saluran hingar yang tidak dikesan oleh semakan. Denganstabilizers="all"sesuatu ralat dikira sebagai logik jika ia mengubah keadaan yang disediakan, iaitu tepat apa yang diukur oleh fidelity.method: Kaedah yang digunakan untuk mencari semakan sah. Kami menggunakanwindowedyang mensampel secara rawak banyak subset tetingkap spatial bagi sokongan (wayar) qubit sasaran. Nilai sah lain ialahgeneticdanwindowed_genetic.- Output
add_pauli_checksmengandungi litar dengan bilangan semakan yang bertambah, daripada litar tanpa semakan kepada litar dengan semakan pada setiap qubit sasaran (dengan andaian semakan sah ditemui).
Lihat Bahagian II-IV maklumat tambahan untuk maklumat terperinci tentang mencari semakan Pauli ruang-masa yang baik
import matplotlib.pyplot as plt
from qiskit_paulice import add_pauli_checks
# Find checks on circuit
checked = add_pauli_checks(
circuit_isa,
target_qubits,
noise_model,
ancilla_qubits=ancilla_qubits,
cost="gamma",
method="windowed",
seed=seed,
stabilizers="all",
)
print(f"Physical layout of payload+ancillas: {layout + ancilla_qubits}")
print("Checked circuit:")
checked[-1].circuit.draw("mpl", fold=-1, idle_wires=False)
Physical layout of payload+ancillas: [68, 69, 78, 89, 90, 91, 98, 111, 112, 113, 119, 133, 67, 70, 88, 92, 110, 114, 132]
Checked circuit:

6. Tentukan penstabil untuk diukur
Untuk menguji prestasi pengesanan ralat, kita akan menganggar fidelity keadaan penstabil, , yang disediakan secara ideal oleh litar berbanding keadaan berhingar, , yang sebenarnya dikeluarkan oleh perkakasan. Unjuran ke atas keadaan penstabil tulen, , ditakrifkan sebagai purata seragam ke atas unsur kumpulan penstabilnya :
Dengan memasukkan ini ke dalam persamaan fidelity, kita melihat bahawa fidelity keadaan berhingar, , setara dengan purata nilai jangkaan semua penstabil, , terhadap :
Bagi masalah yang lebih besar, mengukur (atau malah menyenaraikan) kesemua penstabil tidak boleh dilaksanakan, jadi fidelity sering dianggarkan dengan mengukur sampel rawak penstabil. Mensampel penstabil secara seragam rawak daripada memberikan penganggar fidelity yang tidak berat sebelah, ,
Untuk eksperimen ini kita akan menjadikan litar jauh lebih dalam untuk meningkatkan kesan hingar gate, dan kita akan menambah semakan pada setiap qubit yang mempunyai qubit ancilla jiran. Walaupun terdapat cara yang lebih canggih untuk mensampel penstabil bagi menganggar fidelity, dalam contoh ini kita akan mensampel penstabil dengan kebarangkalian seragam.
from qiskit.quantum_info import Clifford, Pauli, PauliList
num_stabilizers = 100
num_shots = 1_000
num_checks = len(target_qubits)
depth = 72
circuit = random_clifford_circuit(num_qubits, depth, rng)
# Create all 2^N stabilizers
stabilizers = PauliList([Pauli("I" * num_qubits)])
for generator in (
Pauli(label) for label in Clifford(circuit).to_labels(mode="S")
):
stabilizers = stabilizers + stabilizers.compose(generator)
# Choose some random stabilizers from the set
keep = np.where(stabilizers.x.any(axis=1) | stabilizers.z.any(axis=1))[0]
chosen = np.random.default_rng(seed).choice(
keep, size=min(num_stabilizers, len(keep)), replace=False
)
stabilizers = stabilizers[chosen]
depth = circuit.depth(lambda x: x.operation.num_qubits == 2)
print(
f"Sampled {len(stabilizers)} stabilizers of {circuit.num_qubits}-qubit circuit with two-qubit depth of {depth}: {{{stabilizers[0]}, {stabilizers[1]}, ...}}"
)
Sampled 100 stabilizers of 12-qubit circuit with two-qubit depth of 72: {-YIIYZZZIZIYY, -XIXYIIZXYIZX, ...}
7. Cari semakan yang baik untuk keseluruhan keadaan
Kita mencari satu set semakan untuk litar dalam tersebut tepat seperti dalam langkah 5. Memandangkan semakan hanya membaca ancilla mereka, litar yang disemak yang sama digunakan untuk setiap penstabil yang disampel. Litar yang dipulangkan hanya mengukur ancilla semakan, jadi kita akan menambah pengukuran muatan dalam langkah seterusnya.
import time
# Transpile the deep payload without measurements and find one set of checks for the whole state
payload_isa = pm.run(circuit)
t0 = time.time()
checked = add_pauli_checks(
payload_isa,
target_qubits,
noise_model,
ancilla_qubits=ancilla_qubits,
cost="gamma",
method="windowed",
seed=seed,
stabilizers="all",
)
print(
f"Added {len(checked[-1].check_qubits)} checks in {time.time() - t0:.0f}s."
)
print(
f"2-qubit depth increased from {payload_isa.depth(lambda x: len(x.qubits) == 2)} to "
f"{checked[-1].circuit.depth(lambda x: len(x.qubits) == 2)} when adding {num_checks} checks."
)
Added 7 checks in 3s.
2-qubit depth increased from 72 to 85 when adding 7 checks.
8. Sampel daripada litar yang berhingar
Kita mensampel setiap varian, daripada muatan kosong hingga litar dengan kesemua 7 semakan, bagi setiap penstabil yang disampel. Varian tanpa semakan ialah muatan kosong dengan pengukuran yang sama, jadi ia berfungsi sebagai rujukan berhingar. Daripada menambah putaran asas bagi setiap penstabil pada salinan setiap varian, kita membiarkan samplomatic memilih asas pengukuran pada masa pensampelan. Bagi setiap varian kita:
-
Ukur muatan dan kumpulkan pengukuran terminal ke dalam satu kotak dengan pass manager boxing samplomatic. Kotak itu membawa anotasi
ChangeBasis, jadibuildmemulangkan litar templat yang gate pengubah asasnya berparameter, bersama samplex yang mengira parameter bagi asas yang diminta. -
Tambah templat dan samplex ke
QuantumProgram. Samplex mengambil satu asas pengukuran bagi setiap qubit dalam kotak, dalam urutan fizikal menaik, dikodkan sebagai , , , . Menghantar tatasusunan asas dengan paksi utama berpanjang100menjadikan satu item mengukur kesemua100penstabil: muatan dalam asas setiap penstabil dan ancilla dalam . Qubit di luar sokongan sesuatu penstabil turut diukur dalam , dan bitnya diabaikan apabila nilai jangkaan penstabil dikira.
Primitif Executor daripada qiskit-ibm-runtime menjalankan keseluruhan program dalam satu job. Apabila diberi AerSimulator sebagai ganti QPU, ia berjalan secara setempat pada simulator itu, jadi kita memberikannya satu dengan hingar gate depolarisasi yang sama seperti yang kita gunakan untuk mencari semakan, dan menggunakan kaedah penstabil kerana semua litar ialah Clifford. Qiskit Aer hanya diperlukan untuk membina simulator ini: mod setempat mengambil seed daripada pilihan Executor tetapi mengabaikan noise_model mereka, jadi hingar mesti ditetapkan pada simulator itu sendiri. Menghantar backend QPU sebaliknya akan menjalankan program yang sama pada perkakasan. Setelah simulasi selesai, kita akan mengira kadar pemilihan-pasca dan nilai jangkaan untuk mendapat gambaran sejauh mana pengesanan ralat berfungsi sebelum menggambarkan data.
from qiskit import ClassicalRegister
from qiskit_aer import AerSimulator
from qiskit_aer.noise import NoiseModel as AerNoiseModel
from qiskit_aer.noise import depolarizing_error
from qiskit_ibm_runtime import Executor
from qiskit_ibm_runtime.quantum_program import QuantumProgram
from samplomatic.builders import build
from samplomatic.transpiler import generate_boxing_pass_manager
# Group the terminal measurements into a box whose measurement basis is chosen at sampling time
boxing_pm = generate_boxing_pass_manager(
enable_gates=False, measure_annotations="change_basis"
)
# bases[i, q]: basis in which physical qubit q is measured for stabilizer i (I=0, Z=1, X=2, Y=3)
bases = np.ones((len(stabilizers), backend.num_qubits), dtype=np.uint8)
bases[:, layout] = stabilizers.z + 2 * stabilizers.x
program = QuantumProgram(shots=num_shots)
for variant in checked:
circ = variant.circuit.copy()
circ.add_register(meas := ClassicalRegister(num_qubits, "meas"))
circ.measure(layout, meas)
template, samplex = build(boxing_pm.run(circ))
(basis_input,) = samplex.inputs().get_specs()
measured_qubits = sorted(layout + list(variant.check_qubits))
program.append_samplex_item(
template,
samplex=samplex,
samplex_arguments={basis_input.name: bases[:, measured_qubits]},
shape=(len(stabilizers),),
)
# Simulate the program with the same gate noise we used to find the checks
aer_nm = AerNoiseModel()
aer_nm.add_all_qubit_quantum_error(
depolarizing_error(noise_model.gate_noise, 2), ["cz"]
)
noisy_sim = AerSimulator(method="stabilizer", noise_model=aer_nm)
# outs[k][register]: bits of the variant with k checks, shape (stabilizers, shots, bits)
t0 = time.time()
outs = (
Executor(noisy_sim, options={"simulator": {"seed_simulator": seed}})
.run(program)
.result()
)
print(
f"Sampled {len(checked)} circuits for {len(stabilizers)} stabilizers in {time.time() - t0:.0f}s."
)
Sampled 8 circuits for 100 stabilizers in 77s.
9. Pilih-pasca sampel litar yang disemak
Dalam pakej ini sesuatu semakan dilaksanakan menggunakan gate berkusut antara satu qubit ancilla dan satu qubit sasaran. Setiap ancilla bermula dalam , jadi menstabilkan keadaan inputnya. Merambat ke hadapan dari permulaan ancilla melalui keseluruhan litar yang disemak menghasilkan didarab dengan penstabil keadaan yang disediakan, jadi semakan lulus jika bit ancilla ialah . Sesuatu sampel dikekalkan jika setiap semakan lulus.
Litar yang disemak mengukur ancilla ke dalam daftar checks_c, jadi sesuatu sampel dikekalkan jika setiap bit daftar itu ialah 0. Secara amnya, kaedah get_postselection_method bagi instans CheckedCircuit yang dikeluarkan daripada add_pauli_checks memetakan rentetan bit sesuatu shot kepada sindrom setiap semakan sekaligus; bagi semakan yang dicari untuk penstabil keadaan yang disediakan, setiap sindrom hanyalah bit ancilla-nya, jadi kita membaca daftar itu secara langsung.
Dalam carta, kita melihat bahawa penambahan lebih banyak semakan menyebabkan kadar pemilihan-pasca menurun. Walaupun matlamat pengesanan ralat ialah menangkap sebanyak mungkin ralat, apabila kadar pemilihan-pasca menurun lebih banyak shot mungkin diperlukan untuk mencapai ketepatan sasaran tertentu. Jika kadar pemilihan-pasca jatuh terlalu rendah, bilangan shot yang diperlukan untuk mengumpul satu sampel logik mungkin menjadi terlalu tinggi. Dalam contoh ini, kadar pemilihan-pasca kelihatan menumpu, membayangkan bahawa semakan tambahan menyumbang keupayaan pengesanan tambahan yang kurang. Walaupun penambahan lebih banyak semakan Pauli secara amnya memberikan keupayaan pengesanan ralat yang lebih besar, ia datang dengan kos kedalaman litar yang meningkat dan kos pensampelan yang meningkat.
def keep_mask(out):
"""Per-shot True/False: every ancilla read 0, so every Pauli check passed."""
if "checks_c" not in out: # the bare payload has no checks
return np.ones(out["meas"].shape[:-1], dtype=bool)
return ~out["checks_c"].any(axis=-1)
# masks[k][i]: kept shots of stabilizer i measured on the variant with k checks
masks = [keep_mask(out) for out in outs]
psrs_arr = np.array([mask.mean(axis=-1) for mask in masks]).T
ks = np.arange(psrs_arr.shape[1])
fig, ax = plt.subplots(figsize=(8, 4))
ax.plot(ks, psrs_arr.T, color="darkorange", alpha=0.15, linewidth=1)
ax.plot(
ks,
np.median(psrs_arr, axis=0),
color="black",
linewidth=1,
linestyle="--",
label="median",
)
ax.set(
xlabel="Checks committed",
ylabel="Post-selection rate",
ylim=(0, 1.05),
title=f"Per-stabilizer post-selection rate ({len(psrs_arr)} stabilizers)",
)
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

10. Bandingkan fidelity keadaan berhingar asal dan yang dipilih-pasca
Kita melihat bahawa pemilihan-pasca hanya sampel tanpa ralat dikesan sangat meningkatkan nilai jangkaan bagi setiap penstabil, dan dengan itu meningkatkan anggaran fidelity keadaan penstabil. Walaupun nilai pemilihan-pasca dikira menggunakan hanya sampel yang lulus setiap semakan, nilai jangkaan jauh lebih tepat dan varians sampel lebih rendah. Kita juga kini dapat melihat bahawa kadar pemilihan-pasca di atas sebenarnya menumpu kepada nilai yang serupa dengan fidelity litar berhingar. Kadar pemilihan-pasca menumpu kerana semakan menangkap sebahagian besar ralat yang sebenarnya berlaku semasa pelaksanaan litar.
def expectation_values(out, mask):
"""EV of each stabilizer from the shots of ``out`` kept by ``mask``.
Bit q of ``meas`` is virtual qubit q, so a stabilizer's eigenvalue in a shot is the parity of
the bits in its support, times its sign.
"""
support = stabilizers.x | stabilizers.z
parity = (out["meas"] & support[:, None, :]).sum(axis=-1) % 2
signs = np.where(stabilizers.phase == 2, -1.0, 1.0)
eigenvalues = np.where(parity, -1.0, 1.0) * signs[:, None]
kept = mask.sum(axis=-1)
return np.where(
kept > 0,
(eigenvalues * mask).sum(axis=-1) / np.maximum(kept, 1),
np.nan,
)
evs_noisy = expectation_values(outs[0], masks[0])
evs_post = expectation_values(outs[-1], masks[-1])
print(
f"ideal fidelity: 1.0\n"
f"noisy fidelity: {np.nanmean(evs_noisy):.4f}\n"
f"postselected fidelity: {np.nanmean(evs_post):.4f}\n"
f"mean post-sel rate: {masks[-1].mean():.3f}\n"
)
evs_ideal = np.ones(len(stabilizers))
idx = np.arange(len(stabilizers))
def cum_mean_sem(ys):
valid = ~np.isnan(ys)
s = np.cumsum(np.where(valid, ys, 0.0))
s2 = np.cumsum(np.where(valid, ys**2, 0.0))
n = np.maximum(np.cumsum(valid).astype(float), 1)
mean = s / n
sem = np.sqrt(np.maximum(s2 / n - mean**2, 0) / n)
return np.where(np.cumsum(valid) > 0, mean, np.nan), sem
def strip(ax, ys, color, label):
m, s = np.nanmean(ys), np.nanstd(ys)
ax.axhspan(
m - s, m + s, color=color, alpha=0.15, label=f"{label} mean ± std"
)
ax.axhline(m, color=color, linewidth=1, linestyle="--")
fig, ax = plt.subplots(figsize=(8, 4))
ax.axhline(np.nanmean(evs_ideal), color="black", linewidth=1.5, label="ideal")
strip(ax, evs_noisy, "red", "noisy")
strip(ax, evs_post, "green", "post-sel")
ax.scatter(
idx, evs_noisy, color="red", s=22, alpha=0.7, zorder=3, label="noisy <G>"
)
ax.scatter(
idx,
evs_post,
color="green",
s=22,
alpha=0.7,
zorder=3,
label="post-sel <G>",
)
ax.set(
xlabel="stabilizer index",
ylabel=r"$\langle G \rangle$",
ylim=(-0.1, 1.1),
title="Per-stabilizer EVs",
)
ax.legend(loc="lower left", ncol=2)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
M = np.arange(1, len(stabilizers) + 1)
fig, ax = plt.subplots(figsize=(8, 4))
for ys, color, label in [
(evs_ideal, "black", "ideal"),
(evs_noisy, "red", "noisy"),
(evs_post, "green", "post-sel"),
]:
cm, sem = cum_mean_sem(ys)
ax.plot(M, cm, color=color, linewidth=1.5, label=label)
ax.fill_between(M, cm - sem, cm + sem, color=color, alpha=0.15)
ax.set(
xlabel="number of stabilizers averaged",
ylabel="running fidelity estimate",
title="Fidelity convergence vs. number of stabilizers",
)
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
ideal fidelity: 1.0
noisy fidelity: 0.5582
postselected fidelity: 0.9747
mean post-sel rate: 0.535

Memandangkan satu set semakan digunakan untuk setiap penstabil, hanya terdapat satu lengkung gamma untuk dilihat: nilai fungsi kos selepas setiap semakan dikomit. Bagi masalah lain pada set qubit fizikal yang berbeza, dan dengan model hingar yang tidak sempurna (ingat, model hingar kita dalam tutorial ini memodelkan dengan tepat hingar yang menjejaskan sampel), urutan semakan ditambah pada qubit sasaran boleh memberi kesan yang lebih ketara terhadap cara lengkung itu menumpu.
Lengkung gamma yang menumpu kepada 1.0 ini membayangkan bahawa semakan tambahan tidak akan dapat mengesan banyak ralat tambahan, kerana terdapat sangat sedikit ralat termodel yang tidak dilindungi oleh semakan. Ini mengukuhkan pemerhatian kita sebelum ini bahawa penumpuan kadar pemilihan-pasca disebabkan oleh ketiadaan ralat yang tidak dilindungi dalam litar.
costs = [variant.cost for variant in checked]
fig, ax = plt.subplots(figsize=(8, 4))
ax.plot(range(len(costs)), costs, color="steelblue", marker="o")
ax.set(
xlabel="Checks committed",
ylabel="Gamma",
yscale="log",
title="Gamma of the noise left uncovered by the checks",
)
ax.grid(True, alpha=0.3, which="both")
plt.tight_layout()
plt.show()