Lewati ke konten utama
Proyek ini masih dalam tahap beta. Menemukan bug? Buat isu GitHub. Punya pertanyaan atau ide? Tanya di Discussions — kami sangat menghargai masukan kamu!
Bagaimana kualitas terjemahan di halaman ini?

Optimalkan persiapan determinan Slater

Penting

Konsep di panduan ini saat ini hanya tersedia di API Python. Fungsionalitas yang setara akan tersedia di API C pada rilis mendatang.

Sebuah gate InitializeModes mendeklarasikan okupasi mode referensi; gate OrbitalRotation merotasi basis partikel tunggal. Ditempatkan berurutan, kedua gate itu menyiapkan sebuah determinan Slater; mode dimulai dalam determinan referensi lalu dirotasi ke orbital target. Gate PrepareSlaterDeterminant menangkap komposisi itu, dan karena membawa okupasi referensi, transpilasinya bisa memakai givens_decomposition_slater() yang persegi panjang, yang hanya merealisasikan orbital yang terisi, bukan dekomposisi persegi penuh yang dibutuhkan OrbitalRotation saja. Itu sintesis yang jelas lebih murah (lihat hasilnya di akhir panduan ini).

Pass transpiler MergeSlaterDeterminantPreparation mendeteksi pola InitializeModes-lalu-OrbitalRotation di sebuah FermionicCircuit dan menulis ulangnya menjadi gate PrepareSlaterDeterminant, sehingga tahap sintesis berikutnya otomatis memakai dekomposisi yang lebih ringkas. Dalam simulasi, penulisan ulang ini tidak mengubah state: PrepareSlaterDeterminant bersifat validasi-lalu-rotasi (ia memvalidasi okupasi referensi lalu menerapkan rotasi, persis seperti kedua gate terpisah), jadi penggabungan ini membuka sintesis yang lebih murah tanpa mengubah state yang disiapkan.

Di sepanjang panduan ini, setiap circuit digambar sebelum dan sesudah pass, berdampingan: input di kiri, dan hasil menjalankan pass padanya di kanan. Dua helper melakukannya. merge menjalankan pass pada salinan circuit (lewat FermionicDAGCircuit-nya) dan mengonversi hasilnya kembali ke FermionicCircuit yang bisa digambar, dan draw_merge menggambar kedua bagian dalam satu gambar sebelum/sesudah:

>>> import matplotlib.pyplot as plt
>>> import numpy as np
>>>
>>> from qiskit_fermions.circuit import FermionicCircuit
>>> from qiskit_fermions.circuit.library import InitializeModes, OrbitalRotation
>>> from qiskit_fermions.transpiler import FermionicCircuitToDAG
>>> from qiskit_fermions.transpiler.converters import FermionicDAGToCircuit
>>> from qiskit_fermions.transpiler.passes import MergeSlaterDeterminantPreparation
>>>
>>> def merge(circuit):
... dag = FermionicCircuitToDAG().run(circuit)
... merged = MergeSlaterDeterminantPreparation().run(dag)
... return FermionicDAGToCircuit().run(merged)
>>>
>>> def natural_size(circuit):
... # the figure size Qiskit picks for a circuit on its own (discard the probe figure)
... probe = circuit.draw("mpl")
... size = probe.get_size_inches()
... plt.close(probe)
... return size
>>>
>>> def draw_merge(circuit):
... merged = merge(circuit)
... # let Qiskit size each half naturally, then lay them out side by side
... (bw, bh), (aw, ah) = natural_size(circuit), natural_size(merged)
... fig, (before, after) = plt.subplots(1, 2, figsize=(bw + aw, max(bh, ah)))
... circuit.draw("mpl", ax=before)
... merged.draw("mpl", ax=after)
... before.set_title("before")
... after.set_title("after")
... return fig

Apa yang digabungkan​

Pass ini mengenali tiga pola, semuanya bergantung pada konvensi mode block-spin yang dipakai InitializeModes dan OrbitalRotation. Untuk sistem dengan norb orbital spasial, mode 0..norb adalah sektor spin-alpha, dan norb..2*norb adalah sektor spin-beta. Persiapan determinan Slater dilakukan per sektor spin karena setiap sektor punya bentuk (elektron, orbital) sendiri.

Register penuh (atau tanpa spin)​

Pola paling sederhana adalah memakai InitializeModes, langsung diikuti OrbitalRotation pada mode yang sama. Ini kasus tanpa spin (satu determinan atas semua mode) dan juga circuit berspin yang rotasinya mencakup kedua sektor sekaligus. Kedua gate melebur menjadi satu PrepareSlaterDeterminant:

>>> circuit = FermionicCircuit(4)
>>> circuit.append(InitializeModes([1, 1, 0, 0]), circuit.modes)
>>> circuit.append(OrbitalRotation(np.eye(4)), circuit.modes)
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>

A mode initialization and rotation fusing into one Slater determinant preparation.

Per sektor spin​

Bentuknya sama, tapi dibatasi ke satu separuh spin. Di sini, dua pasangan inisialisasi-lalu-rotasi yang independen, satu per sektor, masing-masing melebur sendiri menjadi PrepareSlaterDeterminant:

>>> circuit = FermionicCircuit(6) # norb = 3: alpha modes 0..3, beta modes 3..6
>>> circuit.append(InitializeModes([1, 1, 0]), circuit.modes[:3])
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[:3])
>>> circuit.append(InitializeModes([1, 0, 0]), circuit.modes[3:])
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[3:])
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>

Two per-sector initialize-and-rotate pairs, each fusing into its own preparation.

Inisialisasi global, rotasi per spin​

Ini berarti satu InitializeModes register penuh (2 * norb) diikuti dua aksi OrbitalRotation, satu di tiap separuh spin yang bersebelahan. Pass memisahkan okupasi global di batas sektor dan menghasilkan dua gate PrepareSlaterDeterminant. Kedua rotasi bisa muncul dalam urutan mana pun.

Inilah bentuk yang dihasilkan workflow local unitary cluster Jastrow (LUCJ) (lihat panduan LUCJ); pengguna menaruh InitializeModes (biasanya referensi Hartree-Fock dengan memakai from_hartree_fock()) di depan circuit lalu menambahkan ansatz UCJ, yang dua rotasi orbital per-spin pertamanya langsung mengikuti inisialisasi setelah ansatz didekomposisi.

>>> circuit = FermionicCircuit(6) # norb = 3, nelec = (2, 1)
>>> circuit.append(InitializeModes.from_hartree_fock(3, (2, 1)), circuit.modes)
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[:3])
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[3:])
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>

A global initialization with two per-spin rotations fusing into two preparations.

Pola ini juga berlaku ketika hanya satu separuh spin yang dirotasi oleh gate yang langsung mengikuti inisialisasi, bentuk yang muncul ketika hanya rotasi orbital satu sektor spin yang kebetulan ada di depan circuit. Separuh yang dirotasi menjadi PrepareSlaterDeterminant beserta rotasinya, dan separuh lainnya disiapkan dengan rotasi identitas. Persiapan identitas disintesis menjadi gate X referensi yang memang akan dihasilkan InitializeModes untuk separuh itu, jadi padding-nya tidak menambah gate sama sekali, sementara sintesis Slater yang lebih ringkas tetap terbuka untuk separuh yang dirotasi:

>>> circuit = FermionicCircuit(6)
>>> circuit.append(InitializeModes.from_hartree_fock(3, (2, 1)), circuit.modes)
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[:3])
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>

A global initialization with a single per-spin rotation fusing into two preparations.

Yang dibiarkan apa adanya​

Pass ini sengaja dibuat konservatif. Ia hanya melebur ketika kedua gate benar-benar bisa dikomposisikan menjadi persiapan determinan Slater, dan menyalin yang lainnya tanpa perubahan. “Langsung diikuti” dipahami berdasarkan graf aliran data circuit. OrbitalRotation hanya melebur ketika InitializeModes adalah satu-satunya pendahulunya di semua modenya. Di gambar-gambar di bawah, sebelum dan sesudahnya identik: tidak ada yang melebur.

Ada operasi di antaranya​

Kalau ada gate apa pun yang menyentuh mode di antara inisialisasi dan rotasi, keduanya tidak lagi membentuk persiapan murni, jadi tidak ada yang digabungkan:

>>> from qiskit_fermions.circuit.library import Evolution
>>> from qiskit_fermions.operators import FermionOperator, ann, cre
>>>
>>> circuit = FermionicCircuit(4)
>>> circuit.append(InitializeModes([1, 1, 0, 0]), circuit.modes)
>>> number_op = FermionOperator.from_dict({(cre(0), ann(0)): 1.0})
>>> circuit.append(Evolution(4, number_op, time=0.5), circuit.modes)
>>> circuit.append(OrbitalRotation(np.eye(4)), circuit.modes)
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>

An operation between the initialization and rotation blocks the merge.

Rotasi parsial yang bukan separuh spin​

Rotasi yang mencakup tepat satu separuh spin yang bersebelahan dari inisialisasi register penuh memang melebur (lihat bagian sebelumnya). Tapi rotasi pada himpunan mode parsial lain (lebih sedikit mode daripada inisialisasi dan bukan salah satu dari dua separuh spinnya, seperti sub-rentang yang melintasi batas sektor) bukan persiapan per sektor. Menggabungkannya akan diam-diam menghilangkan peran inisialisasi pada mode yang ditinggalkan, jadi pass membiarkan kedua gate di tempatnya:

>>> circuit = FermionicCircuit(4) # norb = 2: the only spin halves are modes [0, 1] and [2, 3]
>>> circuit.append(InitializeModes([1, 1, 0, 0]), circuit.modes)
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[:3])
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>

A partial rotation that is not a spin half is left unmerged.

Kenapa ini penting: keuntungan sintesis​

Penggabungan ini bermanfaat karena PrepareSlaterDeterminant disintesis menjadi jauh lebih sedikit gate daripada OrbitalRotation yang diserapnya. Determinan dengan mm elektron atas nn orbital butuh paling banyak m(n−m)m(n-m) rotasi dua-qubit dan tanpa gate fase diagonal, dibandingkan n(n−1)/2n(n-1)/2 rotasi ditambah nn fase pada rotasi orbital persegi penuh.

Pass manager Jordan-Wigner preset menjalankan MergeSlaterDeterminantPreparation di tahap optimasinya, jadi pengurangan ini terjadi otomatis. Transpilasikan persiapan dua elektron dan enam orbital sampai menjadi QuantumCircuit lalu hitung gate-nya:

Catatan

Preset menjalankan MergeOrbitalRotations tepat sebelum MergeSlaterDeterminantPreparation. Ini penting ketika serangkaian gate OrbitalRotation berurutan mengikuti inisialisasi. Pass yang lebih awal lebih dulu meringkas rangkaian itu menjadi satu rotasi, yang kemudian dikontraksikan oleh MergeSlaterDeterminantPreparation dengan inisialisasi menjadi satu PrepareSlaterDeterminant. Tanpa urutan itu, hanya rotasi pertama dari rangkaian yang terserap dan sisanya akan disintesis dengan dekomposisi persegi penuh.

>>> from qiskit_fermions.transpiler.presets import generate_preset_jw_pass_manager
>>>
>>> # a reproducible random 6 x 6 orbital rotation
>>> rng = np.random.default_rng(1)
>>> z = rng.standard_normal((6, 6)) + 1j * rng.standard_normal((6, 6))
>>> q, r = np.linalg.qr(z)
>>> rotation = q * (np.diagonal(r) / np.abs(np.diagonal(r)))
>>>
>>> circuit = FermionicCircuit(6)
>>> circuit.append(InitializeModes([1, 1, 0, 0, 0, 0]), circuit.modes)
>>> circuit.append(OrbitalRotation(rotation), circuit.modes)
>>>
>>> pm = generate_preset_jw_pass_manager()
>>> prepared = pm.run(circuit)
>>> print(dict(sorted(prepared.count_ops().items())))
{'x': 2, 'xx_plus_yy': 8}
>>> prepared.draw("mpl", fold=-1)
<Figure size ... with 1 Axes>

The synthesized merged Slater determinant preparation.

Bandingkan dengan mensintesis rotasi orbital yang sama sendirian. Tanpa inisialisasi sebelumnya, tidak ada yang bisa digabung, jadi preset kembali ke dekomposisi persegi penuh, yang lebih dalam dan membawa gate fase diagonal:

>>> reference = FermionicCircuit(6)
>>> reference.append(OrbitalRotation(rotation), reference.modes)
>>> reference_prepared = pm.run(reference)
>>> print(dict(sorted(reference_prepared.count_ops().items())))
{'rz': 6, 'xx_plus_yy': 15}
>>> reference_prepared.draw("mpl", fold=-1)
<Figure size ... with 1 Axes>

The synthesized bare orbital rotation, with its extra phase gates.

Dekomposisi yang lebih ringkas membuang keenam gate fase dan hampir separuhkan jumlah rotasi dua-qubit. Determinan yang disiapkan benar sampai fase global (tidak relevan secara fisis untuk persiapan state), yang memang akan diperbaiki oleh gate fase.

Dua cara untuk menguraikan gate​

Sebuah PrepareSlaterDeterminant bisa diuraikan dengan dua cara berbeda, dan keduanya tidak bisa saling menggantikan:

decompose()​

Mengembangkan gate menjadi definisinya: InitializeModes untuk referensi diikuti OrbitalRotation penuh. Ini setara dengan gate sebagai operator, untuk state input apa pun.

PrepareSlaterDeterminant.synthesis, applied by F2FSynthesis​

Mengganti gate dengan dekomposisi yang lebih ringkas dari panduan ini. Ini setara dengan gate hanya sebagai persiapan state: menghasilkan determinan yang sama, sampai fase global, ketika mode dimulai dalam vakum. Ia lebih murah justru karena mengasumsikan hal itu.

Pakai sintesis ketika gate menyiapkan state pada vakum, yang memang tujuan gate ini dan yang dilakukan transpiler untukmu. Pakai decompose() ketika gate harus tetap bekerja sebagai operator, misalnya karena gate lain mendahuluinya pada mode yang sama, atau ketika kamu ingin memeriksa atau mensimulasikan gate biasanya. Pass F2FSynthesis tidak bisa tahu kasus mana yang sedang dihadapinya, jadi circuit yang kamu berikan yang harus menyiapkan determinan dari vakum.

Mensimulasikan circuit dengan ffsim tidak butuh keduanya: setiap gate menerapkan seluruh uniternya, jadi circuit yang memuat PrepareSlaterDeterminant disimulasikan apa adanya, dan tetap di jalur cepat untuk gate OrbitalRotation di sekitarnya. Menguraikan hanya gate Slater dengan circuit.decompose("PrepareSlaterDeterminant") juga membiarkan rotasi-rotasi itu utuh.