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

تصحيح المهام باستخدام NEAT

Package versions

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

qiskit[all]~=2.5.2
qiskit-ibm-runtime~=0.47.0
qiskit-aer~=0.17

يمكنك استخدام كلاس Neat لتحليل تأثير الضوضاء على حِمل عمل Estimator. للتحقق من صحة الصياغة، استخدم وضع الاختبار المحلي.

استخدام كلاس Neat​

قبل إرسال حِمل عمل يستهلك موارد كبيرة لتنفيذه على العتاد الفعلي، يمكنك استخدام فئة IBM Quantum Compute NEAT (أداة تحليل المُقدِّر المشوَّش) للتحقق من أن حِمل عمل Estimator لديك مُعَدٌّ بالشكل الصحيح، ومن المرجح أن يُعيد نتائج دقيقة، ويستخدم الخيارات الأنسب للمسألة المحددة، وغير ذلك.

تقوم Neat بتحويل الدوائر الكمية المُدخَلة إلى دوائر كليفورد (Clifford) لمحاكاة أكثر كفاءة، مع الحفاظ على بنيتها وعمقها. تتعرض دوائر كليفورد لمستويات مشابهة من الضوضاء، وتُعدّ وسيلة جيدة لدراسة الدائرة الأصلية موضع الاهتمام. أولاً، استورد الحزم ذات الصلة واحصل على مصادقة خدمة IBM Quantum Compute Service.

تجهيز البيئة​

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

_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_aer": "qiskit-aer", "qiskit_ibm_runtime": "qiskit-ibm-runtime"}
_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 numpy as np
import random

from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.quantum_info import SparsePauliOp

from qiskit_ibm_runtime import QiskitRuntimeService, NoiseLearnerV3

# Legacy server-side primitive before qiskit-ibm-runtime 0.50.0
# from qiskit_ibm_runtime import Estimator

# Client-side primitive introduced in qiskit-ibm-runtime 0.50.0
from qiskit_ibm_runtime.executor_estimator import Estimator
from qiskit_ibm_runtime.debug_tools import Neat

from qiskit_aer.noise import NoiseModel, depolarizing_error
# Choose the least busy backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# Generate a preset pass manager
# This will be used to convert the abstract circuit to an equivalent
# Instruction Set Architecture (ISA) circuit.

pm = generate_preset_pass_manager(backend=backend, optimization_level=0)

# Set the random seed
random.seed(10)

تهيئة دائرة هدف​

تأمّل دائرة كمية من ستة كيوبتات تمتلك الخصائص التالية:

  • تتناوب بين دورانات RZ عشوائية وطبقات من بوابات CNOT.

  • تمتلك بنية مرآة؛ أي إنها تُطبّق مؤثراً أحادياً (unitary) U يتبعه معكوسه.

def generate_circuit(n_qubits, n_layers):
r"""
A function to generate a pseudo-random a circuit with ``n_qubits`` qubits
and ``2*n_layers`` entangling layers of the type used in this notebook.
"""
# An array of random angles
angles = [
[random.random() for q in range(n_qubits)] for s in range(n_layers)
]

qc = QuantumCircuit(n_qubits)
qubits = list(range(n_qubits))

# do random circuit
for layer in range(n_layers):
# rotations
for q_idx, qubit in enumerate(qubits):
qc.rz(angles[layer][q_idx], qubit)

# cx gates
control_qubits = (
qubits[::2] if layer % 2 == 0 else qubits[1 : n_qubits - 1 : 2]
)
for qubit in control_qubits:
qc.cx(qubit, qubit + 1)

# undo random circuit
for layer in range(n_layers)[::-1]:
# cx gates
control_qubits = (
qubits[::2] if layer % 2 == 0 else qubits[1 : n_qubits - 1 : 2]
)
for qubit in control_qubits:
qc.cx(qubit, qubit + 1)

# rotations
for q_idx, qubit in enumerate(qubits):
qc.rz(-angles[layer][q_idx], qubit)

return qc

# Generate a random circuit
qc = generate_circuit(6, 3)
# Convert the abstract circuit to an equivalent ISA circuit.
isa_qc = pm.run(qc)

qc.draw("mpl", idle_wires=0)

Output of the previous code cell

اختر مؤثرات Z أحادية الباولي كمشاهدات، واستخدمها لتهيئة الكتل الموحّدة للبدائيات (PUBs).

# Initialize the observables
obs = ["ZIIIII", "IZIIII", "IIZIII", "IIIZII", "IIIIZI", "IIIIIZ"]
print(f"Observables: {obs}")

# Map the observables to the backend's layout
isa_obs = [SparsePauliOp(o).apply_layout(isa_qc.layout) for o in obs]

# Initialize the PUBs, which consist of six-qubit circuits
# with `n_layers` 1, ..., 6
all_n_layers = [1, 2, 3, 4, 5, 6]

pubs = [(pm.run(generate_circuit(6, n)), isa_obs) for n in all_n_layers]
Observables: ['ZIIIII', 'IZIIII', 'IIZIII', 'IIIZII', 'IIIIZI', 'IIIIIZ']

تحويل الدوائر إلى كليفورد​

دوائر PUB المعرّفة سابقًا ليست دوائر كليفورد، مما يجعلها صعبة المحاكاة كلاسيكيًا. مع ذلك، يمكنك استخدام طريقة Neat to_clifford لتحويلها إلى دوائر كليفورد لمحاكاة أكثر كفاءة. تُعد طريقة to_clifford غلافًا حول تمريرة Transpiler ConvertISAToClifford، والتي يمكن استخدامها أيضًا بشكل مستقل. على وجه الخصوص، تستبدل بوابات الـ Qubit الفردي غير كليفورد في الدائرة الأصلية ببوابات Qubit فردي من نوع كليفورد، لكنها لا تغيّر بوابات الـ Qubit المزدوج، أو عدد الـ Qubits، أو عمق الدائرة.

راجع محاكاة فعالة لدوائر المُثبِّت باستخدام بدائيات Qiskit Aer لمزيد من المعلومات حول محاكاة دوائر كليفورد. أولًا، قم بتهيئة Neat.

# You could specify a custom `NoiseModel` here. If `None`, `Neat`
# pulls the noise model from the given backend
noise_model = None

# Initialize `Neat`
analyzer = Neat(backend, noise_model)

بعد ذلك، حوّل الـPUBs إلى دوائر كليفورد.

clifford_pubs = analyzer.to_clifford(pubs)

clifford_pubs[0].circuit.draw("mpl", idle_wires=0)

Output of the previous code cell

التطبيق 1: تحليل تأثير الضوضاء على مخرجات الدائرة​

يوضّح هذا المثال كيفية استخدام Neat لدراسة تأثير نماذج الضوضاء المختلفة على PUBs بوصفها دالةً لعمق الدائرة، وذلك عبر تشغيل محاكاتٍ في ظروف مثالية (ideal_sim) وأخرى بضوضاء (noisy_sim). يمكن أن يكون ذلك مفيداً لوضع توقعات حول جودة النتائج التجريبية قبل تشغيل المهمة على وحدة معالجة الكم (QPU). لمزيد من المعلومات حول نماذج الضوضاء، راجع المحاكاة الدقيقة والمحاكاة بالضوضاء مع Qiskit Aer primitives

تدعم النتائج المحاكاة العمليات الرياضية، وبالتالي يمكن مقارنتها مع بعضها (أو مع النتائج التجريبية) لحساب مقاييس الجودة.

تنبيه

قد تتأثر وحدة QPU بأنواع مختلفة من الضوضاء. نموذج ضوضاء Qiskit Aer المستخدم هنا لا يحاكي سوى بعضها، وبالتالي فمن المرجح أن يكون أقل حدةً من الضوضاء الموجودة على وحدة QPU الفعلية.

للاطلاع على تفاصيل الأخطاء المُضمَّنة عند تهيئة نموذج الضوضاء من وحدة QPU، راجع مرجع Aer API لـ NoiseModel.

ابدأ بإجراء محاكاتٍ كلاسيكية مثالية وأخرى بضوضاء.

# Perform a noiseless simulation
ideal_results = analyzer.ideal_sim(clifford_pubs)
print(f"Ideal results:\n {ideal_results}\n")

# Perform a noisy simulation with the backend's noise model
noisy_results = analyzer.noisy_sim(clifford_pubs)
print(f"Noisy results:\n {noisy_results}\n")
Ideal results:
NeatResult([NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.]))])
Noisy results:
NeatResult([NeatPubResult(vals=array([0.99609375, 0.99414062, 0.98632812, 0.9921875 , 0.97460938,
0.97851562])), NeatPubResult(vals=array([0.984375 , 0.98828125, 0.98046875, 0.98632812, 0.96679688,
0.98828125])), NeatPubResult(vals=array([0.9609375 , 0.9765625 , 0.953125 , 0.95898438, 0.95703125,
0.96679688])), NeatPubResult(vals=array([0.96875 , 0.97070312, 0.93164062, 0.94335938, 0.93164062,
0.97460938])), NeatPubResult(vals=array([0.9140625 , 0.9296875 , 0.91796875, 0.92382812, 0.90429688,
0.91992188])), NeatPubResult(vals=array([0.89648438, 0.9140625 , 0.921875 , 0.94921875, 0.92773438,
0.95117188]))])

بعد ذلك، طبّق العمليات الرياضية لحساب الفرق المطلق. يستخدم بقية الدليل الفرق المطلق بوصفه مقياساً للجودة لمقارنة النتائج المثالية بالنتائج المشوَّشة أو التجريبية، غير أنه يمكن إعداد مقاييس جودة مشابهة أيضاً.

يوضّح الفرق المطلق أن تأثير الضوضاء يزداد مع زيادة أحجام الدوائر.

# Figure of merit: Absolute difference
def rdiff(res1, res2):
r"""The absolute difference between `res1` and res2`.

--> The closer to `0`, the better.
"""
d = abs(res1 - res2)
return np.round(d.vals * 100, 2)

for idx, (ideal_res, noisy_res) in enumerate(
zip(ideal_results, noisy_results)
):
vals = rdiff(ideal_res, noisy_res)

# Print the mean absolute difference for the observables
mean_vals = np.round(np.mean(vals), 2)
print(
f"Mean absolute difference between ideal and noisy results "
f"for circuits with {all_n_layers[idx]} layers:\n {mean_vals}%\n"
)
Mean absolute difference between ideal and noisy results for circuits with 1 layers:
1.3%

Mean absolute difference between ideal and noisy results for circuits with 2 layers:
1.76%

Mean absolute difference between ideal and noisy results for circuits with 3 layers:
3.78%

Mean absolute difference between ideal and noisy results for circuits with 4 layers:
4.66%

Mean absolute difference between ideal and noisy results for circuits with 5 layers:
8.17%

Mean absolute difference between ideal and noisy results for circuits with 6 layers:
7.32%

يمكنك اتباع هذه الإرشادات التقريبية والمبسَّطة لتحسين الدوائر من هذا النوع:

  • إذا كان متوسط الفرق المطلق أكبر من 90%، فمن المرجح ألا يُجدي تخفيف الأخطاء نفعاً.

  • إذا كان متوسط الفرق المطلق أقل من 90%، فمن المرجح أن تتمكن تضخيم الخطأ الاحتمالي (PEA) من تحسين النتائج.

  • إذا كان متوسط الفرق المطلق أقل من 80%، فمن المرجح أيضاً أن يتمكن ZNE مع طيّ البوابات من تحسين النتائج.

نظراً لأن جميع الفوارق المطلقة أعلاه أقل من 90%، فإن تطبيق PEA على الدائرة الأصلية سيُحسّن على الأرجح جودة نتائجها. يمكنك تحديد نماذج ضوضاء مختلفة في المحلل. يُجري المثال التالي نفس الاختبار لكنه يُضيف نموذج ضوضاء مخصصاً.

# Set up a noise model with strength 0.02 on every two-qubit gate
noise_model = NoiseModel()
for qubits in backend.coupling_map:
noise_model.add_quantum_error(
depolarizing_error(0.02, 2), ["ecr", "cx", "cz"], qubits
)

# Update the analyzer's noise model
analyzer.noise_model = noise_model

# Perform a noiseless simulation
ideal_results = analyzer.ideal_sim(clifford_pubs)

# Perform a noisy simulation with the backend's noise model
noisy_results = analyzer.noisy_sim(clifford_pubs)

# Compare the results
for idx, (ideal_res, noisy_res) in enumerate(
zip(ideal_results, noisy_results)
):
values = rdiff(ideal_res, noisy_res)

# Print the mean absolute difference for the observables
mean_values = np.round(np.mean(values), 2)
print(
f"Mean absolute difference between ideal and noisy results "
f"for circuits with {all_n_layers[idx]} layers:\n {mean_values}%\n"
)
Mean absolute difference between ideal and noisy results for circuits with 1 layers:
4.16%

Mean absolute difference between ideal and noisy results for circuits with 2 layers:
8.92%

Mean absolute difference between ideal and noisy results for circuits with 3 layers:
12.86%

Mean absolute difference between ideal and noisy results for circuits with 4 layers:
17.45%

Mean absolute difference between ideal and noisy results for circuits with 5 layers:
25.82%

Mean absolute difference between ideal and noisy results for circuits with 6 layers:
27.96%

كما هو موضّح، بمعرفة نموذج الضوضاء، يمكنك محاولة قياس تأثير الضوضاء على PUBs المعنية (في نسختها Cliffordized) قبل تشغيلها على وحدة QPU.

التطبيق 2: قياس أداء استراتيجيات مختلفة​

يستخدم هذا المثال Neat للمساعدة في تحديد أفضل الخيارات لـ PUBs الخاصة بك. للقيام بذلك، فكّر في تشغيل مسألة تقدير مع PEA، والتي لا يمكن محاكاتها باستخدام qiskit_aer. يمكنك استخدام Neat للمساعدة في تحديد عوامل تضخيم الضوضاء الأنسب، ثم استخدام تلك العوامل عند تشغيل التجربة الأصلية على وحدة QPU.

# Generate a circuit with six qubits and six layers
isa_qc = pm.run(generate_circuit(6, 3))

# Use the same observables as previously
pubs = [(isa_qc, isa_obs)]
clifford_pubs = analyzer.to_clifford(pubs)
noise_factors = [
[1, 1.1],
[1, 1.1, 1.2],
[1, 1.5, 2],
[1, 1.5, 2, 2.5, 3],
[1, 4],
]
# Run the PUBs on a QPU
estimator = Estimator(backend)
estimator.options.default_shots = 100000
estimator.options.twirling.enable_gates = True
estimator.options.twirling.enable_measure = True
estimator.options.twirling.shots_per_randomization = 100
estimator.options.resilience.measure_mitigation = True
estimator.options.resilience.zne_mitigation = True
estimator.options.resilience.zne.amplifier = "pea"

# Learn noise model
layers = estimator.find_unique_layers(clifford_pubs)
learner = NoiseLearnerV3(backend)
learner_job = learner.run(layers)
learner_result = learner_job.result()

# Convert results to Pauli-Lindblad noise maps.
pauli_lindblad_maps = learner_result.to_pauli_lindblad_maps()

# Assign the learned noise maps so PEA uses them.
estimator.options.resilience.layer_noise_model = zip(
layers, pauli_lindblad_maps
)

jobs = []
for factors in noise_factors:
estimator.options.resilience.zne.noise_factors = factors
jobs.append(estimator.run(clifford_pubs))

results = [job.result() for job in jobs]
# Perform a noiseless simulation
ideal_results = analyzer.ideal_sim(clifford_pubs)
# Look at the mean absolute difference to quickly determine
# the best choice for your options
for factors, res in zip(noise_factors, results):
d = rdiff(ideal_results[0], res[0])
print(
f"Mean absolute difference for factors "
f"{factors}:\n {np.round(np.mean(d), 2)}%\n"
)
Mean absolute difference for factors [1, 1.1]:
10.9%

Mean absolute difference for factors [1, 1.1, 1.2]:
4.64%

Mean absolute difference for factors [1, 1.5, 2]:
4.33%

Mean absolute difference for factors [1, 1.5, 2, 2.5, 3]:
5.66%

Mean absolute difference for factors [1, 4]:
4.53%

تشير النتيجة ذات الفرق الأصغر إلى الخيارات الأنسب للاستخدام.

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