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?

Simulasikan sistem kuantum noisy dengan Pauli propagation

Di panduan ini kita pakai paket pauli-prop untuk mensimulasikan secara klasik dinamika waktu model Ising medan transversal (TFIM) sembilan-qubit yang noisy pada kisi persegi 3x3. Kita pakai instruksi PauliLindbladError untuk mendefinisikan noise channel, Λ\Lambda, yang bekerja pada sekumpulan lapisan entangling, U\mathcal{U}. Lalu kita mempropagasikan observable, OO, mundur melalui circuit yang noisy dan mengestimasi nilai ekspektasi untuk berbagai noise model, serta untuk kasus tanpa noise.

Noisy EV

Saat observable dipropagasikan mundur melalui circuit, tiap noise channel, Λk\Lambda_k, yang terkait dengan lapisan entangling, Uk\mathcal{U}_k, meredam suku-suku Pauli di OO yang anti-komut dengan generator Pauli-Lindblad-nya. Secara spesifik, jika Gk,iG_{k,i} adalah generator Pauli dari Λk\Lambda_k dengan laju, γk,i\gamma_{k,i}, maka suku Pauli, PP, di OO berubah menjadi: cP↦cPe−2γk,iif {P,Gk,i}=0c_P \mapsto c_P e^{-2\gamma_{k,i}} \quad \text{if } \{P, G_{k,i}\}=0, dengan cPc_P adalah koefisien dari PP. Setelah OO dipropagasikan ke awal circuit, nilai ekspektasi terhadap keadaan nol, ∣0⟩⊗N|0\rangle^{\otimes N}, bisa dihitung dengan mudah dengan menjumlahkan koefisien tiap suku diagonal di OO (suku yang berisi ZZ atau II pada semua qubit).

Alur kerja:

  • Tentukan kisi TFIM, dan pakai edge coloring untuk mengidentifikasi himpunan minimal lapisan entangling
  • Hasilkan noise model sintetis, Λk\Lambda_k, untuk setiap lapisan entangling yang unik, UkU_k
    • Buat noise model dengan berbagai skala untuk mempelajari dampak noise gate pada sistem
  • Buat circuit kuantum tanpa noise dan yang noisy untuk berbagai kedalaman dan skala noise yang diminati
    • Di circuit yang noisy, instruksi PauliLindbladError disisipkan sebelum tiap lapisan entangling
  • Pakai Pauli propagation untuk mensimulasikan nilai ekspektasi eksak dari sistem pada berbagai kedalaman
    • Untuk sembilan qubit, ini dilakukan dengan membiarkan OO tumbuh sampai 494^9 suku, mencakup seluruh ruang Pauli
  • Pakai Pauli propagation untuk mensimulasikan nilai ekspektasi yang noisy
  • Amati bagaimana noise gate yang makin besar menurunkan akurasi model kuantum

Buat kisi persegi 3x3 dan temukan 4-coloring pada edge-nya​

Vertex dalam graf mewakili qubit, dan edge mewakili koneksi antara dua qubit. Edge coloring berkorespondensi dengan lapisan entangling yang unik di circuit kuantum, sehingga gate pada koneksi dengan warna berbeda tidak bisa diterapkan secara bersamaan.

Mengidentifikasi himpunan minimal lapisan entangling yang unik sering penting untuk mengimplementasikan protokol noise-learning yang efisien, karena noise tiap lapisan harus dipelajari secara independen. Makin banyak lapisan yang harus kita pelajari, makin banyak shot yang perlu diambil dari QPU. Untuk demo ini, kita pakai informasi lapisan untuk membangun circuit yang noisy dan menyuntikkan instruksi PauliLindbladError dari qiskit-aer sebelum tiap lapisan entangling untuk memodelkan noise gate QPU.

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

_needed = {"matplotlib": "matplotlib", "numpy": "numpy", "pauli_prop": "pauli-prop", "qiskit": "qiskit", "qiskit_addon_utils": "qiskit-addon-utils", "qiskit_aer": "qiskit-aer", "rustworkx": "rustworkx"}
_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")
from collections import defaultdict

import numpy as np
from qiskit.transpiler import CouplingMap
from qiskit_addon_utils.coloring import auto_color_edges

# Define rectangular square-lattice on 20 qubits
num_rows = 3
num_cols = 3
num_qubits = num_rows * num_cols

coupling_map = CouplingMap.from_grid(
num_rows=num_rows, num_columns=num_cols, bidirectional=False
)

# Create mapping from color to edge list
coloring = auto_color_edges(coupling_map.get_edges())
color_to_edge = defaultdict(list)
for edge, color in coloring.items():
color_to_edge[color].append(edge)
from rustworkx import PyDiGraph
from rustworkx.visualization import graphviz_draw

# Inspect graph coupling and unique entangling layers
print(
f"The circuit will have {num_qubits} qubits and {len(color_to_edge)} unique entangling layers."
)
sq_lattice = PyDiGraph()
sq_lattice.extend_from_weighted_edge_list(
[
(source, target, color)
for ((source, target), color) in coloring.items()
]
)

def color_edge_4color(edge):
color_dict = {0: "red", 1: "green", 2: "blue", 3: "orange"}
return {"color": color_dict[edge]}

graphviz_draw(sq_lattice, edge_attr_fn=color_edge_4color, method="neato")
The circuit will have 9 qubits and 4 unique entangling layers.

Output of the previous code cell

Hasilkan noise model sintetis​

Sebelum membuat circuit kuantum, kita hasilkan noise model (instance PauliLindbladError) untuk tiap lapisan entangling. Nanti kita akan menyematkannya sebagai instruksi di circuit kuantum kita. Untuk tiap lapisan, kita hasilkan noise channel dengan berbagai skala. Secara spesifik, kita hasilkan noise model dengan Error Per Layered Gate (EPLG) sekitar .0004, .0008, .0012, .0016, dan .002.

from qiskit.quantum_info import SparsePauliOp, pauli_basis
from qiskit_aer.noise import PauliLindbladError

# Pauli-Lindblad noise parameters
seed = 1764
target_EPLGs = [0.0004, 0.0008, 0.0012, 0.0016, 0.002]

def generate_random_pauli_lindblad_noise(
edges,
num_qubits: int | None = None,
noise_scale: float = 1e-3,
seed: int | None = None,
) -> PauliLindbladError:
"""Generate random Pauli-Lindblad noise over the full Pauli basis."""
if num_qubits is None:
num_qubits = np.max(edges)

basis_paulis = [p for p in pauli_basis(2) if np.sum(p.x + p.z)]
basis_paulis = SparsePauliOp.from_sparse_list(
[
(pauli.to_label(), edge, 1)
for pauli in basis_paulis
for edge in edges
],
num_qubits=num_qubits,
)
basis_paulis = basis_paulis.simplify()
basis_paulis = basis_paulis.paulis

rng = np.random.default_rng(seed=seed)
rates = rng.random(len(basis_paulis)) * noise_scale

return PauliLindbladError(generators=basis_paulis, rates=rates)

num_generators = (
(num_rows * num_cols)
+ (num_rows - 1) * num_cols
+ num_rows * (num_cols - 1)
)
noise_scales = [
EPLG * (num_rows * num_cols) / num_generators for EPLG in target_EPLGs
]
noise_models_per_EPLG = [
[
generate_random_pauli_lindblad_noise(
color_to_edge[color],
num_qubits=num_qubits,
noise_scale=noise_scale,
seed=seed,
)
for color in range(len(color_to_edge))
]
for noise_scale in noise_scales
]

Buat circuit kuantum​

Untuk demo ini, kita simulasikan dinamika waktu model Ising medan transversal (TFIM) untuk jumlah langkah Trotter yang makin banyak (1-10 langkah). Untuk tiap dari 10 kedalaman circuit, kita simulasikan efek noise gate, dengan noise model berbagai skala (EPLGs = .0004, .0008, .0012, .0016, .002). Noise disisipkan ke QuantumCircuit sebagai instruksi PauliLindbladError dari Qiskit Aer. Hamiltonian yang dipakai adalah:

H=−J∑⟨i,j⟩ZiZj+h∑iXiH = -J\sum\limits_{\langle i,j \rangle} Z_iZ_j + h\sum\limits_iX_i

dengan J>0J>0 menggambarkan kopling antar spin tetangga terdekat, i<ji<j, dan hh adalah medan transversal global.

Di sini kita implementasikan Hamiltonian yang berevolusi waktu pada berbagai skala waktu dan noise. Kita buat total 60 circuit: 10 circuit tanpa noise dengan kedalaman Trotter yang bervariasi, dan 50 circuit yang noisy untuk 10 kedalaman Trotter pada lima skala noise. Diberikan sebuah graf konektivitas, model ini diparametrisasi oleh beberapa variabel:

  • num_steps: Jumlah langkah Trotter
  • J: Kekuatan kopling antar situs yang terhubung
  • h: Kekuatan medan magnet eksternal
  • dt: Perubahan waktu pada satu langkah Trotter
  • initial_state_angle: Eksitasi awal, Ry(θ)R_y(\theta), yang diterapkan secara seragam ke semua qubit
from typing import Any

from qiskit import QuantumCircuit

# Ising model parameters
num_steps = 10
J = -1.0
dt = 0.25 / abs(J)
h = 2.0 * abs(J)
initial_state_angle = np.pi / 18.0
rx_angle = 2.0 * h * dt
rzz_angle = 2.0 * J * dt

def generate_ising_circuit(
num_qubits: int,
num_steps: int,
rx_angle: float,
rzz_angle: float,
coloring: dict[Any, list[tuple[int, int]]],
layer_noise_models: list[PauliLindbladError] | None = None,
initial_state_angle: float | None = None,
) -> QuantumCircuit:
"""Generate a quantum circuit implementing a transverse-field Ising model"""
qc = QuantumCircuit(num_qubits)
if initial_state_angle:
qc.ry(initial_state_angle, range(num_qubits))
qc.rx(rx_angle / 2, range(num_qubits))
for i in range(num_steps):
for j, layer in enumerate(coloring):
edges = coloring[layer]
if layer_noise_models:
qc.append(layer_noise_models[j], qargs=range(num_qubits))
for edge in edges:
qc.rzz(rzz_angle, *edge)
if i == num_steps - 1:
qc.rx(rx_angle / 2, range(num_qubits))
else:
qc.rx(rx_angle, range(num_qubits))
return qc

# Create the noiseless and noisy circuits
noiseless_circs = []
noisy_circs = []
for steps in range(1, num_steps + 1):
noiseless_circs.append(
generate_ising_circuit(
num_qubits,
steps,
rx_angle,
rzz_angle,
color_to_edge,
initial_state_angle=initial_state_angle,
)
)
noisy_circs_per_step = []
for noise_models in noise_models_per_EPLG:
noisy_circs_per_step.append(
generate_ising_circuit(
num_qubits,
steps,
rx_angle,
rzz_angle,
color_to_edge,
layer_noise_models=noise_models,
initial_state_angle=initial_state_angle,
)
)
noisy_circs.append(noisy_circs_per_step)
print(
f"{num_steps} noiseless and {num_steps * len(target_EPLGs)} noisy Trotter circuits generated. {num_steps} different depths across {len(target_EPLGs)} different noise models"
)
print("\nBelow: Initial state and one noisy Trotter step.")
noisy_circs[0][0].draw("mpl", fold=-1)
10 noiseless and 50 noisy Trotter circuits generated. 10 different depths across 5 different noise models

Below: Initial state and one noisy Trotter step.

Output of the previous code cell

Tentukan observable dan jalankan simulasi​

Untuk demo ini, kita simulasikan nilai ekspektasi dari korelator dua-situs rata-rata:

⟨O⟩=⟨Ztot2(s)⟩=1N2∑⟨Ψ(θ)∣(U†)sZjZk(U)s∣Ψ(θ)⟩\langle O \rangle = \langle Z_{tot}^2(s) \rangle = \frac{1}{N^2}\sum \langle \Psi(\theta)|(\mathscr{U}^{\dagger})^sZ_jZ_k(\mathscr{U})^s|\Psi(\theta) \rangle

dengan Ψ(θ)\Psi(\theta) berkorespondensi dengan rotasi Ry(θ)R_y(\theta) seragam pada semua qubit, Us\mathscr{U}^s menggambarkan ss lapisan Trotter, dan (j,k)(j,k) mengindeks semua pasangan vertex yang terhubung pada kisi.

Terakhir, kita pakai pauli_prop untuk mensimulasikan nilai ekspektasi observable untuk tiap circuit. Untuk demo sembilan-qubit ini, kita melakukan semua simulasi secara eksak. Tidak ada pemotongan Pauli propagation yang dilakukan; karena itu, perbedaan nilai ekspektasi di antara berbagai noise model sepenuhnya bisa dikaitkan dengan error gate. Proses simulasi ditangani dalam empat langkah:

  • Evolusikan gate Clifford di circuit ke depan circuit memakai pauli_prop.evolve_through_cliffords
  • Propagasikan observable melalui bagian non-Clifford dari circuit memakai pauli_prop.propagate_through_circuit
    • Kita melakukan simulasi eksak dengan membiarkan observable tumbuh sampai ukuran seluruh ruang Pauli, 494^9
  • Propagasikan observable yang sudah berevolusi melalui bagian Clifford dari circuit memakai SparsePauliOp.evolve milik Qiskit
  • Estimasi nilai ekspektasi terhadap keadaan nol, ∣0⟩⊗N|0\rangle^{\otimes N}, dengan menjumlahkan koefisien tiap suku diagonal di OO (suku yang berisi ZZ atau II pada semua qubit)
import time

from pauli_prop import evolve_through_cliffords, propagate_through_circuit
from qiskit.quantum_info import Pauli

# Average ZZ-correlator observable
id_pauli = Pauli("I" * num_qubits)
observable = 2 * SparsePauliOp(
[
id_pauli.dot(Pauli("ZZ"), [i, j])
for i in range(num_qubits)
for j in range(i + 1, num_qubits)
]
)
observable /= num_qubits**2

# Pauli propagation parameters
max_terms = 4**num_qubits # Exact propagation
atol = 1e-12

# Run simulations
exact_evs = []
noisy_evs = [[] for _ in range(len(target_EPLGs))]
st = time.perf_counter()
for i, noiseless_circ in enumerate(noiseless_circs):
cliff, non_cliff = evolve_through_cliffords(noiseless_circ)
evolved_obs = propagate_through_circuit(
observable, non_cliff, max_terms=max_terms, atol=atol, frame="h"
)[0]
evolved_obs.paulis = evolved_obs.paulis.evolve(cliff, frame="h")
exact_evs.append(
float(evolved_obs.coeffs[~evolved_obs.paulis.x.any(axis=1)].sum())
)
for j in range(len(target_EPLGs)):
noisy_circ = noisy_circs[i][j]
cliff, non_cliff = evolve_through_cliffords(noisy_circ)
evolved_obs = propagate_through_circuit(
observable, non_cliff, max_terms=max_terms, atol=1e-12, frame="h"
)[0]
evolved_obs.paulis = evolved_obs.paulis.evolve(cliff, frame="h")
noisy_evs[j].append(
float(evolved_obs.coeffs[~evolved_obs.paulis.x.any(axis=1)].sum())
)
print(
f"Ran {len(noiseless_circs)} noiseless and {len(target_EPLGs) * num_steps} noisy simulations in {int(time.perf_counter() - st)}s."
)
Ran 10 noiseless and 50 noisy simulations in 103s.

Amati efek error gate pada model​

Ingat bahwa, karena ini eksperimen sembilan-qubit, rutin Pauli propagation-nya eksak, dan semua error di plot noise bisa dikaitkan dengan error gate.

import matplotlib.pyplot as plt

xs = range(1, num_steps + 1)
plt.plot(xs, exact_evs, label="Noiseless", color="black", marker="o")
colors = [".3", ".4", ".5", ".6", ".7"]
for i, evs in enumerate(noisy_evs):
plt.plot(
xs,
evs,
label=f"{target_EPLGs[i]} EPLG",
linestyle="--",
color=colors[i],
marker="o",
)
plt.xlabel("# Trotter steps")
plt.ylabel(r"$\langle Z_{tot}^2 \rangle$")
plt.legend()
plt.show()

Output of the previous code cell