تجميع حدود المؤثر: استخدم بنية المؤثر
التجميع هو إحدى العمليات ضمن qiskit_fermions.operators.terms، التي تجمع روتينات تقسّم حدود المؤثر بحسب بنيتها.
كما هو موضح في دليل المؤثرات، يمكن للمؤثرات تخزين مصفوفة مجموعات، وهي جزء اختياري من بنية البيانات المتناثرة يربط كل حد بفهرس مجموعة. الحدود التي تشترك في فهرس المجموعة نفسه تشكّل مجموعة. وهذا يتيح لك الاستفادة من البنية بشكل منهجي في المعالجة اللاحقة.
بتجميع الحدود المترابطة (سواء بحسب الخصائص الفيزيائية أو العلاقات الجبرية أو التناظرات الخاصة بالمسألة)، يمكنك الحصول على عدة فوائد:
-
تركيب أمثل للدارات: تحمل الحدود المجمَّعة معلومات خاصة بالمسألة عبر المكدس، مما يتيح تحسينًا أبسط للدارات المركَّبة. ينتج عن ذلك عمق دارة وعدد بوابات أقل.
-
الحفاظ على البنية الفيزيائية: تشفّر المجموعات بنية ذات معنى من المسألة الأصلية (مثل أنماط التفاعل أو التناظرات) يمكن استخدامها في تفكيكات أكثر دلالة فيزيائيًا.
-
اختيار مجموعات التدفق: يمكن للتجميع أن يشفّر بشكل طبيعي مجموعات التدفق وبنى أخرى من نظرية الرسوم البيانية ذات صلة بالمحاكاة الكمومية، كما هو موضح في [1].
-
استقرار خوارزمي أفضل: كما يوضح روتين SqDRIFT، يمكن لبعض الخوارزميات أن تستفيد من استقرار أو أداء أفضل بفضل الحفاظ على الخصائص الفيزيائية.
-
عوامل صيغة الجداء: تقسّم صيغة الجداء الفرميونية المؤثر إلى مجموعاته، لذا يحدد التجميع كلفة التطور الزمني وخطأ تروتر معًا. راجع تركيب بوابة Evolution في الفضاء الفرميوني.
الاستخدام
أبسط طريقة لتعريف تجميع للمؤثر هي ضبط الخاصية groups للمؤثر المستهدف. فيما يلي مثال على تجميع الحدود بحسب مجموعات تدفق الخطوط كما في الشكل 1c من [1].
توفّر الوحدة qiskit_fermions.operators.terms.grouping طرقًا مريحة لتحديد البنية في المؤثر تلقائيًا وإسناد فهارس المجموعات وفقًا لذلك.
>>> import rustworkx as rx
>>> graph = rx.PyDiGraph()
>>> _ = graph.add_nodes_from(range(6))
>>> edges = [(0, 1), (1, 2), (3, 0), (1, 4), (5, 2), (5, 4), (4, 3)]
>>> groups = [0, 0, 1, 2, 1, 3, 3]
>>> edges_with_group_as_payload = [(i, j, g) for (i, j), g in zip(edges, groups)]
>>> _ = graph.add_edges_from(edges_with_group_as_payload)
>>> from rustworkx.visualization import mpl_draw
>>> mpl_draw(
... graph,
... pos={i: (i % 3, i // 3) for i in range(6)},
... edge_labels=str,
... with_labels=True,
... )
<Figure size ... with 1 Axes>
هذه شبكة بسيطة من ثلاثة في اثنين من أنماط ماجورانا موصولة بحواف موجَّهة. تمثّل كل حافة حدًّا في المؤثر يؤثر على النمطين المتصلين. الهدف هو تجميع هذه الحدود بحسب تسميات حوافها (الظاهرة على الرسم البياني). توضع الحدود المتصلة بحواف لها التسمية نفسها في المجموعة نفسها. يوضح الكود التالي كيفية القيام بذلك:
- PYTHON
- C
>>> from qiskit_fermions.operators import MajoranaOperator
>>>
>>> # Construct operator directly using sparse arrays
>>> # 7 terms, each with coefficient 1.0, modes from edge endpoints
>>> op = MajoranaOperator(
... coeffs=[1.0] * 7,
... modes=[node for edge in edges for node in edge],
... boundaries=[0, 2, 4, 6, 8, 10, 12, 14],
... )
>>>
>>> # Assign group indices to terms (groups is part of the sparse data structure)
>>> op.groups = groups
>>>
>>> # Partition operator by groups
>>> grouped_ops = op.split_out_groups()
>>>
>>> # Inspect each group
>>> for i, g in enumerate(grouped_ops):
... print(f"Group {i}: {list(sorted(g.iter_terms()))}")
Group 0: [([0, 1], (1+0j)), ([1, 2], (1+0j))]
Group 1: [([3, 0], (1+0j)), ([5, 2], (1+0j))]
Group 2: [([1, 4], (1+0j))]
Group 3: [([4, 3], (1+0j)), ([5, 4], (1+0j))]
#include <qiskit_fermions.h>
uint64_t num_terms = 7;
uint64_t num_modes = 14;
uint32_t modes[14] = {0, 1, 1, 2, 3, 0, 1, 4, 5, 2, 5, 4, 4, 3};
QkComplex64 coeffs[7] = {{1.0, 0.0}, {1.0, 0.0}, {1.0, 0.0}, {1.0, 0.0},
{1.0, 0.0}, {1.0, 0.0}, {1.0, 0.0}};
uint32_t boundaries[8] = {0, 2, 4, 6, 8, 10, 12, 14};
QfMajoranaOperator *op = qf_maj_op_new(num_terms, num_modes, coeffs, modes, boundaries);
uint32_t groups[7] = {0, 0, 1, 2, 1, 3, 3};
qf_maj_op_set_groups(op, groups, num_terms);
QfMajoranaOperator *group_ops[4];
qf_maj_op_split_out_groups(op, NULL, 0, group_ops);
تحليل التجميع
لا تفرض المجموعات أي معنى بذاتها: تسجّل مصفوفة المجموعات أي الحدود تنتمي معًا، لكنها لا تسجّل السبب أبدًا. لا يوجد روتين في هذه المكتبة يبلّغ عمّا إذا كان التجميع "صحيحًا"، لأنه لا يوجد مفهوم وحيد للصحة يمكن القياس عليه. ومع ذلك، تفترض الجهات المستهلكة اللاحقة خصائص معينة في التجميع، وتتيح لك الدوال في هذا القسم التحقق من هذا الافتراض مسبقًا بدلاً من دفع كلفته في كل استدعاء.
الاصطلاح الأكثر شيوعًا هو أن كل مجموعة هرميتية بشكل منفصل. وهذا ما يجعل صيغ الجداء العشوائية الفرميونية ممكنة أصلاً: فالحد المنفرد مع ليس هرميتيًا، لذا فتطوره الزمني ليس أحاديًا ولا يمكن أخذ عينات منه بمفرده. أما اقترانه مع فينتج مجموعة يمكن ذلك معها.
- PYTHON
- C
>>> from qiskit_fermions.operators import FermionOperator
>>> from qiskit_fermions.operators.terms.grouping import (
... group_coeff_means,
... groups_are_hermitian,
... groups_have_uniform_coeffs,
... )
>>>
>>> # a conjugate pair, plus one unpaired term. The operator is built from the sparse
>>> # arrays rather than from a dictionary, because only the former fixes the term order
>>> # that the group indices below are paired with.
>>> pair_and_single = FermionOperator(
... [1.0, 1.0, 1.0],
... [True, False, True, False, True, False],
... [0, 1, 1, 0, 2, 3],
... [0, 2, 4, 6],
... )
>>> pair_and_single.groups = [0, 0, 1]
>>>
>>> groups_are_hermitian(pair_and_single) # group 1 is unpaired
[True, False]
>>> group_coeff_means(pair_and_single)
[1.0, 1.0]
#include <qiskit_fermions.h>
uint64_t num_terms = 3;
uint64_t num_actions = 6;
bool actions[6] = {true, false, true, false, true, false};
uint32_t modes[6] = {0, 1, 1, 0, 2, 3};
QkComplex64 coeffs[3] = {{1.0, 0.0}, {1.0, 0.0}, {1.0, 0.0}};
uint32_t boundaries[4] = {0, 2, 4, 6};
QfFermionOperator *op =
qf_ferm_op_new(num_terms, num_actions, coeffs, actions, modes, boundaries);
uint32_t groups[3] = {0, 0, 1};
qf_ferm_op_set_groups(op, groups, num_terms);
bool hermitian[2];
qf_ferm_op_groups_are_hermitian(op, 1e-8, hermitian);
double means[2];
qf_ferm_op_group_coeff_means(op, means);
يُفرَض فهرس مجموعة واحد لكل حد
تحتوي مصفوفة المجموعات على فهرس واحد لكل حد، وإسناد مصفوفة بطول مختلف يُطلق خطأ. لا شيء لاحقًا يعيد فحص هذا الشرط، لذا يُفرَض عند دخول الفهارس إلى المؤثر: المصفوفة الأقصر من اللازم ستُسقط الحدود الأخيرة بصمت، والأطول من اللازم ستختلق مجموعات لا يحملها أي حد.
>>> pair_and_single.groups = [0, 0] # one index short, on a three-term operator
Traceback (most recent call last):
ValueError: expected one group index per term, but got 2 for 3 terms
مسح فهارس المجموعات بإسناد None مسموح به دائمًا.
الفحصان مستقلان
تبلّغ groups_have_uniform_coeffs() عمّا إذا كانت معاملات المجموعة متطابقة، وهو الافتراض الذي تقوم عليه group_coeff_means() عند حساب متوسط المقادير. وهي ليست صيغة أضعف من فحص الهرميتية: لا أحد من الفحصين يستلزم الآخر، في أي من الاتجاهين.
>>> from qiskit_fermions.operators.terms.grouping import (
... groups_are_hermitian,
... groups_have_uniform_coeffs,
... )
>>> # uniform coefficients, but not Hermitian: the terms are not each other's adjoint
>>> uniform = FermionOperator(
... [1.0, 1.0],
... [True, False, True, False],
... [0, 1, 2, 3],
... [0, 2, 4],
... )
>>> uniform.groups = [0, 0]
>>> groups_have_uniform_coeffs(uniform), groups_are_hermitian(uniform)
([True], [False])
>>> # Hermitian, but with mixed magnitudes: two conjugate pairs at different scales
>>> mixed = FermionOperator(
... [1.0, 1.0, 5.0, 5.0],
... [True, False, True, False, True, False, True, False],
... [0, 1, 1, 0, 2, 3, 3, 2],
... [0, 2, 4, 6, 8],
... )
>>> mixed.groups = [0, 0, 0, 0]
>>> groups_are_hermitian(mixed), groups_have_uniform_coeffs(mixed)
([True], [False])
افتراضيًا تُقارَن المقادير، لأن هذا ما تحسب group_coeff_means() متوسطه. الزوج المترافق الهرميتي ذو المعاملات المركبة له مقادير متساوية لكن معاملات غير متساوية، لذا يحقق الصيغة الافتراضية فقط.
>>> conjugate = FermionOperator(
... [1.0j, -1.0j],
... [True, False, True, False],
... [0, 1, 1, 0],
... [0, 2, 4],
... )
>>> conjugate.groups = [0, 0]
>>> groups_have_uniform_coeffs(conjugate)
[True]
>>> groups_have_uniform_coeffs(conjugate, abs=False)
[False]
ترث groups_are_hermitian() الضمان أحادي الجانب من is_hermitian(): القيمة True موثوقة دائمًا، بينما القيمة False موثوقة فقط لأنواع المؤثرات التي يكون شكلها الطبيعي صيغة قانونية حقيقية. راجع نوع المؤثر المحدد لمعرفة أيهما ينطبق.
A. Gandon et al., Stabilizer-based quantum simulation of fermion dynamics with local qubit encodings, arXiv:2512.11418v2.