クイック・スタート
このガイドでは、この qiskit-paulice パッケージの最小限の動作例を紹介します。 我々は、クリフォード回路の実行中にエラーを検出するために時空間パウリチェックを用い、その後、エラーが検出されなかったサンプルのみを事後選択する。 忠実度の向上を定量的に示す、より詳しいエンドツーエンドのワークフローについては、 詳細なチュートリアルをご覧ください。
ワークフロー・ステップ
- 入力の準備を行います:クリフォード・ペイロード回路、チェック演算を実行するターゲット/アンシラ量子ビット、およびノイズモデルです。
- 適切な時空パウリチェックを見つけ、それらを回路に追加する。
- チェック済みの回路をサンプルとして確認してください。
- エラーが検出されなかったサンプルのみを抽出してください。
1. 入力データの準備
add_pauli_checks 少なくとも1つの端子測定を含むクリフォード回路、チェックの実装に使用されるターゲット量子ビット、および候補となるチェックの評価に用いられるノイズモデルを受け取る。 ibm_bostonここでは、浅いランダムなクリフォード回路を構築し、 1D の物理量子ビットチェーン上に配置し、各ペイロード量子ビットを隣接するアンシラとペアリングし(これにより、チェックにはSWAPゲートが不要となる)、バックエンドのベンチマークデータから大まかな脱分極ノイズモデルを推定する。
import numpy as np
from qiskit import QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_paulice.layout import get_check_qubits
from qiskit_paulice.noise_models import NoiseModel
# Backend and a 1D chain of physical qubits to run on
backend = QiskitRuntimeService().backend("ibm_boston")
layout = [68, 69, 78, 89, 90, 91, 98, 111, 112, 113, 119, 133]
# A shallow brickwork random Clifford payload circuit
rng = np.random.default_rng(1764)
circuit = QuantumCircuit(len(layout))
circuit.h(range(circuit.num_qubits))
for d in range(4):
for i in range(d % 2, circuit.num_qubits - 1, 2):
circuit.cz(i, i + 1)
for q in range(circuit.num_qubits):
if rng.integers(0, 2):
circuit.sx(q)
if rng.integers(0, 2):
circuit.s(q)
circuit.measure_all()
# Pair each payload qubit with a neighboring ancilla to host a check (no SWAPs needed)
target_qubits, ancilla_qubits = get_check_qubits(backend.coupling_map, layout)
# A rough depolarizing noise model from backend benchmark data, used to score checks
noise_model = NoiseModel.from_backend(
backend, layout, uniform_gate_noise=True
)
circuit.draw("mpl", fold=-1)Output:
2. 時空パウリチェックを検索して追加する
add_pauli_checks 各ターゲット量子ビットに対して、効果的で低重みのチェックを検索し、それらを回路に追加して、一連の CheckedCircuit インスタンスを返す。 各インスタンスには CheckedCircuit 、チェックの数が徐々に増えており、チェックから 0 、ターゲット量子ビット1つにつき1回のチェックまで含まれています。 各チェックは、回路の深さがわずかに増えるという代償を払う代わりに、エラー検出能力を向上させます。 この例では、すべてのターゲット量子ビットに対してチェックを行う回路を使用します。
from qiskit_paulice import add_pauli_checks
# The circuit has virtual qubits, so we specify our target qubits with virtual indices
target_qubits_v = [layout.index(q) for q in target_qubits]
# Add spacetime Pauli checks
checked_circuit = add_pauli_checks(circuit, target_qubits_v, noise_model)[-1]
checked_circuit.circuit.draw("mpl", fold=-1, scale=0.4, idle_wires=False)Output:
3. チェック済みの回路のサンプルを取得する
ibm_bostonサンプリングを行う前に、検証済みの回路を物理量子ビット(initial_layout = layout + ancilla_qubits)上にトランスパイルし、のネイティブ基底ゲートセットに変換します。 ノイズの多いQPUをエミュレートするために、チェックの選択に使用されたもの noise_model と同じエラー率を持つ、ノイズの多いAerスタビライザーバックエンドを用いてサンプリングを行う。
from qiskit import transpile
from qiskit_aer import AerSimulator
from qiskit_aer.noise import NoiseModel as AerNoiseModel
from qiskit_aer.noise import ReadoutError, depolarizing_error
# Transpile once: lay the checked circuit out on our qubits and into the native basis
isa_circuit = transpile(
checked_circuit.circuit,
backend,
initial_layout=layout + ancilla_qubits,
optimization_level=0,
)
# Build an Aer noise model matching the depolarizing model used to pick checks
aer_noise = AerNoiseModel()
aer_noise.add_all_qubit_quantum_error(
depolarizing_error(noise_model.gate_noise, 2), ["cz"]
)
p = noise_model.readout_noise
aer_noise.add_all_qubit_readout_error(ReadoutError([[1 - p, p], [p, 1 - p]]))
simulator = AerSimulator(method="stabilizer", noise_model=aer_noise)
counts = (
simulator.run(isa_circuit, shots=1000, seed_simulator=1764)
.result()
.get_counts()
)
print(f"sampled {sum(counts.values())} shots")Output:
sampled 1000 shots
4. エラーが検出されなかったポストセレクトサンプル
get_postselection_method 各ショットをそのシンドロームベクトルに対応付けます。シンドロームがすべてゼロのショット、つまりどのチェックでもエラーが検出されなかったショットのみを保持します。 フラグが立てられたショットを排除することで、検出されたエラーが残存する分布から除去され、サンプリングレートは低下するものの、その忠実度は向上する。 定量的な忠実度の比較については、チュートリアルをご覧ください。
# Keep only the shots in which no check reported an error
ps_fn = checked_circuit.get_postselection_method()
counts_postselected = {
bs: n for bs, n in counts.items() if not ps_fn(bs).any()
}
kept, total = sum(counts_postselected.values()), sum(counts.values())
print(f"kept {kept} of {total} shots ({kept / total:.0%})")Output:
kept 927 of 1000 shots (93%)