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

المحاكاة الدقيقة باستخدام primitives في Qiskit SDK

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

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

qiskit[all]~=2.3.0

تُنفِّذ الـ primitives المرجعية في Qiskit SDK محاكاة statevector محلية. لا تدعم هذه المحاكاة نمذجة ضوضاء الأجهزة، لكنها مفيدة لنمذجة الخوارزميات بسرعة قبل الانتقال إلى تقنيات محاكاة أكثر تقدمًا (باستخدام Qiskit Aer) أو التشغيل على أجهزة حقيقية (Qiskit Runtime primitives).

يستطيع الـ Estimator primitive حساب قيم التوقع للدوائر، ويستطيع الـ Sampler primitive أخذ عينات من توزيعات المخرجات للدوائر.

تشرح الأقسام التالية كيفية استخدام الـ primitives المرجعية لتشغيل سير عملك محليًا.

استخدام Estimator المرجعي

التطبيق المرجعي لـ EstimatorV2 في qiskit.primitives الذي يعمل على محاكيات statevector محلية هو فئة StatevectorEstimator. يمكنه قبول الدوائر والـ observables والمعاملات كمدخلات، ويُعيد قيم التوقع المحسوبة محليًا.

الكود التالي يُعدِّد المدخلات التي ستُستخدم في الأمثلة اللاحقة. النوع المتوقع للـ observables هو qiskit.quantum_info.SparsePauliOp. لاحظ أن الدائرة في المثال مُحددة المعاملات، لكن يمكنك أيضًا تشغيل الـ Estimator على دوائر غير مُحددة المعاملات.

ملاحظة

أي دائرة تُمرَّر إلى Estimator يجب ألا تحتوي على أي قياسات.

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit
from qiskit import QuantumCircuit
from qiskit.circuit import Parameter

# circuit for which you want to obtain the expected value
circuit = QuantumCircuit(2)
circuit.ry(Parameter("theta"), 0)
circuit.h(0)
circuit.cx(0, 1)
circuit.draw("mpl", style="iqp")

Output of the previous code cell

from qiskit.quantum_info import SparsePauliOp
import numpy as np

# observable(s) whose expected values you want to compute

observable = SparsePauliOp(["II", "XX", "YY", "ZZ"], coeffs=[1, 1, -1, 1])

# value(s) for the circuit parameter(s)
parameter_values = [[0], [np.pi / 6], [np.pi / 2]]
حوّل الدوائر والـ observables إلى صيغة ISA

يتطلب سير عمل Qiskit Runtime primitives تحويل الدوائر والـ observables لتستخدم فقط التعليمات التي يدعمها QPU (يُشار إليها بـ دوائر وobservables بنية مجموعة التعليمات (ISA)). لا تزال الـ primitives المرجعية تقبل التعليمات المجردة لأنها تعتمد على محاكاة statevector محلية، لكن transpiling الدائرة قد يظل مفيدًا من حيث تحسين الدائرة.

# Generate a pass manager without providing a backend
from qiskit.transpiler import generate_preset_pass_manager

pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)

تهيئة Estimator

أنشئ نسخة من qiskit.primitives.StatevectorEstimator.

from qiskit.primitives import StatevectorEstimator

estimator = StatevectorEstimator()

التشغيل والحصول على النتائج

يستخدم هذا المثال دائرة واحدة فقط (من نوع QuantumCircuit) وobservable واحد.

شغِّل التقدير عن طريق استدعاء الطريقة StatevectorEstimator.run، التي تُعيد نسخة من كائن PrimitiveJob. يمكنك الحصول على النتائج من الوظيفة (كـكائن qiskit.primitives.PrimitiveResult) باستخدام الطريقة qiskit.primitives.PrimitiveJob.result.

job = estimator.run([(circuit, observable, parameter_values)])
result = job.result()
print(f" > Result class: {type(result)}")
> Result class: <class 'qiskit.primitives.containers.primitive_result.PrimitiveResult'>

الحصول على قيمة التوقع من النتيجة

تُخرج نتائج الـ primitives مصفوفة من كائنات PubResult، حيث كل عنصر في المصفوفة هو كائن PubResult يحتوي في بياناته على مصفوفة التقييمات المقابلة لكل تركيبة دائرة-observable في الـ PUB.

لاسترداد قيم التوقع والبيانات الوصفية لتقييم الدائرة الأولى (والوحيدة في هذه الحالة)، يجب الوصول إلى data للـ PUB 0:

print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")
> Expectation value: [4.         3.73205081 2.        ]
> Metadata: {'target_precision': 0.0, 'circuit_metadata': {}}

ضبط خيارات تشغيل Estimator

بشكل افتراضي، يُنفِّذ الـ Estimator المرجعي حسابًا دقيقًا للـ statevector استنادًا إلى فئة quantum_info.Statevector. لكن يمكن تعديل ذلك لإدخال تأثير الحمل الزائد للأخذ بالعينات (المعروف أيضًا بـ "shot noise").

يقبل الـ Estimator وسيطة precision تُعبِّر عن أشرطة الخطأ التي يجب أن يستهدفها تطبيق الـ primitive لتقديرات قيم التوقع. هذا هو الحمل الزائد للأخذ بالعينات ويُعرَّف حصريًا في الطريقة .run(). يتيح لك ذلك ضبط الخيار بدقة حتى مستوى الـ PUB.

# Estimate expectation values for two PUBs, both with 0.05 precision.
precise_job = estimator.run(
[(circuit, observable, parameter_values)], precision=0.05
)

للاطلاع على مثال كامل، راجع صفحة أمثلة Primitives.

استخدام Sampler المرجعي

التطبيق المرجعي لـ SamplerV2 في qiskit.primitives هو فئة StatevectorSampler. يأخذ الدوائر والمعاملات كمدخلات ويُعيد نتائج أخذ العينات من توزيعات الاحتمالات للمخرجات كتوزيع شبه احتمالي لحالات المخرجات.

الكود التالي يُعدِّد المدخلات المستخدمة في الأمثلة اللاحقة. لاحظ أن هذه الأمثلة تُشغِّل دائرة واحدة محددة المعاملات، لكن يمكنك أيضًا تشغيل الـ Sampler على دوائر غير محددة المعاملات.

from qiskit import QuantumCircuit

circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
circuit.draw("mpl", style="iqp")

Output of the previous code cell

ملاحظة

أي دائرة كمية تُمرَّر إلى Sampler يجب أن تحتوي على قياسات.

حوّل الدوائر والـ observables إلى صيغة ISA

يتطلب سير عمل Qiskit Runtime primitives تحويل الدوائر لتستخدم فقط التعليمات التي يدعمها QPU (يُشار إليها بدوائر ISA). لا تزال الـ primitives المرجعية تقبل التعليمات المجردة لأنها تعتمد على محاكاة statevector محلية، لكن transpiling الدائرة قد يظل مفيدًا من حيث تحسين الدائرة.

# Generate a pass manager without providing a backend
from qiskit.transpiler import generate_preset_pass_manager

pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(qc)

تهيئة SamplerV2

أنشئ نسخة من qiskit.primitives.StatevectorSampler:

from qiskit.primitives import StatevectorSampler

sampler = StatevectorSampler()

التشغيل والحصول على النتائج

# execute 1 circuit with Sampler
job = sampler.run([circuit])
pub_result = job.result()[0]
print(f" > Result class: {type(pub_result)}")
> Result class: <class 'qiskit.primitives.containers.sampler_pub_result.SamplerPubResult'>

تقبل الـ Primitives عدة PUBs كمدخلات، وتحصل كل PUB على نتيجتها الخاصة. لذلك يمكنك تشغيل دوائر مختلفة مع تركيبات متنوعة من المعاملات والـ observables، واسترداد نتائج الـ PUB:

from qiskit.transpiler import generate_preset_pass_manager

# create two circuits
circuit1 = circuit.copy()
circuit2 = circuit.copy()

# transpile circuits
pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit1 = pm.run(circuit1)
isa_circuit2 = pm.run(circuit2)
# execute 2 circuits using Sampler
job = sampler.run([(isa_circuit1), (isa_circuit2)])
pub_result_1 = job.result()[0]
pub_result_2 = job.result()[1]
print(f" > Result class: {type(pub_result)}")
> Result class: <class 'qiskit.primitives.containers.sampler_pub_result.SamplerPubResult'>

الحصول على توزيع الاحتمال أو نتيجة القياس

تُعاد نتائج القياس كـ bitstrings أو counts. تُظهر الـ bitstrings نتائج القياس محافظةً على ترتيب اللقطات الذي قِيست به. تُنظِّم كائنات نتائج الـ Sampler البيانات وفقًا لأسماء السجلات الكلاسيكية للدوائر المدخلة، لضمان التوافق مع الدوائر الديناميكية.

ملاحظة

اسم السجل الكلاسيكي الافتراضي هو "meas". سيُستخدَم هذا الاسم لاحقًا للوصول إلى bitstrings القياس.

# Define quantum circuit with 2 qubits
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
circuit.draw()
┌───┐      ░ ┌─┐
q_0: ┤ H ├──■───░─┤M├───
└───┘┌─┴─┐ ░ └╥┘┌─┐
q_1: ─────┤ X ├─░──╫─┤M├
└───┘ ░ ║ └╥┘
meas: 2/══════════════╩══╩═
0 1
# Transpile circuit
pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(circuit)
# Run using sampler
result = sampler.run([circuit]).result()
# Access result data for PUB 0
data_pub = result[0].data
# Access bitstring for the classical register "meas"
bitstrings = data_pub.meas.get_bitstrings()
print(f"The number of bitstrings is: {len(bitstrings)}")
# Get counts for the classical register "meas"
counts = data_pub.meas.get_counts()
print(f"The counts are: {counts}")
The number of bitstrings is: 1024
The counts are: {'11': 515, '00': 509}

تغيير خيارات التشغيل

بشكل افتراضي، يُنفِّذ الـ Sampler المرجعي حسابًا دقيقًا للـ statevector استنادًا إلى فئة quantum_info.Statevector. لكن يمكن تعديل ذلك لإدخال تأثير الحمل الزائد للأخذ بالعينات (المعروف أيضًا بـ "shot noise"). لمساعدتك في إدارة هذا الحمل الزائد، تقبل واجهة الـ Sampler وسيطة shots يمكن تعريفها على مستوى الـ PUB.

يفترض هذا المثال أنك عرَّفت دائرتين.

# Sample two circuits at 128 shots each.
sampler.run([isa_circuit1, isa_circuit2], shots=128)
# Sample two circuits at different amounts of shots. The "None"s are necessary
# as placeholders
# for the lack of parameter values in this example.
sampler.run([(isa_circuit1, None, 123), (isa_circuit2, None, 456)])
<qiskit.primitives.primitive_job.PrimitiveJob at 0x7fa430e39dd0>

للاطلاع على مثال كامل، راجع صفحة أمثلة Primitives.

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

توصيات