انتقل إلى المحتوى الرئيسي
هذا المشروع في مرحلة تجريبية. وجدت خطأ؟ افتح مشكلة على GitHub. لديك سؤال أو فكرة؟ اسأل في Discussions — نحب سماع ملاحظاتك!
كيف جودة الترجمة في هذه الصفحة؟

الاستفادة من trim SQD لتحسين اختيار التكوينات

السلوك الافتراضي لـ diagonalize_fermionic_hamiltonian هو تشخيص كل دفعة جزئية والاحتفاظ بأفضل نتيجة. أما Trim SQD فتعامل الدفعات على أنها مرشّحات تُفحص: تُشخَّص كل دفعة، ثم تُقلَّم إلى التكوينات ذات الوزن الأكبر، ثم تُدمج الناجيات من جميع الدفعات وتُشخَّص معًا. هذا التشخيص المدموج هو الذي تُبلغ عنه التكرارة بطاقته، ومخرجاته المقلَّمة هي التي تغذّي التكرار التالي.

يستند تنفيذ trim SQD إلى بحث مشترك من مؤسسة كليفلاند كلينك وRIKEN وIBM®. إذا استخدمت هذه الميزة في بحثك، فاستشهد أيضًا بالورقة البحثية. تجد الاستشهاد في الملف CITATION.bib، بمفتاح الاستشهاد merz2026crossing.

لمزيد من التفاصيل حول شيفرة SQD المستخدمة في هذا المثال، راجع الدرس التعليمي للهاميلتونيان الكيميائي.

إعداد الجزيء​

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"numpy": "numpy", "pyscf": "pyscf", "qiskit_addon_sqd": "qiskit-addon-sqd"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
import numpy as np
import pyscf
import pyscf.mcscf
from qiskit_addon_sqd.counts import generate_bit_array_uniform
from qiskit_addon_sqd.fermion import diagonalize_fermionic_hamiltonian
from qiskit_addon_sqd.trim import TrimPolicy

# Build N2 molecule
mol = pyscf.gto.Mole()
mol.build(
atom=[["N", (0, 0, 0)], ["N", (1.0, 0, 0)]],
basis="6-31g",
symmetry="Dooh",
)

# Define active space
n_frozen = 2
active_space = range(n_frozen, mol.nao_nr())

# Get molecular integrals
scf = pyscf.scf.RHF(mol).run()
num_orbitals = len(active_space)
n_electrons = int(sum(scf.mo_occ[active_space]))
num_elec_a = (n_electrons + mol.spin) // 2
num_elec_b = (n_electrons - mol.spin) // 2
cas = pyscf.mcscf.CASCI(scf, num_orbitals, (num_elec_a, num_elec_b))
mo = cas.sort_mo(active_space, base=0)
hcore, nuclear_repulsion_energy = cas.get_h1cas(mo)
eri = pyscf.ao2mo.restore(1, cas.get_h2cas(mo), num_orbitals)

# Compute exact energy
exact_energy = cas.run().e_tot

# Create a seed to control randomness throughout this workflow
rng = np.random.default_rng(24)

# Generate random samples
bit_array = generate_bit_array_uniform(
10_000, num_orbitals * 2, rand_seed=rng
)
WARN: Unable to to identify input symmetry using original axes.
Different symmetry axes will be used.

converged SCF energy = -108.835236570774
CASCI E = -109.046671778080 E(CI) = -32.8155692383188 S^2 = 0.0000000

تشغيل الحلقة​

يقوم تكرار trim SQD واحد بما يلي:

  1. جولة الفحص. قسّم سلاسل البتات إلى دفعات. شخّص كل دفعة واحتفظ بالنسبة trim_ratio من سلاسل CI الخاصة بها، لكل قطاع سبين، مرتّبة بحسب الوزن.

  2. الجولة المدموجة. شخّص الناجيات المدموجة مرة واحدة. هذه هي النتيجة التي يُبلغ عنها التكرار، وسلاسله المقلَّمة هي التي تغذّي التكرار التالي.

يؤدي sci_solver الخاص بالحلقة كلتا الجولتين، ويستخدم افتراضيًا حلّال PySCF الخاص بهذه الحزمة.

فعّل trim SQD بتمرير TrimPolicy كما يلي:

result = diagonalize_fermionic_hamiltonian(
hcore,
eri,
bit_array,
samples_per_batch=300,
norb=num_orbitals,
nelec=(num_elec_a, num_elec_b),
num_batches=5,
max_iterations=5,
policy=TrimPolicy(trim_ratio=0.5, max_strings_per_trim=250),
symmetrize_spin=True,
max_dim=250,
seed=rng,
)
sci_state = result.sci_state
print(
f"Merged subspace: {len(sci_state.ci_strs_a)} x {len(sci_state.ci_strs_b)} CI strings"
)
print(f"Exact energy: {exact_energy}")
print(f"Estimated energy: {result.energy + nuclear_repulsion_energy}")
Merged subspace: 148 x 148 CI strings
Exact energy: -109.04667177808024
Estimated energy: -109.034369147063

تحقّق مما إذا كان الفحص مفيدًا​

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

يعتمد الفحص على اختلاف الدفعات عن بعضها. تتولى TrimPolicy ذلك عبر تعديل سلوك diagonalize_fermionic_hamiltonian بحيث تسحب مجموعة واحدة من samples_per_batch * num_batches سلسلة بتات وتوزّعها على الدفعات، فلا تظهر أي سلسلة بتات في أكثر من دفعة.

تحديد حدود الفضاء الجزئي​

يمتدّ الفضاء الجزئي هنا بواسطة الجداء الديكارتي لمصفوفتَي سلاسل CI، واحدة لسبين ألفا وأخرى لسبين بيتا.

يرتّب التقليم التكوينات، لكن ما يُنقل هو قائمتان من السلاسل لكل سبين. فالاحتفاظ بأعلى 150 تكوينًا وزنًا في فضاء جزئي يشمل عادةً قرابة 150 سلسلة ألفا مميزة و150 سلسلة بيتا مميزة، ويمتدّ جداؤهما على نحو 22,500 تكوين، لا 150. لذا فإن الفضاء الجزئي المسلَّم إلى التكرار التالي أكبر عمومًا بكثير من المجموعة التي رُتّبت، وينمو تربيعيًا مع ما تحتفظ به.

يُحدَّد حدّ التقليم وحدّ الترحيل بشكل منفصل، لأنهما يحدثان في نقطتين مختلفتين: يقلّص التقليم كل دفعة في جولة الفحص، بينما الترحيل هو ما ينتقل إلى التكرار التالي.

أربع وسائط تحدّده، ومن المفيد ضبط واحدة منها على الأقل:

  • trim_ratio هي نسبة سلاسل كل دفعة، لكل قطاع سبين، التي تنجو من الفحص. تقلّص القيم الأدنى الفضاء الجزئي المدموج لكنها تهمل تكوينات يجب على التكرار التالي إعادة اكتشافها بأخذ العيّنات.

  • max_strings_per_trim تحدّ من عدد السلاسل التي تسهم بها كل دفعة، لكل قطاع، بصرف النظر عن النسبة. مفيد مع symmetrize_spin، الذي يدمج قطاعَي السبين في قائمة واحدة وبذلك يضاعف تقريبًا ما تقدّمه كل دفعة. هذا الحد لكل دفعة: يمكن أن يحتوي الفضاء الجزئي المدموج على ما يصل إلى num_batches أضعاف عدد السلاسل، ناقصًا ما كان مشتركًا بين الدفعات، وهو عمليًا ليس كثيرًا.

  • carryover_threshold و**carryover_ratio** و**max_carryover** تحدّ من السلاسل التي تغذّي التكرار التالي، وتُطبَّق على الفضاء الجزئي المدموج ككل لا على كل دفعة. carryover_threshold هو حد القطع للسعة الذي تستخدمه StandardPolicy، بالقيمة الافتراضية نفسها؛ أما الاثنان الآخران فيحدّان العدد، وهو ما لا يفعله حد القطع وحده.

  • max_dim، في diagonalize_fermionic_hamiltonian نفسها، تحدّ كل قطاع سبين في كل فضاء جزئي تشخّصه الحلقة، بما في ذلك المدموج. وهي بمثابة شبكة أمان وتُطبَّق على ما تُرجعه السياسة، بعد دمج الترحيل مع عيّنات جديدة.

يضبط التشغيل أعلاه max_strings_per_trim=250 وmax_dim=250 معًا، فلا يمكن أن يخرج عن السيطرة لا ما تسهم به كل دفعة ولا الفضاء الجزئي المبني منها.

استخدام حلّال قيم ذاتية خارجي​

يمكنك استخدام trim SQD مع حلّال قيم ذاتية خارجي بتمرير الوسيطة sci_solver:

from sbd.sbd_solver import create_sbd_solver

result = diagonalize_fermionic_hamiltonian(
hcore,
eri,
bit_array,
samples_per_batch=300,
norb=num_orbitals,
nelec=(num_elec_a, num_elec_b),
num_batches=5,
policy=TrimPolicy(trim_ratio=0.1),
sci_solver=create_sbd_solver(sbd_config={"method": 0, "eps": 1e-10}),
seed=rng,
)

تتطلب الشيفرة السابقة تثبيت حزمة sbd-eigensolver من PyPI.

توزيع العمل​

مع TrimPolicy، تستدعي الحلقة sci_solver مرتين في كل تكرار: مرة مع جميع الدفعات، ومرة مع الفضاء الجزئي المدموج. يمر كلا الاستدعاءين عبر واجهة sci_solver المعتادة، التي يستدعيها دعم المعالجة المتعددة في هذه الحزمة بشكل جماعي على كل رتبة MPI.

يتوفر مستويان من التوازي، وهما مستقلان:

  • داخل التشخيص الواحد. يمكن لحلّال مثل SBD توزيع تشخيص واحد على عدة رتب.

  • عبر الدفعات. يمكن للحلّال من حيث المبدأ تشخيص الدفعات بالتزامن.

عند إعطاء مجموعة من الدفعات، يحدّد الحلّال كيفية تفويض العمل إلى عمليات العمل التابعة له.