انتقل إلى المحتوى الرئيسي
هذا المشروع في مرحلة تجريبية. وجدت خطأ؟ افتح مشكلة على GitHub. لديك سؤال أو فكرة؟ اسأل في Discussions — نحب سماع ملاحظاتك!
كيف جودة الترجمة في هذه الصفحة؟

محاكاة الأنظمة الكمية ذات الضوضاء باستخدام انتشار باولي

في هذا الدليل نستخدم حزمة pauli-prop لمحاكاة الديناميكا الزمنية لنموذج Ising بالمجال المستعرض (TFIM) ذي الضوضاء بتسعة كيوبتات على شبكة مربعة 3x3 بشكل كلاسيكي. نستخدم تعليمات PauliLindbladError لتعريف قناة ضوضاء، Λ\Lambda، تؤثر على مجموعة من طبقات التشابك، U\mathcal{U}. ثم ننشر القابل للرصد، OO، للخلف عبر الدائرة ذات الضوضاء ونقدّر القيم المتوقعة لمجموعة متنوعة من نماذج الضوضاء، إضافة إلى الحالة بدون ضوضاء.

Noisy EV

عند نشر القابل للرصد للخلف عبر الدائرة، تقوم كل قناة ضوضاء، Λk\Lambda_k، مرتبطة بطبقة التشابك، Uk\mathcal{U}_k، بإضعاف حدود باولي في OO التي تتبادل بالتضاد مع مولّدات باولي-ليندبلاد الخاصة بها. وبالتحديد، إذا كان Gk,iG_{k,i} مولّد باولي للقناة Λk\Lambda_k بمعدل γk,i\gamma_{k,i}، فإن حد باولي، 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 قبل كل طبقة تشابك
  • استخدم انتشار باولي لمحاكاة القيم المتوقعة الدقيقة للنظام عند أعماق مختلفة
    • بالنسبة لتسعة كيوبتات، يتم ذلك بترك OO ينمو حتى 494^9 حداً، مما يغطي فضاء باولي بالكامل
  • استخدم انتشار باولي لمحاكاة القيم المتوقعة مع الضوضاء
  • لاحظ كيف تؤدي زيادة ضوضاء البوابات إلى تدهور دقة النموذج الكمي

أنشئ شبكة مربعة 3x3 وأوجد تلويناً رباعياً للحواف​

تمثّل الرؤوس في الرسم البياني الكيوبتات، وتمثّل الحواف الاتصال بين كيوبتين. يقابل تلوين الحواف طبقات التشابك الفريدة في الدائرة الكمية، بحيث لا يمكن تطبيق البوابات على الاتصالات ذات الألوان المختلفة في الوقت نفسه.

غالباً ما يكون تحديد مجموعة دنيا من طبقات التشابك الفريدة مهماً لتنفيذ بروتوكولات تعلّم الضوضاء بكفاءة، لأن ضوضاء كل طبقة يجب تعلّمها بشكل مستقل. وكلما زاد عدد الطبقات التي يجب تعلّمها، زاد عدد اللقطات التي نحتاج إلى أخذها من 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) لكل طبقة من طبقات التشابك. سنضمّنها كتعليمات في دوائرنا الكمية لاحقاً. لكل طبقة، ننشئ قنوات ضوضاء بمقاييس مختلفة. وبالتحديد، ننشئ نماذج ضوضاء بـ الخطأ لكل بوابة طبقية (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 خطوات). ولكل عمق من أعماق الدوائر العشرة، نحاكي تأثير ضوضاء البوابات باستخدام نماذج ضوضاء بمقاييس مختلفة (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 دائرة ذات ضوضاء للأعماق العشرة عبر خمسة مقاييس ضوضاء. وبافتراض رسم بياني للاتصال، يُعرَّف النموذج بعدد من المتغيرات:

  • 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 عدد ss من طبقات Trotter، ويفهرس (j,k)(j,k) كل أزواج الرؤوس المتصلة على الشبكة.

أخيراً، نستخدم pauli_prop لمحاكاة القيم المتوقعة للقابل للرصد لكل دائرة. وفي هذا العرض التوضيحي بتسعة كيوبتات، نجري كل المحاكاات بشكل دقيق. لن يتم إجراء أي حذف في انتشار باولي؛ وبالتالي يمكن عزو الفروق في القيم المتوقعة بين نماذج الضوضاء المختلفة بالكامل إلى خطأ البوابات. تتم عملية المحاكاة في أربع خطوات:

  • طوّر بوابات Clifford في الدائرة إلى مقدمة الدائرة باستخدام pauli_prop.evolve_through_cliffords
  • انشر القابل للرصد عبر الجزء غير Clifford من الدائرة باستخدام pauli_prop.propagate_through_circuit
    • نجري محاكاات دقيقة بالسماح للقابل للرصد بالنمو إلى حجم فضاء باولي الكامل، 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.

لاحظ تأثير خطأ البوابات على النموذج​

تذكّر أنه بما أن هذه تجربة بتسعة كيوبتات، فإن إجراء انتشار باولي دقيق، ويمكن عزو كل الخطأ في رسوم الضوضاء إلى خطأ البوابات.

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