تجميع حدود المؤثر: استخدم بنية المؤثر
BosonOperator يقدر يوسم كل حد من حدوده بـ فهرس مجموعة. الوسوم ما إلها معنى بحد ذاتها: هي فقط تقول أي الحدود تنتمي لبعضها. أما ما تعنيه الوسوم فيقرره من يستهلكها. في هذه الحزمة المستهلكون هم بوابة التطور الزمني التي تُقسَّم إلى عامل واحد لكل مجموعة، وتمريرة qDRIFT العشوائية التي تأخذ عينات من مجموعات كاملة.
هذا الدليل يوضح كيف تعيّن المجموعات، وكيف تتحقق من الخصائص التي يعتمد عليها المستهلك، وكيف ترتّب المؤثر بحيث يظل اختيار بضع مجموعات من مؤثر كبير رخيصًا.
لا تأتي هذه الحزمة بأي استراتيجية تجميع تلقائية. أنت تعيّن الفهارس بنفسك، مما تعرفه عن نموذجك.
عيّن المجموعات
المثال الجاري سلسلة Bose-Hubbard من ثلاثة مواقع. كل رابطة تساهم بزوج قفز، والتفاعلات داخل الموقع تشكّل جزءًا قطريًا. أبسط طريقة لبناء مؤثر مجمَّع هي from_terms_with_groups()، التي تأخذ كل حد مع فهرس مجموعته كثلاثية (actions, coefficient, group). أنت تقرر المجموعة لحظة كتابة الحد، حين لسا تعرف ليش ينتمي هناك:
>>> from qiskit_bosons.operators import BosonOperator, ann, cre
>>>
>>> bonds = [(0, 1), (1, 2)]
>>> terms = []
>>> for group, (left, right) in enumerate(bonds): # one group per bond
... terms.append(([cre(left), ann(right)], -1.0, group))
... terms.append(([cre(right), ann(left)], -1.0, group))
>>> for site in range(3): # the on-site interactions share one group
... terms.append(([cre(site), cre(site), ann(site), ann(site)], 1.0, len(bonds)))
>>> hamiltonian = BosonOperator.from_terms_with_groups(terms)
>>> len(hamiltonian), hamiltonian.num_groups()
(7, 3)
الحدود تُخزَّن بالترتيب اللي تعطيه، فما يمكن أن تنتهي فهارس المجموعات ملحقة بالحدود الخطأ.
المؤثر الموجود أصلًا لازم يُجمَّع بعد الواقعة، بتعيين الخاصية groups. هنا تحتاج حذر، لأن from_dict() لا تحدد الترتيب الذي تخزّن به حدودها. كتابة القائمة بالموضع ممكن عندها تلحق فهرسًا بالحد الخطأ. اشتق كل فهرس من الحد نفسه بدل ذلك:
>>> existing = BosonOperator.from_dict({tuple(actions): coeff for actions, coeff, _ in terms})
>>> existing.groups = [
... len(bonds)
... if len({mode for _, mode in term}) == 1
... else bonds.index(tuple(sorted({mode for _, mode in term})))
... for term, _ in existing.iter_terms()
... ]
>>> existing.num_groups(), existing.equiv(hamiltonian)
(3, True)
عدد الفهارس لازم يطابق عدد الحدود. أي شيء غير ذلك يُرفض، لأن القائمة الأقصر ستُسقط الحدود الأخيرة بصمت في أي مكان تُكرَّر فيه الحدود مع مجموعاتها:
>>> try:
... hamiltonian.groups = [0, 1]
... except ValueError as error:
... print(error)
expected one group index per term, but got 2 for 7 terms
تحقق مما يعتمد عليه المستهلك
ثلاث دوال في qiskit_bosons.operators.terms تجيب كل واحدة عن سؤال ضيق واحد حول تجميع موجود. ولا واحدة تقول إن التجميع «صحيح»، لأن الصحة نسبية للمستهلك.
هل كل مجموعة هرميتية؟ هذه الخاصية الأهم. التطور الزمني لمجموعة أحادي فقط إذا كانت المجموعة هرميتية، وحد قفز منفرد ليس كذلك. فقط الزوج مع مرافقه هرميتي:
>>> from qiskit_bosons.operators.terms import groups_are_hermitian
>>> groups_are_hermitian(hamiltonian)
[True, True, True]
التجميع الذي يفصل زوج قفز يُكتشف فورًا:
>>> split = BosonOperator.from_terms_with_groups(
... [(actions, coeff, index) for index, (actions, coeff, _) in enumerate(terms)]
... ) # one term per group
>>> [bool(flag) for flag in groups_are_hermitian(split)]
[False, False, False, False, True, True, True]
تم رصد حدود القفز الأربعة. أما إدخالات True الثلاثة فهي الحدود الموضعية ، وهي هيرميتية بحد ذاتها. يمكن أن يتكرر النمط (mode) داخل الحد بشكل مشروع، ولا يُسقَط شيء بسبب ذلك. تتبع الأعلام ترتيب terms، ولهذا يمكن قراءة كل علم مقابل الحد الذي يصفه.
هل المعاملات متجانسة؟ تزن صيغة المنتج العشوائية المجموعة بمتوسط مقدار معاملاتها، وهذا وصف دقيق للمجموعة فقط إذا كانت المقادير متطابقة:
>>> from qiskit_bosons.operators.terms import group_coeff_means, groups_have_uniform_coeffs
>>> groups_have_uniform_coeffs(hamiltonian)
[True, True, True]
>>> group_coeff_means(hamiltonian)
[1.0, 1.0, 1.0]
لا يستلزم أي من الفحصين الآخر: المعاملات المتجانسة لا تجعل المجموعة هيرميتية، والمجموعة الهيرميتية قد تخلط مقادير مختلفة. افتراضيًا تتم مقارنة المقادير، لأن هذا هو ما يحسب متوسطه group_coeff_means(). الزوج الهيرميتي ذو المعاملات المركبة له مقادير متساوية لكن معاملات غير متساوية، لذلك يحقق الصيغة الافتراضية فقط:
>>> conjugate = BosonOperator.from_terms_with_groups(
... [([cre(0), ann(1)], 1.0j, 0), ([cre(1), ann(0)], -1.0j, 0)]
... )
>>> groups_have_uniform_coeffs(conjugate)
[True]
>>> groups_have_uniform_coeffs(conjugate, abs=False)
[False]
groups_are_hermitian() مبنية على is_hermitian()، التي تقارن المؤثر بمرافقه بعد الترتيب الطبيعي. وهذه متطابقة في الجبر غير المبتور، لذا فالمجموعة التي تجتاز الفحص هيرميتية كمؤثر بوزوني. راجع مبادئ تصميم تمثيلات المؤثرات لمعرفة كيف تُرتَّب حواصل ضرب مؤثرات السلّم وتُقارَن.
ترتيب الحدود حسب المجموعة
تُرجع split_out_groups() مؤثرًا واحدًا لكل مجموعة مطلوبة. عندما تكون حدود المجموعة مبعثرة في المؤثر، عليها أن تفحص كل حد لتجدها. وعندما تشكّل كل مجموعة مقطعًا متصلًا واحدًا، تجري بحثًا ثنائيًا عن حدود المجموعات بدلًا من ذلك، فتتبع تكلفة البحث عدد المجموعات المطلوبة لا عدد الحدود المخزَّنة.
تنتج group_order() هذا التخطيط. وهي فرز مستقر حسب فهرس المجموعة: لا تتغير قيمة المؤثر، وتحتفظ الحدود داخل المجموعة بترتيبها النسبي.
>>> from qiskit_bosons.operators.terms import group_order
>>> scattered = BosonOperator.from_terms_with_groups(
... [(actions, coeff, group) for (actions, coeff, _), group in zip(terms, [1, 0, 1, 0, 2, 2, 2])]
... )
>>> ordered = group_order(scattered)
>>> ordered.groups
[0, 0, 1, 1, 2, 2, 2]
>>> ordered.equiv(scattered)
True
كلا التخطيطين يُرجعان المؤثرات نفسها؛ الفرق في التكلفة فقط:
>>> request = [2, 0]
>>> all(
... a.equiv(b)
... for a, b in zip(
... scattered.split_out_groups(request), ordered.split_out_groups(request), strict=True
... )
... )
True
الخطوات التالية
يُختار التجميع هنا، على المستوى البوزوني، بينما لا تزال بنية النموذج ظاهرة.
-
يوضح تركيب بوابة Evolution في الفضاء البوزوني كيف تستخدمه لتقسيم تطور زمني بطريقة Trotter، ولماذا يقلل ذلك خطأً لا تستطيع صيغة المنتج على مستوى الكيوبت تقليله.
-
يشرح مبادئ تصميم تمثيلات المؤثرات بنية بيانات المؤثر التي تحمل فهارس المجموعات، وكيف تُخزَّن حدودها وتُرتَّب.
-
يطبّق 2. التجميع قبل التعيين تجميعًا على نموذج كامل.