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 gate folding at custom na extrapolation

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. Ina-amplify ng gabay na ito ang noise gamit ang "digital gate folding", na nangangahulugang pinapalitan ang mga two-qubit gate ng katumbas na sequence ng gate at ng inverse nito. Halimbawa, ang pagpapalit ng isang unitary UU ng UU†UU U^† U ay magbibigay ng noise amplification factor na 3. Maaari mo ring ipatupad ang amplification gamit ang probabilistic error amplification (PEA), na nangangailangan ng karagdagang hakbang ng pag-aaral ng noise model at tinatalakay sa gabay na probabilistic error amplification. Nag-e-extrapolate ang gabay na ito gamit ang ilang built-in na fitting strategy (linear at exponential), at ipinapakita rin nito kung paano ipatupad ang sarili mong custom na fitting function.

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.

Gate folding

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. Maghanda ng ZNE program na may digital gate folding at mag-sample ng mga circuit na may iba't ibang noise scale
  5. Mag-extrapolate sa zero-noise limit gamit ang built-in at custom na fitting function
  6. Ihambing ang mga na-mitigate na expectation value

Tukuyin ang circuit at observable​

Kalkulahin 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.

# 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

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.

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()

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. Itinakda mo ang boxing option na measure_annotations sa "change_basis", na nagro-rotate ng mga measurement sa kinakailangang mga basis, at idinagdag mo 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 QPU access ay magpapatakbo ng program gamit ang Executor na binuo mula sa isang backend ng QiskitRuntimeService. Dito, magpapasa ka ng AerSimulator sa Executor, na nagpapatakbo ng program 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 gate, 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 kasalukuyang mga 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

Maghanda ng ZNE program na may digital gate folding at mag-sample ng mga circuit na may iba't ibang noise scale​

Maghanda ng ZNE program na naglalaman ng mga circuit na may tumataas na noise scale. Kumakatawan ang bawat noise factor sa multiple ng mga entangling gate na taglay ng circuit (ang noise factor na 3.0 ay may tatlong beses na mas maraming entangling gate kaysa sa orihinal na circuit). Ang circuit ng bawat noise factor ay tini-twirl gamit ang parehong bilang ng mga randomization at shot gaya ng unmitigated na program.

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
)

I-draw ang unang Trotter step ng twirled na template na may noise factor 1 at may noise factor 3 para makita ang amplification. Para sa noise factor na 3.0, pinapalitan ang bawat layer ng circuit ng layer, ng inverse nito, at muli ng layer.

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
)

Output of the previous code cell

Output of the previous code cell

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

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

Mag-extrapolate sa zero-noise limit gamit ang built-in at custom na fitting function​

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 na halaga para tantiyahin ang expectation value ng noise-free na observable. Maraming paraan para mag-fit ng data, at ang tamang pagpili ng function ay nakadepende sa katangian ng data na sine-sample. Nagbibigay ang GateFolding.postprocess ng ilang built-in na extrapolation function, kabilang ang linear, exponential, at double_exponential. Tumatanggap ang method na postprocess ng listahan ng mga extrapolation method, sinusuri ang mga ito nang sunud-sunod, at ibinabalik ang unang halaga na may physically-valid na mga halaga at finite na variance. Nangangahulugan ito na malaki ang epekto ng pagkakasunod-sunod ng pagtukoy mo sa mga extrapolator sa kung alin ang mapipili. Ang pagsumite ng fallback bilang extrapolator ay nangangahulugang kung walang nakitang valid na sagot, pipiliin at ibabalik ang expectation value ng pinakamaliit na noise factor. Kung hindi isinumite ang fallback at walang extrapolator na nagbigay ng physically-valid na halaga, ibinabalik ang resulta na may pinakamaliit na std. Sa ilalim ng Pauli noise, exponential na bumababa ang isang observable ayon sa noise factor, kaya sa 30 layer (15 Trotter step, 2 layer bawat step) ng mga entangling gate, ang exponential extrapolator ang natural na pagpipilian; maaari ka ring mag-extrapolate nang linear, na karaniwang pagpipilian kapag maliit ang pagbaba.

Para gumamit ng custom na fitting function, ipasa ang "custom" na extrapolator kasama ng isang custom_fit tuple na naglalaman ng fitting function, isang listahan na may paunang hula para sa bawat parameter nito, at mga bound. Ipinapasa ang mga ito sa scipy.optimize.curve_fit. Maaaring ulitin ang post-processing gamit ang iba't ibang extrapolator nang hindi muling pinapatakbo ang program. Dito, gumagamit ka ng custom na square-root na fitting function. Para makita ang extrapolation, i-plot ang site-averaged na magnetization na kinalkula sa bawat noise factor para sa θ=0.15π\theta = 0.15\pi, ang tatlong fit, at ang site average ng mga na-extrapolate na expectation value sa zero-noise na punto. Halos linear ang hitsura ng tatlong data point, pero kitang-kitang mas mababa sa eksaktong halaga ang linear extrapolation. Gaya ng inaasahan, ang exponential fit ang pinakaepektibong nagtatantiya ng noiseless na expectation value.

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()

Output of the previous code cell

Ihambing ang mga na-mitigate na expectation value​

I-plot ang site-averaged na magnetization ng bawat method sa buong sweep ng mga Ising rotation angle. Nababawi ng exponential extrapolation ang eksaktong magnetization sa loob ng mga error bar nito, habang inaalis ng linear extrapolation ang malaking bahagi ng bias pero patuloy na kulang ang tantiya, dahil exponential ang tunay na pagbaba. Malayong sumobra ang tantiya ng square-root fit.

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()

Output of the previous code cell