PEC dengan TREX
Pembatalan ralat probabilistik (PEC) ialah teknik untuk mengurangkan hingar kuantum dalam pengiraan nilai jangkaan observable. Ia membolehkan anda mengira anggaran nilai jangkaan yang tidak berbias. Teknik ini berfungsi dengan melaksanakan satu ensemble litar berhingar yang disuntik dengan songsangan model hingar yang dipelajari. Model hingar itu kemudian dihampiri kepada saluran Pauli dengan menggunakan Pauli twirling (juga dikenali sebagai randomized compiling). Model hingar sedemikian boleh dipelajari dengan cekap dan boleh dimitigasi dengan menggunakan PEC (lihat Probabilistic error cancellation with sparse Pauli-Lindblad models on noisy quantum processors untuk maklumat lanjut).
Twirled Readout Error eXtinction (TREX) ialah teknik yang memitigasi ralat pengukuran dan berguna untuk menganggar nilai jangkaan observable Pauli. Ia bergantung pada twirling pengukuran (menyuntik gate X secara rawak sebelum pengukuran dan membalikkan keputusan yang diukur), yang mengpepenjurukan matriks pemindahan ralat bacaan dan memudahkan penyongsangannya. Satu sebutan penskalaan semula dipelajari daripada saluran hingar pepenjuru dengan mengukur litar kalibrasi twirled yang dimulakan dalam keadaan sifar.
Panduan ini menunjukkan cara menggunakan PEC untuk memitigasi hingar gate dan menggabungkannya dengan TREX untuk turut memitigasi hingar pengukuran semasa menganggar nilai jangkaan observable.
Aliran kerjanya adalah seperti berikut:
- Nyatakan litar dan observable.
- Kira nilai jangkaan tepat dengan menggunakan simulator statevector Qiskit.
- Kira nilai jangkaan berhingar tanpa mitigasi.
- Pelajari hingar lapisan berbelit dengan menggunakan qiskit-noise-learning.
- Sediakan atur cara PEC + TREX dan tambahkan litar kalibrasi TREX kepadanya.
- Bandingkan nilai jangkaan yang dimitigasi.
Nyatakan litar dan observable
Sediakan litar cermin bagi evolusi masa Trotter untuk model Ising medan melintang linear pada lapan qubit. Cerminkan litar untuk membatalkan unitari litar dan membentuk litar identiti. Ini membolehkan anda mengira nilai jangkaan unggul setiap observable dengan mudah. Selain itu, pencerminan menggandakan kedalaman litar, yang seterusnya menggandakan kesan hingar.
Gunakan sudut untuk memastikan semua gate dalam litar daripada kumpulan Clifford, membolehkan simulasi yang lebih pantas dengan menggunakan simulator stabilizer.
# 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_mitigation": "qiskit-mitigation", "qiskit_noise_learning": "qiskit-noise-learning", "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 warnings
import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
warnings.filterwarnings("ignore")
def construct_ising_circuit(
num_qubits: int,
num_trotter_steps: int,
rx_angle: float,
barrier: bool = True,
) -> QuantumCircuit:
circuit = QuantumCircuit(num_qubits)
for _step in range(num_trotter_steps):
circuit.rx(rx_angle, range(num_qubits))
if barrier:
circuit.barrier()
for first_qubit in (1, 2):
for idx in range(first_qubit, num_qubits, 2):
# equivalent to Rzz(-pi/2):
circuit.sdg([idx - 1, idx])
circuit.cz(idx - 1, idx)
if barrier:
circuit.barrier()
return circuit
def mirror_circuit(circuit: QuantumCircuit) -> QuantumCircuit:
mirror_circ = circuit.copy_empty_like()
mirror_circ.compose(circuit, inplace=True)
mirror_circ.barrier()
mirror_circ.compose(circuit.inverse(), inplace=True)
mirror_circ.measure_active()
return mirror_circ
# define observables
num_qubits = 8
observables = [
SparsePauliOp("I" * ((num_qubits - 1) - q) + "Z" + "I" * q)
for q in range(num_qubits)
]
# Instantiate circuit
num_trotter_steps = 5
rx_angle = np.pi / 2
circuit = construct_ising_circuit(
num_qubits, num_trotter_steps, rx_angle, barrier=False
)
mirrored_circuit = mirror_circuit(circuit)
print("Visualization of 1 mirrored Trotter step")
mirror_circuit(
construct_ising_circuit(num_qubits, 1, rx_angle, barrier=False)
).draw("mpl", scale=0.7)
Visualization of 1 mirrored Trotter step
Kira nilai jangkaan berhingar tanpa mitigasi
QuantumProgram mengandungi litar kuantum dan samplex dalam format yang sesuai untuk primitif Executor. MitigationTask.prepare membina QuantumProgram daripada litar, observable dan nilai parameter, serta membungkus litar dalam kotak dengan pilihan pengotakan yang diberikan. Tetapkan pilihan pengotakan measure_annotations kepada "change_basis" untuk memutar pengukuran ke dalam asas yang diperlukan, dan tambahkan add_tags: "unique_box" supaya simulator boleh memetakan model hingar ke atas kotak litar. Atur cara ini di-Pauli-twirl secara lalai, yang pada perkakasan menukarkan hingar koheren kepada hingar Pauli yang diandaikan oleh kaedah mitigasi. num_randomizations menetapkan bilangan instans twirled bagi setiap litar yang disampel dan shots_per_randomization menetapkan bilangan shot yang diterima setiap instans, jadi hasil darabnya ialah had shot bagi setiap litar.
Jika anda mempunyai akses QPU, anda boleh melaksanakan atur cara dengan Executor yang dibina daripada backend QiskitRuntimeService. Contoh ini menggunakan AerSimulator untuk menjalankan atur cara secara setempat. Pilihan simulator layer_noise_model miliknya menyuntik model hingar Pauli-Lindblad rawak pada setiap lapisan gate berbelit, yang dijana dengan random_pauli_lindblad_noise daripada qiskit-mitigation dengan ralat bagi setiap gate berlapis (EPLG) sebanyak 0.003, lazim bagi QPU Heron semasa.
import matplotlib.pyplot as plt
from qiskit.circuit import CircuitInstruction
from qiskit.quantum_info import PauliLindbladMap, pauli_basis
from qiskit.transpiler import PassManager
from qiskit_aer import AerSimulator
from qiskit_ibm_runtime import Executor
from qiskit_mitigation import MitigationTask, find_combined_unique_layers
from qiskit_noise_learning.aer_executor.insert_noise_pass import (
InsertNoisePass,
)
from samplomatic import Tag
from samplomatic.utils import get_annotation
def generate_random_pauli_lindblad_noise(
layers,
num_qubits: int,
eplg: float = 3e-3,
readout_noise_scale: float = 3e-2,
seed: int | None = None,
) -> tuple[
list[tuple[CircuitInstruction, PauliLindbladMap]], PauliLindbladMap
]:
"""Generate random Pauli-Lindblad noise over the full Pauli basis."""
layer_noise_model = []
for layer in layers:
layer_instructions = layer.operation.params[0]
contain_measurement = any(
inst.name.startswith("measure") for inst in layer_instructions
)
if not contain_measurement:
edges = [
(inst.qubits[0]._index, inst.qubits[1]._index)
for inst in layer_instructions
] # extract the edges of the layer
basis_paulis = [p for p in pauli_basis(2) if np.sum(p.x + p.z)]
rng = np.random.default_rng(seed=seed)
num_generators = len(basis_paulis)
rates = rng.random(len(basis_paulis)) * eplg / num_generators
layer_noise = PauliLindbladMap.from_sparse_list(
[
(pauli.to_label(), edge, rate)
for pauli, rate in zip(basis_paulis, rates, strict=True)
for edge in edges
],
num_qubits=num_qubits,
)
layer_noise_model.append((layer, layer_noise))
# generate a readout noise model
rng = np.random.default_rng(seed=seed)
rates = rng.random(num_qubits) * readout_noise_scale
readout_noise_model = PauliLindbladMap.from_sparse_list(
[("X", [q], rate) for q, rate in enumerate(rates)],
num_qubits=num_qubits,
)
return layer_noise_model, readout_noise_model
def add_readout_noise(program, readout_noise_model):
"""Injects readout noise to the circuits in a quantum program."""
readout_noise_map = {}
for item in program.items:
readout_tag = item.circuit[-1].label[
-4:
] # extract the readout layer tag from the circuit
readout_noise_map[readout_tag] = readout_noise_model
item.circuit = PassManager(
[InsertNoisePass(noise_dict=readout_noise_map, noise_after=False)]
).run(item.circuit)
num_randomizations = 300
shots_per_randomization = 64
boxing_options = {"add_tags": "unique_box"}
# find the unique layers to generate the layered noise for
box_layers = find_combined_unique_layers(
[mirrored_circuit], custom_boxing_options=boxing_options
)
layer_noise_model, readout_noise_model = generate_random_pauli_lindblad_noise(
box_layers, num_qubits, seed=42
)
# a local noisy simulator in place of a QPU backend
noisy_executor = Executor(
AerSimulator(method="stabilizer"),
options={"simulator": {"layer_noise_model": layer_noise_model}},
)
task = MitigationTask()
program = task.prepare(
circuit=mirrored_circuit,
observables=observables,
parameters=None,
num_randomizations=num_randomizations,
shots_per_randomization=shots_per_randomization,
custom_boxing_options=boxing_options,
)
# For the simulation - we inject readout noise to the circuits of the program
add_readout_noise(program, readout_noise_model)
program._semantic_role = "simulation" # flag the program for the simulator
unmitigated_res = task.postprocess(noisy_executor.run(program).result())
x_vals = np.array(range(num_qubits))
fig, ax = plt.subplots(figsize=(7, 4))
ax.plot(x_vals, [1] * len(x_vals), "k-", alpha=0.5, label="Exact")
ax.errorbar(
x_vals,
unmitigated_res.data.evs,
unmitigated_res.data.stds,
fmt="o",
label="Unmitigated",
)
x_tick_labels = [r"$Z_{ {i} }$" for i in range(num_qubits)]
ax.set_xlabel(r"$\theta/\pi$")
ax.set_ylabel("Expectation value")
ax.legend()
ax.set_xticks(x_vals, x_tick_labels)
plt.tight_layout()
plt.show()
Pelajari hingar lapisan berbelit
PEC bermula dengan mempelajari model hingar twirled bagi setiap lapisan gate berbelit dalam litar. Jika anda mengandaikan model hingar Markovian, kedalaman litar diandaikan tidak menjejaskan saluran hingar bagi sesuatu lapisan berbelit. Oleh itu, hanya model hingar bagi setiap lapisan gate berbelit yang unik perlu dipelajari. Litar yang digunakan dalam panduan ini boleh dipecahkan kepada dua lapisan unik. Mereka bentuk litar supaya mempunyai set lapisan berbelit unik yang minimum adalah penting jika anda ingin mempelajari model hingar, kerana lapisan unik mesti dipelajari secara berasingan. Jika litar anda mempunyai terlalu banyak lapisan unik, pembelajaran hingar boleh menjadi tidak praktikal disebabkan kos persampelan untuk mempelajari banyak lapisan.
qiskit-noise-learning digunakan untuk mempelajari hingar. Pembelajar mengulang setiap lapisan sebanyak fragment_depths kali dalam litar pembelajarannya, melakukan twirl pada setiap litar sebanyak num_randomizations kali, dan memadankan pereputan fidelity yang diukur kepada model Pauli-Lindblad. Parameter ini boleh menjejaskan ketepatan model yang dipelajari. Semakin tepat model hingar yang dipelajari, semakin tepat pembatalan hingar tersebut.
Kod berikut melukis model hingar yang dipelajari dengan kedua-dua lapisan digabungkan menjadi satu peta. Setiap qubit ialah carta pai bagi kadar X, Y dan Z berpemberat-1 yang dijumlahkan merentas kedua-dua lapisan, dan setiap sisi menunjukkan kadar berpemberat-2 yang dipelajari. Sebagai contoh, kadar 3e-4 bermaksud ralat itu berlaku pada kira-kira tiga daripada setiap 10,000 penggunaan lapisan tersebut.
Mitigasi hingar bacaan dilakukan dalam pasca-pemprosesan dengan menggunakan TREX, berbeza daripada model hingar gate, yang diperlukan untuk penyediaan atur cara bagi menyuntik anti-hingar yang sepadan dengan model hingar yang dipelajari. Oleh itu, hingar bacaan boleh dilaksanakan dan dipelajari semasa mempelajari hingar gate, atau semasa melaksanakan atur cara yang dimitigasi. Mempelajari hingar bacaan bersama hingar gate mungkin bermanfaat untuk mempelajari model hingar yang konsisten dengan dirinya (lihat Disambiguating Pauli noise in quantum computers sebagai contoh). Dalam kes apabila hingar gate dan hingar bacaan dipisahkan dalam model hingar, anda disyorkan mempelajari hingar bacaan semasa melaksanakan atur cara yang dimitigasi untuk mengelakkan hanyutan hingar antara ketika ia dipelajari dengan ketika ia dilaksanakan.
Dalam panduan ini, hingar bacaan dipelajari semasa melaksanakan atur cara yang dimitigasi dengan menggunakan litar kalibrasi TREX.
from qiskit.providers.fake_provider import GenericBackendV2
from qiskit.quantum_info import PauliList
from qiskit_ibm_runtime.results import LayerError, PauliLindbladError
from qiskit_ibm_runtime.visualization.embeddings import Embedding
from qiskit_mitigation import PEC
from qiskit_noise_learning.aer_executor import AerExecutor
from qiskit_noise_learning.noise_learner import LearningOptions, NoiseLearner
from samplomatic import InjectNoise
line_coupling = [(i, i + 1) for i in range(num_qubits - 1)]
backend = GenericBackendV2(
num_qubits=num_qubits,
basis_gates=["id", "rz", "sx", "x", "cz"],
coupling_map=line_coupling,
)
task_pec = PEC()
# first learn only the layers PEC will inject noise into, i.e. not the measurement layer
pec_layers = [
layer
for layer in task_pec.find_unique_layers(mirrored_circuit, boxing_options)
if get_annotation(layer.operation, InjectNoise)
]
layers = task_pec.find_unique_layers(mirrored_circuit, boxing_options)
learning_options = LearningOptions(
fragment_depths=[2, 4, 8, 16],
num_randomizations=250,
shots_per_randomizations=16,
)
# qiskit-noise-learning is not compatible with the Executor in local mode,
# and requires the simulator present in the package for running simulation
noise_dict = {
get_annotation(layer.operation, Tag).ref: noise_layer
for layer, noise_layer in layer_noise_model
}
noise_learner_simulator = AerExecutor(
qasm_simulator=AerSimulator(method="stabilizer"), noise_dict=noise_dict
)
learner = NoiseLearner(
backend, options=learning_options, executor=noise_learner_simulator
)
learned_noise = (
learner.run(pec_layers).result().to_dict()
) # keyed by InjectNoise reference
# Draw the learned model on the line, laid out as a line with the qubits in order.
# Composing the two layers' maps sums their rates, giving one map over all the edges.
embedding = Embedding(
[(0, 0), (0, 2), (0, 4), (2, 4), (4, 4), (4, 2), (4, 0), (2, 0)],
line_coupling,
)
first, second = learned_noise.values()
learned_model = first.compose(second).simplify()
error = PauliLindbladError(
PauliList(
learned_model.get_qubit_sparse_pauli_list_copy().to_pauli_list()
),
learned_model.rates,
)
fig = LayerError(circuit, list(range(num_qubits)), error).draw_map(
embedding, radius=0.4, height=460, width=620
)
fig.update_traces(
marker={
"cmin": 0,
"cmax": max(learned_model.rates),
"colorbar": {"tickformat": ".1e", "title": "rate"},
},
selector={"mode": "markers"},
)
fig.show()
Sediakan atur cara PEC dan TREX gabungan
Di sini kami menyediakan kedua-dua tugas TREX dan PEC, dan menambahkan tugas TREX sebagai pemboleh ubah kepada tugas PEC. Ini memastikan semua sifat yang diperlukan untuk mitigasi TREX ditambahkan pada item PEC yang dimitigasi (seperti twirling pengukuran). Hingar yang disuntik disampel sekali bagi setiap perawakan rawak, jadi ralat statistik PEC dikawal oleh bilangan perawakan rawak dan bukannya bilangan shot bagi setiap perawakan rawak. Oleh itu, banyak perawakan rawak digunakan - setiap satu dengan beberapa shot sahaja - dengan mengekalkan jumlah shot yang sama seperti atur cara tanpa mitigasi.
Litar kalibrasi TREX ditambahkan pada atur cara untuk mempelajari hingar bacaan semasa atur cara dilaksanakan. Litar kalibrasi TREX sangat cetek, yang menjadikan perawakan rawak tambahan untuknya agak murah dan membolehkan hingar bacaan dipelajari dengan lebih tepat.
from qiskit_mitigation import TREX
trex = TREX()
program_pec = task_pec.prepare(
circuit=mirrored_circuit,
observables=observables,
parameters=None,
num_randomizations=1200,
shots_per_randomization=16,
custom_boxing_options=boxing_options,
trex=trex,
noise_maps=learned_noise, # PEC option
noise_gain=0, # PEC option
max_sampling_overhead=100, # PEC option
)
program_pec = trex.prepare(
num_randomizations=1500,
quantum_program=program_pec,
custom_boxing_options=boxing_options,
)
# For the simulation - we inject readout noise to the circuits of the program
add_readout_noise(program_pec, readout_noise_model)
Periksa overhed shot, laksanakan atur cara, dan lakukan pasca-pemprosesan
PEC mengembalikan anggaran nilai jangkaan yang tidak berbias, dengan kos overhed shot yang besar. Overhed shot yang diperlukan boleh dianggarkan dengan mengira faktor gamma litar, yang mencerminkan kesan model hingar yang dipelajari terhadap litar. Secara lalai, bilangan perawakan rawak bagi tugas PEC diskalakan mengikut jumlah anggaran overhed shot yang diperlukan untuk mendapatkan anggaran tidak berbias (~). Untuk mengelakkan penskalaan automatik ini, anda boleh menetapkan parameter scale_randomizations_by_gamma kepada False. Overhed shot dihadkan oleh parameter max_sampling_overhead.
Anda patut menyemak bilangan shot yang diperlukan oleh PEC untuk mencapai anggaran tidak berbias sebelum pelaksanaan bagi memutuskan sama ada jumlah ini mampu ditanggung, kerana ia meningkat secara eksponen dengan kadar hingar dan kedalaman litar.
print(f"The circuit calculated gamma is: {task_pec.gamma}")
print(f"The circuit shot overhead is ~{task_pec.gamma**2}")
The circuit calculated gamma is: 1.2774846896089953
The circuit shot overhead is ~1.631967132185391
program_pec._semantic_role = (
"simulation" # flag the program for the simulator
)
job_pec_results = noisy_executor.run(program_pec).result()
# post-processing
pec_res = task_pec.postprocess(job_pec_results)
# In order to post process the results without TREX, we call `postprocess` again, but instead of the default options we
# pass only the PEC item result and the `measure_noise_data` as `None`
pec_without_trex_res = task_pec.postprocess(
job_pec_results[task_pec._program_item_index], measure_noise_data=None
)
Bandingkan nilai jangkaan yang dimitigasi
Akhir sekali, plotkan nilai jangkaan bagi setiap observable. PEC + TREX memulihkan nilai jangkaan tepat dalam julat ralatnya, manakala keputusan tanpa mitigasi berbias jauh di bawahnya.
x = np.array(range(num_qubits))
x_tick_labels = [r"$Z_{ {i} }$" for i in range(num_qubits)]
fig, ax = plt.subplots(figsize=(8, 5))
ax.errorbar(
x,
unmitigated_res.data.evs,
unmitigated_res.data.stds,
fmt="o",
label="Twirl only",
)
ax.errorbar(
x,
pec_without_trex_res.data.evs,
pec_without_trex_res.data.stds,
fmt="o",
label="PEC only",
)
ax.errorbar(
x,
pec_res.data.evs,
pec_res.data.stds,
fmt="o",
label="PEC + TREX",
)
ax.plot(x, [1] * len(x), "--", color="black", alpha=0.5, label="Exact")
tick_labels = [f"$Z_{ {i} }$" for i in range(num_qubits)]
ax.set_xticks(x, tick_labels)
ax.set_ylabel("Expectation value")
ax.legend()
ax.set_title("PEC+TREX Mitigated Expectation Values")
plt.tight_layout()
plt.show()