クリフォード回路へのドーピング
1. クリフォード回路を構築する
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_paulice": "qiskit-paulice"}
_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
from qiskit_paulice import CheckedCircuit
rng = np.random.default_rng(1764)
circuit = QuantumCircuit(10)
circuit.h(range(circuit.num_qubits))
for d in range(6):
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()
plain = CheckedCircuit(circuit)
2. 有効なすべてのワイヤーにドーピングする
doped_circuit, doped_wires = plain.dope()
print(f"{len(doped_wires)} sites, e.g. {doped_wires[:3]}")
doped_circuit.draw("mpl", fold=-1, scale=0.5)
24 sites, e.g. (Wire(qubit=0, after_instruction=0), Wire(qubit=1, after_instruction=1), Wire(qubit=2, after_instruction=2))
3. 固定数のワイヤーにドーピングする
_, doped_wires = plain.dope(num_sites=8, seed=1764)
print(doped_wires)
(Wire(qubit=2, after_instruction=2), Wire(qubit=4, after_instruction=4), Wire(qubit=6, after_instruction=6), Wire(qubit=8, after_instruction=8), Wire(qubit=9, after_instruction=9), Wire(qubit=2, after_instruction=39), Wire(qubit=9, after_instruction=47), Wire(qubit=7, after_instruction=59))
4. 回転角を選択する
from qiskit.quantum_info import Clifford
s_doped_circuit, s_doped_wires = plain.dope(angle=np.pi / 2)
Clifford(
s_doped_circuit.remove_final_measurements(inplace=False)
) # raises if not Clifford
print(f"S doping uses the same {len(s_doped_wires)} sites and stays Clifford")
S doping uses the same 24 sites and stays Clifford
5. すべてのドーピング構成に対応する1つのパラメーター化テンプレート
template, template_wires = plain.dope(wires="after_entangling", angle=None)
print(f"{len(template_wires)} parametrized sites")
template.draw("mpl", fold=-1, scale=0.5)
21 parametrized sites

dope に角度をバインドして構成を選びます。ここでは、各サイトがランダムに T ゲートまたは恒等演算になります。
angles = rng.choice([0.0, np.pi / 4], size=len(template_wires))
configuration = template.assign_parameters(angles)
print(f"{np.count_nonzero(angles)} T gates in this configuration")
11 T gates in this configuration
6. チェック済みの回路にドーピングする
from qiskit.quantum_info import Pauli, Statevector
from qiskit_paulice import NoiseModel, add_pauli_checks
noise_model = NoiseModel(gate_noise=0.002, readout_noise=0.005)
checked = add_pauli_checks(
circuit, target_qubits=[0, 2, 4, 6, 8], noise_model=noise_model, seed=1764
)[-1]
doped_checked, doped_checked_wires = checked.dope()
print(
f"{len(doped_checked_wires)} sites preserve all {len(checked.check_qubits)} checks"
)
# The Z parity over each check's support is +1 for every ideal output
state = Statevector(doped_checked.remove_final_measurements(inplace=False))
num_qubits = doped_checked.num_qubits
for support in checked.check_support:
parity = Pauli(
"".join(
"Z" if q in support else "I" for q in reversed(range(num_qubits))
)
)
print(
f"check on qubits {support}: <Z parity> = {state.expectation_value(parity).real:.3f}"
)
doped_checked.draw("mpl", fold=-1, scale=0.5, idle_wires=False)
19 sites preserve all 5 checks
check on qubits (1, 2, 10): <Z parity> = 1.000
check on qubits (1, 2, 4, 11): <Z parity> = 1.000
check on qubits (3, 5, 12): <Z parity> = 1.000
check on qubits (6, 13): <Z parity> = 1.000
check on qubits (14,): <Z parity> = 1.000

7. ドーピング後の忠実度に上限を設ける
rates = checked.estimate_fault_rates(noise_model, seed=1764)
print(
f"harmless rate: {rates.harmless_rate:.4f} ± {rates.harmless_stderr:.4f}"
)
harmless rate: 0.0067 ± 0.0003