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?

Transpile ansatz LUCJ

Penting

Konsep-konsep di panduan ini hanya tersedia di API Python.

Ansatz local unitary cluster Jastrow (LUCJ) adalah parametrisasi yang ringkas dan efisien untuk hardware dari fungsi gelombang elektronik yang terkorelasi. Ini anggota dari keluarga unitary cluster Jastrow (UCJ) yang lebih umum dan berbentuk

∣Ψ⟩=(∏k=1LUk eiJk Uk†)∣Φ0⟩,\lvert \Psi \rangle = \left(\prod_{k=1}^{L} \mathcal{U}_k\, e^{i \mathcal{J}_k}\, \mathcal{U}_k^\dagger\right) \lvert \Phi_0 \rangle,

dengan ∣Φ0⟩\lvert \Phi_0 \rangle adalah state referensi (biasanya Hartree-Fock), tiap Uk\mathcal{U}_k adalah rotasi orbital, dan tiap Jk\mathcal{J}_k adalah operator Coulomb diagonal

J=12∑ij,στJijστ niσ njτ,\mathcal{J} = \frac12 \sum_{ij,\sigma\tau} \mathbf{J}^{\sigma\tau}_{ij}\, n_{i\sigma}\, n_{j\tau},

dengan niσn_{i\sigma} adalah operator bilangan pada orbital spasial ii dengan spin σ\sigma.

ffsim membangun operator ansatz; package ini mengubahnya jadi circuit dan menurunkan circuit itu ke qubit. Panduan ini membahas bagian kedua dari kalimat tadi. Alasan menempuh jalur ini adalah proses penurunan itu sendiri: gate Qiskit milik ffsim khusus untuk transformasi Jordan-Wigner, sedangkan FermionicCircuit tidak berasumsi apa pun soal encoding fermion-ke-qubit, sehingga ansatz yang sama bisa disintesis lewat encoding apa pun yang cocok dengan perangkat target.

Jadi operator ansatz tiba di sini dalam keadaan siap pakai, dan semua yang ada di bawah ini adalah apa yang terjadi padanya setelahnya: gate itu terurai menjadi apa, bagaimana ia diturunkan lewat Jordan-Wigner, bagaimana ia dicocokkan dengan coupling map perangkat, dan apa lagi yang masih dibutuhkan penurunan non-Jordan-Wigner. Untuk membangun, memparametrisasi, atau mensimulasikan ansatz UCJ sejak awal, lihat panduan ffsim sendiri.

Lihat juga

Panduan hubungan ffsim untuk pembagian kerja kedua package, dan panduan transpilasi untuk tahap-tahap pipeline yang dipakai di bawah.

1. Ambil operator LUCJ dari ffsim​

Ansatz diinisialisasi dari amplitudo perhitungan coupled-cluster singles and doubles (CCSD). Jalankan restricted Hartree-Fock lalu CCSD untuk molekul hidrogen di basis 6-31g, dengan PySCF untuk kimia kuantumnya, lalu serahkan amplitudonya ke from_t_amplitudes() milik ffsim. Fungsi ini melakukan double factorization pada amplitudo t2t_2 untuk mendapatkan matriks Coulomb diagonal dan rotasi orbital per layer, dan menurunkan rotasi orbital akhir yang opsional dari amplitudo t1t_1. Jumlah pengulangan LL adalah apa pun hasil faktorisasi itu; n_reps memotongnya, menukar akurasi dengan circuit yang lebih dangkal.

>>> import ffsim
>>> import pyscf
>>> import pyscf.cc
>>>
>>> # build the molecule and run Hartree-Fock
>>> mol = pyscf.gto.Mole()
>>> mol.build(
... atom=[["H", (0, 0, 0)], ["H", (0, 0, 0.74)]],
... basis="6-31g",
... symmetry="Dooh",
... verbose=0,
... )
<pyscf.gto.mole.Mole object at ...>
>>> scf = pyscf.scf.RHF(mol).run()
>>>
>>> norb = scf.mo_coeff.shape[1]
>>> nelec = (mol.nelec[0], mol.nelec[1])
>>>
>>> # run CCSD for the t-amplitudes, then factorize them into an ansatz operator
>>> ccsd = pyscf.cc.CCSD(scf).run()
>>> t1, t2 = ccsd.t1, ccsd.t2
>>>
>>> ucj_op = ffsim.UCJOpSpinBalanced.from_t_amplitudes(t2, t1=t1, n_reps=2)

Di sinilah ansatz berhenti jadi urusan ffsim. ffsim menawarkan pilihan di sini yang tidak dilihat maupun perlu diketahui package ini, seperti faktorisasi ("terkompresi") yang dioptimalkan secara variasional di balik optimize=True, atau pengemasan vektor parameter yang dikendalikan oleh optimizer variasional. Semuanya menghasilkan jenis operator yang sama, dan semua yang ada di bawah berjalan tanpa perubahan pada salah satunya.

2. Ubah operator jadi fermionic circuit​

Gate UCJ membungkus operator dan mengekspresikannya sebagai circuit di atas mode fermionik. Gate ini adalah unitary murni tanpa referensi sendiri, jadi tambahkan gate InitializeModes di depannya (dibuat dengan from_hartree_fock()) untuk menyediakan referensi Hartree-Fock tempat ansatz diterapkan.

>>> from qiskit_fermions.circuit import FermionicCircuit
>>> from qiskit_fermions.circuit.library import InitializeModes, UCJ
>>>
>>> ansatz = UCJ(ucj_op)
>>>
>>> circuit = FermionicCircuit(2 * norb)
>>> circuit.append(InitializeModes.from_hartree_fock(norb, nelec), circuit.modes)
>>> circuit.append(ansatz, circuit.modes)

Mendekomposisi circuit memperlihatkan anatominya, dan inilah representasi yang jadi dasar kerja sisa panduan ini. Gate InitializeModes menyiapkan determinan referensi, dan tiap layer ansatz menyumbang sebuah OrbitalRotation Uk†\mathcal{U}_k^\dagger, lalu eiJke^{i\mathcal{J}_k} (sebuah Evolution dari operator Coulomb diagonal Jk\mathcal{J}_k), lalu Uk\mathcal{U}_k, dengan OrbitalRotation terakhir di ujung. Rotasi orbital bekerja per sektor spin, jadi masing-masing ditempatkan pada mode alpha 0..norb dan mode beta norb..2*norb secara independen.

>>> circuit.decompose().draw("mpl", fold=-1)
<Figure size ... with 1 Axes>

The gates that the UCJ ansatz decomposes into.

Setiap gate dalam dekomposisi itu masih fermionik: OrbitalRotation dan Evolution didefinisikan pada mode, dan belum ada qubit atau operator Pauli yang muncul. Itulah yang membuat encoding tetap terbuka, dan ansatz tetap berada di keadaan ini sampai tahap sintesis memilih satu.

Catatan

Tiap layer berakhir dengan Uk\mathcal{U}_k dan layer berikutnya dimulai dengan Uk+1†\mathcal{U}_{k+1}^\dagger, jadi gate OrbitalRotation yang bersebelahan bisa digabung jadi satu rotasi. Pass transpilasi yang melakukan fusi ini direncanakan untuk pengembangan mendatang.

3. Pilih encoding fermion-ke-qubit​

Circuit ini masih fermionik, dan di titik inilah package ini menunjukkan gunanya dalam workflow. FermionicCircuit tidak berasumsi bagaimana mode menjadi qubit, jadi encoding adalah pilihan di tahap sintesis, bukan sesuatu yang tertanam di ansatz. Itulah nilai tambah package ini dibanding gate Qiskit milik ffsim, yang khusus untuk transformasi Jordan-Wigner.

Untuk Jordan-Wigner saja, kamu tidak butuh jalan memutar ini: ffsim menyediakan UCJOpSpinBalancedJW dan saudara-saudaranya, yang bisa membawa operator UCJ ke circuit qubit sendirian. Alasan melewatkan ansatz lewat fermionic circuit adalah segala hal lain yang bisa diberikan sebuah encoding. Encoding lokal, misalnya, mengorbankan qubit tambahan untuk membatasi bobot Pauli tiap suku, yang dimanfaatkan panduan flow-set 1D dan 2D agar depth two-qubit satu langkah Trotter tidak bergantung pada ukuran sistem.

Khusus untuk ansatz UCJ, tahap sintesis harus mencakup dua jenis gate:

  • Evolusi Coulomb diagonal. Evolution sudah agnostik terhadap encoding: MapperFnEvolutionSynthesis menerima sebuah fungsi mapper, jadi layer eiJke^{i \mathcal{J}_k} diturunkan lewat encoding apa pun yang bisa kamu ekspresikan sebagai fungsi mapper. Panduan flow-set menunjukkan cara menulisnya.

  • Rotasi orbital dan persiapan Slater-determinant. Baik OrbitalRotation maupun PrepareSlaterDeterminant yang jadi hasil fusi dari InitializeModes di depan dan rotasinya (lihat di bawah) tidak di-map ke qubit begitu saja. Pass F2FSynthesis lebih dulu memecahnya, di ruang fermionik, menjadi gate Evolution pada satu atau dua mode masing-masing (dan, untuk persiapan, sebuah InitializeModes dari referensi), sehingga encoding apa pun yang bisa me-map sebuah Evolution bisa menanganinya. Di bawah Jordan-Wigner, faktor antar mode bersebelahan menjadi objek XXPlusYYGatepada tahap qubit.

  • Inisialisasi. Plugin untuk InitializeModes tidak diparametrisasi oleh encoding: plugin ini mendokumentasikan asumsinya berupa encoding basis okupasi dengan mapping mode-ke-qubit 1-ke-1, dan langsung mengeluarkan gate pada basis itu. Encoding yang berbeda butuh plugin sendiri untuk ini, dan belum ada yang disediakan.

Cara mentranspilasi circuit UCJ di bawah encoding non-Jordan-Wigner masih merupakan pertanyaan riset terbuka, dan package ini menyediakan kerangka untuk mengembangkan pipeline semacam itu. Sisa panduan ini menyusun pipeline transpiler untuk encoding Jordan-Wigner, setara dengan yang dihasilkan oleh generate_preset_jw_pass_manager(), sebagai contoh.

4. Turunkan lewat Jordan-Wigner​

Penurunan terjadi di F2QSynthesis, yang cara kerjanya mirip HighLevelSynthesis milik Qiskit: ia menelusuri instruksi fermionik dan menyerahkan tiap instruksi ke plugin yang terdaftar untuk nama gate tersebut. Atribut F2QSynthesis.methods menyimpan satu F2QSynthesisPlugin per nama gate, jadi menyusun tahap ini berarti memilih plugin untuk setiap gate fermionik yang ada di circuit:

>>> from qiskit_fermions.mappers.library import jordan_wigner
>>> from qiskit_fermions.transpiler.passes import F2QSynthesis
>>> from qiskit_fermions.transpiler.passes.synthesis import (
... MapperFnEvolutionSynthesis,
... TrivialOccupationInitializeModesSynthesis,
... )
>>>
>>> synthesis = F2QSynthesis()
>>> synthesis.methods["Evolution"] = MapperFnEvolutionSynthesis(jordan_wigner)
>>> synthesis.methods["InitializeModes"] = TrivialOccupationInitializeModesSynthesis()

Menetapkan instance seperti ini juga cara plugin buatanmu sendiri masuk ke pipeline, tanpa perlu mendaftarkannya di mana pun lebih dulu.

Di sinilah encoding ditetapkan, lewat dua cara yang dijelaskan di bagian 3: secara eksplisit, dengan menyerahkan jordan_wigner() ke MapperFnEvolutionSynthesis, dan secara implisit, lewat plugin lain yang sudah mengasumsikannya.

Tahap itu lalu dimasukkan ke MultiStagePassManager yang tahapnya berjalan dalam urutan tetap:

>>> from qiskit.passmanager import MultiStagePassManager
>>> from qiskit.transpiler import generate_preset_pass_manager
>>> from qiskit.transpiler.passes.synthesis.high_level_synthesis import HLSConfig
>>>
>>> from qiskit_fermions.transpiler import (
... FermionicCircuitToDAG,
... FermionicPassManager,
... QuantumDAGToCircuit,
... )
>>> from qiskit_fermions.transpiler.passes import (
... F2FSynthesis,
... HopEvolutionSynthesis,
... MergeOrbitalRotations,
... MergeSlaterDeterminantPreparation,
... TrivialF2QLayout,
... )
>>>
>>> pm = MultiStagePassManager(
... input=FermionicCircuitToDAG(),
... optimization=FermionicPassManager(
... [
... MergeOrbitalRotations(),
... MergeSlaterDeterminantPreparation(),
... F2FSynthesis(
... {"OrbitalRotation": "Givens", "PrepareSlaterDeterminant": "Givens"}
... ),
... ]
... ),
... layout=FermionicPassManager(TrivialF2QLayout()),
... synthesis=synthesis,
... qubit=generate_preset_pass_manager(
... hls_config=HLSConfig(PauliEvolution=[HopEvolutionSynthesis(), "default"])
... ),
... output=QuantumDAGToCircuit(),
... )

Tahap optimization berjalan sebelum sintesis, pada fermionic circuit, dan inilah yang membuatnya berharga: ia menulis ulang gate selagi masih fermionik dan agnostik terhadap encoding, sehingga penghematannya terbawa ke encoding apa pun yang menyusul. MergeOrbitalRotations menyatukan tiap rangkaian gate OrbitalRotation yang berurutan menjadi satu, yang penting untuk ansatz UCJ karena setiap layer berakhir dengan Uk\mathcal{U}_k dan layer berikutnya dimulai dengan Uk+1†\mathcal{U}_{k+1}^\dagger. MergeSlaterDeterminantPreparation kemudian meleburkan InitializeModes di depan dan rotasi sesudahnya menjadi satu PrepareSlaterDeterminant, yang oleh sintesis diturunkan lewat dekomposisi Slater tereduksi yang lebih murah, bukan rotasi persegi penuh. Urutannya penting: menggabungkan rotasi lebih dulu memunculkan satu rotasi tepat setelah inisialisasi, yang kemudian dilebur ke dalamnya oleh fusi Slater. F2FSynthesis ada paling akhir, karena ia memecah rotasi apa pun yang tersisa, dan melakukannya lebih awal akan membuat dua pass penggabung tidak punya apa-apa untuk dilebur.

Tahap-tahap sisanya hanyalah pipa penghubung. input dan output mengonversi antara circuit dan DAG, layout menetapkan mode fermionik ke qubit (TrivialF2QLayout me-map mode i ke qubit i), dan qubit adalah pass manager Qiskit biasa yang mengambil alih begitu tidak ada lagi yang fermionik.

Menjalankannya butuh satu langkah persiapan: gate komposit UCJ harus didekomposisi dulu menjadi gate primitifnya, supaya tahap optimization bisa melihat rotasi dan evolusi satu per satu.

>>> transpiled = pm.run(circuit.decompose())
>>> print(dict(sorted(transpiled.count_ops().items())))
{'rz': 14, 'rzz': 12, 'x': 2, 'xx_plus_yy': 28}

Tanpa perangkat target, ini di-map ke 2 * norb qubit dengan asumsi konektivitas all-to-all; rotasi orbital disintesis menjadi objek XXPlusYYGatedan evolusi Coulomb diagonal menjadi objek RZZGate:

>>> transpiled.draw("mpl", fold=-1)
<Figure size ... with 1 Axes>

The Jordan-Wigner transpiled LUCJ circuit.

Pipeline inilah yang dirakit oleh generate_preset_jw_pass_manager(), jadi dalam praktik pakai preset-nya, dan susun tahap-tahapnya secara manual hanya kalau kamu perlu mengubah salah satunya:

>>> from qiskit_fermions.transpiler.presets import generate_preset_jw_pass_manager
>>>
>>> preset = generate_preset_jw_pass_manager()
>>> preset.run(circuit.decompose()).count_ops() == transpiled.count_ops()
True

5. Sesuaikan dengan topologi perangkat​

Perangkat nyata punya coupling map qubit yang terbatas, dan ansatz LUCJ dirancang agar cocok dengannya. Interaksi spin-sama (pairs_aa) membentuk dua rantai linear dan interaksi alpha-beta (pairs_ab) menjembatani keduanya. generate_lucj_pass_manager() milik ffsim membangun pipeline qubit yang sadar-perangkat untuk struktur ini, dan mengembalikan subset pairs_ab yang benar-benar bisa ditampung hardware. Masukkan pipeline itu ke tahap qubit milik preset sambil mempertahankan sintesis fermion-ke-qubit bawaan package ini:

>>> from ffsim.qiskit import generate_lucj_pass_manager
>>> from qiskit.providers.fake_provider import GenericBackendV2
>>> from qiskit.transpiler import CouplingMap
>>>
>>> # a heavy-hex device coupling map (any BackendV2 works, e.g. a real fake_provider backend)
>>> coupling_map = CouplingMap.from_heavy_hex(5)
>>> backend = GenericBackendV2(
... num_qubits=coupling_map.size(),
... basis_gates=["cp", "xx_plus_yy", "p", "x", "swap"],
... coupling_map=coupling_map,
... )
>>>
>>> # nearest-neighbor same-spin chain; let the pass manager choose the alpha-beta pairs
>>> pairs_aa = [(p, p + 1) for p in range(norb - 1)]
>>>
>>> pm = generate_preset_jw_pass_manager()
>>> hls_config = HLSConfig(PauliEvolution=[HopEvolutionSynthesis(), "default"])
>>> pm.qubit, allowed_pairs_ab = generate_lucj_pass_manager(
... backend,
... norb,
... "heavy-hex",
... (pairs_aa, None),
... optimization_level=3,
... seed_transpiler=0,
... hls_config=hls_config,
... )
>>>
>>> # the alpha-beta interactions the heavy-hex connectivity can implement
>>> print(allowed_pairs_ab)
[(0, 0)]

Komposisi itulah intinya: tahap qubit sadar-perangkat, sementara tahap sintesis fermion-ke-qubit di depannya tetap milik package ini, sehingga tetap bisa diganti. Mengganti tahap qubit memang menghilangkan apa yang didaftarkan preset di situ, yaitu HopEvolutionSynthesis yang mengubah faktor Givens dari rotasi orbital menjadi objek XXPlusYYGateTeruskan lewat hls_config seperti di atas, atau basis perangkat harus dicapai tanpanya.

Dengan pm.qubit sekarang diatur ke pipeline yang sadar-perangkat, menjalankan pass manager akan menata circuit pada qubit backend dan merutekannya ke coupling map. Untuk ansatz yang tidak dibatasi dari langkah 1, yang operator Coulomb diagonalnya masih berisi suku alpha-beta yang tidak bisa dijangkau hardware secara langsung, router harus menyisipkan banyak gate SWAP untuk menjembataninya:

>>> naive = pm.run(circuit.decompose())
>>> naive.num_qubits # laid out on the full heavy-hex device register
57
>>> naive_swaps = naive.count_ops()["swap"]
>>> naive_swaps # many SWAPs to bridge the unreachable alpha-beta interactions
33

Batasi ansatz pada interaksi yang bisa diimplementasikan hardware

Solusinya adalah memasukkan allowed_pairs_ab kembali ke konstruksi ansatz, lewat argumen interaction_pairs dari from_t_amplitudes(), sehingga operator Coulomb diagonal hanya berisi suku alpha-beta yang bisa diimplementasikan langsung oleh coupling map. Ansatz lalu cocok dengan topologi perangkat dan router nyaris tidak perlu menyentuhnya:

>>> restricted = UCJ(
... ffsim.UCJOpSpinBalanced.from_t_amplitudes(
... t2, t1=t1, n_reps=2, interaction_pairs=(pairs_aa, allowed_pairs_ab)
... )
... )
>>>
>>> circuit = FermionicCircuit(2 * norb)
>>> circuit.append(InitializeModes.from_hartree_fock(norb, nelec), circuit.modes)
>>> circuit.append(restricted, circuit.modes)
>>>
>>> transpiled = pm.run(circuit.decompose())
>>> restricted_swaps = transpiled.count_ops()["swap"]
>>> restricted_swaps # far fewer routing SWAPs than the unrestricted ansatz
4

Menggambar hanya qubit yang aktif (idle_wires=False) menunjukkan circuit yang dibatasi pada dua rantai spin dan jembatan alpha-beta, diekspresikan dalam basis gate perangkat. Layout dan routing menyebar mode logis ke seluruh qubit fisik perangkat, jadi berikan wire_order yang diambil dari layout akhir circuit (final_index_layout() mendaftar qubit fisik tempat tiap qubit input berakhir, berurutan sesuai qubit input) untuk menggambar wire kembali dalam urutan mode semula:

>>> wire_order = transpiled.layout.final_index_layout(filter_ancillas=False)
>>> transpiled.draw("mpl", idle_wires=False, fold=-1, wire_order=wire_order)
<Figure size ... with 1 Axes>

The hardware-restricted LUCJ circuit routed onto the heavy-hex device coupling map.

Catatan

Jumlah gate dan depth persis setelah layout bergantung pada pass routing/optimasi dan perangkat yang dipilih, jadi tidak ditampilkan di sini. Poin pentingnya adalah co-design; mengekspresikan ansatz dengan rantai pairs_aa tetangga-terdekat dan jembatan pairs_ab yang difilter hardware menjaga circuit hasil sintesis tetap dekat dengan topologi perangkat, meminimalkan overhead routing (gate SWAP yang disisipkan). Lihat FermionicSlaterGivensDecomposition untuk pilihan sintesis terkait (minimize_2q_gate_count), yang menukar jumlah gate two-qubit dengan depth hasil routing.

Langkah berikutnya​

  • Pelajari cara kerja masing-masing gate di dokumentasi qiskit_fermions.circuit.library dan panduan fermionic circuit.

  • Jelajahi panduan penjelasan operator untuk memahami cara membangun operator fermionik dan Hamiltonian secara langsung.

  • Lihat bagaimana fermionic circuit di-map ke qubit di panduan transpilasi.

  • Baca dokumentasi F2QSynthesis untuk cara lain mengonfigurasi tahap sintesis: dictionary config yang memilih plugin lewat nama pendek yang mereka daftarkan di entry point qiskit_fermions.transpiler.synthesis, bukan lewat instance.

  • Baca panduan hubungan ffsim untuk memahami bagaimana kedua package membagi kerja, dan protokol mana yang memungkinkan ffsim mensimulasikan circuit package ini.