توليد دوائر SqDRIFT
في التشخيص القطري الكمومي المعتمد على العيّنات (SQD)، عليك اختيار أنساتز (ansatz) تُعاين منه السلاسل الثنائية. أما متغيّر SqDRIFT فيستخدم بدلًا من ذلك مجموعة من دوائر التطور الزمني المبنية مباشرة من الهاميلتوني المستهدف. يتحقق ذلك بأخذ عيّنات جزئية لمؤثرات تطور زمني أصغر من الهاميلتوني بناءً على معاملاته، وهو ما يُعرف بطريقة تروترة qDRIFT.
يوضح دليل البدء هذا كيفية توليد مجموعة من هذه الدوائر العشوائية.
1. إعداد الهاميلتوني
لأغراض هذا الدليل، حمّل هاميلتوني البنية الإلكترونية لجزيء N2 من ملف FCIDUMP. توجد وسائل أخرى لبناء FermionOperator. احرص على مراجعة توثيقه، وكذلك qiskit_fermions.operators.library.
- PYTHON
- C
>>> from qiskit_fermions.operators.library import FCIDump
>>> from qiskit_fermions.operators import FermionOperator
>>>
>>> fcidump = FCIDump.from_file("docs/guides/n2.fcidump")
>>> num_modes = 2 * fcidump.norb
>>> hamil = FermionOperator.from_fcidump(fcidump)
#include <qiskit_fermions.h>
QfFCIDump* fcidump = NULL;
qf_fcidump_from_file("docs/guides/n2.fcidump", &fcidump);
QfFermionOperator* hamil = qf_ferm_op_from_fcidump(fcidump);
uint32_t num_modes = 2 * qf_fcidump_norb(fcidump);
2. تجميع حدود الهاميلتوني
استفد من التناظرات الكثيرة الموجودة في هاميلتوني البنية الإلكترونية عن طريق تجميع الحدود المترابطة التي لها معاملات متطابقة. يغيّر هذا الإجراء توزيع معاملات المؤثر الذي يعاين منه بروتوكول qDRIFT، لكنه لا يؤثر على ضمانات تقاربه. والأهم أن تجميع الحدود المترابطة بالتناظر يؤدي إلى إلغاء مفيد لحدود باولي، ما ينتج عمقًا أقصر للدائرة عمومًا عند تطوير الحالة زمنيًا تحت تأثيرها.
توفّر الوحدة qiskit_fermions.operators.terms.grouping دوال مساعدة لتجميع حدود المؤثر. يُشرح ذلك بمزيد من التفصيل في هذا الدليل.
يفترض تنفيذ group_terms_by_electronic_structure() أن حدود الهاميلتوني مرتّبة ترتيبًا طبيعيًا (normal-ordered)!
- PYTHON
- C
>>> from qiskit_fermions.operators.terms.grouping import group_terms_by_electronic_structure
>>> from qiskit_fermions.operators.terms.ordering import canonical_order
>>>
>>> canon = canonical_order(hamil.normal_ordered().simplify(atol=1e-16))
>>> exit_code = group_terms_by_electronic_structure(canon, num_modes, two_body_physicist_order=False)
>>> assert exit_code is None
>>> print(canon.groups) # the groups attribute now contains some list of group indices
[0, ...]
QfFermionOperator* normal = qf_ferm_op_normal_ordered(hamil, NULL);
QfFermionOperator* simplified = qf_ferm_op_simplify(normal, 1e-16);
QfFermionOperator* canon = qf_ferm_op_canonical_order(simplified);
QfExitCode exit = qf_ferm_op_group_terms_by_electronic_structure(canon, num_modes, false);
assert(exit == QfExitCode_Success);
qf_ferm_op_free(normal);
qf_ferm_op_free(simplified);
يحتوي هاميلتوني البنية الإلكترونية الكامل على حدود معينة لا يؤثر إدراجها في دائرة التطور الزمني على السلاسل الثنائية الملحوظة، وبالتالي لا ينتج عنه سوى زيادة في كلفة المعاينة. لذلك يُوصى بتصفية هذه الحدود من الهاميلتوني في هذه المرحلة، قبل بناء بوابة Evolution في الخطوة التالية.
الحدود التي تنطبق عليها هذه المواصفات هي القطرية في أساس أعداد الإشغال، أي حواصل ضرب مؤثرات العدد (). يشمل ذلك الإزاحة الثابتة للطاقة، التي لا يُدخل تطورها الزمني سوى طور عام في الدائرة، ومؤثرات العدد المفردة التي يكافئ تطورها الزمني دورانات Z على كيوبت واحد، وكذلك حواصل الضرب الأعلى رتبة مثل . لا يستطيع أي منها تغيير إشغال نمط ما، لذا فإن حذفها لا يكلّف أي محتوى تهييجي. وعلى عكس الرفض لكل سحبة في الخطوة 5، تحدث هذه التصفية مرة واحدة، على الهاميلتوني نفسه، لذا يُعاد بناء توزيع المعاملات الذي يعاين منه البروتوكول بصورة متسقة من المؤثر المُصفّى بدلًا من إعادة تطبيعه في منتصف السحب.
تزيل الدالة filter_diagonal_terms() هذه الحدود من المؤثر في مكانه:
- PYTHON
- C
>>> from qiskit_fermions.operators.terms.filtering import filter_diagonal_terms
>>>
>>> filter_diagonal_terms(canon)
qf_ferm_op_filter_diagonal_terms(canon);
التصفية هنا، مرة واحدة، أرخص بكثير من التصفية المتكررة. تعمل FermionicQDrift مرة واحدة لكل دائرة محوَّلة، لذا فإن تصفية الهاميلتوني في البداية، بدلًا من تصفيته عند كل استدعاء، تتجنب إعادة العمل لكل دائرة تُولَّد منه.
ولنفس السبب، من المجدي ترتيب حدود الهاميلتوني حسب فهرس المجموعة هنا. فهارس المجموعات هي وسم لكل حد ولا تدل على مكان وجود تلك الحدود، لذا فإن حدود المجموعة الواحدة تكون عمومًا متناثرة في أنحاء المؤثر: فالتجميع المطبَّق أعلاه يسند الفهارس بحسب أول مصادفة، مما يترك الحد الثاني للمجموعة بعيدًا عن حدّها الأول.
تعاين FermionicQDrift مجموعات كاملة وتبحث عن كل مجموعة مسحوبة باستخدام split_out_groups(). إيجاد مجموعة بين حدود متناثرة يعني مسحها جميعًا، لذا فإن كلفة البحث نفسها سواء سُحبت مجموعة واحدة أو أربعون. تجمع group_order() كل مجموعة في مقطع متصل واحد، وبعدها يصبح البحث بحثًا ثنائيًا على حدود المجموعات وكلفته تساوي كلفة المجموعات المسحوبة لا كلفة الحدود المحتفَظ بها. تصبح كلفة البحث نفسه أقل بكثير؛ أما مقدار ما يوفره ذلك في عملية تحويل معينة فيعتمد على ما يفعله الخط الباقي، إذ يهيمن التعيين والتركيب بعد زوال المسح:
- PYTHON
- C
>>> from qiskit_fermions.operators.terms.ordering import group_order
>>>
>>> canon = group_order(canon)
>>> print(canon.groups[:4])
[0, 0, ...]
QfFermionOperator* grouped = qf_ferm_op_group_order(canon);
يعيد هذا ترتيب الحدود دون تغيير قيمة المؤثر، ويلزم حدوثه مرة واحدة فقط بغض النظر عن عدد التوزيعات العشوائية للدائرة المسحوبة منه.
3. تحضير دائرة التطور الزمني
حضّر دائرة التطور الزمني للهاميلتوني والدائرة الأساسية التي تُسحب منها العيّنات. تحتوي qiskit_fermions.circuit.library على كل المكوّنات اللازمة لذلك، بما يتوافق مع اصطلاحات Qiskit.
بروتوكول qDRIFT هو طريقة التركيب FermionicQDrift، وتحمله بوابة Evolution على أنه synthesis الخاص بها. تقدّم الطريقة بعض المعاملات التي يمكنك استخدامها لتخصيص الدوائر المراد توليدها:
-
طول كل دائرة بدلالة مجموعات التهييج:
num_groups -
اختياريًا، بذرة لمولّد الأعداد العشوائية:
rng -
اختياريًا، أوزان معاينة محسوبة مسبقًا، إذا أصبح التحويل نفسه عنق زجاجة:
weights(راجع (اختياري) حساب أوزان المعاينة مسبقًا أدناه)
ولأن البوابة هي التي تحمل الطريقة، فإن كل ما يصف مؤثر البوابة، مثل تلك الأوزان، يقع بجوار ذلك المؤثر مباشرة.
- PYTHON
- C
>>> from qiskit_fermions.circuit import FermionicCircuit
>>> from qiskit_fermions.circuit.library import Evolution
>>> from qiskit_fermions.circuit.library.synthesis import FermionicQDrift
>>>
>>> time = 1.0 # you can choose a desired scaling factor here
>>> num_groups = 10
>>> qdrift = FermionicQDrift(num_groups, rng=19)
>>> evo_gate = Evolution(num_modes, canon, time, synthesis=qdrift)
>>>
>>> circ = FermionicCircuit(num_modes)
>>> circ.append(evo_gate, circ.modes)
// WARNING: Qiskit's C API does not yet allow us to implement circuits
// with custom gate definitions.
لا يهيّئ هذا المثال أنماط الفرميونات بجسيمات، ولا يقيس حالتها النهائية.
4. تحويل الدائرة
تتكامل الوحدة qiskit_fermions.transpiler مباشرة مع خط التحويل في Qiskit، مما يتيح تحويل FermionicCircuit المبنية أعلاه مباشرة إلى QuantumCircuit.
استخدم تعيين فرميون إلى كيوبت jordan_wigner() لتحويل الهاميلتوني المعبَّر عنه بالفرميونات إلى تعبير بسلاسل باولي بدلًا من ذلك. يمكن إجراء ذلك مباشرة ضمن عملية التحويل باستخدام تمريرة التحويل F2QSynthesis. استخدم generate_preset_jw_pass_manager() لبناء FermionicStagedPassManager، الذي يضمن استخدام ترميز جوردان-فيغنر بشكل متسق لجميع تعليمات الدائرة.
والأهم، أضف تمريرة Decompose الخاصة بـ Qiskit إلى مرحلة optimization من خط التحويل. فهي تفكّك بوابة Evolution باستخدام طريقة التركيب التي تحملها البوابة، وهي طريقة qDRIFT المحضّرة أعلاه. وهذا يضمن ألا تستخدم الدائرة التطور الزمني للهاميلتوني بأكمله، الذي سيتجاوز عمقه قدرات عتاد الحوسبة الكمومية المتاح حاليًا.
بدلًا من ذلك، تأخذ عيّنة جزئية من عدد ثابت من groups لحدود الهاميلتوني لكل دائرة، في كل مرة تُحوَّل فيها الدائرة. وبهذا يمكنك توليد عدة توزيعات عشوائية للدائرة كما تتطلب خوارزمية SqDRIFT عن طريق تشغيل خط التحويل مرارًا. عدد الدوائر المراد توليدها هو num_sqdrift_randomizations.
- PYTHON
- C
>>> from qiskit.transpiler.passes import Decompose
>>> from qiskit_fermions.transpiler import FermionicPassManager
>>> from qiskit_fermions.transpiler.presets import generate_preset_jw_pass_manager
>>>
>>> pm = generate_preset_jw_pass_manager()
>>> pm.optimization = FermionicPassManager([Decompose(["Evolution"])])
>>>
>>> num_sqdrift_randomizations = 10
>>> sqdrift_circuits = [
... pm.run(circ) for _ in range(num_sqdrift_randomizations)
... ]
// WARNING: Qiskit's C API does not yet allow us to implement circuits
// with custom gate definitions, which we therefore also cannot transpile
// via this API.
تثبّت الأمثلة السابقة بذرة rng الخاصة بطريقة التركيب FermionicQDrift، بحيث يمكن إعادة إنتاج تسلسل التوزيعات العشوائية.
يقبل الخط نفسه طريقة qDRIFT في موضعين آخرين. تأخذ تمريرة F2FSynthesis طريقة واحدة لـكل بوابة Evolution بالاسم، F2FSynthesis({"Evolution": FermionicQDrift(num_groups)})، وهذا كل ما يلزم حين لا تحمل أي بوابة شيئًا خاصًا بمؤثرها. وتستخدم الخطوة التالية الموضع الثالث، وهو تمريرة QDriftTrotterization.
5. تصفية التهييجات المسحوبة حسب الإشغال
إلى جانب الحدود القطرية التي صُفّيت في الخطوة السابقة، قد يعمل تهييج مسحوب بالكامل ضمن مجموعة أنماط إشغالها ثابت أصلًا (كلها مشغولة أو كلها غير مشغولة)، حيث لا يستطيع نقل جسيم من جانب إلى آخر. لا يخبرك مثل هذا التهييج بشيء عن السلاسل الثنائية المُعاينة، لذا فإن الخانة المصروفة عليه خانة مهدورة. تقوم تمريرة QDriftTrotterization، التي تغلّف طريقة FermionicQDrift نفسها، برفض هذه التهييجات عند سحبها وسحب بديل عنها، بحيث تحمل كل خانة من خانات num_groups في الدائرة الناتجة تهييجًا يربط بين الجانبين.
هذه التصفية ليست مجانية، ولهذا السبب هي تمريرة منفصلة. ما تحافظ عليه هو ميزانية خانات num_groups المُعاينة، لا عمق الدائرة: فبما أن هذه الميزانية ثابتة، يُستبدل التهييج المرفوض ولا يُحذف، ويفسح تهييج رخيص المجال لتهييج رابط كلفة تركيبه أعلى. توقّع أن تكون الدائرة المصفّاة أعمق من غير المصفّاة المسحوبة من البذرة نفسها.
تؤدي إعادة تطبيع السحب على التهييجات المقبولة إلى تغيير التوزيع الذي يعاين منه بروتوكول qDRIFT، وعلى عكس التجميع في الخطوة 2 فإنها تكسر أيضًا ضمان تقارب البروتوكول: لم يعد حاصل الضرب المُعاين يساوي في المتوسط التطور تحت الهاميلتوني الذي مرّرته. ينتهي الأمر بكل تهييج محتفَظ به موزونًا بمقلوب احتمال القبول، ولا تسهم التهييجات المرفوضة بشيء، ولا يُعدَّل لا مجموع مقدار المعاملات ولا زمن التطور لكل بوابة للتعويض.
قاعدة القبول أيضًا تقريب مفرط. فهي تقارن دعائم الأنماط فقط، لذا لا تحمل أي معلومة عن السعة، ولا تنمو المجموعات المتتبَّعة إلا: فالتهييج المحصور في المجموعة المشغولة خامل فعلًا على حالة جداء، لكن المنطق نفسه يضعف في السحبات اللاحقة، بعد أن تكون التهييجات المقبولة سابقًا قد تركت الحالة متشابكة ووسمت معظم الأنماط بأنها «غير مؤكدة».
استخدم التصفية في سير عمل معاينة السلاسل الثنائية الذي يصفه هذا الدليل، حيث تغذي الدوائر المعالجة اللاحقة لـ SQD وتهمّ السلاسل الثنائية المُعاينة وحدها. لا تستخدمها إذا كنت تنوي تقدير قيمة توقعية من هذه الدوائر، أو الاعتماد على حد خطأ qDRIFT بأي طريقة أخرى: فهذه النتائج تحمل انحيازًا لا تصححه التمريرة.
تحتاج هذه التصفية إلى معرفة أيّ الأنماط تبدأ مشغولة. لذلك فهي تتطلب بوابة InitializeModes تسبق بوابات Evolution في الدائرة؛ وInitializeModes.from_hartree_fock() طريقة ملائمة لبناء واحدة. أضف واحدة هنا للمرجع هارتري-فوك لجزيء N2 (سبعة إلكترونات ألفا وسبعة بيتا في 14 مدارًا فراغيًا) وقارن التهييجات المُعاينة مع التصفية وبدونها:
- PYTHON
- C
>>> from qiskit_fermions.circuit.library import InitializeModes
>>> from qiskit_fermions.transpiler.passes import F2FSynthesis, QDriftTrotterization
>>>
>>> init = InitializeModes.from_hartree_fock(fcidump.norb, (7, 7))
>>>
>>> hf_circ = FermionicCircuit(num_modes)
>>> hf_circ.append(init, hf_circ.modes)
>>> hf_circ.append(Evolution(num_modes, canon, time), hf_circ.modes)
>>>
>>> num_groups = 5
>>> qdrift_unfiltered = F2FSynthesis(
... {"Evolution": FermionicQDrift(num_groups, rng=3480)}
... )
>>> qdrift_trivial = QDriftTrotterization(
... FermionicQDrift(num_groups, rng=3480)
... )
>>>
>>> for instruction in FermionicPassManager(qdrift_unfiltered).run(hf_circ)._inner.data:
... if instruction.operation.name == "Evolution":
... print(sorted(instruction.operation.operator.get_support()))
[2, 4]
[41, 45, 52]
[15, 45, 55]
[41, 52, 53]
[10, 16, 37, 38]
>>>
>>> for instruction in FermionicPassManager(qdrift_trivial).run(hf_circ)._inner.data:
... if instruction.operation.name == "Evolution":
... print(sorted(instruction.operation.operator.get_support()))
[0, 1, 6, 7]
[0, 1, 28, 29]
[4, 13, 55]
[13, 20, 40, 41]
[0, 1, 28, 29]
// WARNING: Qiskit's C API does not yet allow us to implement circuits
// with custom gate definitions, which we therefore also cannot transpile
// via this API.
لا يمس أي من التهييجات المسحوبة بدون تصفية المجموعة المشغولة (0-6 و28-34) على الإطلاق، لذا لا يستطيع أي منها نقل جسيم بين نمط مشغول وآخر غير مشغول. أما مع التصفية، فتُرفض الخمسة جميعها وتُستبدل بتهييجات تربط فعلًا نمطًا مشغولًا بنمط غير مشغول. فعلى سبيل المثال، التهييج الأول المقبول [0, 1, 6, 7] ينقل جسيمًا بين الأنماط المشغولة 0 و1 و6 والنمط غير المشغول 7.
ما إن يُقبل تهييج، يصبح كل نمط في دعامته «غير مؤكد» ومؤهلًا بالتالي للعب أي من الدورين في العيّنات اللاحقة، فتستمر مجموعتا الأنماط المشغولة وغير المشغولة في النمو كلما قُبلت تهييجات أكثر. وهذا ما يجعل التهييج الثاني [0, 1, 28, 29] مقبولًا. فأنماطه الأربعة كلها من الأنماط المشغولة أصلًا، لذا لا يرتبط بأي نمط غير مشغول أصلًا. ولا يُقبل إلا لأن النمطين 0 و1 أصبحا غير مؤكدين (وبالتالي مؤهلين ليكونا جانب «غير المشغول» في الربط) بعد أن مسّهما التهييج الأول.
هذا النمو هو ما يجعل القاعدة تقريبًا مفرطًا لا اختبارًا: فكلما أصبحت أنماط أكثر غير مؤكدة، قلّ عدد السحبات المرفوضة، ويكون تأثير التصفية على توزيع المعاينة أكبر ما يمكن في السحبات الأولى.
للحكم على مدى تحريك التصفية للتوزيع، اقرأ أعداد الرفض التي تسجلها التمريرة في بيانات الدائرة الوصفية. يُخزَّن إدخال واحد لكل بوابة Evolution مُصفّاة، وتقدّر نسبة المقبول إلى الإجمالي احتمال القبول، وهو العامل الذي رُجّحت به التهييجات المحتفَظ بها:
- PYTHON
- C
>>> # a freshly seeded pass, so the counts do not depend on earlier draws
>>> qdrift_counted = QDriftTrotterization(
... FermionicQDrift(num_groups, rng=3480)
... )
>>> filtered = FermionicPassManager(qdrift_counted).run(hf_circ)
>>> discarded = filtered.metadata.get("filter_trivial.discarded")
>>> emitted = filtered.metadata.get("filter_trivial.emitted")
>>> print(f"{emitted[0]} kept, {discarded[0]} discarded")
5 kept, 7 discarded
>>> print(f"acceptance probability: {emitted[0] / (emitted[0] + discarded[0]):.2f}")
acceptance probability: 0.42
// WARNING: Qiskit's C API does not yet allow us to implement circuits
// with custom gate definitions, which we therefore also cannot transpile
// via this API.
تحتاج التصفية إلى معلومات إشغال تصفّي بالنسبة إليها. تهيّئ بوابة PrepareSlaterDeterminant هذه المعلومات تمامًا كما تفعل InitializeModes، وأي OrbitalRotation تسم كل نمط تعمل عليه بأنه «غير مؤكد». وبدون أي منها، أو حين تقع كل الأنماط المهيّأة على جانب واحد (كل الأنماط مشغولة أو كلها غير مشغولة)، تُصدر التمريرة UserWarning وتترك المعاينة دون تصفية لبوابة Evolution تلك. أما على هاميلتوني لم تعد حدوده المتبقية قادرة على الربط بين المجموعتين المتتبَّعتين، فترفع التمريرة بدلًا من ذلك RuntimeError بعد QDriftTrotterization.MAX_SAMPLE_RETRIES من حالات الرفض المتتالية.
(اختياري) حساب أوزان المعاينة مسبقًا
لا تلجأ إلى هذا إلا إذا كان التحويل نفسه عنق زجاجة. فهو لا يغيّر شيئًا في الدوائر التي تحصل عليها.
تسحب FermionicQDrift كل مجموعة باحتمال يتناسب مع مقدار معاملها، وتشتق تلك المقادير من الهاميلتوني عند كل استدعاء: قيمة واحدة لكل حد في الهاميلتوني، تُختزل إلى قيمة واحدة لكل مجموعة. ولأن الطريقة عديمة الحالة فهي تكرر هذا الاختزال لكل توزيع عشوائي، رغم أن النتيجة متطابقة في كل مرة. يتيح لك المعامل weights حسابها مرة واحدة باستخدام group_coeff_means() وتمرير النتيجة، مما ينقل الكلفة خارج الحلقة ويُبقي الطريقة خالية من أي حالة مخزّنة مؤقتًا تعتمد على الهاميلتوني.
هذا هو نظير ترتيب الحدود في الخطوة 2. ذلك الترتيب يُخرج بحث المجموعة من الحلقة، وهذا يُخرج اختزال الأوزان، ولا يكون أحدهما ملحوظًا إلا بقدر غياب الآخر، لذا من المجدي تطبيقهما معًا. وبما أن canon أُعيد ترتيبها أعلاه، فإن الاثنين يتركّبان مباشرة.
تصف الأوزان مجموعات مؤثر بعينه، لذا فهي تنتمي إلى بوابة Evolution الخاصة بذلك المؤثر: ابنِ الطريقة لتلك البوابة ومرّرها على أنها synthesis الخاص بالبوابة، تمامًا كما في الخطوة 3.
- PYTHON
- C
>>> from qiskit_fermions.operators.terms.grouping import group_coeff_means
>>>
>>> weights = group_coeff_means(canon) # `canon` is group-ordered, from step 2
>>> qdrift_weighted = FermionicQDrift(10, rng=19, weights=weights)
>>> weighted_gate = Evolution(num_modes, canon, time, synthesis=qdrift_weighted)
>>>
>>> weighted_circ = FermionicCircuit(num_modes)
>>> weighted_circ.append(weighted_gate, weighted_circ.modes)
>>>
>>> weighted_circuits = [
... pm.run(weighted_circ) for _ in range(num_sqdrift_randomizations)
... ]
>>> len(weighted_circuits)
10
// WARNING: Qiskit's C API does not yet allow us to implement circuits
// with custom gate definitions, which we therefore also cannot transpile
// via this API.
وبما أن هذه هي الأوزان نفسها التي كانت التمريرة ستحسبها، فالدوائر هي نفسها كما كانت من قبل؛ وإنما اختفى الاختزال المتكرر فقط.
يُتوقَّع إدخال واحد لكل مجموعة، لا لكل حد، متى كان المؤثر يحمل مجموعات، لأن المؤثر المجمَّع يُعاين مجموعةً مجموعة. ويجب أن تكون الإدخالات غير سالبة: فالوزن مقدار، لأن بروتوكول qDRIFT يفكّك بقيم موجبة، وتنتمي إشارة المعامل إلى ، حيث تقرؤها التمريرة من المؤثر مباشرة.
الأوزان مقادير مطلقة، لا تفضيلات نسبية. ومجموعها يحدد أيضًا زمن التطور، لذا فضرب كل إدخال في يعاين بالطريقة نفسها لكنه يطوّر لمدة بدلًا من . وبالتالي فإن تمرير أي شيء غير مقادير معاملات الهاميلتوني نفسها يغيّر التطور الذي تقرّبه المجموعة، ولهذا فهذه أداة للأداء لا وسيلة لضبط المعاينة.
ولأن الأوزان تنتمي إلى البوابة التي تصف مؤثرها، فقد تحتوي الدائرة على عدة بوابات Evolution، لكل منها أوزان مؤثرها الخاص. وتحترمها تمريرة QDriftTrotterization في الخطوة 5 بالطريقة نفسها: فهي تسحب بوابة بواسطة FermionicQDrift التي تحملها البوابة، وترجع إلى التي بُنيت بها للبوابات التي لا تحمل أيًّا منها. أما نوع آخر من طرق التركيب على بوابة فليس بيانًا عن qDRIFT، لذا تتجاهله التمريرة. وتمرير الطريقة إلى تمريرة باسم البوابة بدلًا من ذلك، كما في F2FSynthesis({"Evolution": ...})، يطبّق طريقة واحدة على كل بوابة Evolution، لذا فالأوزان الممرَّرة هناك ذات معنى فقط لدائرة فيها بوابة واحدة. ويُتحقَّق من طول الأوزان مقابل كل بوابة تُطبَّق عليها.
(اختياري) تحسين فهرسة أنماط الفرميونات
يمكنك إضافة خطوة تحسين إضافية إلى خط التحويل تقلّل مسافة امتدادات تهييجات الفرميونات عن طريق إعادة تسمية فهارس أنماط الفرميونات. أُدخل هذا التحسين في ورقة SqDRIFT وتنفّذه build_excitation_span_minimization_model(). ويمكن إدراجه بسهولة في خط المحوِّل باستخدام تمريرة RelabelModes:
- PYTHON
- C
>>> from pyomo.environ import SolverFactory
>>> from qiskit_fermions.transpiler.passes import RelabelModes
>>>
>>> solver = SolverFactory("appsi_highs")
>>> solver.options["time_limit"] = 10
>>>
>>> qdrift = F2FSynthesis({"Evolution": FermionicQDrift(5, rng=19)})
>>> relabel = RelabelModes(solver=solver)
>>>
>>> pm.optimization = FermionicPassManager([qdrift, relabel])
>>>
>>> relabeled_circ = pm.run(circ)
>>> # if the automatic mode relabeling was successful, the circuit's
>>> # metadata will contain the mode `permutation` information
// WARNING: This feature is not available via the C API.
يتطلب استخدام التحسين التلقائي داخل RelabelModes (الذي يستفيد من build_excitation_span_minimization_model()) الاعتماد الاختياري الذي يديره HAS_PYOMO.
تغيّر إعادة التسمية النمط الذي يحمله كيوبت معين، لذا لا يحصل التشخيص القطري في الفضاء الجزئي على السلاسل الثنائية الصحيحة إلا إذا أُخذ ذلك في الحسبان. ويعتمد مكان هذا الحساب على مكان إجراء القياس.
أضف القياسات إلى FermionicCircuit باستخدام FermionicCircuit.measure_all() قبل التحويل، ولا يلزمك فعل شيء: فكل قياس يسمّي النمط الذي يقرؤه، لذا تحمله إعادة التسمية معها وتُفهرس السلاسل الثنائية المُعاينة بالنمط الأصلي.
أما إذا قِستَ QuantumCircuit المحوَّلة بدلًا من ذلك، فيجب معالجة السلاسل الثنائية لاحقًا باستخدام permutation المسجَّل في بيانات الدائرة الوصفية. راجع توثيق RelabelModes لمعرفة الوصفة.
الخطوات التالية
بعد أن ولّدت بنجاح مجموعة من الدوائر، يمكنك معاينة سلاسل ثنائية منها. ولتحقيق ذلك، يجب تنفيذ الدوائر على العتاد. راجع توثيق Qiskit للحصول على تعليمات مفصّلة.
بمجرد الحصول على عيّنات السلاسل الثنائية، يمكن استخدامها مع معاملات الهاميلتوني لإجراء المعالجة اللاحقة لـ SQD، كما هو موضح في دروس إضافة SQD.