Sintesis Gate Evolution dalam ruang fermionik
Konsep dalam panduan ini pada masa ini hanya tersedia dalam Python API. Fungsi yang setara akan disediakan dalam C API pada keluaran akan datang.
Gate Evolution membawa evolusi masa bagi keseluruhan operator. Sintesis fermion-ke-fermion menulis semula Gate itu menjadi FermionicCircuit yang terdiri daripada Gate fermionik yang lebih kecil. Kedua-dua belah penulisan semula kekal dalam ruang fermionik, dan inilah yang membezakannya daripada sintesis fermion-ke-qubit yang menyusul: groups operator (lihat Kumpulkan sebutan operator: gunakan struktur operator) kekal selepasnya, manakala pemetaan fermion-ke-qubit mengembalikan SparseObservable, yang tidak mempunyai konsep pengumpulan.
Dua kenop memilih kaedah. Evolution.synthesis menetapkannya pada satu Gate, dan laluan Transpiler F2FSynthesis menetapkannya pada keseluruhan litar. Kedua-duanya menerima F2FSynthesisPlugin, iaitu antara muka yang dilaksanakan oleh sebarang penulisan semula sedemikian. Kaedah yang disediakan pakej ini pada masa ini untuk evolusi ialah formula hasil darab, yang digunakan oleh bahagian panduan selebihnya: FermionicLieTrotter untuk formula peringkat pertama (lalai) dan FermionicSuzukiTrotter untuk peringkat genap yang lebih tinggi. Apa-apa lagi yang melaksanakan antara muka ini dipilih dengan cara yang sama, dan bahagian tentang kedua-dua kenop itu terpakai kepadanya tanpa perubahan.
Langkah fermion-ke-fermion ini adalah pilihan. Gate Evolution boleh terus ke peringkat fermion-ke-qubit, yang memetakan dan mensintesisnya secara keseluruhan tanpa mengira bilangan sebutan yang dipegangnya. Memecahkannya terlebih dahulu ialah satu pilihan, dibuat kerana Gate yang terhasil setiap satunya lebih murah atau kerana pemecahan itu mendedahkan struktur yang dapat digunakan oleh peringkat kemudian.
Kedua-dua langkah ini boleh digubah, jadi evolusi boleh berakhir dianggarkan pada salah satu peringkat, pada kedua-duanya, atau pada kedua-duanya tidak. Formula hasil darab membuat anggaran kecuali faktornya berkomut; kaedah sintesis tidak semestinya membuat anggaran sama sekali.
Melangkau langkah ini tidak semestinya bermakna kehilangan pengumpulan sepenuhnya. Membungkus pemeta dengan group_wise() memetakan operator satu kumpulan pada satu masa dan menjumlahkan hasilnya, supaya sebutan Pauli yang dipetakan tiba secara berkumpulan dan bukannya bersilang-selang. Formula hasil darab sebelah qubit mensintesisnya mengikut turutan yang diterimanya, jadi sebutan berkomut yang akhirnya bersebelahan boleh dijadualkan bersama.
Pada Hamiltonian di bawah, itu memulihkan keseluruhan faedah kedalaman dua-qubit hasil pemecahan: 18 berbanding 30, dengan bilangan Gate yang tidak berubah. Perbezaannya ialah di mana struktur itu dinyatakan. Memecahkan dalam ruang fermionik menjadikan setiap kumpulan satu Gate yang berasingan, yang dapat dilihat dan digunakan oleh setiap peringkat kemudian; group_wise meninggalkan satu Gate dan hanya menyusun semula sebutan di dalamnya, yang memadai untuk penjadualan tetapi tidak meninggalkan apa-apa untuk dikerjakan oleh laluan fermionik. Gunakannya apabila evolusi mesti kekal sebagai satu Gate, dan gunakan formula hasil darab fermionik jika tidak.
Bina Hamiltonian berkumpulan
Kedua-dua formula hasil darab memecahkan operator kepada faktor, dan bagi operator berkumpulan faktor tersebut ialah kumpulannya. Oleh itu pengumpulan menentukan kos dan ketepatan hasilnya. Ambil rantai Fermi-Hubbard enam tapak, dikumpulkan mengikut cara sebutannya berkomut: sebutan lompatan pada ikatan genap, sebutan lompatan pada ikatan ganjil, dan interaksi pepenjuru.
>>> from qiskit_fermions.operators import FermionOperator, cre, ann
>>>
>>> def fermi_hubbard_chain(num_sites, tunneling, interaction):
... """A 1D Fermi-Hubbard chain grouped into even bonds, odd bonds, and the interaction."""
... terms, groups = [], []
... for offset in (0, 1):
... for j in range(offset, num_sites - 1, 2):
... terms += [
... ([cre(j), ann(j + 1)], -tunneling),
... ([cre(j + 1), ann(j)], -tunneling),
... ]
... groups += [offset, offset]
... for j in range(num_sites - 1):
... terms.append(([cre(j), ann(j), cre(j + 1), ann(j + 1)], interaction))
... groups.append(2)
... operator = FermionOperator.from_terms(terms)
... operator.groups = groups
... return operator
>>>
>>> num_sites = 6
>>> hamiltonian = fermi_hubbard_chain(num_sites, tunneling=1.0, interaction=2.0)
>>> hamiltonian.num_groups()
3
Lima belas sebutan dalam tiga kumpulan. Setiap kumpulan lompatan memasangkan setiap sebutan dengan pasangan konjugatnya, yang penting: faktor mesti Hermitian supaya eksponennya unitari, dan itu tidak terhasil daripada hakikat bahawa jumlahnya Hermitian. groups_are_hermitian() menyemak konvensyen itu dan bukannya membiarkannya kepada pemeriksaan visual:
>>> from qiskit_fermions.operators.terms.grouping import groups_are_hermitian
>>>
>>> groups_are_hermitian(hamiltonian)
[True, True, True]
Memisahkan daripada pasangannya akan merosakkannya, dan membiarkan operator tidak berkumpulan mempunyai kesan yang sama, kerana kedua-dua formula kemudian memecahkannya sebutan demi sebutan dan yang tunggal tidak Hermitian:
>>> ungrouped = fermi_hubbard_chain(num_sites, tunneling=1.0, interaction=2.0)
>>> ungrouped.groups = range(len(list(ungrouped.iter_terms()))) # one group per term
>>> all(groups_are_hermitian(ungrouped))
False
Faktor sedemikian ialah operator fermionik yang sah, dan pakej ini akan membina Gate untuknya, jadi tiada apa yang merungut sehingga operator yang dipetakan sampai kepada PauliEvolutionGate, yang memerlukan pekali nyata dan menimbulkan ValueError: Operator contains complex coefficients, which are not supported. Menyemak pengumpulan lebih awal menukarkan kegagalan lewat itu menjadi jawapan yang tersedia sebelum sebarang litar dibina.
Tetapkan kaedah pada satu Gate
Lulus kaedah sintesis kepada Gate, kemudian uraikannya. Setiap Gate yang dikeluarkannya kembali sebagai Gate Evolution tersendiri:
>>> from qiskit_fermions.circuit import FermionicCircuit
>>> from qiskit_fermions.circuit.library import Evolution
>>> from qiskit_fermions.circuit.library.synthesis import (
... FermionicLieTrotter,
... FermionicSuzukiTrotter,
... )
>>>
>>> total_time = 1.0
>>>
>>> def factors(synthesis):
... circuit = FermionicCircuit(num_sites)
... circuit.append(
... Evolution(num_sites, hamiltonian, time=total_time, synthesis=synthesis),
... circuit.modes,
... )
... return circuit.decompose().count_ops()["Evolution"]
>>>
>>> factors(FermionicLieTrotter())
3
>>> factors(FermionicSuzukiTrotter(order=2))
5
>>> factors(FermionicSuzukiTrotter(order=2, reps=4))
17
>>> factors(FermionicSuzukiTrotter(order=2, reps=4, merge_adjacent=False))
20
Peringkat pertama mengeluarkan satu faktor bagi setiap kumpulan. Peringkat kedua mengeluarkan palindrom simetri, jadi dua kumpulan luar muncul dua kali pada separuh masa masing-masing, menjadikannya lima. Empat ulangan itu akan memberikan dua puluh, tetapi setiap ulangan berakhir pada kumpulan yang sama dengan permulaan ulangan berikutnya. Gate bersebelahan itu berevolusi di bawah operator yang sama, jadi ia digabungkan menjadi satu Gate dengan masa terjumlah, yang menjimatkan satu Gate bagi setiap sambungan. Lulus merge_adjacent=False untuk mengekalkan jujukan buku teks. Setiap peringkat memecahkan operator dengan cara yang sama; yang berubah ialah susunan faktor dan masa evolusinya, bukan pembahagiannya.
Faktor tersebut ditandakan atomic, jadi menguraikan semula membiarkannya di tempatnya dan bukannya memecahkannya lagi. Penguraian mempunyai titik tetap, yang menjadikannya selamat untuk memanggil decompose() berulang kali.
Tetapkan kaedah untuk keseluruhan saluran paip
Menetapkan synthesis bagi setiap Gate bermakna menyalurkan pilihan melalui segala yang membinanya, termasuk UCC dan UCJ, yang membina Gate mereka secara dalaman. Laluan F2FSynthesis sebaliknya menerapkan satu kaedah pada setiap Evolution dalam litar, jadi pilihan dibuat sekali untuk saluran paip. Ia dikonfigurasikan mengikut nama Gate, sama seperti F2QSynthesis, dan Gate yang tidak dinamakan dibiarkan seperti sedia ada:
>>> from qiskit_fermions.transpiler import FermionicPassManager
>>> from qiskit_fermions.transpiler.passes import F2FSynthesis
>>>
>>> circuit = FermionicCircuit(num_sites)
>>> circuit.append(Evolution(num_sites, hamiltonian, time=total_time), circuit.modes)
>>>
>>> expanded = FermionicPassManager(
... F2FSynthesis({"Evolution": ("SuzukiTrotter", (), {"order": 2, "reps": 4})})
... ).run(circuit)
>>> expanded.count_ops()["Evolution"]
17
Ini ialah bilangan yang sama yang diberikan oleh decompose() untuk kaedah itu di atas. Laluan MergeEvolutions menerapkan penggabungan yang sama pada Gate bersebelahan yang tidak datang daripada satu sintesis, seperti sambungan antara litar berturutan. Laluan ini melaksanakan formula itu sendiri, jadi tiada langkah pengembangan berasingan diperlukan, dan menjalankannya dua kali tidak mengubah apa-apa, kerana faktor yang dikeluarkannya ialah atomic dan oleh itu tidak membawa takrifan tersendiri.
Lulus apply=False untuk hanya memilih kaedah dan menyerahkan pengembangan kepada sesuatu yang lain, seperti Decompose Qiskit (menamakan Gate sebagai Decompose("Evolution") mengehadkannya kepada evolusi, membiarkan Gate fermionik lain kepada pemalam sintesis masing-masing).
Ini berguna untuk memilih kaedah bagi keseluruhan litar terlebih dahulu sambil menangguhkan pengembangan ke titik apabila ia dikehendaki, contohnya untuk memeriksa litar dahulu:
>>> selected = FermionicPassManager(
... F2FSynthesis({"Evolution": ("SuzukiTrotter", (), {"order": 2})}, apply=False)
... ).run(circuit)
>>> selected.count_ops()["Evolution"] # selected, not yet expanded
1
>>> selected.decompose().count_ops()["Evolution"] # expanded on demand
5
Namun, ketahui apa yang dilakukan oleh Gate yang tidak dikembangkan: ia sampai ke peringkat fermion-ke-qubit secara keseluruhan, di mana ia dipetakan tanpa synthesis dirujuk. Itu mudah menyebabkan kesilapan semasa membandingkan kaedah, kerana setiap satunya kemudian menghasilkan output yang sama dan perbandingan secara senyap tidak mengukur apa-apa. Pada Hamiltonian di atas, membiarkan Gate peringkat kedua tidak dikembangkan meruntuhkan litar yang jauh lebih besar di bawah kepada kos satu evolusi terpeta, tanpa mengira kaedah yang dipilih.
Ukur manfaatnya
Peringkat yang lebih tinggi memerlukan kedalaman, jadi persoalannya bukan sama ada ia lebih tepat tetapi sama ada ia lebih tepat bagi setiap Gate. Dua nombor diperlukan bagi setiap kaedah: ralat keadaan yang dihasilkannya, dan kos dua-qubit litar yang menghasilkannya.
Ralat diukur dengan ffsim, yang mensimulasikan FermionicCircuit secara langsung (lihat Bagaimana pakej ini berkaitan dengan ffsim). Tiada pemetaan fermion-ke-qubit terlibat, jadi perbandingan ini mengasingkan formula hasil darab fermionik daripada apa-apa yang mungkin disumbangkan oleh pemetaan atau sintesis sebelah qubit. Ia juga berfungsi dalam sektor bilangan zarah tetap, yang lebih kecil daripada ruang penuh dan merupakan ruang yang sebenarnya diduduki oleh evolusi.
>>> import ffsim
>>> import numpy as np
>>> from scipy.sparse.linalg import expm_multiply
>>> from qiskit_fermions.transpiler.presets import generate_preset_jw_pass_manager
>>>
>>> norb, nelec = num_sites, (3, 0) # three spinless fermions on six modes
>>> initial = ffsim.hartree_fock_state(norb, nelec)
>>>
>>> hamiltonian_operator = ffsim.linear_operator(hamiltonian, norb=norb, nelec=nelec)
>>> exact = expm_multiply(
... -1j * total_time * hamiltonian_operator,
... initial,
... traceA=-1j * total_time * ffsim.trace(hamiltonian, norb=norb, nelec=nelec),
... )
>>>
>>> def cost_and_error(synthesis):
... circuit = FermionicCircuit(num_sites)
... circuit.append(
... Evolution(num_sites, hamiltonian, time=total_time, synthesis=synthesis),
... circuit.modes,
... )
... # the method is set on the gate, so decompose() is what expands it here; a pipeline
... # would reach for F2FSynthesis instead (see the note above)
... circuit = circuit.decompose()
... evolved = ffsim.apply_unitary(initial, circuit, norb=norb, nelec=nelec)
... error = 1 - abs(np.vdot(evolved, exact)) ** 2
... qubit_circuit = generate_preset_jw_pass_manager().run(circuit).decompose(reps=6)
... two_qubit = lambda instruction: len(instruction.qubits) == 2
... return error, qubit_circuit.count_ops()["cx"], qubit_circuit.depth(two_qubit)
>>>
>>> methods = [
... ("order 1, reps 1 ", FermionicLieTrotter()),
... ("order 2, reps 1 ", FermionicSuzukiTrotter(order=2)),
... ("order 1, reps 10", FermionicLieTrotter(reps=10)),
... ("order 2, reps 4 ", FermionicSuzukiTrotter(order=2, reps=4)),
... ]
>>> costs = {label: cost_and_error(synthesis) for label, synthesis in methods}
>>> labels, counts, depths = [], [], []
>>> for label, (error, gates, depth) in costs.items():
... labels.append(label.strip().replace(", ", "\n"))
... counts.append(gates)
... depths.append(depth)
... print(f"{label} error {error:.6f}")
order 1, reps 1 error 0.515997
order 2, reps 1 error 0.180958
order 1, reps 10 error 0.004026
order 2, reps 4 error 0.000421
Kos dua-qubit sengaja ditinggalkan daripada jadual itu. Ia bergantung pada sintesis sebelah qubit yang dipilih Qiskit, yang bertambah baik antara keluaran, jadi nombor yang dicetak di sini hanya akan menjadi petikan sesaat dan bukan sifat formula hasil darab. Rajah di bawah memplotkannya sebaliknya, dan apa yang menjadi asas perbandingan dinyatakan sebagai hubungan:
Baca dua kaedah terakhir bersama, kerana itulah perbandingan yang penting. Peringkat dua dengan empat ulangan kira-kira sepuluh kali lebih tepat daripada peringkat satu dengan sepuluh, dan ia mencapainya dengan lebih sedikit Gate berbelit dan bukan lebih banyak, seperti yang ditunjukkan oleh rajah. Membelanjakan bajet Gate pada peringkat yang lebih tinggi mengatasi membelanjakannya pada lebih banyak langkah peringkat pertama di sini.
Keputusan itu tidak universal. Meningkatkan reps dan meningkatkan order mengurangkan ralat pada kadar yang berbeza bagi jenis kos yang sama, dan yang mana menang bergantung pada operator dan masa yang dievolusikan. Dua baris di atas ialah cara untuk mengetahuinya bagi sesuatu masalah tertentu.
Formula fermionik tergubah dengan formula yang dipilih untuk peringkat fermion-ke-qubit (lihat MapperFnEvolutionSynthesis.product_formula). Apabila kedua-duanya menganggar, ketepatan hasil ditentukan oleh yang lebih lemah antara keduanya, jadi menaikkan peringkat di sini sementara formula sebelah qubit kekal peringkat pertama memberikan sedikit manfaat.
Hadkan laluan kepada Gate yang mendapat manfaat
Peringkat yang lebih tinggi hanya membantu apabila faktor tidak berkomut. Jika semuanya berkomut, formula peringkat pertama sudah tepat dan faktor tambahan hanyalah overhed semata-mata. Operator pepenjuru sepenuhnya menjadikannya nyata, kerana operator nombor berkomut sesama sendiri:
>>> from qiskit_fermions.transpiler.presets import generate_preset_jw_pass_manager
>>>
>>> diagonal = FermionOperator.from_terms(
... [([cre(j), ann(j), cre(j + 1), ann(j + 1)], 1.0 + 0.1 * j) for j in range(num_sites - 1)]
... )
>>> diagonal.groups = list(range(num_sites - 1))
>>>
>>> def diagonal_cost(synthesis):
... circuit = FermionicCircuit(num_sites)
... circuit.append(
... Evolution(num_sites, diagonal, time=total_time, synthesis=synthesis), circuit.modes
... )
... transpiled = generate_preset_jw_pass_manager().run(circuit.decompose())
... return transpiled.decompose(reps=6).count_ops()["cx"]
>>>
>>> diagonal_cost(FermionicLieTrotter())
10
>>> diagonal_cost(FermionicSuzukiTrotter(order=2))
18
Kedua-dua litar tepat, jadi yang kedua membelanjakan 80% lebih banyak Gate berbelit tanpa faedah. Operator Coulomb pepenjuru bagi UCJ mempunyai bentuk ini, itulah sebabnya F2FSynthesis.filter wujud: ia mengambil predikat ke atas DAGOpNode dan membiarkan Gate yang ditolak tidak disentuh, jadi satu laluan boleh menaikkan peringkat pada Gate yang mendapat manfaat dan membiarkan selebihnya pada peringkat pertama.
Litar yang mengandungi kedua-dua jenis evolusi menunjukkan apa yang dijimatkan. Predikat di bawah hanya menerima Gate apabila operatornya menggerakkan zarah antara mod, iaitu tepat apabila kumpulan tidak berkomut dan peringkat yang lebih tinggi mempunyai sesuatu untuk diperoleh:
>>> def is_diagonal(operator):
... """Whether every term only counts particles, rather than moving them between modes."""
... return all(
... sorted(mode for created, mode in actions if created)
... == sorted(mode for created, mode in actions if not created)
... for actions, _ in operator.iter_terms()
... )
>>>
>>> def mixed_circuit():
... circuit = FermionicCircuit(num_sites)
... circuit.append(Evolution(num_sites, hamiltonian, time=total_time), circuit.modes)
... circuit.append(Evolution(num_sites, diagonal, time=total_time), circuit.modes)
... return circuit
>>>
>>> def two_qubit_cost(circuit):
... transpiled = generate_preset_jw_pass_manager().run(circuit).decompose(reps=6)
... two_qubit = lambda instruction: len(instruction.qubits) == 2
... return transpiled.count_ops()["cx"], transpiled.depth(two_qubit)
>>>
>>> order_2 = FermionicSuzukiTrotter(order=2)
>>>
>>> everywhere = two_qubit_cost(
... FermionicPassManager(F2FSynthesis({"Evolution": order_2})).run(mixed_circuit())
... )
>>> restricted = two_qubit_cost(
... FermionicPassManager(
... F2FSynthesis(
... {"Evolution": order_2}, filter=lambda node: not is_diagonal(node.op.operator)
... )
... ).run(mixed_circuit())
... )
>>> everywhere[0] - restricted[0] # the gates the filter saved
8
>>> restricted[0] < everywhere[0] and restricted[1] < everywhere[1]
True
Gate yang dijimatkan oleh penapis ialah tepat yang dibelanjakan oleh peringkat dua pada evolusi pepenjuru, yang dinilai oleh perbandingan pepenjuru di atas pada lapan (18 berbanding 10). Ia tidak memberi apa-apa di situ, kerana evolusi itu sudah tepat pada peringkat pertama, jadi membuangnya tidak menjejaskan ketepatan.
Bandingkan kedua-dua litar peringkat dua sesama sendiri, bukan dengan yang tidak dikembangkan. Membiarkan kedua-dua Gate tidak dikembangkan menghantar setiap satu ke peringkat fermion-ke-qubit secara keseluruhan, iaitu kesilapan yang diterangkan oleh nota apply=False di atas: ia boleh menghasilkan bilangan Gate berbelit yang sama dengan litar yang ditapis sedangkan ia litar yang berbeza pada kedalaman yang berbeza, jadi membaca faedah penapis daripada nombor itu tidak mengukur apa-apa.
Tiada satu pun kenop mengesahkan bahawa faktor adalah Hermitian, dan operator yang dikumpulkan dengan salah boleh kelihatan lebih baik pada peringkat genap berbanding peringkat pertama sedangkan masih salah, kerana simetrisasi membatalkan sebahagian ralat. Kumpulkan pasangan konjugat bersama, seperti yang dilakukan oleh fermi_hubbard_chain di atas.
Langkah seterusnya
-
Baca Kumpulkan sebutan operator: gunakan struktur operator untuk mengetahui cara kumpulan yang dipecahkan oleh formula hasil darab ini ditetapkan.
-
Baca Transpilasi litar fermionik untuk peringkat yang dilalui oleh
FermionicCircuit. -
Lihat Simulasikan dinamik Fermi-Hubbard 1D dengan set aliran untuk kes contoh di mana formula fermionik membekalkan bilangan langkah yang tidak dapat diberikan oleh sintesis sebelah qubit tersuai.
-
Rujuk
synthesisuntuk kaedah sintesis itu sendiri, danFermionicQDriftuntuk alternatif rawak.