Ekstrapolasi hingar sifar dengan gate folding dan ekstrapolasi tersuai
Ekstrapolasi hingar sifar (ZNE) ialah teknik untuk mengurangkan hingar kuantum dalam pengiraan nilai jangkaan observable. Ia berfungsi dengan melaksanakan litar kuantum pada skala hingar yang meningkat, mengambil nilai jangkaan berkenaan skala hingar tersebut, dan mengekstrapolasi untuk menganggar nilai jangkaan hingar sifar. Kedua-dua peringkat penguatan hingar dan ekstrapolasi boleh dilaksanakan dengan cara yang berbeza. Panduan ini menguatkan hingar dengan "digital gate folding", yang bermaksud gate dua qubit digantikan dengan jujukan setara gate itu dan songsangannya. Sebagai contoh, menggantikan unitari dengan akan menghasilkan faktor penguatan hingar 3. Anda juga boleh melaksanakan penguatan dengan penguatan ralat probabilistik (PEA), yang memerlukan langkah tambahan mempelajari model hingar dan diliputi dalam panduan penguatan ralat probabilistik. Panduan ini mengekstrapolasi menggunakan beberapa strategi penyuaian terbina dalam (linear dan eksponen), dan juga menunjukkan cara melaksanakan fungsi penyuaian tersuai anda sendiri.
Perlu diingat, walaupun ZNE boleh berkesan dalam mengurangkan hingar kuantum dengan kos pensampelan yang lebih rendah daripada kaedah lain, ia tidak mempunyai sempadan pada pincang.
Aliran kerjanya adalah seperti berikut:
- Tentukan litar dan observable
- Kira nilai jangkaan tepat menggunakan simulator statevector Qiskit
- Kira nilai jangkaan berhingar yang tidak dikurangkan
- Sediakan program ZNE dengan digital gate folding dan litar sampel pada skala hingar yang berbeza
- Ekstrapolasi ke had hingar sifar dengan fungsi penyuaian terbina dalam dan tersuai
- Bandingkan nilai jangkaan yang dikurangkan
Tentukan litar dan observable
Kira magnetisasi purata-tapak bagi model Ising medan melintang (TFIM) Trotter pada lapan qubit dalam topologi cincin. Anda akan menyapu beberapa nilai kekuatan medan melintang dan memplot magnetisasi sebagai fungsi kekuatan medan.
# 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", "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 numpy as np
from qiskit.circuit import Parameter, QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
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("Visualization of 1 Trotter step")
trotter_circuit(num_qubits, theta, layers, 1).draw("mpl", scale=0.7)
Visualization of 1 Trotter step
Kira nilai jangkaan tepat menggunakan simulator statevector Qiskit
Gunakan kelas Statevector Qiskit untuk mengira nilai jangkaan tepat pada setiap tapak bagi setiap nilai .
import matplotlib.pyplot as plt
from qiskit.quantum_info import Statevector
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()
Kira nilai jangkaan berhingar yang tidak dikurangkan
QuantumProgram mengandungi litar kuantum dan samplex dalam format yang sesuai untuk primitif Executor. MitigationTask.prepare membina QuantumProgram daripada litar, observable, dan nilai parameter, serta mengotakkan litar dengan pilihan pengotakan yang diberikan. Anda menetapkan pilihan pengotakan measure_annotations kepada "change_basis", yang memutarkan pengukuran ke dalam asas yang diperlukan, dan menambah add_tags: "unique_box" supaya simulator dapat memetakan model hingar ke atas kotak litar. Program ini di-twirl Pauli secara lalai, yang pada perkakasan menukar hingar koheren kepada hingar Pauli yang diandaikan oleh kaedah mitigasi. num_randomizations menetapkan berapa banyak contoh twirl bagi setiap litar yang disampel dan shots_per_randomization menetapkan berapa banyak shot yang diterima setiap contoh, jadi hasil darabnya ialah bajet shot bagi setiap litar.
Pengguna yang mempunyai akses QPU akan melaksanakan program dengan Executor yang dibina daripada Backend QiskitRuntimeService. Di sini, anda akan menghantar AerSimulator kepada Executor, yang menjalankan program secara setempat, dan menggunakan pilihan simulator layer_noise_modelnya untuk 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, tipikal bagi QPU Heron semasa.
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()
Sediakan program ZNE dengan digital gate folding dan litar sampel pada skala hingar yang berbeza
Sediakan program ZNE yang mengandungi litar pada skala hingar yang meningkat. Setiap faktor hingar mewakili gandaan gate berbelit yang terkandung dalam litar (faktor hingar 3.0 mengandungi gate berbelit tiga kali lebih banyak daripada litar asal). Litar bagi setiap faktor hingar di-twirl dengan bilangan rawakan dan shot yang sama seperti program yang tidak dikurangkan.
from qiskit_mitigation import GateFolding
task_gf = GateFolding()
program_zne = task_gf.prepare(
circuit=circuit,
observables=observables,
parameters=parameter_values,
num_randomizations=num_randomizations,
shots_per_randomization=shots_per_randomization,
custom_boxing_options=boxing_options,
noise_factors=[1, 3, 5], # ZNE option
)
Lukis langkah Trotter pertama templat twirl dengan faktor hingar 1 dan dengan faktor hingar 3 untuk menggambarkan penguatan. Bagi faktor hingar 3.0, setiap lapisan litar digantikan dengan lapisan itu, songsangannya, dan lapisan itu sekali lagi.
import IPython.display
def first_trotter_step(template, noise_factor):
"""The template up to the closing barrier of its first Trotter step (two layers, each folded ``noise_factor`` times)."""
toy = template.copy_empty_like()
for instruction in template.data:
toy.append(instruction)
if (
instruction.operation.name == "barrier"
and instruction.operation.label.startswith(
f"R{2 * noise_factor - 1}@"
)
):
break
return toy
IPython.display.display(
first_trotter_step(program_zne.items[0].circuit, 1).draw(
"mpl", scale=0.7, plot_barriers=False
)
)
first_trotter_step(program_zne.items[1].circuit, 3).draw(
"mpl", fold=-1, plot_barriers=False
)

Akhir sekali, jalankan program pada simulator berhingar. Keputusannya menjadi input kepada ekstrapolasi dalam langkah seterusnya.
program_zne._semantic_role = (
"simulation" # flag the program for the simulator
)
job_zne_results = noisy_executor.run(program_zne).result()
Ekstrapolasi ke had hingar sifar dengan fungsi penyuaian terbina dalam dan tersuai
Setelah anda mempunyai data bagi setiap skala hingar, anda boleh menyuaikan fungsi pada data dan mengekstrapolasi kembali ke nilai hingar sifar untuk menganggar nilai jangkaan observable bebas hingar. Terdapat banyak cara untuk menyuaikan data, dan pilihan fungsi yang betul bergantung pada sifat data yang disampel. GateFolding.postprocess menyediakan beberapa fungsi ekstrapolasi terbina dalam, termasuk linear, exponential, dan double_exponential. Kaedah postprocess menerima senarai kaedah ekstrapolasi, menilainya mengikut urutan, dan memulangkan nilai pertama dengan nilai sah secara fizikal dan varians terhingga. Ini bermakna urutan anda menyatakan pengekstrapolasi mempunyai kesan besar terhadap pengekstrapolasi yang dipilih. Menyerahkan fallback sebagai pengekstrapolasi bermakna jika tiada jawapan sah ditemui, nilai jangkaan bagi faktor hingar terkecil akan dipilih dan dipulangkan. Jika fallback tidak diserahkan dan tiada pengekstrapolasi menghasilkan nilai sah secara fizikal, keputusan dengan std terkecil dipulangkan. Di bawah hingar Pauli, sesuatu observable mereput secara eksponen dengan faktor hingar, jadi dengan 30 lapisan (15 langkah Trotter, 2 lapisan setiap langkah) gate berbelit, pengekstrapolasi eksponen ialah pilihan semula jadi; anda juga boleh mengekstrapolasi secara linear, yang merupakan pilihan lazim apabila pereputan kecil.
Untuk menggunakan fungsi penyuaian tersuai, hantar pengekstrapolasi "custom" bersama tuple custom_fit yang mengandungi fungsi penyuaian, senarai dengan tekaan awal bagi setiap parameternya, dan sempadan. Ini dipanjangkan kepada scipy.optimize.curve_fit. Pemprosesan pasca boleh diulang dengan pengekstrapolasi yang berbeza tanpa melaksanakan semula program. Di sini anda menggunakan fungsi penyuaian punca kuasa dua tersuai. Untuk menggambarkan ekstrapolasi, plot magnetisasi purata-tapak yang dikira pada setiap faktor hingar bagi , ketiga-tiga penyuaian, dan purata tapak bagi nilai jangkaan terekstrapolasi pada titik hingar sifar. Ketiga-tiga titik data kelihatan hampir linear, namun ekstrapolasi linear mendarat dengan ketara di bawah nilai tepat. Seperti yang diramalkan, penyuaian eksponen menganggar nilai jangkaan tanpa hingar dengan paling berkesan.
from scipy.optimize import curve_fit
zne_mitigated_exp = task_gf.postprocess(
job_zne_results, extrapolator=["exponential", "linear"]
)
zne_mitigated_lin = task_gf.postprocess(
job_zne_results, extrapolator=["linear"]
)
def sqrt_fit(x, a, b):
return a * np.sqrt(x) + b
custom_fit = (sqrt_fit, [1.0, 1.0], (-np.inf, np.inf))
zne_mitigated_custom = task_gf.postprocess(
job_zne_results,
extrapolator=["custom", "exponential", "linear"],
custom_fit=custom_fit,
)
# The result holds the expectation value at each noise factor and the extrapolated value, but
# not the fit parameters, so we refit the same models here to draw the curves.
noise_factors = np.array([1, 3, 5])
angle = 3 # theta = 0.15 pi, the angle with the largest unmitigated error
amplifications = zne_mitigated_exp.data.evs_noise_factors[:, angle, :].mean(
axis=0
)
x_fit = np.linspace(0, 5, 51)
fits = {
"exponential": (
lambda x, a, b: a * np.exp(b * x),
zne_mitigated_exp,
"black",
),
"linear": (lambda x, a, b: a * x + b, zne_mitigated_lin, "blue"),
"square-root": (sqrt_fit, zne_mitigated_custom, "green"),
}
plt.plot(
noise_factors, amplifications, "o", color="red", label="amplifications"
)
for name, (fit, res, color) in fits.items():
params, _ = curve_fit(fit, noise_factors, amplifications, p0=[1.0, -0.1])
plt.plot(
x_fit, fit(x_fit, *params), "--", color=color, label=f"{name} fit"
)
plt.plot(
[0],
res.data.evs[:, angle].mean(),
"*",
color=color,
markersize=15,
label=f"{name} 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()

Bandingkan nilai jangkaan yang dikurangkan
Plot magnetisasi purata-tapak bagi setiap kaedah merentasi sapuan sudut putaran Ising. Ekstrapolasi eksponen memulihkan magnetisasi tepat dalam julat ralatnya, manakala ekstrapolasi linear menghapuskan banyak pincang tetapi sentiasa menganggar terlalu rendah, kerana pereputan sebenar adalah eksponen. Penyuaian punca kuasa dua menganggar terlalu tinggi dengan teruk.
results = {
"Unmitigated": unmitigated_res,
"ZNE exponential": zne_mitigated_exp,
"ZNE linear": zne_mitigated_lin,
"ZNE square-root": zne_mitigated_custom,
}
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()
