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

التصريف الكمي التقريبي لدوائر تطور الزمن

تقدير الاستخدام: خمس دقائق على معالج Eagle (ملاحظة: هذا تقدير فحسب. قد يختلف وقت التشغيل لديك.)

الخلفية النظرية

يوضح هذا الدرس التطبيقي كيفية تنفيذ التصريف الكمي التقريبي باستخدام الشبكات الترابطية (AQC-Tensor) مع Qiskit لتحسين أداء الدوائر الكمية. نطبّق AQC-Tensor في سياق تطور زمني مُرتّب وفق Trotter لتقليل عمق الدائرة مع الحفاظ على دقة المحاكاة، باتباع إطار Qiskit لإعداد الحالة والتحسين. ستتعلم كيفية إنشاء دائرة ansatz منخفضة العمق من دائرة Trotter الأولية، وتحسينها باستخدام الشبكات الترابطية، وإعدادها لتنفيذها على عتاد كمي.

الهدف الرئيسي هو محاكاة تطور الزمن لـ Hamiltonian نموذجي بعمق دائرة مُخفَّض. يتحقق ذلك باستخدام الإضافة AQC-Tensor لـ Qiskit، qiskit-addon-aqc-tensor، التي تستغل الشبكات الترابطية، وتحديدًا حالات جداء المصفوفات (MPS)، لضغط الدائرة الأولية وتحسينها. من خلال التعديلات التكرارية، تحافظ دائرة ansatz المضغوطة على الأمانة مع الدائرة الأصلية مع بقائها قابلة للتطبيق على العتاد الكمي قريب المدى. يمكن الاطلاع على مزيد من التفاصيل في التوثيق المقابل مع مثال بسيط للبدء.

يُعدّ التصريف الكمي التقريبي مفيدًا بشكل خاص في المحاكاة الكمية التي تتجاوز أوقات الترابط في العتاد، إذ يسمح بإجراء محاكاة معقدة بكفاءة أكبر. يرشدك هذا الدرس التطبيقي عبر إعداد سير عمل AQC-Tensor في Qiskit، ويشمل ذلك تهيئة Hamiltonian وتوليد دوائر Trotter وترجمة الدائرة المحسّنة النهائية لجهاز مستهدف.

المتطلبات

قبل البدء بهذا الدرس التطبيقي، تأكد من تثبيت ما يلي:

  • Qiskit SDK الإصدار 1.0 أو أحدث، مع دعم التصور المرئي
  • Qiskit Runtime الإصدار 0.22 أو أحدث (pip install qiskit-ibm-runtime)
  • الإضافة AQC-Tensor لـ Qiskit (pip install 'qiskit-addon-aqc-tensor[aer,quimb-jax]')

الإعداد

# Added by doQumentation — installs packages not in the Binder environment
%pip install -q qiskit-addon-aqc-tensor quimb
import numpy as np
import quimb.tensor
import datetime
import matplotlib.pyplot as plt

from scipy.optimize import OptimizeResult, minimize

from qiskit.quantum_info import SparsePauliOp, Pauli
from qiskit.transpiler import CouplingMap
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit import QuantumCircuit
from qiskit.synthesis import SuzukiTrotter

from qiskit_addon_utils.problem_generators import (
generate_time_evolution_circuit,
)
from qiskit_addon_aqc_tensor.ansatz_generation import (
generate_ansatz_from_circuit,
)
from qiskit_addon_aqc_tensor.objective import MaximizeStateFidelity
from qiskit_addon_aqc_tensor.simulation.quimb import QuimbSimulator
from qiskit_addon_aqc_tensor.simulation import tensornetwork_from_circuit
from qiskit_addon_aqc_tensor.simulation import compute_overlap

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator

from rustworkx.visualization import graphviz_draw

الجزء الأول. مثال صغير النطاق

يستخدم الجزء الأول من هذا الدرس التطبيقي مثالًا صغير النطاق مؤلفًا من 10 مواقع لتوضيح عملية تعيين مشكلة محاكاة كمية إلى دائرة كمية قابلة للتنفيذ. سنستكشف هنا ديناميكيات نموذج XXZ ذي 10 مواقع، مما يتيح لنا بناء دائرة كمية وتحسينها قبل التوسع إلى أنظمة أكبر.

يُدرَس نموذج XXZ على نطاق واسع في الفيزياء لفحص تفاعلات الدوران والخصائص المغناطيسية. نُعدّ Hamiltonian ليكون ذا شروط حدودية مفتوحة مع تفاعلات تعتمد على الموقع بين المواقع المجاورة على طول السلسلة.

Hamiltonian النموذج والكمية القابلة للرصد

يُعرَّف Hamiltonian لنموذج XXZ ذي 10 مواقع على النحو التالي:

H^XXZ=i=1L1Ji,(i+1)(XiX(i+1)+YiY(i+1)+2ZiZ(i+1)),\hat{\mathcal{H}}_{XXZ} = \sum_{i=1}^{L-1} J_{i,(i+1)}\left(X_i X_{(i+1)}+Y_i Y_{(i+1)}+ 2\cdot Z_i Z_{(i+1)} \right) \, ,

حيث Ji,(i+1)J_{i,(i+1)} هو معامل عشوائي يقابل الحافة (i,i+1)(i, i+1)، و L=10L=10 هو عدد المواقع.

من خلال محاكاة تطور هذا النظام بعمق دائرة مخفّض، يمكننا اكتساب رؤى حول استخدام AQC-Tensor لضغط الدوائر وتحسينها.

إعداد Hamiltonian والكمية القابلة للرصد

قبل تعيين مشكلتنا، نحتاج إلى إعداد خريطة الاقتران وHamiltonian والكمية القابلة للرصد لنموذج XXZ ذي 10 مواقع.

# L is the number of sites, also the length of the 1D spin chain
L = 10

# Generate the coupling map
edge_list = [(i - 1, i) for i in range(1, L)]
# Generate an edge-coloring so we can make hw-efficient circuits
even_edges = edge_list[::2]
odd_edges = edge_list[1::2]
coupling_map = CouplingMap(edge_list)

# Generate random coefficients for our XXZ Hamiltonian
np.random.seed(0)
Js = np.random.rand(L - 1) + 0.5 * np.ones(L - 1)
hamiltonian = SparsePauliOp(Pauli("I" * L))
for i, edge in enumerate(even_edges + odd_edges):
hamiltonian += SparsePauliOp.from_sparse_list(
[
("XX", (edge), Js[i] / 2),
("YY", (edge), Js[i] / 2),
("ZZ", (edge), Js[i]),
],
num_qubits=L,
)

# Generate a ZZ observable between the two middle qubits
observable = SparsePauliOp.from_sparse_list(
[("ZZ", (L // 2 - 1, L // 2), 1.0)], num_qubits=L
)

print("Hamiltonian:", hamiltonian)
print("Observable:", observable)
graphviz_draw(coupling_map.graph, method="circo")
Hamiltonian: SparsePauliOp(['IIIIIIIIII', 'IIIIIIIIXX', 'IIIIIIIIYY', 'IIIIIIIIZZ', 'IIIIIIXXII', 'IIIIIIYYII', 'IIIIIIZZII', 'IIIIXXIIII', 'IIIIYYIIII', 'IIIIZZIIII', 'IIXXIIIIII', 'IIYYIIIIII', 'IIZZIIIIII', 'XXIIIIIIII', 'YYIIIIIIII', 'ZZIIIIIIII', 'IIIIIIIXXI', 'IIIIIIIYYI', 'IIIIIIIZZI', 'IIIIIXXIII', 'IIIIIYYIII', 'IIIIIZZIII', 'IIIXXIIIII', 'IIIYYIIIII', 'IIIZZIIIII', 'IXXIIIIIII', 'IYYIIIIIII', 'IZZIIIIIII'],
coeffs=[1. +0.j, 0.52440675+0.j, 0.52440675+0.j, 1.0488135 +0.j,
0.60759468+0.j, 0.60759468+0.j, 1.21518937+0.j, 0.55138169+0.j,
0.55138169+0.j, 1.10276338+0.j, 0.52244159+0.j, 0.52244159+0.j,
1.04488318+0.j, 0.4618274 +0.j, 0.4618274 +0.j, 0.9236548 +0.j,
0.57294706+0.j, 0.57294706+0.j, 1.14589411+0.j, 0.46879361+0.j,
0.46879361+0.j, 0.93758721+0.j, 0.6958865 +0.j, 0.6958865 +0.j,
1.391773 +0.j, 0.73183138+0.j, 0.73183138+0.j, 1.46366276+0.j])
Observable: SparsePauliOp(['IIIIZZIIII'],
coeffs=[1.+0.j])

Output of the previous code cell

بعد تعريف Hamiltonian، يمكننا المضي قدمًا في بناء الحالة الأولية.

# Generate an initial state
initial_state = QuantumCircuit(L)
for i in range(L):
if i % 2:
initial_state.x(i)

الخطوة 1: تعيين المدخلات الكلاسيكية إلى مشكلة كمية

الآن بعد أن بنينا Hamiltonian الذي يصف تفاعلات الدوران والحقول المغناطيسية الخارجية التي تُميّز النظام، نتبع ثلاث خطوات رئيسية في سير عمل AQC-Tensor:

  1. توليد دائرة AQC المحسّنة: باستخدام Trotterization، نقرّب التطور الأولي، ثم يُضغط لتقليل عمق الدائرة.
  2. إنشاء دائرة تطور الزمن المتبقية: التقاط التطور للوقت المتبقي بعد القطعة الأولية.
  3. دمج الدوائر: دمج دائرة AQC المحسّنة مع دائرة التطور المتبقية في دائرة تطور زمني كاملة جاهزة للتنفيذ.

يُنشئ هذا النهج ansatz منخفض العمق للتطور المستهدف، مما يدعم المحاكاة الفعّالة ضمن قيود العتاد الكمي قريب المدى.

تحديد جزء تطور الزمن المراد محاكاته كلاسيكيًا

هدفنا هو محاكاة تطور الزمن لـ Hamiltonian النموذجي المُعرَّف سابقًا باستخدام تطور Trotter. لجعل هذه العملية فعّالة للعتاد الكمي، نقسّم التطور إلى قطعتين:

  • القطعة الأولية: هذا الجزء الأولي من التطور، من ti=0.0t_i = 0.0 إلى tf=0.2t_f = 0.2، قابل للمحاكاة باستخدام MPS ويمكن "تصريفه" بكفاءة باستخدام AQC-Tensor. باستخدام الإضافة AQC-Tensor لـ Qiskit، نولّد دائرة مضغوطة لهذه القطعة، يُشار إليها بـ aqc_target_circuit. نظرًا لأن هذه القطعة ستُحاكى على محاكي الشبكة الترابطية، يمكننا استخدام عدد أكبر من طبقات Trotter دون التأثير بشكل ملحوظ على موارد العتاد. نضع aqc_target_num_trotter_steps = 32 لهذه القطعة.

  • القطعة اللاحقة: هذا الجزء المتبقي من التطور، من t=0.2t = 0.2 إلى t=0.4t = 0.4، سيُنفَّذ على العتاد الكمي، يُشار إليه بـ subsequent_circuit. نظرًا لقيود العتاد، نسعى إلى استخدام أقل عدد ممكن من طبقات Trotter للحفاظ على عمق دائرة قابل للإدارة. نستخدم لهذه القطعة subsequent_num_trotter_steps = 3.

اختيار وقت التقسيم

نختار t=0.2t = 0.2 وقتًا للتقسيم لتحقيق التوازن بين قابلية المحاكاة الكلاسيكية وجدوى العتاد. في مراحل مبكرة من التطور، يظل التشابك في نموذج XXZ منخفضًا بما يكفي للأساليب الكلاسيكية كـ MPS للتقريب بدقة.

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

# Generate the AQC target circuit (initial segment)
aqc_evolution_time = 0.2
aqc_target_num_trotter_steps = 32

aqc_target_circuit = initial_state.copy()
aqc_target_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_target_num_trotter_steps),
time=aqc_evolution_time,
),
inplace=True,
)
# Generate the subsequent circuit
subsequent_num_trotter_steps = 3
subsequent_evolution_time = 0.2

subsequent_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=subsequent_num_trotter_steps),
time=subsequent_evolution_time,
)
subsequent_circuit.draw("mpl", fold=-1)

Output of the previous code cell

لتمكين مقارنة ذات معنى، سنولّد دائرتين إضافيتين:

  • دائرة مقارنة AQC: تتطور هذه الدائرة حتى aqc_evolution_time لكنها تستخدم نفس مدة خطوة Trotter الخاصة بـ subsequent_circuit. تُعدّ مقارنةً بـ aqc_target_circuit، إذ تُظهر التطور الذي سنرصده دون استخدام عدد متزايد من خطوات Trotter. سنشير إلى هذه الدائرة بـ aqc_comparison_circuit.

  • الدائرة المرجعية: تُستخدم هذه الدائرة كخط أساس للحصول على النتيجة الدقيقة. تُحاكي التطور الكامل باستخدام الشبكات الترابطية لحساب النتيجة الدقيقة، مما يوفر مرجعًا لتقييم فعّالية AQC-Tensor. سنشير إلى هذه الدائرة بـ reference_circuit.

# Generate the AQC comparison circuit
aqc_comparison_num_trotter_steps = int(
subsequent_num_trotter_steps
/ subsequent_evolution_time
* aqc_evolution_time
)
print(
"Number of Trotter steps for comparison:",
aqc_comparison_num_trotter_steps,
)

aqc_comparison_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_comparison_num_trotter_steps),
time=aqc_evolution_time,
)
Number of Trotter steps for comparison: 3
# Generate the reference circuit
evolution_time = 0.4
reps = 200

reference_circuit = initial_state.copy()
reference_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=reps),
time=evolution_time,
),
inplace=True,
)

توليد ansatz والمعاملات الأولية من دائرة Trotter بخطوات أقل

الآن بعد أن بنينا دوائرنا الأربع، لنمضِ قدمًا في سير عمل AQC-Tensor. أولًا، نبني دائرة "جيدة" لها نفس زمن التطور كدائرة الهدف، لكن مع خطوات Trotter أقل (وبالتالي طبقات أقل).

ثم نمرر هذه الدائرة "الجيدة" إلى دالة generate_ansatz_from_circuit الخاصة بـ AQC-Tensor. تُحلّل هذه الدالة اتصال الكيوبت الثنائي للدائرة وتُعيد شيئين:

  1. دائرة ansatz مُعلمَنة عامة لها نفس اتصال الكيوبت الثنائي كالدائرة المُدخَلة.
  2. معاملات تُنتج الدائرة المُدخَلة (الجيدة) عند تعويضها في ansatz.

قريبًا سنأخذ هذه المعاملات ونعدّلها بصورة تكرارية لتقريب دائرة ansatz من MPS الهدف قدر الإمكان.

aqc_ansatz_num_trotter_steps = 1

aqc_good_circuit = initial_state.copy()
aqc_good_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_ansatz_num_trotter_steps),
time=aqc_evolution_time,
),
inplace=True,
)

aqc_ansatz, aqc_initial_parameters = generate_ansatz_from_circuit(
aqc_good_circuit
)
aqc_ansatz.draw("mpl", fold=-1)

Output of the previous code cell

print(f"AQC Comparison circuit: depth {aqc_comparison_circuit.depth()}")
print(f"Target circuit: depth {aqc_target_circuit.depth()}")
print(
f"Ansatz circuit: depth {aqc_ansatz.depth()}, with {len(aqc_initial_parameters)} parameters"
)
AQC Comparison circuit: depth 36
Target circuit: depth 385
Ansatz circuit: depth 7, with 156 parameters

اختيار إعدادات محاكاة الشبكة الترابطية

نستخدم هنا محاكي دائرة matrix-product state الخاص بـ Quimb، إلى جانب jax لتوفير التدرج.

simulator_settings = QuimbSimulator(
quimb.tensor.CircuitMPS, autodiff_backend="jax"
)

بعد ذلك، نبني تمثيل MPS للحالة الهدف التي سيُقرَّب إليها باستخدام AQC-Tensor. يُتيح هذا التمثيل التعامل الفعّال مع التشابك، مقدّمًا وصفًا مضغوطًا للحالة الكمية لمزيد من التحسين.

aqc_target_mps = tensornetwork_from_circuit(
aqc_target_circuit, simulator_settings
)
print("Target MPS maximum bond dimension:", aqc_target_mps.psi.max_bond())

# Obtains the reference MPS, where we can obtain the exact expectation value by examining the `local_expectation``
reference_mps = tensornetwork_from_circuit(
reference_circuit, simulator_settings
)
reference_expval = reference_mps.local_expectation(
quimb.pauli("Z") & quimb.pauli("Z"), (L // 2 - 1, L // 2)
).real.item()
print("Reference MPS maximum bond dimension:", reference_mps.psi.max_bond())
Target MPS maximum bond dimension: 5
Reference MPS maximum bond dimension: 7

لاحظ أنه باختيار عدد أكبر من خطوات Trotter للحالة الهدف، قللنا فعليًا من خطأ Trotter مقارنةً بالدائرة الأولية. يمكننا تقييم الأمانة (ψ1ψ22|\langle \psi_1 | \psi_2 \rangle|^2) بين الحالة المُعدَّة بالدائرة الأولية والحالة الهدف لقياس هذا الفرق.

good_mps = tensornetwork_from_circuit(aqc_good_circuit, simulator_settings)
starting_fidelity = abs(compute_overlap(good_mps, aqc_target_mps)) ** 2
print("Starting fidelity:", starting_fidelity)
Starting fidelity: 0.9982464959067222

تحسين معاملات ansatz باستخدام حسابات MPS

في هذه الخطوة، نُحسّن معاملات ansatz بتقليل دالة تكلفة بسيطة هي MaximizeStateFidelity، باستخدام مُحسِّن L-BFGS من SciPy. نختار معيار توقف للأمانة يضمن تجاوزها لأمانة الدائرة الأولية دون AQC-Tensor. عند الوصول إلى هذا الحد، ستُظهر الدائرة المضغوطة خطأ Trotter أقل وعمقًا مخفّضًا مقارنةً بالدائرة الأصلية. باستخدام وقت CPU إضافي، يمكن مواصلة التحسين لزيادة الأمانة.

# Setting values for the optimization
aqc_stopping_fidelity = 1
aqc_max_iterations = 500

stopping_point = 1.0 - aqc_stopping_fidelity
objective = MaximizeStateFidelity(
aqc_target_mps, aqc_ansatz, simulator_settings
)

def callback(intermediate_result: OptimizeResult):
fidelity = 1 - intermediate_result.fun
print(
f"{datetime.datetime.now()} Intermediate result: Fidelity {fidelity:.8f}"
)
if intermediate_result.fun < stopping_point:
# Good enough for now
raise StopIteration

result = minimize(
objective,
aqc_initial_parameters,
method="L-BFGS-B",
jac=True,
options={"maxiter": aqc_max_iterations},
callback=callback,
)
if (
result.status
not in (
0,
1,
99,
)
): # 0 => success; 1 => max iterations reached; 99 => early termination via StopIteration
raise RuntimeError(
f"Optimization failed: {result.message} (status={result.status})"
)

print(f"Done after {result.nit} iterations.")
aqc_final_parameters = result.x
2025-04-14 11:46:52.174235 Intermediate result: Fidelity 0.99795851
2025-04-14 11:46:52.218249 Intermediate result: Fidelity 0.99822826
2025-04-14 11:46:52.280924 Intermediate result: Fidelity 0.99829675
2025-04-14 11:46:52.356214 Intermediate result: Fidelity 0.99832474
2025-04-14 11:46:52.411609 Intermediate result: Fidelity 0.99836131
2025-04-14 11:46:52.453747 Intermediate result: Fidelity 0.99839954
2025-04-14 11:46:52.496184 Intermediate result: Fidelity 0.99846517
2025-04-14 11:46:52.542046 Intermediate result: Fidelity 0.99865029
2025-04-14 11:46:52.583679 Intermediate result: Fidelity 0.99872332
2025-04-14 11:46:52.628732 Intermediate result: Fidelity 0.99892359
2025-04-14 11:46:52.690386 Intermediate result: Fidelity 0.99900640
2025-04-14 11:46:52.759398 Intermediate result: Fidelity 0.99907169
2025-04-14 11:46:52.819496 Intermediate result: Fidelity 0.99911423
2025-04-14 11:46:52.884505 Intermediate result: Fidelity 0.99918716
2025-04-14 11:46:52.947919 Intermediate result: Fidelity 0.99921278
2025-04-14 11:46:53.012808 Intermediate result: Fidelity 0.99924853
2025-04-14 11:46:53.083626 Intermediate result: Fidelity 0.99928797
2025-04-14 11:46:53.153235 Intermediate result: Fidelity 0.99933028
2025-04-14 11:46:53.153235 Intermediate result: Fidelity 0.99933028
2025-04-14 11:46:53.221371 Intermediate result: Fidelity 0.99935757
2025-04-14 11:46:53.286211 Intermediate result: Fidelity 0.99938140
2025-04-14 11:46:53.352391 Intermediate result: Fidelity 0.99940964
2025-04-14 11:46:53.420472 Intermediate result: Fidelity 0.99944051
2025-04-14 11:46:53.486279 Intermediate result: Fidelity 0.99946828
2025-04-14 11:46:53.552338 Intermediate result: Fidelity 0.99948723
2025-04-14 11:46:53.618688 Intermediate result: Fidelity 0.99951011
2025-04-14 11:46:53.690878 Intermediate result: Fidelity 0.99954718
2025-04-14 11:46:53.762725 Intermediate result: Fidelity 0.99956267
2025-04-14 11:46:53.829784 Intermediate result: Fidelity 0.99958949
2025-04-14 11:46:53.897477 Intermediate result: Fidelity 0.99960498
2025-04-14 11:46:53.954633 Intermediate result: Fidelity 0.99961308
2025-04-14 11:46:54.010125 Intermediate result: Fidelity 0.99962894
2025-04-14 11:46:54.064717 Intermediate result: Fidelity 0.99964121
2025-04-14 11:46:54.118892 Intermediate result: Fidelity 0.99964348
2025-04-14 11:46:54.183236 Intermediate result: Fidelity 0.99964860
2025-04-14 11:46:54.245521 Intermediate result: Fidelity 0.99965695
2025-04-14 11:46:54.305792 Intermediate result: Fidelity 0.99966398
2025-04-14 11:46:54.355819 Intermediate result: Fidelity 0.99967816
2025-04-14 11:46:54.409580 Intermediate result: Fidelity 0.99968293
2025-04-14 11:46:54.457979 Intermediate result: Fidelity 0.99968936
2025-04-14 11:46:54.505891 Intermediate result: Fidelity 0.99969223
2025-04-14 11:46:54.551084 Intermediate result: Fidelity 0.99970009
2025-04-14 11:46:54.601817 Intermediate result: Fidelity 0.99970724
2025-04-14 11:46:54.650097 Intermediate result: Fidelity 0.99970987
2025-04-14 11:46:54.714727 Intermediate result: Fidelity 0.99971237
2025-04-14 11:46:54.780052 Intermediate result: Fidelity 0.99971916
2025-04-14 11:46:54.871994 Intermediate result: Fidelity 0.99971940
2025-04-14 11:46:54.958244 Intermediate result: Fidelity 0.99972465
2025-04-14 11:46:55.011057 Intermediate result: Fidelity 0.99972763
2025-04-14 11:46:55.175339 Intermediate result: Fidelity 0.99972894
2025-04-14 11:46:56.688912 Intermediate result: Fidelity 0.99972894
Done after 50 iterations.
parameters = [float(param) for param in aqc_final_parameters]
print("Final parameters:", parameters)
Final parameters: [-7.853983035039254, 1.5707966468427772, 1.5707962768868613, -1.570798010835122, 1.570794480409574, 1.5707972214146968, -1.570796593027083, 1.5707968206822998, -1.5707959018046258, -1.5707991700969144, 1.5707965852600927, 4.712386891737442, -7.853980840717957, 1.5707967508132654, 1.5707943162503217, -1.5707955382023582, 1.5707958007156742, 1.570796096113293, -1.5707928509846847, 1.5707971042943747, -1.570797909276557, -1.5707941020637393, 1.5707980179540793, 4.712389823219363, -1.5707928752386107, 1.5707996426312891, -1.5707975640471001, -1.570794132802984, 1.5707944361599957, 4.712390747060803, 0.1048818190315936, 0.06686710468840577, -0.0668645844756557, -3.1415923537135466, 1.2374931269696063, 6.323169390432535e-07, 3.53229204771738e-08, 2.1091105688681484, 6.283186439944202, 0.12152258846156239, 0.07961752617254866, -0.07961775088604585, -1.6564278051174865e-06, 2.0771163596472384, 3.141592651630471, -6.283185775192653, 1.7691609006726954, 3.1415922910116216, 0.19837572065074083, 0.11114901449078964, -0.11115124544944892, -3.141591983034976, 0.8570788408766729, 4.201601390404146e-07, -3.141593736550978, 0.34652010942396333, 6.283186232785291, 0.13606356527241956, 0.03891676349289617, -0.03891524189533726, -1.5707965732853424, 1.5707968967088564, -0.3086133992238162, 1.5707957152428194, 1.5707968398959653, -0.32062737993080026, 0.11027416939993417, 0.0726167290795046, -0.07262020423334464, -2.3729431959735024e-06, 1.8204437429254703, 9.299060301196612e-07, -3.141592899563451, 2.103269568939461, 3.1415937539734626, 0.11536891854817125, 0.09099022308254198, -0.09098864958606581, -3.1415913307373127, 2.078429034357281, -1.509777998069368e-06, -3.1415922600663255, 1.5189162645358172, -3.1415878461323583, 0.09999070991480716, 0.04352011445148391, -0.04351849541849812, -1.570797642506462, 1.570795238023824, 0.8903442644396505, 1.5707962698006606, 1.5707946765132268, 0.9098791754570567, 0.10448284343424026, 0.07317037684936827, -0.07316718173961152, -3.141592682240966, 2.1665363080039612, -7.450882112394189e-07, -5.771181304929921e-07, 2.615334999517103, -3.1415914971653898, 0.1890887078648001, 0.13578163074571992, -0.13578078143610256, 7.156734195912883e-07, 1.7915385305413096, -5.188866034727312e-07, 1.2827742939197711e-06, 1.2348316581417487, 6.28318357406372, 0.08061187643781703, 0.03820789039271876, -0.03820731868804904, 1.5707964027727628, 1.570798734462218, 4.387336153720882, -1.570795722044763, 1.570798457375325, 4.450361734163248, 0.092360147257953, 0.06047700345049011, -0.06048592856713045, -3.141591214829027, 2.6593289993286047, -2.366937342261038e-07, 8.112162974032695e-08, 1.8907014631413432, 8.355881261853104e-07, 0.23303641819370874, 0.14331998953606456, -0.1433194488304741, -3.141591621822901, 0.7455776479558791, 3.1415914520163586, -3.1415933560496105, 0.7603938554148255, -1.6230983177616282e-06, 0.07186349688535713, 0.03197144517771341, -0.031971177878588546, -4.712389048748508, 1.5707948403165752, 1.2773619319829186, -1.5707990802172127, 1.5707957676951863, 1.289083769394045, 0.13644999397718796, 0.032761460443590046, -0.032762060585195645, -1.5707977610073176, 1.5707964181578042, -3.4826435600366983, -4.712389691708343, 1.570794277502252, 2.799088046133275]

في هذه المرحلة، لا يلزم سوى إيجاد المعاملات النهائية لدائرة الـ ansatz. يمكننا بعد ذلك دمج دائرة AQC المُحسَّنة مع دائرة التطور المتبقية لإنشاء دائرة تطور زمني كاملة قابلة للتنفيذ على العتاد الكمي.

aqc_final_circuit = aqc_ansatz.assign_parameters(aqc_final_parameters)
aqc_final_circuit.compose(subsequent_circuit, inplace=True)
aqc_final_circuit.draw("mpl", fold=-1)

Output of the previous code cell

نحتاج أيضًا إلى دمج aqc_comparison_circuit مع دائرة التطور المتبقية. ستُستخدم هذه الدائرة لمقارنة أداء الدائرة المُحسَّنة بواسطة AQC-Tensor مع الدائرة الأصلية.

aqc_comparison_circuit.compose(subsequent_circuit, inplace=True)
aqc_comparison_circuit.draw("mpl", fold=-1)

Output of the previous code cell

الخطوة 2: تحسين المسألة لتنفيذها على العتاد الكمي

اختَر العتاد المناسب. سنستخدم هنا أيًّا من أجهزة IBM Quantum® المتاحة التي تضم ما لا يقل عن 127 كيوبت.

service = QiskitRuntimeService()
backend = service.least_busy(min_num_qubits=127)
print(backend)

نُجري عملية الـ transpilation للـ PUBs (الدائرة والمقاييس المرصودة) لتتوافق مع بنية التعليمات المدعومة (ISA) للعتاد. من خلال تعيين optimization_level=3، يعمل المُحوِّل على تحسين الدائرة لتناسب سلسلة أحادية البُعد من الكيوبتات، مما يقلل من الضوضاء التي تؤثر على دقة الدائرة. بمجرد تحويل الدوائر إلى صيغة متوافقة مع العتاد، نُطبِّق تحويلًا مقابلًا على المقاييس المرصودة لضمان توافقها مع تخطيط الكيوبتات المُعدَّل.

pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=3
)
isa_circuit = pass_manager.run(aqc_final_circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
print("Observable info:", isa_observable)
print("Circuit depth:", isa_circuit.depth())
isa_circuit.draw("mpl", fold=-1, idle_wires=False)
Observable info: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZ'],
coeffs=[1.+0.j])
Circuit depth: 111

Output of the previous code cell

أجرِ عملية الـ transpilation لدائرة المقارنة.

isa_comparison_circuit = pass_manager.run(aqc_comparison_circuit)
isa_comparison_observable = observable.apply_layout(
isa_comparison_circuit.layout
)
print("Observable info:", isa_comparison_observable)
print("Circuit depth:", isa_comparison_circuit.depth())
isa_comparison_circuit.draw("mpl", fold=-1, idle_wires=False)
Observable info: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZ'],
coeffs=[1.+0.j])
Circuit depth: 158

Output of the previous code cell

الخطوة 3: التنفيذ باستخدام Qiskit primitives

في هذه الخطوة، نُنفِّذ الدائرة المُحوَّلة على العتاد الكمي (أو backend محاكاة). باستخدام الفئة EstimatorV2 من qiskit_ibm_runtime، نُهيِّئ مُقدِّرًا لتشغيل الدائرة وقياس المقياس المرصود المحدد. تُوفِّر نتيجة المهمة القيمةَ المتوقعة للمقياس المرصود، مما يمنحنا رؤى حول أداء الدائرة على العتاد المستهدف.

estimator = Estimator(backend)
job = estimator.run([(isa_circuit, isa_observable)])
print("Job ID:", job.job_id())
job.result()
Job ID: czyhqdxd8drg008hx0yg
PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(), dtype=float64>), stds=np.ndarray(<shape=(), dtype=float64>), ensemble_standard_error=np.ndarray(<shape=(), dtype=float64>)), metadata={'shots': 4096, 'target_precision': 0.015625, 'circuit_metadata': {}, 'resilience': {}, 'num_randomizations': 32})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': False, 'enable_measure': True, 'num_randomizations': 'auto', 'shots_per_randomization': 'auto', 'interleave_randomizations': True, 'strategy': 'active-accum'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': False, 'pec_mitigation': False}, 'version': 2})

نفِّذ عملية التشغيل لدائرة المقارنة.

job_comparison = estimator.run([(isa_comparison_circuit, isa_observable)])
print("Job Comparison ID:", job.job_id())
job_comparison.result()
Job Comparison ID: czyhqdxd8drg008hx0yg
PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(), dtype=float64>), stds=np.ndarray(<shape=(), dtype=float64>), ensemble_standard_error=np.ndarray(<shape=(), dtype=float64>)), metadata={'shots': 4096, 'target_precision': 0.015625, 'circuit_metadata': {}, 'resilience': {}, 'num_randomizations': 32})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': False, 'enable_measure': True, 'num_randomizations': 'auto', 'shots_per_randomization': 'auto', 'interleave_randomizations': True, 'strategy': 'active-accum'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': False, 'pec_mitigation': False}, 'version': 2})

الخطوة 4: المعالجة اللاحقة وإرجاع النتيجة بالصيغة الكلاسيكية المطلوبة

في هذه الحالة، لا حاجة إلى إعادة البناء. يمكننا فحص النتيجة مباشرةً عبر الوصول إلى قيمة التوقع من مخرجات التنفيذ.

# AQC results
hw_results = job.result()
hw_results_dicts = [pub_result.data.__dict__ for pub_result in hw_results]
hw_expvals = [
pub_result_data["evs"].tolist() for pub_result_data in hw_results_dicts
]
aqc_expval = hw_expvals[0]

# AQC comparison results
hw_comparison_results = job_comparison.result()
hw_comparison_results_dicts = [
pub_result.data.__dict__ for pub_result in hw_comparison_results
]
hw_comparison_expvals = [
pub_result_data["evs"].tolist()
for pub_result_data in hw_comparison_results_dicts
]
aqc_compare_expval = hw_comparison_expvals[0]

print(f"Exact: \t{reference_expval:.4f}")
print(
f"AQC: \t{aqc_expval:.4f}, |∆| = {np.abs(reference_expval- aqc_expval):.4f}"
)
print(
f"AQC Comparison:\t{aqc_compare_expval:.4f}, |∆| = {np.abs(reference_expval- aqc_compare_expval):.4f}"
)
Exact:         	-0.5252
AQC: -0.4903, |∆| = 0.0349
AQC Comparison: 0.5424, |∆| = 1.0676

مخطط أعمدة لمقارنة نتائج دوائر AQC والمقارنة والنتائج الدقيقة.

plt.style.use("seaborn-v0_8")

labels = ["AQC Result", "AQC Comparison Result"]
values = [abs(aqc_expval), abs(aqc_compare_expval)]

plt.figure(figsize=(10, 6))
bars = plt.bar(labels, values, color=["tab:blue", "tab:purple"])
plt.axhline(
y=abs(reference_expval), color="red", linestyle="--", label="Exact Result"
)
plt.xlabel("Results")
plt.ylabel("Absolute Expected Value")
plt.title("AQC Result vs AQC Comparison Result (Absolute Values)")
plt.legend()
for bar in bars:
y_val = bar.get_height()
plt.text(
bar.get_x() + bar.get_width() / 2.0,
y_val,
round(y_val, 2),
va="bottom",
)
plt.show()

Output of the previous code cell

الجزء الثاني: التوسع إلى أنظمة أكبر

يستند الجزء الثاني من هذا البرنامج التعليمي إلى المثال السابق من خلال التوسع إلى نظام أكبر يضم 50 موقعًا، مما يوضح كيفية تعيين مسائل محاكاة كمية أكثر تعقيدًا على دوائر كمية قابلة للتنفيذ. نستكشف هنا ديناميكيات نموذج XXZ ذي 50 موقعًا، مما يتيح لنا بناء دائرة كمية ضخمة وتحسينها تعكس أحجام الأنظمة الأكثر واقعية.

يُعرَّف هاميلتوني نموذج XXZ ذي 50 موقعًا على النحو الآتي:

H^XXZ=i=1L1Ji,(i+1)(XiX(i+1)+YiY(i+1)+2ZiZ(i+1)),\hat{\mathcal{H}}_{XXZ} = \sum_{i=1}^{L-1} J_{i,(i+1)}\left(X_i X_{(i+1)}+Y_i Y_{(i+1)}+ 2\cdot Z_i Z_{(i+1)} \right) \, ,

حيث Ji,(i+1)J_{i,(i+1)} هو معامل عشوائي يقابل الحافة (i,i+1)(i, i+1)، وL=50L=50 هو عدد المواقع. عرِّف خريطة الاقتران والحواف الخاصة بالهاميلتوني.

L = 50  # L = length of our 1D spin chain

# Generate the edge list for this spin-chain
edge_list = [(i - 1, i) for i in range(1, L)]
# Generate an edge-coloring so we can make hw-efficient circuits
even_edges = edge_list[::2]
odd_edges = edge_list[1::2]

# Instantiate a CouplingMap object
coupling_map = CouplingMap(edge_list)

# Generate random coefficients for our XXZ Hamiltonian
np.random.seed(0)
Js = np.random.rand(L - 1) + 0.5 * np.ones(L - 1)

hamiltonian = SparsePauliOp(Pauli("I" * L))
for i, edge in enumerate(even_edges + odd_edges):
hamiltonian += SparsePauliOp.from_sparse_list(
[
("XX", (edge), Js[i] / 2),
("YY", (edge), Js[i] / 2),
("ZZ", (edge), Js[i]),
],
num_qubits=L,
)

observable = SparsePauliOp.from_sparse_list(
[("ZZ", (L // 2 - 1, L // 2), 1.0)], num_qubits=L
)

# Generate an initial state
L = hamiltonian.num_qubits
initial_state = QuantumCircuit(L)
for i in range(L):
if i % 2:
initial_state.x(i)

الخطوة 1: تعيين المدخلات الكلاسيكية على مسألة كمية

بالنسبة لهذه المسألة الأكبر، نبدأ ببناء الهاميلتوني لنموذج XXZ ذي 50 موقعًا، مُعرِّفين تفاعلات السبين-سبين والحقول المغناطيسية الخارجية عبر جميع المواقع. بعد ذلك، نتبع ثلاث خطوات رئيسية:

  1. توليد دائرة AQC المُحسَّنة: استخدام تحليل Trotterization لتقريب التطور الأولي، ثم ضغط هذا الجزء لتقليل عمق الدائرة.
  2. إنشاء دائرة التطور الزمني المتبقية: التقاط التطور الزمني المتبقي بعد الجزء الأولي.
  3. دمج الدوائر: دمج دائرة AQC المُحسَّنة مع دائرة التطور المتبقية لإنشاء دائرة تطور زمني كاملة جاهزة للتنفيذ.

ولِّد دائرة AQC الهدف (الجزء الأولي).

aqc_evolution_time = 0.2
aqc_target_num_trotter_steps = 32

aqc_target_circuit = initial_state.copy()
aqc_target_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_target_num_trotter_steps),
time=aqc_evolution_time,
),
inplace=True,
)

ولِّد الدائرة اللاحقة (الجزء المتبقي).

subsequent_num_trotter_steps = 3
subsequent_evolution_time = 0.2

subsequent_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=subsequent_num_trotter_steps),
time=subsequent_evolution_time,
)

ولِّد دائرة مقارنة AQC (الجزء الأولي، لكن بنفس عدد خطوات Trotter الخاصة بالدائرة اللاحقة).

# Generate the AQC comparison circuit
aqc_comparison_num_trotter_steps = int(
subsequent_num_trotter_steps
/ subsequent_evolution_time
* aqc_evolution_time
)
print(
"Number of Trotter steps for comparison:",
aqc_comparison_num_trotter_steps,
)

aqc_comparison_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_comparison_num_trotter_steps),
time=aqc_evolution_time,
)
Number of Trotter steps for comparison: 3

ولِّد دائرة المرجع.

evolution_time = 0.4
reps = 200

reference_circuit = initial_state.copy()
reference_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=reps),
time=evolution_time,
),
inplace=True,
)

ولِّد دائرة ansatz والمعاملات الأولية انطلاقًا من دائرة Trotter ذات خطوات أقل.

aqc_ansatz_num_trotter_steps = 1

aqc_good_circuit = initial_state.copy()
aqc_good_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_ansatz_num_trotter_steps),
time=aqc_evolution_time,
),
inplace=True,
)

aqc_ansatz, aqc_initial_parameters = generate_ansatz_from_circuit(
aqc_good_circuit
)
print(f"AQC Comparison circuit: depth {aqc_comparison_circuit.depth()}")
print(f"Target circuit: depth {aqc_target_circuit.depth()}")
print(
f"Ansatz circuit: depth {aqc_ansatz.depth()}, with {len(aqc_initial_parameters)} parameters"
)
AQC Comparison circuit: depth 36
Target circuit: depth 385
Ansatz circuit: depth 7, with 816 parameters

اضبط إعدادات محاكاة شبكة الموترات، ثم أنشئ تمثيل حالة حاصل الضرب المصفوفي (matrix product state) للحالة الهدف من أجل التحسين. بعد ذلك، قيِّم الـ fidelity بين الدائرة الأولية والحالة الهدف لقياس الفرق في خطأ Trotter.

simulator_settings = QuimbSimulator(
quimb.tensor.CircuitMPS, autodiff_backend="jax"
)

# Build the matrix-product representation of the state to be approximated by AQC
aqc_target_mps = tensornetwork_from_circuit(
aqc_target_circuit, simulator_settings
)
print("Target MPS maximum bond dimension:", aqc_target_mps.psi.max_bond())

# Obtains the reference MPS, where we can obtain the exact expectation value by examining the `local_expectation``
reference_mps = tensornetwork_from_circuit(
reference_circuit, simulator_settings
)
reference_expval = reference_mps.local_expectation(

quimb.pauli("Z") & quimb.pauli("Z"), (L // 2 - 1, L // 2)
).real.item()

# Compute the starting fidelity
good_mps = tensornetwork_from_circuit(aqc_good_circuit, simulator_settings)
starting_fidelity = abs(compute_overlap(good_mps, aqc_target_mps)) ** 2
print("Starting fidelity:", starting_fidelity)
Target MPS maximum bond dimension: 5
Starting fidelity: 0.9926466919924161

لتحسين معاملات النموذج الأولي (ansatz)، نُقلّل دالة التكلفة MaximizeStateFidelity باستخدام مُحسِّن L-BFGS من SciPy، مع تعيين معيار التوقف بحيث يتجاوز دقة (fidelity) الدائرة الأولية التي لا تستخدم AQC-Tensor. يضمن هذا أن تمتلك الدائرة المضغوطة خطأ Trotter أقل وعمقاً مخفّضاً في آنٍ واحد.

# Setting values for the optimization
aqc_stopping_fidelity = 1
aqc_max_iterations = 500

stopping_point = 1.0 - aqc_stopping_fidelity
objective = MaximizeStateFidelity(
aqc_target_mps, aqc_ansatz, simulator_settings
)

def callback(intermediate_result: OptimizeResult):
fidelity = 1 - intermediate_result.fun
print(
f"{datetime.datetime.now()} Intermediate result: Fidelity {fidelity:.8f}"
)
if intermediate_result.fun < stopping_point:
# Good enough for now
raise StopIteration

result = minimize(
objective,
aqc_initial_parameters,
method="L-BFGS-B",
jac=True,
options={"maxiter": aqc_max_iterations},
callback=callback,
)
if (
result.status
not in (
0,
1,
99,
)
): # 0 => success; 1 => max iterations reached; 99 => early termination via StopIteration
raise RuntimeError(
f"Optimization failed: {result.message} (status={result.status})"
)

print(f"Done after {result.nit} iterations.")
aqc_final_parameters = result.x
2025-04-14 11:48:28.705807 Intermediate result: Fidelity 0.99795851
2025-04-14 11:48:28.743265 Intermediate result: Fidelity 0.99822826
2025-04-14 11:48:28.776629 Intermediate result: Fidelity 0.99829675
2025-04-14 11:48:28.816153 Intermediate result: Fidelity 0.99832474
2025-04-14 11:48:28.856437 Intermediate result: Fidelity 0.99836131
2025-04-14 11:48:28.896432 Intermediate result: Fidelity 0.99839954
2025-04-14 11:48:28.936670 Intermediate result: Fidelity 0.99846517
2025-04-14 11:48:28.982069 Intermediate result: Fidelity 0.99865029
2025-04-14 11:48:29.026130 Intermediate result: Fidelity 0.99872332
2025-04-14 11:48:29.067426 Intermediate result: Fidelity 0.99892359
2025-04-14 11:48:29.110742 Intermediate result: Fidelity 0.99900640
2025-04-14 11:48:29.161362 Intermediate result: Fidelity 0.99907169
2025-04-14 11:48:29.207933 Intermediate result: Fidelity 0.99911423
2025-04-14 11:48:29.266772 Intermediate result: Fidelity 0.99918716
2025-04-14 11:48:29.331727 Intermediate result: Fidelity 0.99921278
2025-04-14 11:48:29.401694 Intermediate result: Fidelity 0.99924853
2025-04-14 11:48:29.467980 Intermediate result: Fidelity 0.99928797
2025-04-14 11:48:29.533281 Intermediate result: Fidelity 0.99933028
2025-04-14 11:48:29.600833 Intermediate result: Fidelity 0.99935757
2025-04-14 11:48:29.670816 Intermediate result: Fidelity 0.99938140
2025-04-14 11:48:29.736928 Intermediate result: Fidelity 0.99940964
2025-04-14 11:48:29.802931 Intermediate result: Fidelity 0.99944051
2025-04-14 11:48:29.869177 Intermediate result: Fidelity 0.99946828
2025-04-14 11:48:29.940156 Intermediate result: Fidelity 0.99948723
2025-04-14 11:48:30.005751 Intermediate result: Fidelity 0.99951011
2025-04-14 11:48:30.070853 Intermediate result: Fidelity 0.99954718
2025-04-14 11:48:30.139171 Intermediate result: Fidelity 0.99956267
2025-04-14 11:48:30.210506 Intermediate result: Fidelity 0.99958949
2025-04-14 11:48:30.279647 Intermediate result: Fidelity 0.99960498
2025-04-14 11:48:30.348016 Intermediate result: Fidelity 0.99961308
2025-04-14 11:48:30.414311 Intermediate result: Fidelity 0.99962894
2025-04-14 11:48:30.488910 Intermediate result: Fidelity 0.99964121
2025-04-14 11:48:30.561298 Intermediate result: Fidelity 0.99964348
2025-04-14 11:48:30.632214 Intermediate result: Fidelity 0.99964860
2025-04-14 11:48:30.705703 Intermediate result: Fidelity 0.99965695
2025-04-14 11:48:30.775679 Intermediate result: Fidelity 0.99966398
2025-04-14 11:48:30.842629 Intermediate result: Fidelity 0.99967816
2025-04-14 11:48:30.912357 Intermediate result: Fidelity 0.99968293
2025-04-14 11:48:30.979420 Intermediate result: Fidelity 0.99968936
2025-04-14 11:48:31.049196 Intermediate result: Fidelity 0.99969223
2025-04-14 11:48:31.125391 Intermediate result: Fidelity 0.99970009
2025-04-14 11:48:31.201256 Intermediate result: Fidelity 0.99970724
2025-04-14 11:48:31.272424 Intermediate result: Fidelity 0.99970987
2025-04-14 11:48:31.338907 Intermediate result: Fidelity 0.99971237
2025-04-14 11:48:31.404800 Intermediate result: Fidelity 0.99971916
2025-04-14 11:48:31.475226 Intermediate result: Fidelity 0.99971940
2025-04-14 11:48:31.547746 Intermediate result: Fidelity 0.99972465
2025-04-14 11:48:31.622827 Intermediate result: Fidelity 0.99972763
2025-04-14 11:48:31.819516 Intermediate result: Fidelity 0.99972894
2025-04-14 11:48:33.444538 Intermediate result: Fidelity 0.99972894
Done after 50 iterations.
parameters = [float(param) for param in aqc_final_parameters]

أنشئ الدائرة النهائية للتحويل البرمجي (transpilation) عن طريق تجميع النموذج الأولي (ansatz) المُحسَّن مع بقية دائرة التطور الزمني.

aqc_final_circuit = aqc_ansatz.assign_parameters(aqc_final_parameters)
aqc_final_circuit.compose(subsequent_circuit, inplace=True)
aqc_comparison_circuit.compose(subsequent_circuit, inplace=True)

الخطوة 2: تحسين المسألة لتنفيذها على العتاد الكمومي

اختر الواجهة الخلفية (backend).

service = QiskitRuntimeService()
backend = service.least_busy(min_num_qubits=127)
print(backend)

حوِّل الدائرة المكتملة برمجياً على العتاد الهدف تمهيداً لتنفيذها. يمكن بعد ذلك إرسال دائرة ISA الناتجة لتنفيذها على الواجهة الخلفية.

pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=3
)
isa_circuit = pass_manager.run(aqc_final_circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
print("Observable info:", isa_observable)
print("Circuit depth:", isa_circuit.depth())
isa_circuit.draw("mpl", fold=-1, idle_wires=False)
Observable info: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j])
Circuit depth: 122

Output of the previous code cell

isa_comparison_circuit = pass_manager.run(aqc_comparison_circuit)
isa_comparison_observable = observable.apply_layout(
isa_comparison_circuit.layout
)
print("Observable info:", isa_comparison_observable)
print("Circuit depth:", isa_comparison_circuit.depth())
isa_comparison_circuit.draw("mpl", fold=-1, idle_wires=False)
Observable info: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j])
Circuit depth: 158

Output of the previous code cell

الخطوة 3: التنفيذ باستخدام Qiskit primitives

في هذه الخطوة، نُشغِّل الدائرة المُحوَّلة برمجياً على العتاد الكمومي (أو واجهة خلفية محاكاة) باستخدام EstimatorV2 من qiskit_ibm_runtime لقياس المُراقَب (observable) المحدد. ستوفر نتيجة المهمة رؤى قيّمة حول أداء الدائرة على العتاد الهدف.

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

للاطلاع على القائمة الكاملة لخيارات التنفيذ المتاحة، راجع توثيق qiskit-ibm-runtime.

twirling_options = {
"enable_gates": True,
"enable_measure": True,
"num_randomizations": 300,
"shots_per_randomization": 100,
"strategy": "active",
}

zne_options = {
"amplifier": "gate_folding",
"noise_factors": [1, 2, 3],
"extrapolated_noise_factors": list(np.linspace(0, 3, 31)),
"extrapolator": ["exponential", "linear", "fallback"],
}

meas_learning_options = {
"num_randomizations": 512,
"shots_per_randomization": 512,
}

resilience_options = {
"measure_mitigation": True,
"zne_mitigation": True,
"zne": zne_options,
"measure_noise_learning": meas_learning_options,
}

estimator_options = {
"resilience": resilience_options,
"twirling": twirling_options,
}

estimator = Estimator(backend, options=estimator_options)
job = estimator.run([(isa_circuit, isa_observable)])
print("Job ID:", job.job_id())
job.result()
Job ID: czyjx6crxz8g008f63r0
PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(), dtype=float64>), stds=np.ndarray(<shape=(), dtype=float64>), evs_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), stds_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), ensemble_stds_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), evs_extrapolated=np.ndarray(<shape=(3, 31), dtype=float64>), stds_extrapolated=np.ndarray(<shape=(3, 31), dtype=float64>)), metadata={'shots': 30000, 'target_precision': 0.005773502691896258, 'circuit_metadata': {}, 'resilience': {'zne': {'extrapolator': 'exponential'}}, 'num_randomizations': 300})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': True, 'enable_measure': True, 'num_randomizations': 300, 'shots_per_randomization': 100, 'interleave_randomizations': True, 'strategy': 'active'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': True, 'pec_mitigation': False, 'zne': {'noise_factors': [1, 2, 3], 'extrapolator': ['exponential', 'linear', 'fallback'], 'extrapolated_noise_factors': [0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9, 1, 1.1, 1.2000000000000002, 1.3, 1.4000000000000001, 1.5, 1.6, 1.7000000000000002, 1.8, 1.9000000000000001, 2, 2.1, 2.2, 2.3000000000000003, 2.4000000000000004, 2.5, 2.6, 2.7, 2.8000000000000003, 2.9000000000000004, 3]}}, 'version': 2})
job_comparison = estimator.run([(isa_comparison_circuit, isa_observable)])
print("Job Comparison ID:", job.job_id())
job_comparison.result()
Job Comparison ID: czyjx6crxz8g008f63r0
PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(), dtype=float64>), stds=np.ndarray(<shape=(), dtype=float64>), evs_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), stds_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), ensemble_stds_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), evs_extrapolated=np.ndarray(<shape=(3, 31), dtype=float64>), stds_extrapolated=np.ndarray(<shape=(3, 31), dtype=float64>)), metadata={'shots': 30000, 'target_precision': 0.005773502691896258, 'circuit_metadata': {}, 'resilience': {'zne': {'extrapolator': 'exponential'}}, 'num_randomizations': 300})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': True, 'enable_measure': True, 'num_randomizations': 300, 'shots_per_randomization': 100, 'interleave_randomizations': True, 'strategy': 'active'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': True, 'pec_mitigation': False, 'zne': {'noise_factors': [1, 2, 3], 'extrapolator': ['exponential', 'linear', 'fallback'], 'extrapolated_noise_factors': [0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9, 1, 1.1, 1.2000000000000002, 1.3, 1.4000000000000001, 1.5, 1.6, 1.7000000000000002, 1.8, 1.9000000000000001, 2, 2.1, 2.2, 2.3000000000000003, 2.4000000000000004, 2.5, 2.6, 2.7, 2.8000000000000003, 2.9000000000000004, 3]}}, 'version': 2})

الخطوة 4: المعالجة اللاحقة وإعادة النتيجة بالتنسيق الكلاسيكي المطلوب

هنا لا حاجة إلى إعادة بناء البيانات كما في السابق؛ إذ يمكننا الوصول مباشرةً إلى قيمة التوقع من مخرجات التنفيذ لفحص النتيجة.

# AQC results
hw_results = job.result()
hw_results_dicts = [pub_result.data.__dict__ for pub_result in hw_results]
hw_expvals = [
pub_result_data["evs"].tolist() for pub_result_data in hw_results_dicts
]
aqc_expval = hw_expvals[0]

# AQC comparison results
hw_comparison_results = job_comparison.result()
hw_comparison_results_dicts = [
pub_result.data.__dict__ for pub_result in hw_comparison_results
]
hw_comparison_expvals = [
pub_result_data["evs"].tolist()
for pub_result_data in hw_comparison_results_dicts
]
aqc_compare_expval = hw_comparison_expvals[0]

print(f"Exact: \t{reference_expval:.4f}")
print(
f"AQC: \t{aqc_expval:.4f}, |∆| = {np.abs(reference_expval- aqc_expval):.4f}"
)
print(
f"AQC Comparison:\t{aqc_compare_expval:.4f}, |∆| = {np.abs(reference_expval- aqc_compare_expval):.4f}"
)
Exact:         	-0.5888
AQC: -0.4809, |∆| = 0.1078
AQC Comparison: 1.1764, |∆| = 1.7652

ارسم نتائج دوائر AQC والمقارنة والدقيقة لنموذج XXZ ذي الـ 50 موقعاً.

labels = ["AQC Result", "AQC Comparison Result"]
values = [abs(aqc_expval), abs(aqc_compare_expval)]

plt.figure(figsize=(10, 6))
bars = plt.bar(labels, values, color=["tab:blue", "tab:purple"])
plt.axhline(
y=abs(reference_expval), color="red", linestyle="--", label="Exact Result"
)
plt.xlabel("Results")
plt.ylabel("Absolute Expected Value")
plt.title("AQC Result vs AQC Comparison Result (Absolute Values)")
plt.legend()
for bar in bars:
y_val = bar.get_height()
plt.text(
bar.get_x() + bar.get_width() / 2.0,
y_val,
round(y_val, 2),
va="bottom",
)

plt.show()

Output of the previous code cell

الخلاصة

أوضح هذا البرنامج التعليمي كيفية استخدام التجميع الكمومي التقريبي مع شبكات الموترات (AQC-Tensor) لضغط الدوائر وتحسينها من أجل محاكاة الديناميكيات الكمومية على نطاق واسع. باستخدام نموذجَي Heisenberg الصغير والكبير، طبّقنا AQC-Tensor لتقليل عمق الدائرة اللازم لتطور Trotter الزمني. وبتوليد نموذج أولي مُعلمَن (parametrized ansatz) من دائرة Trotter مبسّطة وتحسينه باستخدام تقنية حالة ضرب المصفوفة (MPS)، حققنا تقريباً منخفض العمق للتطور الهدف يتسم بالدقة والكفاءة معاً.

يُبرز سير العمل هنا المزايا الرئيسية لـ AQC-Tensor في تطوير محاكاة كمومية قابلة للتوسع:

  • ضغط الدائرة بصورة ملحوظة: أقدمت AQC-Tensor على تقليل عمق الدائرة اللازم لتطور زمني معقد، مما عزّز جدواها على الأجهزة الحالية.
  • تحسين فعّال: وفّر نهج MPS إطاراً متيناً لتحسين المعاملات، إذ يوازن بين الدقة (fidelity) والكفاءة الحسابية.
  • جاهزية للعتاد: ضمن التحويل البرمجي للدائرة النهائية المُحسَّنة استيفاءها لقيود العتاد الكمومي الهدف.

مع ظهور أجهزة كمومية أكبر وخوارزميات أكثر تطوراً، ستغدو تقنيات من قبيل AQC-Tensor ضرورةً لا غنى عنها لتشغيل محاكاة كمومية معقدة على العتاد القريب المدى، مُجسِّدةً تقدماً واعداً في إدارة العمق والدقة في التطبيقات الكمومية القابلة للتوسع.

استبيان البرنامج التعليمي

يُرجى إجراء هذا الاستبيان القصير لتقديم ملاحظاتك حول هذا البرنامج التعليمي. ستُسهم رؤيتك في مساعدتنا على تحسين محتوياتنا وتجربة المستخدم.

Link to survey