انتقل إلى المحتوى الرئيسي

بناء نماذج الضوضاء

إصدارات الحزم

الكود في هذه الصفحة تم تطويره باستخدام المتطلبات التالية. نوصي باستخدام هذه الإصدارات أو أحدث منها.

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
qiskit-aer~=0.17

تُوضح هذه الصفحة كيفية استخدام وحدة noise في Qiskit Aer لبناء نماذج ضوضاء لمحاكاة الدوائر الكمية في ظل وجود أخطاء. يُفيد ذلك في محاكاة المعالجات الكمية الضوضائية ودراسة تأثير الضوضاء على تنفيذ الخوارزميات الكمية.

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-aer qiskit-ibm-runtime
import numpy as np
from qiskit import QuantumCircuit
from qiskit.quantum_info import Kraus, SuperOp
from qiskit.visualization import plot_histogram
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_aer import AerSimulator

# Import from Qiskit Aer noise module
from qiskit_aer.noise import (
NoiseModel,
QuantumError,
ReadoutError,
depolarizing_error,
pauli_error,
thermal_relaxation_error,
)

وحدة noise في Qiskit Aer

تحتوي وحدة noise في Qiskit Aer على فئات Python لبناء نماذج ضوضاء مخصصة للمحاكاة. هناك ثلاث فئات رئيسية:

  1. فئة NoiseModel التي تخزّن نموذج الضوضاء المستخدم في المحاكاة الضوضائية.

  2. فئة QuantumError التي تصف أخطاء البوابات من نوع CPTP. ويمكن تطبيقها:

    • بعد تعليمات البوابة أو إعادة الضبط
    • قبل تعليمات القياس.
  3. فئة ReadoutError التي تصف أخطاء القراءة الكلاسيكية.

تهيئة نموذج ضوضاء من Backend

يمكنك تهيئة نموذج ضوضاء بمعاملات مُحدَّدة من أحدث بيانات المعايرة لـ Backend فيزيائي:

from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
backend = service.backend("ibm_fez")
noise_model = NoiseModel.from_backend(backend)

سيُنتج هذا نموذج ضوضاء يُقرِّب تقريباً الأخطاء التي ستواجهها عند استخدام ذلك الـ Backend. إذا أردت تحكماً أكثر تفصيلاً في معاملات نموذج الضوضاء، فستحتاج إلى إنشاء نموذجك الخاص كما هو موضح في بقية هذه الصفحة.

الأخطاء الكمية

بدلاً من التعامل مع كائن QuantumError مباشرةً، تتوفر العديد من الدوال المساعدة لإنشاء نوع محدد من أخطاء الكم المعلمة تلقائياً. وهي موجودة في وحدة noise وتشمل دوالاً لأنواع الأخطاء الشائعة في أبحاث الحوسبة الكمية. أسماء الدوال ونوع الخطأ الذي تُعيده هي:

دالة الخطأ القياسيةالتفاصيل
kraus_errorقناة خطأ CPTP عامة لـ n-Qubit تُعطى كقائمة من مصفوفات Kraus [K0,...][K_0, ...].
mixed_unitary_errorخطأ موحد مختلط لـ n-Qubit يُعطى كقائمة من المصفوفات الموحدة والاحتمالات [(U0,p0),...][(U_0, p_0),...].
coherent_unitary_errorخطأ موحد متماسك لـ n-Qubit يُعطى كمصفوفة موحدة واحدة UU.
pauli_errorقناة خطأ Pauli لـ n-Qubit (موحدة مختلطة) تُعطى كقائمة من عوامل Pauli والاحتمالات [(P0,p0),...][(P_0, p_0),...]
depolarizing_errorقناة خطأ إزالة الاستقطاب لـ n-Qubit مُعلمَنة باحتمال إزالة الاستقطاب pp.
reset_errorخطأ إعادة ضبط Qubit واحد مُعلمَن باحتمالات p0,p1p_0, p_1 لإعادة الضبط إلى حالة 0\vert0\rangle، 1\vert1\rangle.
thermal_relaxation_errorقناة استرخاء حراري لـ Qubit واحد مُعلمَنة بثوابت زمن الاسترخاء T1T_1، T2T_2، وزمن البوابة tt، وعدد السكان الحرارية للحالة المثارة p1p_1.
phase_amplitude_damping_errorقناة خطأ تخميد الطور والسعة المُعمَّمة لـ Qubit واحد تُعطى بمعامل تخميد السعة λ\lambda، ومعامل تخميد الطور γ\gamma، وعدد السكان الحرارية للحالة المثارة p1p_1.
amplitude_damping_errorقناة خطأ تخميد السعة المُعمَّمة لـ Qubit واحد تُعطى بمعامل تخميد السعة λ\lambda، وعدد السكان الحرارية للحالة المثارة p1p_1.
phase_damping_errorقناة خطأ تخميد الطور لـ Qubit واحد تُعطى بمعامل تخميد الطور γ\gamma.

دمج الأخطاء الكمية

يمكن دمج كائنات QuantumError باستخدام التركيب وحاصل الضرب التنسوري وتوسيع التنسور (حاصل الضرب التنسوري بترتيب معكوس) لإنتاج QuantumErrors جديدة كما يلي:

  • التركيب: E(ρ)=E2(E1(ρ))\cal{E}(\rho)=\cal{E_2}(\cal{E_1}(\rho)) باستخدام error = error1.compose(error2)
  • حاصل الضرب التنسوري: E(ρ)=(E1E2)(ρ)\cal{E}(\rho) =(\cal{E_1}\otimes\cal{E_2})(\rho) باستخدام error = error1.tensor(error2)
  • توسيع الضرب: E(ρ)=(E2E1)(ρ)\cal{E}(\rho) =(\cal{E_2}\otimes\cal{E_1})(\rho) باستخدام error = error1.expand(error2)

مثال

لبناء خطأ قلب بت بنسبة 5% لـ Qubit واحد:

# Construct a 1-qubit bit-flip and phase-flip errors
p_error = 0.05
bit_flip = pauli_error([("X", p_error), ("I", 1 - p_error)])
phase_flip = pauli_error([("Z", p_error), ("I", 1 - p_error)])
print(bit_flip)
print(phase_flip)
QuantumError on 1 qubits. Noise circuits:
P(0) = 0.05, Circuit =
┌───┐
q: ┤ X ├
└───┘
P(1) = 0.95, Circuit =
┌───┐
q: ┤ I ├
└───┘
QuantumError on 1 qubits. Noise circuits:
P(0) = 0.05, Circuit =
┌───┐
q: ┤ Z ├
└───┘
P(1) = 0.95, Circuit =
┌───┐
q: ┤ I ├
└───┘
# Compose two bit-flip and phase-flip errors
bitphase_flip = bit_flip.compose(phase_flip)
print(bitphase_flip)
QuantumError on 1 qubits. Noise circuits:
P(0) = 0.0025000000000000005, Circuit =
┌───┐┌───┐
q: ┤ X ├┤ Z ├
└───┘└───┘
P(1) = 0.0475, Circuit =
┌───┐┌───┐
q: ┤ X ├┤ I ├
└───┘└───┘
P(2) = 0.0475, Circuit =
┌───┐┌───┐
q: ┤ I ├┤ Z ├
└───┘└───┘
P(3) = 0.9025, Circuit =
┌───┐┌───┐
q: ┤ I ├┤ I ├
└───┘└───┘
# Tensor product two bit-flip and phase-flip errors with
# bit-flip on qubit-0, phase-flip on qubit-1
error2 = phase_flip.tensor(bit_flip)
print(error2)
QuantumError on 2 qubits. Noise circuits:
P(0) = 0.0025000000000000005, Circuit =
┌───┐
q_0: ┤ X ├
├───┤
q_1: ┤ Z ├
└───┘
P(1) = 0.0475, Circuit =
┌───┐
q_0: ┤ I ├
├───┤
q_1: ┤ Z ├
└───┘
P(2) = 0.0475, Circuit =
┌───┐
q_0: ┤ X ├
├───┤
q_1: ┤ I ├
└───┘
P(3) = 0.9025, Circuit =
┌───┐
q_0: ┤ I ├
├───┤
q_1: ┤ I ├
└───┘

التحويل من وإلى عوامل QuantumChannel

يمكننا أيضاً التحويل ذهاباً وإياباً بين كائنات QuantumError في Qiskit Aer وكائنات QuantumChannel في Qiskit.

# Convert to Kraus operator
bit_flip_kraus = Kraus(bit_flip)
print(bit_flip_kraus)
Kraus([[[-9.74679434e-01+0.j,  0.00000000e+00+0.j],
[ 0.00000000e+00+0.j, -9.74679434e-01+0.j]],

[[ 0.00000000e+00+0.j, 2.23606798e-01+0.j],
[ 2.23606798e-01+0.j, -4.96506831e-17+0.j]]],
input_dims=(2,), output_dims=(2,))
# Convert to Superoperator
phase_flip_sop = SuperOp(phase_flip)
print(phase_flip_sop)
SuperOp([[1. +0.j, 0. +0.j, 0. +0.j, 0. +0.j],
[0. +0.j, 0.9+0.j, 0. +0.j, 0. +0.j],
[0. +0.j, 0. +0.j, 0.9+0.j, 0. +0.j],
[0. +0.j, 0. +0.j, 0. +0.j, 1. +0.j]],
input_dims=(2,), output_dims=(2,))
# Convert back to a quantum error
print(QuantumError(bit_flip_kraus))

# Check conversion is equivalent to original error
QuantumError(bit_flip_kraus) == bit_flip
QuantumError on 1 qubits. Noise circuits:
P(0) = 1.0, Circuit =
┌───────┐
q: ┤ kraus ├
└───────┘
True

خطأ القراءة

تُحدَّد أخطاء القراءة الكلاسيكية بقائمة من متجهات احتمالية الإسناد P(AB)P(A|B):

  • AA هي قيمة البت الكلاسيكي المسجَّلة
  • BB هي قيمة البت الحقيقية التي أعادها القياس

على سبيل المثال، لـ Qubit واحد: P(AB)=[P(A0),P(A1)] P(A|B) = [P(A|0), P(A|1)].

# Measurement misassignment probabilities
p0given1 = 0.1
p1given0 = 0.05

ReadoutError([[1 - p1given0, p1given0], [p0given1, 1 - p0given1]])
ReadoutError([[0.95 0.05]
[0.1 0.9 ]])

يمكن أيضاً دمج أخطاء القراءة باستخدام compose وtensor وexpand، تماماً كما هو الحال مع الأخطاء الكمية.

إضافة أخطاء إلى نموذج ضوضاء

عند إضافة خطأ كمي إلى نموذج ضوضاء، يجب تحديد نوع التعليمة التي يعمل عليها والـ Qubits التي سيُطبَّق عليها. هناك نوعان من الأخطاء الكمية:

  1. خطأ كمي على جميع الـ Qubits
  2. خطأ كمي على Qubits محددة

1. خطأ كمي على جميع الـ Qubits

يُطبّق هذا الخطأ نفسه على أي تكرار لتعليمة ما، بغض النظر عن الـ Qubits التي تعمل عليها.

يُضاف باستخدام noise_model.add_all_qubit_quantum_error(error, instructions):

# Create an empty noise model
noise_model = NoiseModel()

# Add depolarizing error to all single qubit u1, u2, u3 gates
error = depolarizing_error(0.05, 1)
noise_model.add_all_qubit_quantum_error(error, ["u1", "u2", "u3"])

# Print noise model info
print(noise_model)
NoiseModel:
Basis gates: ['cx', 'id', 'rz', 'sx', 'u1', 'u2', 'u3']
Instructions with noise: ['u3', 'u2', 'u1']
All-qubits errors: ['u1', 'u2', 'u3']

2. خطأ كمي على Qubits محددة

يُطبّق هذا الخطأ على أي تكرار لتعليمة تعمل على قائمة محددة من الـ Qubits. لاحظ أن ترتيب الـ Qubit مهم: على سبيل المثال، الخطأ المُطبَّق على الـ Qubits [0, 1] لبوابة ثنائية القبيت يختلف عن ذلك المُطبَّق على [1, 0].

يُضاف باستخدام noise_model.add_quantum_error(error, instructions, qubits):

# Create an empty noise model
noise_model = NoiseModel()

# Add depolarizing error to all single qubit u1, u2, u3 gates on qubit 0 only
error = depolarizing_error(0.05, 1)
noise_model.add_quantum_error(error, ["u1", "u2", "u3"], [0])

# Print noise model info
print(noise_model)
NoiseModel:
Basis gates: ['cx', 'id', 'rz', 'sx', 'u1', 'u2', 'u3']
Instructions with noise: ['u3', 'u2', 'u1']
Qubits with noise: [0]
Specific qubit errors: [('u1', (0,)), ('u2', (0,)), ('u3', (0,))]

ملاحظة حول خطأ الـ Qubit غير المحلي

لا يدعم NoiseModel إضافة أخطاء كمية غير محلية للـ Qubit. ينبغي معالجتها خارج نطاق NoiseModel. هذا يعني أنك تحتاج إلى كتابة تمريرة Transpiler خاصة بك (TransformationPass) وتشغيلها مباشرة قبل تشغيل المحاكي إذا كنت بحاجة إلى إدراج أخطائك الكمية في دائرتك وفق شروطك الخاصة.

تنفيذ محاكاة ضوضائية باستخدام نموذج ضوضاء

الأمر AerSimulator(noise_model=noise_model) يُعيد محاكياً مُهيَّأً وفق نموذج الضوضاء المعطى. بالإضافة إلى تعيين نموذج الضوضاء للمحاكي، يُعيد تهيئة البوابات الأساسية للمحاكي وفق بوابات نموذج الضوضاء.

أمثلة على نماذج الضوضاء

سنقدّم الآن بعض الأمثلة على نماذج الضوضاء. في عروضنا نستخدم دائرة اختبار بسيطة تولّد حالة GHZ لـ n-Qubit:

# System Specification
n_qubits = 4
circ = QuantumCircuit(n_qubits)

# Test Circuit
circ.h(0)
for qubit in range(n_qubits - 1):
circ.cx(qubit, qubit + 1)
circ.measure_all()
print(circ)
┌───┐                ░ ┌─┐
q_0: ┤ H ├──■─────────────░─┤M├─────────
└───┘┌─┴─┐ ░ └╥┘┌─┐
q_1: ─────┤ X ├──■────────░──╫─┤M├──────
└───┘┌─┴─┐ ░ ║ └╥┘┌─┐
q_2: ──────────┤ X ├──■───░──╫──╫─┤M├───
└───┘┌─┴─┐ ░ ║ ║ └╥┘┌─┐
q_3: ───────────────┤ X ├─░──╫──╫──╫─┤M├
└───┘ ░ ║ ║ ║ └╥┘
meas: 4/════════════════════════╩══╩══╩══╩═
0 1 2 3

المحاكاة المثالية

# Ideal simulator and execution
sim_ideal = AerSimulator()
result_ideal = sim_ideal.run(circ).result()
plot_histogram(result_ideal.get_counts(0))

Output of the previous code cell

مثال على الضوضاء 1: نموذج ضوضاء خطأ قلب البت الأساسي

لنأخذ نموذج ضوضاء بسيطاً شائعاً في أبحاث نظرية المعلومات الكمية:

  • عند تطبيق بوابة Qubit واحدة، يتم قلب حالة الـ Qubit باحتمال p_gate1.
  • عند تطبيق بوابة ثنائية القبيت، تُطبَّق أخطاء Qubit واحدة على كل Qubit.
  • عند إعادة ضبط Qubit، يتم إعادة ضبطه إلى 1 بدلاً من 0 باحتمال p_reset.
  • عند قياس Qubit، يتم قلب حالته باحتمال p_meas.
# Example error probabilities
p_reset = 0.03
p_meas = 0.1
p_gate1 = 0.05

# QuantumError objects
error_reset = pauli_error([("X", p_reset), ("I", 1 - p_reset)])
error_meas = pauli_error([("X", p_meas), ("I", 1 - p_meas)])
error_gate1 = pauli_error([("X", p_gate1), ("I", 1 - p_gate1)])
error_gate2 = error_gate1.tensor(error_gate1)

# Add errors to noise model
noise_bit_flip = NoiseModel()
noise_bit_flip.add_all_qubit_quantum_error(error_reset, "reset")
noise_bit_flip.add_all_qubit_quantum_error(error_meas, "measure")
noise_bit_flip.add_all_qubit_quantum_error(error_gate1, ["u1", "u2", "u3"])
noise_bit_flip.add_all_qubit_quantum_error(error_gate2, ["cx"])

print(noise_bit_flip)
NoiseModel:
Basis gates: ['cx', 'id', 'rz', 'sx', 'u1', 'u2', 'u3']
Instructions with noise: ['u3', 'u2', 'measure', 'cx', 'reset', 'u1']
All-qubits errors: ['reset', 'measure', 'u1', 'u2', 'u3', 'cx']

تنفيذ المحاكاة الضوضائية

# Create noisy simulator backend
sim_noise = AerSimulator(noise_model=noise_bit_flip)

# Transpile circuit for noisy basis gates
passmanager = generate_preset_pass_manager(
optimization_level=3, backend=sim_noise
)
circ_tnoise = passmanager.run(circ)

# Run and get counts
result_bit_flip = sim_noise.run(circ_tnoise).result()
counts_bit_flip = result_bit_flip.get_counts(0)

# Plot noisy output
plot_histogram(counts_bit_flip)

Output of the previous code cell

مثال 2: الاسترخاء الحراري T1/T2

لنأخذ الآن نموذج خطأ أكثر واقعية يستند إلى الاسترخاء الحراري مع بيئة الـ Qubit:

  • يُعلمَن كل Qubit بثابت زمن الاسترخاء الحراري T1T_1 وثابت زمن تبدد الطور T2T_2.
  • لاحظ أنه يجب أن يكون T22T1T_2 \le 2 T_1.
  • تُحدَّد معدلات الخطأ في التعليمات بناءً على أوقات البوابة وقيم T1T_1 وT2T_2 للـ Qubit.
# T1 and T2 values for qubits 0-3
T1s = np.random.normal(
50e3, 10e3, 4
) # Sampled from normal distribution mean 50 microsec
T2s = np.random.normal(
70e3, 10e3, 4
) # Sampled from normal distribution mean 50 microsec

# Truncate random T2s <= T1s
T2s = np.array([min(T2s[j], 2 * T1s[j]) for j in range(4)])

# Instruction times (in nanoseconds)
time_u1 = 0 # virtual gate
time_u2 = 50 # (single X90 pulse)
time_u3 = 100 # (two X90 pulses)
time_cx = 300
time_reset = 1000 # 1 microsecond
time_measure = 1000 # 1 microsecond

# QuantumError objects
errors_reset = [
thermal_relaxation_error(t1, t2, time_reset) for t1, t2 in zip(T1s, T2s)
]
errors_measure = [
thermal_relaxation_error(t1, t2, time_measure) for t1, t2 in zip(T1s, T2s)
]
errors_u1 = [
thermal_relaxation_error(t1, t2, time_u1) for t1, t2 in zip(T1s, T2s)
]
errors_u2 = [
thermal_relaxation_error(t1, t2, time_u2) for t1, t2 in zip(T1s, T2s)
]
errors_u3 = [
thermal_relaxation_error(t1, t2, time_u3) for t1, t2 in zip(T1s, T2s)
]
errors_cx = [
[
thermal_relaxation_error(t1a, t2a, time_cx).expand(
thermal_relaxation_error(t1b, t2b, time_cx)
)
for t1a, t2a in zip(T1s, T2s)
]
for t1b, t2b in zip(T1s, T2s)
]

# Add errors to noise model
noise_thermal = NoiseModel()
for j in range(4):
noise_thermal.add_quantum_error(errors_reset[j], "reset", [j])
noise_thermal.add_quantum_error(errors_measure[j], "measure", [j])
noise_thermal.add_quantum_error(errors_u1[j], "u1", [j])
noise_thermal.add_quantum_error(errors_u2[j], "u2", [j])
noise_thermal.add_quantum_error(errors_u3[j], "u3", [j])
for k in range(4):
noise_thermal.add_quantum_error(errors_cx[j][k], "cx", [j, k])

print(noise_thermal)
NoiseModel:
Basis gates: ['cx', 'id', 'rz', 'sx', 'u2', 'u3']
Instructions with noise: ['u3', 'u2', 'measure', 'cx', 'reset']
Qubits with noise: [0, 1, 2, 3]
Specific qubit errors: [('reset', (0,)), ('reset', (1,)), ('reset', (2,)), ('reset', (3,)), ('measure', (0,)), ('measure', (1,)), ('measure', (2,)), ('measure', (3,)), ('u2', (0,)), ('u2', (1,)), ('u2', (2,)), ('u2', (3,)), ('u3', (0,)), ('u3', (1,)), ('u3', (2,)), ('u3', (3,)), ('cx', (0, 0)), ('cx', (0, 1)), ('cx', (0, 2)), ('cx', (0, 3)), ('cx', (1, 0)), ('cx', (1, 1)), ('cx', (1, 2)), ('cx', (1, 3)), ('cx', (2, 0)), ('cx', (2, 1)), ('cx', (2, 2)), ('cx', (2, 3)), ('cx', (3, 0)), ('cx', (3, 1)), ('cx', (3, 2)), ('cx', (3, 3))]

تنفيذ المحاكاة الضوضائية

# Run the noisy simulation
sim_thermal = AerSimulator(noise_model=noise_thermal)

# Transpile circuit for noisy basis gates
passmanager = generate_preset_pass_manager(
optimization_level=3, backend=sim_thermal
)
circ_tthermal = passmanager.run(circ)

# Run and get counts
result_thermal = sim_thermal.run(circ_tthermal).result()
counts_thermal = result_thermal.get_counts(0)

# Plot noisy output
plot_histogram(counts_thermal)

Output of the previous code cell

الخطوات التالية

توصيات