Lewati ke konten utama
Proyek ini masih dalam tahap beta. Menemukan bug? Buat isu GitHub. Punya pertanyaan atau ide? Tanya di Discussions — kami sangat menghargai masukan kamu!
Bagaimana kualitas terjemahan di halaman ini?

Zero-noise extrapolation dengan probabilistic error amplification

Zero-noise extrapolation (ZNE) adalah teknik untuk memitigasi noise kuantum dalam perhitungan nilai ekspektasi observable. Caranya dengan menjalankan sirkuit kuantum pada skala noise yang makin besar, mengambil nilai ekspektasi untuk masing-masing skala noise tersebut, lalu mengekstrapolasi untuk memperkirakan nilai ekspektasi tanpa noise. Tahap amplifikasi noise maupun ekstrapolasi bisa diimplementasikan dengan berbagai cara. Di panduan ini kamu akan mengamplifikasi noise dengan probabilistic error amplification (PEA), seperti yang disarankan di "Evidence for the utility of quantum computing before fault tolerance". Untuk mengamplifikasi dengan PEA, kamu harus lebih dulu memodelkan noise yang memengaruhi gate entangling pada sirkuit. Kamu juga bisa mengimplementasikan amplifikasi dengan digital gate folding, yang tidak membutuhkan model noise dan dibahas di panduan gate folding. Kamu akan mempelajari noise-nya dengan qiskit-noise-learning dan mengekstrapolasi dengan strategi fitting eksponensial bawaan.

Perlu diingat, meskipun ZNE bisa efektif memitigasi noise kuantum dengan biaya sampling lebih rendah daripada metode lain, ZNE tidak punya batas untuk bias.

ZNE extrapolation

Alur kerjanya sebagai berikut:

  1. Tentukan sirkuit dan observable
  2. Hitung nilai ekspektasi eksak menggunakan simulator statevector Qiskit
  3. Hitung nilai ekspektasi yang noisy dan belum dimitigasi
  4. Pelajari noise dari layer entangling dengan qiskit-noise-learning
  5. Siapkan program ZNE dengan probabilistic error amplification dan sampel sirkuit dengan skala noise yang bervariasi
  6. Ekstrapolasi ke batas zero-noise
  7. Bandingkan nilai ekspektasi hasil mitigasi

Tentukan sirkuit dan observable​

Di panduan ini kamu akan menghitung magnetisasi rata-rata situs ⟨Z⟩avg=1N∑i=1N⟨Zi⟩\langle Z \rangle_{\text{avg}} = \frac{1}{N} \sum_{i=1}^{N} \langle Z_i \rangle dari model Ising medan transversal (TFIM) yang di-Trotter-kan pada delapan qubit dengan topologi cincin. Kamu akan menyapu beberapa nilai kekuatan medan transversal dan memplot magnetisasi sebagai fungsi kekuatan medan. Pada topologi cincin dengan jumlah qubit genap, layer gate dua-qubit bisa dibagi menjadi dua layer unik, seperti ditunjukkan warna merah dan hijau pada coupling map di bawah. Merancang sirkuit dengan himpunan layer entangling unik yang minimal itu penting kalau kamu ingin mempelajari model noise, karena layer-layer unik harus dipelajari secara terpisah. Kalau sirkuitmu punya terlalu banyak layer unik, noise learning bisa jadi tidak layak karena biaya sampling untuk mempelajari banyak layer.

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"IPython": "IPython", "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", "scipy": "scipy"}
_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 IPython.display
import numpy as np
from qiskit.circuit import Parameter, QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.visualization import plot_coupling_map

warnings.filterwarnings("ignore")

def trotter_circuit(num_qubits, theta, layers, num_steps):
"""Trotterized transverse-field Ising evolution: RX on every site, then a CZ layer per coupling layer."""
circuit = QuantumCircuit(num_qubits)
for _ in range(num_steps):
circuit.rx(theta, range(num_qubits))
for layer in layers:
circuit.barrier()
for edge in layer:
circuit.cz(*edge)
circuit.sdg(edge)
circuit.barrier()
return circuit

num_qubits = 8
trotter_steps = 15
ring_coupling = [(i, (i + 1) % num_qubits) for i in range(num_qubits)]
layers = [
ring_coupling[0::2],
ring_coupling[1::2],
] # two disjoint layers of CZ gates
theta = Parameter("theta")
circuit = trotter_circuit(num_qubits, theta, layers, trotter_steps)

# Parameter values: theta in [0, 0.25 pi] - values for the transverse-field sweep
data_points = 6
parameter_values = np.linspace(0, 0.25 * np.pi, data_points).reshape(
(data_points, 1)
)

# Define observables - Z on each site
observables = [
SparsePauliOp("I" * (num_qubits - q - 1) + "Z" + "I" * q)
for q in range(num_qubits)
]

print("Edges of the same color may execute their gates simultaneously.")
IPython.display.display(
plot_coupling_map(
num_qubits=num_qubits,
qubit_coordinates=None,
coupling_map=ring_coupling,
figsize=(4, 4),
qubit_size=150,
line_width=5,
line_color=[
"red" if edge in layers[0] else "green" for edge in ring_coupling
],
font_size=90,
)
)
print("Visualization of 1 Trotter step")
trotter_circuit(num_qubits, theta, layers, 1).draw("mpl", scale=0.7)
Edges of the same color may execute their gates simultaneously.

Output of the previous code cell

Visualization of 1 Trotter step

Output of the previous code cell

Hitung nilai ekspektasi eksak menggunakan simulator statevector Qiskit​

Gunakan kelas Statevector Qiskit untuk menghitung nilai ekspektasi eksak ZiZ_i pada setiap situs untuk setiap nilai θ\theta.

from qiskit.quantum_info import Statevector
import matplotlib.pyplot as plt

exact_evs = np.array(
[
[
Statevector(circuit.assign_parameters([value]))
.expectation_value(obs)
.real
for value in parameter_values.ravel()
]
for obs in observables
]
) # shape (observables, parameter values)

x_vals = parameter_values.ravel() / np.pi
fig, ax = plt.subplots(figsize=(7, 4))
ax.plot(x_vals, exact_evs.mean(axis=0), "ko-", label="Exact")
ax.set_xlabel(r"$\theta/\pi$")
ax.set_ylabel(r"$\langle Z \rangle_{\text{avg}}$")
ax.legend()
plt.tight_layout()
plt.show()

Output of the previous code cell

Hitung nilai ekspektasi yang noisy dan belum dimitigasi​

Sebuah QuantumProgram berisi sirkuit kuantum dan samplex dalam format yang cocok untuk primitif Executor. MitigationTask.prepare membangun QuantumProgram dari sirkuit, observable, dan nilai parameter, lalu membungkus sirkuit dalam box dengan opsi boxing yang diberikan. Atur opsi boxing measure_annotations ke "change_basis", yang merotasi pengukuran ke basis yang dibutuhkan, dan tambahkan add_tags: "unique_box" agar simulator bisa memetakan model noise ke box-box pada sirkuit. Program ini di-Pauli-twirl secara default, yang pada hardware mengubah noise koheren menjadi noise Pauli yang diasumsikan oleh metode mitigasi. num_randomizations menentukan berapa banyak instans sirkuit hasil twirl yang disampel, dan shots_per_randomization menentukan berapa banyak shot yang diterima tiap instans, sehingga hasil kali keduanya adalah anggaran shot per sirkuit.

Pengguna dengan akses QPU akan menjalankan program dengan Executor yang dibangun dari backend milik QiskitRuntimeService. Di sini kamu akan memberikan AerSimulator ke Executor, yang menjalankan program secara lokal, lalu memakai opsi simulator layer_noise_model untuk menyuntikkan model noise Pauli-Lindblad acak pada setiap layer gate entangling, yang dihasilkan dengan random_pauli_lindblad_noise dari qiskit-mitigation dengan error per layered gate (EPLG) sebesar 0.003, tipikal untuk QPU Heron saat ini.

from qiskit.circuit import CircuitInstruction
from qiskit.quantum_info import PauliLindbladMap, pauli_basis
from qiskit_aer import AerSimulator
from qiskit_ibm_runtime import Executor
from qiskit_mitigation import MitigationTask, find_combined_unique_layers

def generate_random_pauli_lindblad_noise(
layers,
num_qubits: int,
eplg: float = 3e-3,
seed: int | None = None,
) -> list[tuple[CircuitInstruction, 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 contain_measurement: # do not add measurement noise
layer_noise = PauliLindbladMap.identity(num_qubits)
else:
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))
return layer_noise_model

num_randomizations = 50
shots_per_randomization = 64

boxing_options = {
"measure_annotations": "change_basis",
"add_tags": "unique_box",
}

# find the unique layers to generate the layered noise for
box_layers = find_combined_unique_layers(
[circuit], custom_boxing_options=boxing_options
)
layer_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(),
options={"simulator": {"layer_noise_model": layer_noise_model}},
)

task = MitigationTask()
program = task.prepare(
circuit=circuit,
observables=observables,
parameters=parameter_values,
num_randomizations=num_randomizations,
shots_per_randomization=shots_per_randomization,
custom_boxing_options=boxing_options,
)
program._semantic_role = "simulation" # flag the program for the simulator
unmitigated_res = task.postprocess(noisy_executor.run(program).result())

fig, ax = plt.subplots(figsize=(7, 4))
ax.plot(x_vals, exact_evs.mean(axis=0), "k-", label="Exact")
ax.errorbar(
x_vals,
np.mean(unmitigated_res.data.evs, axis=0),
np.mean(unmitigated_res.data.stds, axis=0),
fmt="o-",
label="Unmitigated",
)
ax.set_xlabel(r"$\theta/\pi$")
ax.set_ylabel(r"$\langle Z \rangle_{\text{avg}}$")
ax.legend()
plt.tight_layout()
plt.show()

Output of the previous code cell

Pelajari noise dari layer entangling dengan qiskit-noise-learning​

PEA diawali dengan mempelajari model noise twirled dari setiap layer gate entangling di dalam circuit. Kita mengasumsikan model noise Markovian, sehingga hanya model noise dari setiap layer gate entangling yang unik yang perlu dipelajari, karena kedalaman circuit diasumsikan tidak memengaruhi kanal noise dari suatu layer entangling. Makin akurat model noise yang dipelajari, makin akurat pula amplifikasinya.

Kita memakai qiskit-noise-learning untuk mempelajari noise. Learner mengulang setiap layer sebanyak fragment_depths kali di dalam circuit pembelajarannya, men-twirl setiap circuit sebanyak num_randomizations kali, lalu mencocokkan peluruhan fidelity hasil pengukuran ke model Pauli-Lindblad. Kode berikut menggambar model noise yang dipelajari, dengan kedua layer digabung menjadi satu peta. Setiap qubit berupa diagram pie dari laju X, Y, dan Z berbobot-1 yang dijumlahkan pada kedua layer, dan setiap edge menunjukkan laju berbobot-2 yang dipelajari; laju 3e-4 berarti error itu terjadi kira-kira tiga dari setiap 10.000 kali pemakaian layer.

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 PEA
from qiskit_noise_learning.aer_executor import AerExecutor
from qiskit_noise_learning.noise_learner import LearningOptions, NoiseLearner
from samplomatic import InjectNoise, Tag
from samplomatic.utils import get_annotation

backend = GenericBackendV2(
num_qubits=num_qubits,
basis_gates=["id", "rz", "sx", "x", "cz"],
coupling_map=ring_coupling,
)

task_pea = PEA()
# learn only the layers PEA will inject noise into, i.e. not the measurement layer
pea_layers = [
layer
for layer in task_pea.find_unique_layers(circuit, boxing_options)
if get_annotation(layer.operation, InjectNoise)
]

learning_options = LearningOptions(
fragment_depths=[2, 4, 8, 16],
num_randomizations=50,
shots_per_randomizations=64,
)
# 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(), noise_dict=noise_dict
)
learner = NoiseLearner(
backend, options=learning_options, executor=noise_learner_simulator
)
learned_noise = (
learner.run(pea_layers).result().to_dict()
) # keyed by InjectNoise reference

# Draw the learned model on the ring, laid out as a square with the qubits in ring order.
# Composing the two layers' maps sums their rates, giving one map over all the ring's edges.
embedding = Embedding(
[(0, 0), (0, 2), (0, 4), (2, 4), (4, 4), (4, 2), (4, 0), (2, 0)],
ring_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()

Output of the previous code cell

Siapkan program ZNE dengan amplifikasi error probabilistik dan sampel circuit dengan skala noise yang bervariasi​

Di sini kamu akan menyiapkan program ZNE yang berisi circuit dengan skala noise yang meningkat. Dengan PEA, setiap noise factor mewakili kelipatan noise yang dipelajari yang dialami circuit: noise factor 2.0 menyuntikkan satu salinan tambahan kanal noise yang dipelajari dari setiap layer setelah layer itu, di atas noise yang sudah dimiliki layer tersebut. Karena PEA menskalakan laju noise yang dipelajari secara langsung alih-alih mengulang gate, noise factor apa pun dimungkinkan, termasuk yang bukan bilangan bulat, dan circuit itu sendiri tidak diubah: item program berbagi satu circuit template dan hanya berbeda pada noise yang disuntikkan oleh samplex. Noise yang disuntikkan disampel sekali per randomization, sehingga error statistik PEA dikendalikan oleh jumlah randomization, bukan shot per randomization; karena itu kita memakai banyak randomization dengan beberapa shot saja untuk masing-masing, sambil menjaga total shot tetap sama dengan program tanpa mitigasi. Penting untuk tidak memakai terlalu sedikit randomization, karena jika tidak, kamu mungkin menemui bias sampling pada injeksi noise.

Total sampling overhead naik secara linear terhadap jumlah noise factor.

noise_factors = [1, 2, 3]
program_pea = task_pea.prepare(
circuit=circuit,
observables=observables,
parameters=parameter_values,
num_randomizations=200,
shots_per_randomization=16,
custom_boxing_options=boxing_options,
noise_maps=learned_noise,
noise_factors=noise_factors,
)

Terakhir, jalankan program pada simulator ber-noise. Hasilnya dipakai untuk ekstrapolasi di langkah berikutnya.

program_pea._semantic_role = (
"simulation" # flag the program for the simulator
)
job_pea_results = noisy_executor.run(program_pea).result()

Ekstrapolasi ke batas zero-noise​

Sekarang setelah kamu punya data untuk setiap skala noise, kamu bisa mencocokkan sebuah fungsi ke data tersebut dan mengekstrapolasi balik ke nilai zero-noise untuk memperkirakan expectation value dari observable tanpa noise. Di bawah noise Pauli, sebuah observable meluruh secara eksponensial terhadap noise factor, jadi kamu akan memilih ekstrapolator eksponensial sebagai pilihan pertama, dan beralih ke yang linear jika pencocokan eksponensial gagal. Untuk memvisualisasikan ekstrapolasi, plot magnetisasi rata-rata situs yang dihitung pada setiap noise factor, kurva eksponensialnya, dan rata-rata situs dari expectation value hasil ekstrapolasi pada titik zero-noise. Kita melihat kurvanya sedikit meleset ke bawah untuk observable khusus ini.

from scipy.optimize import curve_fit

pea_mitigated_res = task_pea.postprocess(
job_pea_results, extrapolator=["exponential", "linear"]
)

# The result holds the expectation value at each noise factor and the extrapolated value for
# every observable; we average them over the sites and fit the same model to draw the curve.
angle = 3 # theta = 0.15 pi, the angle with the largest unmitigated error
amplifications = pea_mitigated_res.data.evs_noise_factors[:, angle, :].mean(
axis=0
)
x_fit = np.linspace(0, max(noise_factors), 51)
params, _ = curve_fit(
lambda x, a, b: a * np.exp(b * x),
noise_factors,
amplifications,
p0=[1.0, -0.1],
)

plt.plot(
noise_factors, amplifications, "o", color="red", label="amplifications"
)
plt.plot(
x_fit,
params[0] * np.exp(params[1] * x_fit),
"--",
color="black",
label="exponential fit",
)
plt.plot(
[0],
pea_mitigated_res.data.evs[:, angle].mean(),
"*",
color="black",
markersize=15,
label="extrapolation",
)
plt.plot(
[0],
exact_evs[:, angle].mean(),
"_",
color="red",
markersize=15,
markeredgewidth=3,
label="exact",
)
plt.xlabel("noise factor")
plt.ylabel(r"$\langle Z \rangle_{\text{avg}}$")
plt.legend()
plt.show()

Output of the previous code cell

Bandingkan expectation value hasil mitigasi​

Plot magnetisasi rata-rata situs dari setiap metode di seluruh rentang sudut rotasi Ising. PEA memulihkan magnetisasi eksak dalam batas error bar-nya, sementara hasil tanpa mitigasi bias jauh di bawahnya.

results = {
"Unmitigated": unmitigated_res,
"PEA": pea_mitigated_res,
}

fig, ax = plt.subplots(figsize=(7, 4))
ax.plot(x_vals, exact_evs.mean(axis=0), "k-", label="Exact")
for label, res in results.items():
ax.errorbar(
x_vals,
np.mean(res.data.evs, axis=0),
np.mean(res.data.stds, axis=0),
fmt="o-",
label=label,
)
ax.set_xlabel(r"$\theta/\pi$")
ax.set_ylabel(r"$\langle Z \rangle_{\text{avg}}$")
ax.legend()
ax.set_title(
f"Error-Mitigated Magnetization [{num_qubits}-qubit, {trotter_steps} Trotter steps]"
)
plt.tight_layout()
plt.show()

Output of the previous code cell