Lumaktaw sa pangunahing nilalaman
Ang proyektong ito ay nasa beta. Nakakita ng bug? Mag-bukas ng GitHub issue. May tanong o ideya? Magtanong sa Discussions — gusto naming marinig ang iyong feedback!
Gaano kaganda ang kalidad ng pagsasalin sa pahinang ito?

Zero-noise extrapolation na may probabilistic error amplification

Ang zero-noise extrapolation (ZNE) ay isang teknik para bawasan ang quantum noise sa mga kalkulasyon ng expectation value ng observable. Gumagana ito sa pamamagitan ng pagpapatakbo ng quantum circuit sa tumataas na noise scale, pagkuha ng mga expectation value ayon sa mga noise scale na iyon, at pag-extrapolate para tantiyahin ang zero-noise expectation value. Parehong maipapatupad sa iba't ibang paraan ang mga yugto ng noise amplification at extrapolation. Sa gabay na ito, ia-amplify mo ang noise gamit ang probabilistic error amplification (PEA), gaya ng iminungkahi sa "Evidence for the utility of quantum computing before fault tolerance". Para mag-amplify gamit ang PEA, kailangan mo munang i-model ang noise na nakakaapekto sa mga entangling gate ng circuit. Maaari mo ring ipatupad ang amplification gamit ang digital gate folding, na hindi nangangailangan ng noise model at tinatalakay sa gate folding guide. Aaralin mo ang noise gamit ang qiskit-noise-learning at mag-e-extrapolate ka gamit ang built-in na exponential fitting strategy.

Tandaan, bagama't epektibo ang ZNE sa pagbabawas ng quantum noise sa mas mababang sampling cost kaysa sa ibang paraan, wala itong anumang hangganan sa bias.

ZNE extrapolation

Ganito ang workflow:

  1. Tukuyin ang circuit at observable
  2. Kalkulahin ang eksaktong mga expectation value gamit ang statevector simulator ng Qiskit
  3. Kalkulahin ang maingay at hindi-na-mitigate na mga expectation value
  4. Aralin ang noise ng mga entangling layer gamit ang qiskit-noise-learning
  5. Maghanda ng ZNE program na may probabilistic error amplification at mag-sample ng mga circuit na may iba't ibang noise scale
  6. Mag-extrapolate sa zero-noise limit
  7. Ihambing ang mga na-mitigate na expectation value

Tukuyin ang circuit at observable​

Sa gabay na ito, kakalkulahin mo ang site-averaged na magnetization ⟨Z⟩avg=1N∑i=1N⟨Zi⟩\langle Z \rangle_{\text{avg}} = \frac{1}{N} \sum_{i=1}^{N} \langle Z_i \rangle ng isang Trotterized na transverse-field Ising model (TFIM) sa walong qubit sa ring topology. Magsa-sweep ka ng ilang halaga ng lakas ng transverse field at ipo-plot ang magnetization bilang function ng lakas ng field. Sa ring topology na may even na bilang ng mga qubit, maaaring hatiin sa dalawang natatanging layer ang mga layer ng two-qubit gate, gaya ng ipinapakita ng pula at berdeng kulay sa coupling map sa ibaba. Mahalaga ang pagdidisenyo ng mga circuit na may minimal na set ng natatanging entangling layer kung gusto mong mag-aral ng noise model, dahil dapat na hiwalay na pag-aralan ang bawat natatanging layer. Kung masyadong marami ang natatanging layer sa iyong circuit, maaaring maging imposible ang noise learning dahil sa sampling cost ng pag-aaral ng maraming 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

Kalkulahin ang eksaktong mga expectation value gamit ang statevector simulator ng Qiskit​

Gamitin ang Statevector class ng Qiskit para kalkulahin ang eksaktong expectation value ng ZiZ_i sa bawat site para sa bawat halaga ng θ\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

Kalkulahin ang maingay at hindi-na-mitigate na mga expectation value​

Ang QuantumProgram ay naglalaman ng quantum circuit at samplex sa format na angkop para sa Executor primitive. Binubuo ng MitigationTask.prepare ang isang QuantumProgram mula sa circuit, mga observable, at mga halaga ng parameter, at nilalagyan ng box ang circuit gamit ang ibinigay na mga boxing option. Itakda ang boxing option na measure_annotations sa "change_basis", na nagro-rotate ng mga measurement sa kinakailangang mga basis, at idagdag ang add_tags: "unique_box" para maipa-map ng simulator ang isang noise model sa mga box ng circuit. Pauli-twirled ang program bilang default, na sa hardware ay ginagawang Pauli noise ang coherent noise na ipinapalagay ng mga mitigation method. Itinatakda ng num_randomizations kung ilang twirled na instance ng bawat circuit ang sine-sample, at itinatakda ng shots_per_randomization kung ilang shot ang natatanggap ng bawat instance, kaya ang produkto ng dalawa ang shot budget bawat circuit.

Ang user na may access sa QPU ay magpapatakbo ng programa gamit ang Executor na binuo mula sa isang backend ng QiskitRuntimeService. Dito, magpapasa ka ng AerSimulator sa Executor, na nagpapatakbo ng programa nang lokal, at gagamitin mo ang simulator option nitong layer_noise_model para mag-inject ng random na Pauli-Lindblad noise model sa bawat layer ng entangling gates, na nabuo gamit ang random_pauli_lindblad_noise mula sa qiskit-mitigation na may error per layered gate (EPLG) na 0.003, na tipikal sa mga kasalukuyang Heron QPU.

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

Alamin ang noise ng mga entangling layer gamit ang qiskit-noise-learning​

Nagsisimula ang PEA sa pag-aaral ng twirled noise model ng bawat layer ng entangling gates sa circuit. Ipinapalagay natin ang Markovian na noise model, kaya ang noise model lang ng bawat natatanging layer ng entangling gates ang kailangang matutunan, dahil ipinapalagay na hindi naaapektuhan ng depth ng circuit ang noise channel ng isang entangling layer. Kung mas tumpak ang natutunang noise model, mas tumpak din ang amplification.

Ginagamit natin ang qiskit-noise-learning para matutunan ang noise. Inuulit ng learner ang bawat layer nang fragment_depths beses sa mga learning circuit nito, nagta-twirl ng bawat circuit nang num_randomizations beses, at ifi-fit ang pagbaba ng mga nasukat na fidelity sa isang Pauli-Lindblad model. Ipinapakita ng sumusunod na code ang natutunang noise model, kung saan pinagsama sa isang mapa ang dalawang layer. Ang bawat qubit ay isang pie ng weight-1 na X, Y, at Z rate nito na pinagsama sa dalawang layer, at ipinapakita ng bawat edge ang mga natutunang weight-2 rate; ang rate na 3e-4 ay nangangahulugang nagaganap ang error na iyon sa humigit-kumulang tatlo sa bawat 10,000 gamit ng 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

Maghanda ng ZNE program na may probabilistic error amplification at mag-sample ng mga circuit na may iba't ibang noise scale​

Dito, maghahanda ka ng ZNE program na naglalaman ng mga circuit na may pataas na noise scale. Sa PEA, ang bawat noise factor ay kumakatawan sa multiple ng natutunang noise na nararanasan ng circuit: ang noise factor na 2.0 ay nag-i-inject ng isa pang kopya ng natutunang noise channel ng bawat layer pagkatapos ng layer na iyon, bukod pa sa noise na mayroon na ang layer. Dahil direktang ini-scale ng PEA ang mga natutunang noise rate sa halip na ulitin ang mga gate, posible ang anumang noise factor, kasama ang mga hindi integer, at hindi binabago ang circuit mismo: iisang template circuit ang ginagamit ng mga item ng programa at magkakaiba lang sila sa noise na ini-inject ng samplex. Isang beses sa bawat randomization sina-sample ang ini-inject na noise, kaya ang statistical error ng PEA ay kinokontrol ng bilang ng mga randomization sa halip na ng mga shot bawat randomization; kaya gumagamit tayo ng maraming randomization na may kaunting shot bawat isa, habang pinananatiling pareho ang kabuuang bilang ng shot sa unmitigated na programa. Mahalagang huwag gumamit ng masyadong kaunting randomization, dahil kung hindi, maaari kang makaranas ng sampling bias sa noise injection.

Ang kabuuang sampling overhead ay linear na tumataas kasabay ng bilang ng mga 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,
)

Panghuli, patakbuhin ang programa sa noisy na simulator. Ang mga resulta nito ang gagamitin sa extrapolation sa susunod na hakbang.

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

I-extrapolate sa zero-noise limit​

Ngayong mayroon ka nang data para sa bawat noise scale, maaari kang mag-fit ng function sa data at mag-extrapolate pabalik sa zero-noise value para tantiyahin ang expectation value ng noise-free na observable. Sa ilalim ng Pauli noise, exponential na bumababa ang observable kasabay ng noise factor, kaya ang exponential extrapolator ang pipiliin mo bilang unang pagpipilian, at babalik sa linear kung pumalya ang exponential fit. Para i-visualize ang extrapolation, i-plot ang site-averaged na magnetization na kinalkula sa bawat noise factor, ang exponential fit, at ang site average ng mga na-extrapolate na expectation value sa zero-noise point. Makikita natin na bahagyang kulang ang fit para sa partikular na observable na ito.

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

Ihambing ang mga mitigated na expectation value​

I-plot ang site-averaged na magnetization ng bawat paraan sa buong sweep ng mga Ising rotation angle. Nababawi ng PEA ang eksaktong magnetization sa loob ng mga error bar nito, habang ang unmitigated na resulta ay may bias na mas mababa nang malayo rito.

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