ข้ามไปยังเนื้อหาหลัก
โครงการนี้อยู่ในช่วงเบต้า พบข้อผิดพลาด? สร้าง GitHub Issue มีคำถามหรือไอเดีย? ถามใน Discussions — ยินดีรับฟังความคิดเห็น!
คุณภาพการแปลในหน้านี้เป็นอย่างไร?

จำลองระบบควอนตัมที่มีสัญญาณรบกวนด้วย Pauli propagation

ในคู่มือนี้ เราใช้แพ็กเกจ pauli-prop เพื่อจำลองพลวัตเชิงเวลาของโมเดล Ising สนามตามขวาง (TFIM) เก้าควอนตัมบิตที่มีสัญญาณรบกวนบนแลตทิซสี่เหลี่ยมจัตุรัส 3x3 ด้วยคอมพิวเตอร์แบบคลาสสิก เราใช้คำสั่ง PauliLindbladError เพื่อกำหนดช่องสัญญาณรบกวน Λ\Lambda ที่กระทำต่อชุดของเลเยอร์เอนแทงเกิล U\mathcal{U} จากนั้นเราแพร็อพาเกตออบเซอร์เวเบิล OO ย้อนกลับผ่านเซอร์กิตที่มีสัญญาณรบกวน และประมาณค่าคาดหมายสำหรับโมเดลสัญญาณรบกวนหลากหลายแบบ รวมถึงกรณีที่ไม่มีสัญญาณรบกวน

Noisy EV

เมื่อออบเซอร์เวเบิลถูกแพร็อพาเกตย้อนกลับผ่านเซอร์กิต ช่องสัญญาณรบกวนแต่ละช่อง Λk\Lambda_k ที่เกี่ยวข้องกับเลเยอร์เอนแทงเกิล Uk\mathcal{U}_k จะลดทอนพจน์ Pauli ใน OO ที่แอนติคอมมิวต์กับตัวกำเนิด Pauli-Lindblad ของมัน กล่าวคือ ถ้า Gk,iG_{k,i} เป็นตัวกำเนิด Pauli ของ Λk\Lambda_k ที่มีอัตรา γk,i\gamma_{k,i} แล้วพจน์ Pauli PP ใน OO จะแปลงเป็น: 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 โดยที่ cPc_P คือสัมประสิทธิ์ของ PP เมื่อ OO ถูกแพร็อพาเกตไปยังจุดเริ่มต้นของเซอร์กิตแล้ว ค่าคาดหมายเทียบกับสถานะศูนย์ ∣0⟩⊗N|0\rangle^{\otimes N} สามารถคำนวณได้อย่างตรงไปตรงมาโดยรวมสัมประสิทธิ์ของแต่ละพจน์แนวทแยงใน OO (พจน์ที่มี ZZ หรือ II บนทุกควอนตัมบิต)

ขั้นตอนการทำงาน:

  • ระบุแลตทิซ TFIM และใช้การระบายสีขอบเพื่อหาชุดเลเยอร์เอนแทงเกิลที่น้อยที่สุด
  • สร้างโมเดลสัญญาณรบกวนสังเคราะห์ Λk\Lambda_k สำหรับแต่ละเลเยอร์เอนแทงเกิลที่ไม่ซ้ำกัน UkU_k
    • สร้างโมเดลสัญญาณรบกวนหลายระดับเพื่อศึกษาผลของสัญญาณรบกวนของเกตต่อระบบ
  • สร้างควอนตัมเซอร์กิตแบบไม่มีและมีสัญญาณรบกวนสำหรับความลึกและระดับสัญญาณรบกวนที่สนใจ
    • ในเซอร์กิตที่มีสัญญาณรบกวน คำสั่ง PauliLindbladError จะถูกแทรกก่อนเลเยอร์เอนแทงเกิลแต่ละเลเยอร์
  • ใช้ Pauli propagation เพื่อจำลองค่าคาดหมายที่แน่นอนของระบบที่ความลึกต่าง ๆ
    • สำหรับเก้าควอนตัมบิต ทำได้โดยให้ OO เติบโตถึง 494^9 พจน์ ครอบคลุมปริภูมิ Pauli ทั้งหมด
  • ใช้ Pauli propagation เพื่อจำลองค่าคาดหมายที่มีสัญญาณรบกวน
  • สังเกตว่าสัญญาณรบกวนของเกตที่เพิ่มขึ้นลดความแม่นยำของโมเดลควอนตัมอย่างไร

สร้างแลตทิซสี่เหลี่ยมจัตุรัส 3x3 และหาการระบายสีขอบด้วย 4 สี​

จุดยอดในกราฟแทนควอนตัมบิต และขอบแทนการเชื่อมต่อระหว่างควอนตัมบิตสองตัว การระบายสีขอบสอดคล้องกับเลเยอร์เอนแทงเกิลที่ไม่ซ้ำกันในควอนตัมเซอร์กิต โดยเกตบนการเชื่อมต่อที่เกี่ยวข้องกับสีต่างกันไม่สามารถถูกใช้พร้อมกันได้

การระบุชุดเลเยอร์เอนแทงเกิลที่ไม่ซ้ำกันที่น้อยที่สุดมักสำคัญต่อการทำโปรโตคอลการเรียนรู้สัญญาณรบกวนอย่างมีประสิทธิภาพ เพราะต้องเรียนรู้สัญญาณรบกวนของแต่ละเลเยอร์แยกกัน ยิ่งต้องเรียนรู้หลายเลเยอร์ เรายิ่งต้องเก็บช็อตจาก QPU มากขึ้น สำหรับเดโมนี้ เราใช้ข้อมูลเลเยอร์เพื่อสร้างเซอร์กิตที่มีสัญญาณรบกวน และแทรกคำสั่ง PauliLindbladError จาก qiskit-aer ก่อนเลเยอร์เอนแทงเกิลแต่ละเลเยอร์ เพื่อจำลองสัญญาณรบกวนของเกตใน 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

สร้างโมเดลสัญญาณรบกวนสังเคราะห์​

ก่อนสร้างควอนตัมเซอร์กิต เราสร้างโมเดลสัญญาณรบกวน (อินสแตนซ์ของ PauliLindbladError) สำหรับเลเยอร์เอนแทงเกิลแต่ละเลเยอร์ เราจะฝังมันเป็นคำสั่งในควอนตัมเซอร์กิตของเราภายหลัง สำหรับแต่ละเลเยอร์ เราสร้างช่องสัญญาณรบกวนในระดับต่าง ๆ โดยเฉพาะอย่างยิ่ง เราสร้างโมเดลสัญญาณรบกวนที่มี Error Per Layered Gate (EPLG) ประมาณ .0004, .0008, .0012, .0016, และ .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
]

สร้างควอนตัมเซอร์กิต​

สำหรับเดโมนี้ เราจำลองพลวัตเชิงเวลาของโมเดล Ising สนามตามขวาง (TFIM) สำหรับจำนวนขั้นของ Trotter ที่เพิ่มขึ้น (1-10 ขั้น) สำหรับความลึกของเซอร์กิตทั้ง 10 ระดับ เราจำลองผลของสัญญาณรบกวนของเกต โดยใช้โมเดลสัญญาณรบกวนหลายระดับ (EPLGs = .0004, .0008, .0012, .0016, .002) สัญญาณรบกวนถูกแทรกลงใน QuantumCircuit เป็นคำสั่ง PauliLindbladError จาก Qiskit Aer แฮมิลโทเนียนที่พิจารณาคือ:

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

โดยที่ J>0J>0 อธิบายการคับปลิงของสปินที่อยู่ใกล้กันที่สุด, i<ji<j และ hh คือสนามตามขวางส่วนกลาง

ในที่นี้เราสร้างแฮมิลโทเนียนที่วิวัฒน์ตามเวลาข้ามช่วงเวลาและระดับสัญญาณรบกวนต่าง ๆ เราสร้างเซอร์กิตทั้งหมด 60 เซอร์กิต: เซอร์กิตไม่มีสัญญาณรบกวน 10 เซอร์กิตที่แปรตามความลึกของ Trotter และเซอร์กิตมีสัญญาณรบกวน 50 เซอร์กิตสำหรับความลึก Trotter 10 ระดับข้ามระดับสัญญาณรบกวนห้าระดับ เมื่อกำหนดกราฟการเชื่อมต่อ โมเดลจะถูกกำหนดพารามิเตอร์ด้วยตัวแปรไม่กี่ตัว:

  • num_steps: จำนวนขั้นของ Trotter
  • J: ความแรงของการคับปลิงของไซต์ที่เชื่อมต่อกัน
  • h: ความแรงของสนามแม่เหล็กภายนอก
  • dt: การเปลี่ยนแปลงของเวลาในแต่ละขั้นของ Trotter
  • initial_state_angle: การกระตุ้นเริ่มต้น Ry(θ)R_y(\theta) ที่ใช้กับทุกควอนตัมบิตอย่างสม่ำเสมอ
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

ระบุออบเซอร์เวเบิลและรันการจำลอง​

สำหรับเดโมนี้ เราจำลองค่าคาดหมายของคอร์รีเลเตอร์สองไซต์เฉลี่ย:

⟨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

โดยที่ Ψ(θ)\Psi(\theta) สอดคล้องกับการหมุน Ry(θ)R_y(\theta) แบบเอกรูปบนทุกควอนตัมบิต, Us\mathscr{U}^s อธิบายเลเยอร์ Trotter จำนวน ss เลเยอร์ และ (j,k)(j,k) เป็นดัชนีของทุกคู่จุดยอดที่เชื่อมต่อกันบนแลตทิซ

สุดท้าย เราใช้ pauli_prop เพื่อจำลองค่าคาดหมายของออบเซอร์เวเบิลสำหรับแต่ละเซอร์กิต สำหรับเดโมเก้าควอนตัมบิตนี้ เราทำการจำลองทั้งหมดแบบแน่นอน จะไม่มีการตัดทอนใน Pauli propagation ดังนั้นความแตกต่างของค่าคาดหมายข้ามโมเดลสัญญาณรบกวนต่าง ๆ จึงเป็นผลจากความคลาดเคลื่อนของเกตทั้งหมด กระบวนการจำลองแบ่งเป็นสี่ขั้นตอน:

  • วิวัฒน์เกต Clifford ในเซอร์กิตไปไว้ด้านหน้าของเซอร์กิตโดยใช้ pauli_prop.evolve_through_cliffords
  • แพร็อพาเกตออบเซอร์เวเบิลผ่านส่วนที่ไม่ใช่ Clifford ของเซอร์กิตโดยใช้ pauli_prop.propagate_through_circuit
    • เราทำการจำลองแบบแน่นอนโดยให้ออบเซอร์เวเบิลเติบโตถึงขนาดของปริภูมิ Pauli ทั้งหมด 494^9
  • แพร็อพาเกตออบเซอร์เวเบิลที่วิวัฒน์แล้วผ่านส่วน Clifford ของเซอร์กิตโดยใช้ SparsePauliOp.evolve ของ Qiskit
  • ประมาณค่าคาดหมายเทียบกับสถานะศูนย์ ∣0⟩⊗N|0\rangle^{\otimes N} โดยรวมสัมประสิทธิ์ของแต่ละพจน์แนวทแยงใน OO (พจน์ที่มี ZZ หรือ II บนทุกควอนตัมบิต)
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.

สังเกตผลของความคลาดเคลื่อนของเกตต่อโมเดล​

โปรดจำไว้ว่า เนื่องจากนี่เป็นการทดลองเก้าควอนตัมบิต รูทีน Pauli propagation จึงแน่นอน และความคลาดเคลื่อนทั้งหมดในกราฟสัญญาณรบกวนเป็นผลจากความคลาดเคลื่อนของเกต

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