ข้ามไปยังเนื้อหาหลัก

แฮมิลโทเนียนสำหรับเคมีควอนตัม

มาเริ่มต้นด้วยภาพรวมสั้น ๆ เกี่ยวกับบทบาทของแฮมิลโทเนียนใน VQE กัน

The Hamiltonian in VQE Overview

Dr. Victoria Lipinska พาเราทำความเข้าใจเกี่ยวกับแฮมิลโทเนียนและวิธีการแมปเพื่อใช้ในการคำนวณเชิงควอนตัม

References

บทความต่อไปนี้ถูกอ้างอิงในวิดีโอด้านบน

Preparing Hamiltonians for Quantum Chemistry

ขั้นตอนแรกที่ดีในการนำการคำนวณเชิงควอนตัมมาใช้กับปัญหาทางเคมี คือการกำหนดแฮมิลโทเนียนสำหรับระบบที่สนใจ ที่นี่เราจะจำกัดการอภิปรายไว้ที่แฮมิลโทเนียนทางเคมีควอนตัม เนื่องจากแฮมิลโทเนียนเหล่านั้นต้องการการแมปที่เฉพาะเจาะจงสำหรับระบบของเฟอร์มิออนที่เหมือนกัน

ในฐานะที่คุณทำงานด้านเคมีควอนตัม คุณน่าจะมีซอฟต์แวร์ที่ชื่นชอบสำหรับการจำลองโมเลกุลอยู่แล้ว ซึ่งสามารถสร้างแฮมิลโทเนียนที่อธิบายระบบที่สนใจได้ ที่นี่เราจะใช้โค้ดที่สร้างขึ้นจาก PySCF, numpy และ Qiskit เท่านั้น แต่กระบวนการเตรียมแฮมิลโทเนียนนี้สามารถถ่ายโอนไปยังโซลูชันสำเร็จรูปอื่น ๆ ได้เช่นกัน ความแตกต่างเพียงอย่างเดียวระหว่างวิธีนี้กับซอฟต์แวร์อื่น ๆ คือความแตกต่างเล็กน้อยด้านไวยากรณ์ บางส่วนได้รับการกล่าวถึงในหัวข้อย่อย "ซอฟต์แวร์ของบุคคลที่สาม" เพื่อช่วยในการรวมเวิร์กโฟลว์ที่มีอยู่

การสร้างแฮมิลโทเนียนทางเคมีควอนตัมสำหรับใช้งานบน IBM Quantum® QPUs ประกอบด้วยขั้นตอนต่อไปนี้:

  1. กำหนดโมเลกุลของคุณ (เรขาคณิต, สปิน, active space และอื่น ๆ)
  2. สร้างแฮมิลโทเนียนแบบเฟอร์มิออนิก (ตัวดำเนินการสร้างและทำลาย)
  3. แมปจากแฮมิลโทเนียนแบบเฟอร์มิออนิกไปยังตัวดำเนินการแบบโบโซนิก (ในบริบทนี้ ใช้ตัวดำเนินการ Pauli)
  4. หากใช้ซอฟต์แวร์ของบุคคลที่สาม: จัดการความไม่ตรงกันของไวยากรณ์ระหว่างซอฟต์แวร์ที่สร้างแฮมิลโทเนียนกับ Qiskit

แฮมิลโทเนียนแบบเฟอร์มิออนิกเขียนในรูปของตัวดำเนินการเฟอร์มิออนิก และโดยเฉพาะอย่างยิ่ง คำนึงถึงว่าอิเล็กตรอนเป็นเฟอร์มิออนที่แยกแยะไม่ได้ ซึ่งหมายความว่าพวกมันปฏิบัติตามสถิติที่แตกต่างอย่างสิ้นเชิงจาก Qubit แบบโบโซนิกที่แยกแยะได้ ดังนั้นจึงต้องมีกระบวนการแมป

ผู้ที่คุ้นเคยกับกระบวนการเหล่านี้แล้วน่าจะสามารถข้ามส่วนนี้ได้ เป้าหมาย:

เป้าหมายสุดท้ายคือการได้แฮมิลโทเนียนในรูปแบบ:

# Added by doQumentation — required packages for this notebook
!pip install -q numpy openfermion pyscf qiskit
H = [(1, "XX"), (1, "YY"), (1, "ZZ")]
print(H)
[(1, 'XX'), (1, 'YY'), (1, 'ZZ')]

หรือ

from qiskit.quantum_info import SparsePauliOp

H = SparsePauliOp(["XX", "YY", "ZZ"], coeffs=[1.0 + 0.0j, 1.0 + 0.0j, 1.0 + 0.0j])
print(H)
SparsePauliOp(['XX', 'YY', 'ZZ'],
coeffs=[1.+0.j, 1.+0.j, 1.+0.j])

เราจะเริ่มด้วยการ import แพ็กเกจบางตัว:

import numpy as np
from pyscf import ao2mo, gto, mcscf, scf
  1. กำหนดโมเลกุลของคุณ

ที่นี่เราจะระบุคุณสมบัติของโมเลกุลที่สนใจ ในตัวอย่างนี้ เราเลือกไฮโดรเจนอะตอมคู่ (เนื่องจากแฮมิลโทเนียนที่ได้นั้นสั้นพอที่จะแสดงได้) Python-based Simulations of Chemistry Framework (PySCF) มีชุดโมดูลโครงสร้างอิเล็กทรอนิกส์มากมายที่สามารถใช้งานได้ในหลายกรณี รวมถึงการสร้างแฮมิลโทเนียนโมเลกุลที่เหมาะสมสำหรับการคำนวณเชิงควอนตัม คู่มือ PySCF Quickstart เป็นแหล่งข้อมูลที่ดีเยี่ยมสำหรับคำอธิบายครบถ้วนของตัวแปรและฟังก์ชันทั้งหมด เราจะอธิบายเพียงคร่าว ๆ เนื่องจากหลายคนน่าจะคุ้นเคยแล้ว หากต้องการเข้าใจเพิ่มเติม ให้เยี่ยมชม PySCF โดยย่อ:

distance ใช้สำหรับโมเลกุลอะตอมคู่ หรือเพียงแค่ระบุพิกัด Cartesian สำหรับแต่ละอะตอม ระยะทางเป็นหน่วย Angstrom

gto สร้าง gaussian-type orbitals

basis หมายถึงฟังก์ชันที่ใช้ในการจำลอง molecular orbitals ที่นี่ 'sto-6g' เป็น basis ขั้นต่ำทั่วไป ตั้งชื่อตามการประมาณ Slater-Type Orbitals โดยใช้ primitive Gaussian orbitals 6 ตัว

spin ค่าจำนวนเต็มที่ระบุจำนวนอิเล็กตรอนที่ไม่จับคู่ (เท่ากับ 2S2S) โปรดทราบว่าซอฟต์แวร์บางตัวใช้ multiplicity แทน (2S+12S+1)

charge ประจุของโมเลกุล

symmetry - กลุ่มสมมาตรจุดของโมเลกุล ระบุด้วยสตริงหรือตรวจจับอัตโนมัติโดยตั้งค่า "symmetry = True" ที่นี่ "Dooh" เป็นกลุ่มสมมาตรที่เหมาะสมสำหรับโมเลกุลอะตอมคู่ที่มีอะตอมชนิดเดียวกันสองตัว

distance = 0.735
a = distance / 2
mol = gto.Mole()
mol.build(
verbose=0,
atom=[
["H", (0, 0, -a)],
["H", (0, 0, a)],
],
basis="sto-6g",
spin=0,
charge=0,
symmetry="Dooh",
)
<pyscf.gto.mole.Mole at 0x7fc718f07610>

โปรดทราบว่าสามารถอธิบายพลังงานรวม (ซึ่งรวมถึงพลังงานผลักระหว่างนิวเคลียสและอิเล็กทรอนิกส์), พลังงานออร์บิทัลอิเล็กทรอนิกส์รวม หรือพลังงานของออร์บิทัลอิเล็กทรอนิกส์บางส่วน (โดยมีส่วนที่เสริมกันถูกตรึงไว้) ในกรณีเฉพาะของ H2\text{H}_2 โปรดสังเกตพลังงานที่แตกต่างกันด้านล่าง และสังเกตว่าพลังงานรวมลบด้วยพลังงานผลักระหว่างนิวเคลียสให้ผลเท่ากับพลังงานอิเล็กทรอนิกส์จริง ๆ:

mf = scf.RHF(mol)
mf.scf()

print(
mf.energy_nuc(),
mf.energy_elec()[0],
mf.energy_tot(),
mf.energy_tot() - mol.energy_nuc(),
)
0.7199689944489797 -1.8455976628764188 -1.125628668427439 -1.8455976628764188
active_space = range(mol.nelectron // 2 - 1, mol.nelectron // 2 + 1)
  1. สร้างแฮมิลโทเนียนแบบเฟอร์มิออนิก

scf หมายถึงวิธีการ self-consistent field ในวงกว้าง

rhf เช่นใน mf = scf.RHF(mol) คือ solver ที่ใช้การคำนวณ Restricted Hartree Fock kernel ของสิ่งนี้ (E ด้านล่าง) คือพลังงานรวม รวมถึงการผลักระหว่างนิวเคลียสและ molecular orbitals

mcscf คือแพ็กเกจ multi-configuration self-consistent fields

ao2mo คือการแปลงจาก atomic orbitals เป็น molecular orbitals

เราใช้ตัวแปรต่อไปนี้ด้วย:

ncas: จำนวน orbitals ใน complete active space

nelecas: จำนวนอิเล็กตรอนใน complete active space

E1 = mf.kernel()
mx = mcscf.CASCI(mf, ncas=2, nelecas=(1, 1))
mo = mx.sort_mo(active_space, base=0)
E2 = mx.kernel(mo)[:2]

เราต้องการแฮมิลโทเนียน และมักแยกออกเป็นพลังงานของ electronic core (ecore ไม่เกี่ยวข้องกับการ minimization), ตัวดำเนินการแบบ single-electron (h1e) และพลังงานแบบ two-electron (h2e) สิ่งเหล่านี้ถูกแยกออกมาอย่างชัดเจนด้านล่างในสองบรรทัดสุดท้าย

h1e, ecore = mx.get_h1eff()
h2e = ao2mo.restore(1, mx.get_h2eff(), mx.ncas)

แฮมิลโทเนียนเหล่านี้ปัจจุบันเป็นตัวดำเนินการเฟอร์มิออนิก (สร้างและทำลาย) ที่ใช้กับระบบของเฟอร์มิออน (ที่แยกแยะไม่ได้) และมีความเกี่ยวข้องกับ antisymmetry ภายใต้การแลกเปลี่ยน ส่งผลให้มีสถิติที่แตกต่างจากระบบแบบแยกแยะได้หรือโบโซนิก ในการรันการคำนวณบน IBM Quantum QPUs เราต้องการตัวดำเนินการโบโซนิกที่อธิบายพลังงาน ผลลัพธ์ของการแมปดังกล่าวโดยทั่วไปเขียนในรูปของตัวดำเนินการ Pauli เนื่องจากมีคุณสมบัติทั้ง Hermitian และ unitary มีการแมปหลายแบบที่สามารถใช้ได้ หนึ่งในที่ง่ายที่สุดคือการแปลง Jordan Wigner

  1. การแมปแฮมิลโทเนียน

ควรสังเกตว่ามีเครื่องมือมากมายสำหรับการแมปแฮมิลโทเนียนทางเคมีไปยังรูปแบบที่เหมาะสมสำหรับรันบนคอมพิวเตอร์ควอนตัม ที่นี่เราใช้การแมป Jordan Wigner โดยตรงโดยใช้เพียง PySCF, numpy และ Qiskit เราให้ความเห็นด้านล่างเกี่ยวกับข้อพิจารณาด้านไวยากรณ์สำหรับโซลูชันอื่น ๆ ฟังก์ชัน Cholesky ช่วยให้เราได้การแยกส่วนแบบ low-rank ของพจน์ two-electron ในแฮมิลโทเนียน

def cholesky(V, eps):
# see https://arxiv.org/pdf/1711.02242.pdf section B2
# see https://arxiv.org/abs/1808.02625
# see https://arxiv.org/abs/2104.08957
no = V.shape[0]
chmax, ng = 20 * no, 0
W = V.reshape(no**2, no**2)
L = np.zeros((no**2, chmax))
Dmax = np.diagonal(W).copy()
nu_max = np.argmax(Dmax)
vmax = Dmax[nu_max]
while vmax > eps:
L[:, ng] = W[:, nu_max]
if ng > 0:
L[:, ng] -= np.dot(L[:, 0:ng], (L.T)[0:ng, nu_max])
L[:, ng] /= np.sqrt(vmax)
Dmax[: no**2] -= L[: no**2, ng] ** 2
ng += 1
nu_max = np.argmax(Dmax)
vmax = Dmax[nu_max]
L = L[:, :ng].reshape((no, no, ng))
print(
"accuracy of Cholesky decomposition ",
np.abs(np.einsum("prg,qsg->prqs", L, L) - V).max(),
)
return L, ng

ฟังก์ชัน identity และ creators_destructors แทนที่ตัวดำเนินการสร้างและทำลายในแฮมิลโทเนียนแบบเฟอร์มิออนิกด้วยตัวดำเนินการ Pauli โดย creators_destructors ใช้การแมป Jordan-Wigner

def identity(n):
return SparsePauliOp.from_list([("I" * n, 1)])

def creators_destructors(n, mapping="jordan_wigner"):
c_list = []
if mapping == "jordan_wigner":
for p in range(n):
if p == 0:
ell, r = "I" * (n - 1), ""
elif p == n - 1:
ell, r = "", "Z" * (n - 1)
else:
ell, r = "I" * (n - p - 1), "Z" * p
cp = SparsePauliOp.from_list([(ell + "X" + r, 0.5), (ell + "Y" + r, -0.5j)])
c_list.append(cp)
else:
raise ValueError("Unsupported mapping.")
d_list = [cp.adjoint() for cp in c_list]
return c_list, d_list

สุดท้าย build_hamiltonian ใช้ฟังก์ชัน cholesky, identity และ creators_destructors เพื่อสร้างแฮมิลโทเนียนสุดท้ายที่เหมาะสมสำหรับรันบนคอมพิวเตอร์ควอนตัม

def build_hamiltonian(ecore: float, h1e: np.ndarray, h2e: np.ndarray) -> SparsePauliOp:
ncas, _ = h1e.shape

C, D = creators_destructors(2 * ncas, mapping="jordan_wigner")
Exc = []
for p in range(ncas):
Excp = [C[p] @ D[p] + C[ncas + p] @ D[ncas + p]]
for r in range(p + 1, ncas):
Excp.append(
C[p] @ D[r]
+ C[ncas + p] @ D[ncas + r]
+ C[r] @ D[p]
+ C[ncas + r] @ D[ncas + p]
)
Exc.append(Excp)

# low-rank decomposition of the Hamiltonian
Lop, ng = cholesky(h2e, 1e-6)
t1e = h1e - 0.5 * np.einsum("pxxr->pr", h2e)

H = ecore * identity(2 * ncas)
# one-body term
for p in range(ncas):
for r in range(p, ncas):
H += t1e[p, r] * Exc[p][r - p]
# two-body term
for g in range(ng):
Lg = 0 * identity(2 * ncas)
for p in range(ncas):
for r in range(p, ncas):
Lg += Lop[p, r, g] * Exc[p][r - p]
H += 0.5 * Lg @ Lg

return H.chop().simplify()

สุดท้าย เราใช้ build_hamiltonian เพื่อสร้าง Qubit Hamiltonian จากตัวดำเนินการ Pauli โดยใช้การแปลง Jordan-Wigner ซึ่งยังให้ความแม่นยำของการแยกส่วน Cholesky ที่เราใช้ด้วย

H = build_hamiltonian(ecore, h1e, h2e)
print(H)
accuracy of Cholesky decomposition  2.220446049250313e-16
SparsePauliOp(['IIII', 'IIIZ', 'IZII', 'IIZI', 'ZIII', 'IZIZ', 'IIZZ', 'ZIIZ', 'IZZI', 'ZZII', 'ZIZI', 'YYYY', 'XXYY', 'YYXX', 'XXXX'],
coeffs=[-0.09820182+0.j, -0.1740751 +0.j, -0.1740751 +0.j, 0.2242933 +0.j,
0.2242933 +0.j, 0.16891402+0.j, 0.1210099 +0.j, 0.16631441+0.j,
0.16631441+0.j, 0.1210099 +0.j, 0.17504456+0.j, 0.04530451+0.j,
0.04530451+0.j, 0.04530451+0.j, 0.04530451+0.j])

ตัวอย่าง notebook โมเลกุลนี้ แสดงการตั้งค่าและแฮมิลโทเนียนสำหรับโมเลกุลหลายชนิดที่มีความซับซ้อนแตกต่างกัน ด้วยการปรับเปลี่ยนเล็กน้อย นี่ควรช่วยให้คุณตรวจสอบโมเลกุลเล็ก ๆ ส่วนใหญ่ได้

มาสังเกตสั้น ๆ สองประเด็นสำคัญที่ควรพิจารณาเมื่อสร้างตัวดำเนินการเฟอร์มิออนิกสำหรับโมเลกุล เมื่อประเภทของโมเลกุลเปลี่ยนไป สมมาตรก็จะเปลี่ยนไปด้วย ในทำนองเดียวกัน จำนวน orbitals ที่มีสมมาตรต่าง ๆ เช่น "A1" แบบสมมาตรทรงกระบอก ก็จะเปลี่ยนไปด้วย การเปลี่ยนแปลงเหล่านี้เห็นได้ชัดแม้กระทั่งในการขยายไปยัง LiH ดังที่เห็นที่นี่:

distance = 1.56
mol = gto.Mole()
mol.build(
verbose=0,
atom=[["Li", (0, 0, 0)], ["H", (0, 0, distance)]],
basis="sto-6g",
spin=0,
charge=0,
symmetry="Coov",
)
mf = scf.RHF(mol)
E1 = mf.kernel()

# %% ----------------------------------------------------------------------------------------------

mx = mcscf.CASCI(mf, ncas=5, nelecas=(1, 1))
cas_space_symmetry = {"A1": 3, "E1x": 1, "E1y": 1}
mo = mcscf.sort_mo_by_irrep(mx, mf.mo_coeff, cas_space_symmetry)
E2 = mx.kernel(mo)[:2]
h1e, ecore = mx.get_h1eff()
h2e = ao2mo.restore(1, mx.get_h2eff(), mx.ncas)

นอกจากนี้ยังควรสังเกตว่าอาจสูญเสียความเข้าใจสัญชาตญาณสำหรับแฮมิลโทเนียนสุดท้ายที่ได้ แฮมิลโทเนียนสำหรับ LiH (โดยใช้ Jordan-Wigner mapper) มีถึง 276 พจน์แล้ว

len(build_hamiltonian(ecore, h1e, h2e))
accuracy of Cholesky decomposition  1.1102230246251565e-16
276

เมื่อไม่แน่ใจเกี่ยวกับสมมาตร สามารถสร้างข้อมูลสมมาตรบางอย่างสำหรับโมเลกุลได้โดยตั้งค่า symmetry = True และ verbose = 4:

distance = 1.56
mol = gto.Mole()
mol.build(
verbose=4,
atom=[["Li", (0, 0, 0)], ["H", (0, 0, distance)]],
basis="sto-6g",
spin=0,
charge=0,
symmetry=True,
)
System: uname_result(system='Linux', node='IBM-R912JTRT', release='5.10.102.1-microsoft-standard-WSL2', version='#1 SMP Wed Mar 2 00:30:59 UTC 2022', machine='x86_64')  Threads 16
Python 3.11.12 (main, May 16 2025, 02:33:32) [GCC 11.4.0]
numpy 2.3.1 scipy 1.16.0 h5py 3.14.0
Date: Mon Jun 30 12:56:55 2025
PySCF version 2.9.0
PySCF path /home/porter284/.pyenv/versions/3.11.12/lib/python3.11/site-packages/pyscf

[CONFIG] conf_file None
[INPUT] verbose = 4
[INPUT] num. atoms = 2
[INPUT] num. electrons = 4
[INPUT] charge = 0
[INPUT] spin (= nelec alpha-beta = 2S) = 0
[INPUT] symmetry True subgroup None
[INPUT] Mole.unit = angstrom
[INPUT] Symbol X Y Z unit X Y Z unit Magmom
[INPUT] 1 Li 0.000000000000 0.000000000000 0.000000000000 AA 0.000000000000 0.000000000000 0.000000000000 Bohr 0.0
[INPUT] 2 H 0.000000000000 0.000000000000 1.560000000000 AA 0.000000000000 0.000000000000 2.947972754321 Bohr 0.0

nuclear repulsion = 1.01764848253846
point group symmetry = Coov
symmetry origin: [0. 0. 0.73699319]
symmetry axis x: [1. 0. 0.]
symmetry axis y: [0. 1. 0.]
symmetry axis z: [0. 0. 1.]
num. orbitals of irrep A1 = 4
num. orbitals of irrep E1x = 1
num. orbitals of irrep E1y = 1
number of shells = 4
number of NR pGTOs = 36
number of NR cGTOs = 6
basis = sto-6g
ecp = {}
CPU time: 9.85
<pyscf.gto.mole.Mole at 0x7fc719f94850>

นอกจากข้อมูลที่เป็นประโยชน์อื่น ๆ แล้ว สิ่งนี้ยังส่งคืนทั้ง point group symmetry = Coov และจำนวน orbitals ในแต่ละ irreducible representation

point group symmetry = Coov
num. orbitals of irrep A1 = 4
num. orbitals of irrep E1x = 1
num. orbitals of irrep E1y = 1
number of shells = 4

สิ่งนี้ไม่จำเป็นต้องบอกคุณว่าต้องการ orbitals จำนวนเท่าใดใน active space แต่ช่วยให้คุณเห็นว่ามี orbitals อะไรบ้างและสมมาตรของพวกมัน

การระบุสมมาตรและ orbitals มักมีประโยชน์ แต่คุณยังสามารถระบุจำนวน orbitals ที่ต้องการรวมได้ ลองพิจารณากรณีของเอทิลีนด้านล่าง โดยใช้ verbose = 4 เราสามารถพิมพ์สมมาตรของ orbitals ต่าง ๆ ได้:

# Replace these variables with correct distances:
a = 1
b = 1
c = 1

# Build
mol = gto.Mole()
mol.build(
verbose=4,
atom=[
["C", (0, 0, a)],
["C", (0, 0, -a)],
["H", (0, c, b)],
["H", (0, -c, b)],
["H", (0, c, -b)],
["H", (0, -c, -b)],
],
basis="sto-6g",
spin=0,
charge=0,
symmetry=True,
)
System: uname_result(system='Linux', node='IBM-R912JTRT', release='5.10.102.1-microsoft-standard-WSL2', version='#1 SMP Wed Mar 2 00:30:59 UTC 2022', machine='x86_64')  Threads 16
Python 3.11.12 (main, May 16 2025, 02:33:32) [GCC 11.4.0]
numpy 2.3.1 scipy 1.16.0 h5py 3.14.0
Date: Mon Jun 30 12:57:07 2025
PySCF version 2.9.0
PySCF path /home/porter284/.pyenv/versions/3.11.12/lib/python3.11/site-packages/pyscf

[CONFIG] conf_file None
[INPUT] verbose = 4
[INPUT] num. atoms = 6
[INPUT] num. electrons = 16
[INPUT] charge = 0
[INPUT] spin (= nelec alpha-beta = 2S) = 0
[INPUT] symmetry True subgroup None
[INPUT] Mole.unit = angstrom
[INPUT] Symbol X Y Z unit X Y Z unit Magmom
[INPUT] 1 C 0.000000000000 0.000000000000 1.000000000000 AA 0.000000000000 0.000000000000 1.889726124565 Bohr 0.0
[INPUT] 2 C 0.000000000000 0.000000000000 -1.000000000000 AA 0.000000000000 0.000000000000 -1.889726124565 Bohr 0.0
[INPUT] 3 H 0.000000000000 1.000000000000 1.000000000000 AA 0.000000000000 1.889726124565 1.889726124565 Bohr 0.0
[INPUT] 4 H 0.000000000000 -1.000000000000 1.000000000000 AA 0.000000000000 -1.889726124565 1.889726124565 Bohr 0.0
[INPUT] 5 H 0.000000000000 1.000000000000 -1.000000000000 AA 0.000000000000 1.889726124565 -1.889726124565 Bohr 0.0
[INPUT] 6 H 0.000000000000 -1.000000000000 -1.000000000000 AA 0.000000000000 -1.889726124565 -1.889726124565 Bohr 0.0

nuclear repulsion = 29.3377079104231
point group symmetry = D2h
symmetry origin: [0. 0. 0.]
symmetry axis x: [0. 1. 0.]
symmetry axis y: [1. 0. 0.]
symmetry axis z: [-0. -0. -1.]
num. orbitals of irrep Ag = 4
num. orbitals of irrep B2g = 2
num. orbitals of irrep B3g = 1
num. orbitals of irrep B1u = 4
num. orbitals of irrep B2u = 1
num. orbitals of irrep B3u = 2
number of shells = 10
number of NR pGTOs = 84
number of NR cGTOs = 14
basis = sto-6g
ecp = {}
CPU time: 9.92
<pyscf.gto.mole.Mole at 0x7fc719fa9290>

เราได้:

num. orbitals of irrep Ag = 4

num. orbitals of irrep B2g = 2

num. orbitals of irrep B3g = 1

num. orbitals of irrep B1u = 4

num. orbitals of irrep B2u = 1

num. orbitals of irrep B3u = 2

แต่แทนที่จะระบุ orbitals ทั้งหมดตามสมมาตร เราสามารถเขียนได้ง่าย ๆ ว่า:

active_space = range(mol.nelectron // 2 - 2, mol.nelectron // 2 + 2)

ในวิธีนี้ เราเลือก orbitals หลายตัวที่ใกล้กับระดับการเติม (valence และที่ว่างอยู่) ที่นี่มีการเลือก 5 orbitals เพื่อรวมไว้ใน active space (ตัวที่ 6 ถึง 10)

print(
mol.nelectron // 2 - 2,
mol.nelectron // 2 + 2,
)
6 10
  1. ซอฟต์แวร์ของบุคคลที่สาม

มีแพ็กเกจซอฟต์แวร์หลายตัวที่พัฒนาขึ้นสำหรับเคมีควอนตัม บางตัวมี mapper หลายตัวและเครื่องมือสำหรับจำกัด active spaces ขั้นตอนที่อธิบายไว้ด้านบนเป็นขั้นตอนทั่วไปและใช้กับซอฟต์แวร์ของบุคคลที่สามได้เช่นกัน แต่ซอฟต์แวร์อื่นอาจส่งคืนแฮมิลโทเนียนในรูปแบบที่ Qiskit ไม่รับ ตัวอย่างเช่น ซอฟต์แวร์บางตัวส่งคืนแฮมิลโทเนียนในรูปแบบ:

H = -0.042 [] + -0.045 [X0 X1 Y2 Y3] + ... + 0.178 [Z0] + ... + 0.176 [Z2 Z3] + -0.243 [Z3] โปรดสังเกตโดยเฉพาะว่า Gate มีหมายเลข และตัวดำเนินการ identity ไม่ได้แสดงไว้ ซึ่งตรงข้ามกับแฮมิลโทเนียนที่ใช้ใน Qiskit ซึ่งจะเขียนพจน์ [Z2 Z3] เป็น ZZII (Qubit 0 และ 1 ถูกกระทำโดยตัวดำเนินการ identity, Qubit 2 และ 3 ถูกกระทำโดยตัวดำเนินการ Z โดยเรียง Qubit 0 ไว้ทางขวาสุด)

เพื่อรองรับเวิร์กโฟลว์ที่มีอยู่ของคุณ บล็อกโค้ดด้านล่างแปลงจากไวยากรณ์หนึ่งไปยังอีกไวยากรณ์หนึ่ง ฟังก์ชัน convert_openfermion_to_qiskit รับแฮมิลโทเนียนที่สร้างใน OpenFermion หรือ Tangelo (และแมปไปยังตัวดำเนินการ Pauli แล้วโดยใช้ mapper ที่มีอยู่) และจำนวน Qubit ที่ต้องการสำหรับโมเลกุล เป็นอาร์กิวเมนต์

from openfermion import QubitOperator
from qiskit.quantum_info import SparsePauliOp

def convert_openfermion_to_qiskit(
openfermion_operator: QubitOperator, num_qubits: int
) -> SparsePauliOp:
terms = openfermion_operator.terms

labels = []
coefficients = []

for term, constant in terms.items():
# Default set to identity
operator = list("I" * num_qubits)

# Iterate through PauliSum and replace I with Pauli
for index, pauli in term:
operator[index] = pauli
label = "".join(operator)
labels.append(label)
coefficients.append(constant)

return SparsePauliOp(labels, coefficients)

นอกจากนี้ Python notebook นี้ ยังมีโค้ดตัวอย่างครบถ้วนสำหรับการย้ายแฮมิลโทเนียนจากเวิร์กโฟลว์ซอฟต์แวร์อื่น ๆ ไปยัง Qiskit รวมถึงการแปลงด้านบน

ตอนนี้คุณควรมีชุดเครื่องมือสำหรับการได้แฮมิลโทเนียนที่คุณต้องการเพื่อทำการคำนวณเคมีควอนตัมบนคอมพิวเตอร์ควอนตัม IBM®

Source: IBM Quantum docs — updated 15 ม.ค. 2569
English version on doQumentation — updated 7 พ.ค. 2569
This translation based on the English version of approx. 26 มี.ค. 2569