Prinsip reka bentuk perwakilan pengendali
Panduan ini menerangkan cara BosonOperator disusun dan cara bekerja dengannya: cara membina satu, cara memeriksa dan menggabungkan pengendali, dan cara membawa dua pengendali ke dalam bentuk yang boleh dibandingkan. Mulakan di sini apabila anda menulis Hamiltonian.
BosonOperator ialah hasil tambah tepat bagi hasil darab pengendali penciptaan dan pemusnahan bosonik dengan pekali kompleks. Ia tidak mempunyai had penghunian. Satu mod bosonik boleh memuatkan boson dalam bilangan sembarangan, jadi ruang Fock-nya berdimensi tak terhingga, dan pemangkasan hanya berlaku apabila pengendali dipetakan ke qubit (lihat Memangkas ruang Fock). Oleh itu, segala-galanya dalam panduan ini berlaku dalam aljabar tepat hubungan kekomutan kanonik,
Dua akibat membentuk antara muka ini. Indeks mod boleh berulang dalam satu sebutan, kerana ialah pengendali sah yang mencipta dua boson. Selain itu, menyusun semula dua faktor tidak pernah memperkenalkan tanda, walaupun menggerakkan pengendali pemusnahan melepasi pengendali penciptaan pada mod yang sama menambah satu sebutan, .
Storan
Pengendali menyimpan sebutannya dalam empat tatasusunan rata, serupa dengan format baris jarang termampat bagi matriks jarang:
coeffs | Satu pekali kompleks bagi setiap sebutan. |
actions | Satu boolean bagi setiap faktor: True untuk penciptaan dan False untuk pengendali pemusnahan. |
modes | Satu indeks mod bagi setiap faktor. |
boundaries | Kedudukan dalam actions dan modes di mana setiap sebutan bermula dan berakhir. |
Pembina menerima keempat-empat tatasusunan ini secara terus. Pengendali di bawah mempunyai tiga sebutan, iaitu , , dan pemalar , dan anda boleh membaca semula tatasusunan tersebut dengan kaedah get_*:
>>> from qiskit_bosons.operators import BosonOperator, ann, cre
>>> op = BosonOperator(
... coeffs=[2.0, -1.0j, 0.5],
... actions=[True, False, True, True],
... modes=[0, 1, 1, 1],
... boundaries=[0, 2, 4, 4],
... )
>>> op.get_coeffs()
[(2+0j), (-0-1j), (0.5+0j)]
>>> op.get_actions()
[True, False, True, True]
>>> op.get_modes()
[0, 1, 1, 1]
>>> op.get_boundaries()
[0, 2, 4, 4]
>>> len(op)
3
Sebutan pertama merentangi kedudukan 0 hingga 2 bagi actions dan modes, yang kedua merentangi 2 hingga 4, dan yang ketiga merentangi 4 hingga 4, iaitu cara sebutan pemalar, sebutan tanpa faktor, disimpan. Mod 1 yang berulang dalam sebutan kedua dikekalkan seperti yang ditulis; sesuatu sebutan tidak pernah dibuang atau digabungkan kerana indeks mod muncul dua kali.
Pengendali yang dibina dengan from_dict() tidak menyimpan sebutannya mengikut tertib kamus, jadi tatasusunannya boleh menyenaraikan sebutan dalam tertib berbeza dari satu larian ke larian yang lain. Pengendali itu sendiri sama, dan tiada apa di bawah yang membandingkan atau mencetak pengendali bergantung pada tertib simpanan.
Susun atur ini menjadikan pembinaan dan aritmetik murah, dengan satu akibat: pengendali boleh memegang sebutan yang sama beberapa kali serentak. Hasil tambah dan hasil darab menambah sebutan dan tidak menggabungkannya, jadi panggil simplify() apabila anda memerlukan sebutan unik (lihat Melakukan aritmetik).
Membina pengendali
Cara paling terus untuk membina pengendali ialah from_dict(), yang menerima kamus daripada tuple faktor kepada pekali. Fungsi cre() dan ann() mencipta faktor, dan tuple kosong ialah sebutan pemalar:
>>> b = BosonOperator.from_dict({(ann(0),): 1.0})
>>> b_dag = BosonOperator.from_dict({(cre(0),): 1.0})
>>> number = BosonOperator.from_dict({(cre(0), ann(0)): 1.0})
>>> print(format(number))
1.000000e0 +0.000000e0j * (+0 -0)
Dalam bentuk bercetak, +0 mewakili dan -0 mewakili . Pembina zero() dan one() mengembalikan identiti penambahan dan pendaraban, dan from_terms() membina semula pengendali daripada iterator iter_terms().
Pustaka pengendali menyediakan kuadratur kedudukan dan momentum tanpa dimensi,
sebagai position() dan momentum(). Bersama fungsi komutator dalam modul yang sama, anda boleh menyemak aljabar secara terus:
>>> from qiskit_bosons.operators.library import commutator, momentum, position
>>> q, p = position(0), momentum(0)
>>> q.is_hermitian() and p.is_hermitian()
True
>>> commutator(q, p).equiv(1j * BosonOperator.one())
True
>>> commutator(b, b_dag).equiv(BosonOperator.one())
True
Modul ini juga menyediakan anti_commutator() dan double_commutator(). Ketiga-tiganya menerima pengendali berjenis sama dan mengembalikan pengendali baharu.
Perhatikan tertib hasil darab
Dua pengendali boleh didarab dalam dua cara, dan keduanya mudah dikelirukan. Pengendali @ ialah hasil darab matematik, jadi a @ b ialah . Pengendali & ialah gubahan, yang mengikut konvensyen Qiskit iaitu membaca dari kiri ke kanan: a & b mengaplikasikan a dahulu kemudian b, jadi ia ialah . Fungsi komutator menggunakan @, jadi commutator(a, b) ialah . Membina kurungan itu sendiri menggunakan & menafikannya secara senyap:
>>> (b @ b_dag).equiv(b_dag @ b + BosonOperator.one())
True
>>> (b & b_dag).equiv(b_dag @ b)
True
>>> ((b @ b_dag) - (b_dag @ b)).equiv(BosonOperator.one())
True
>>> ((b & b_dag) - (b_dag & b)).equiv(-BosonOperator.one())
True
Memeriksa pengendali
Pengendali tidak boleh diiterasi secara langsung, kerana sebutannya berada dalam tatasusunan rata di atas. Gunakan iter_terms() sebaliknya, yang menghasilkan senarai pasangan (is_creation, mode) dan satu pekali bagi setiap sebutan. Iterator mengembalikan salinan, jadi mengubahnya tidak mengubah pengendali.
>>> hopping = BosonOperator.from_dict(
... {
... (cre(0), ann(1)): -1.0,
... (cre(1), ann(0)): -1.0,
... (cre(2), cre(2), ann(2), ann(2)): 0.5,
... }
... )
>>> len(hopping)
3
>>> sorted(hopping.iter_terms())
[([(True, 0), (False, 1)], (-1+0j)), ([(True, 1), (False, 0)], (-1+0j)), ([(True, 2), (True, 2), (False, 2), (False, 2)], (0.5+0j))]
Beberapa kaedah meringkaskan struktur pengendali:
>>> hopping.get_support()
{0, 1, 2}
>>> hopping.min_num_modes()
3
>>> hopping.max_rank()
4
get_support() ialah set mod yang menjadi sasaran pengendali, min_num_modes() ialah register terkecil yang boleh memuatkannya, dan max_rank() ialah panjang sebutan terpanjangnya.
Mod ialah indeks abstrak yang melabelkan satu darjah kebebasan bosonik. Ia tidak mempunyai makna sendiri: anda yang menentukan tapak kekisi, koordinat getaran, atau port optik yang diwakili oleh sesuatu mod. BosonicCircuit menggunakan indeks yang sama (lihat Bekerja dengan litar bosonik), jadi pengendali dan gate yang membawanya bersetuju tentang apa itu mod 2 tanpa salah satunya mengetahui apa yang diwakilinya. Tiada apa dalam pengendali yang menetapkan bilangan aras bagi sesuatu mod, dan itu juga sebab saiz register hanyalah had bawah; min_num_modes() ialah register terkecil yang memuatkan pengendali, bukan sifatnya.
Kaedah penghunian melaporkan bilangan boson yang boleh diletakkan oleh satu sebutan dalam satu mod. Ini ialah had bawah bagi had yang diperlukan oleh pemetaan. Ia bukan had yang mencukupi, kerana aplikasi pengendali berulang, seperti dalam evolusi masa, boleh menghuni aras yang lebih tinggi (lihat Memangkas ruang Fock):
>>> hopping.mode_occupations()
[1, 1, 2]
>>> hopping.mode_occupations([2, 0])
[2, 1]
>>> hopping.max_occupation()
2
Dua semakan lanjut melihat kuantiti terabadi. Pengendali mengekalkan bilangan zarah apabila setiap sebutan mempunyai pengendali penciptaan dan pemusnahan yang sama banyak. Menghantar senarai saiz blok menguji syarat yang sama dalam setiap blok mod berturutan:
>>> hopping.conserves_particle_number()
True
>>> move = BosonOperator.from_dict({(cre(0), ann(2)): 1.0})
>>> move.conserves_sector([3])
True
>>> move.conserves_sector([2, 1])
False
Akhir sekali, relabel_modes() menamakan semula mod: mod i menjadi permutation[i]. Senarai mestilah cukup panjang untuk mengindeks setiap mod pengendali dan tidak boleh mengulang entri:
>>> print(format(move.relabel_modes([5, 6, 7])))
1.000000e0 +0.000000e0j * (+5 -7)
Melakukan aritmetik
Pengendali menyokong penambahan, penolakan, penafian, serta pendaraban dan pembahagian dengan nombor, dalam bentuk pengendali biasa atau di tempat:
>>> total = 2 * b_dag + b_dag - 3j * b
>>> len(total)
3
>>> len(total.simplify())
2
>>> total.simplify().equiv(3 * b_dag - 3j * b)
True
Tiga sebutan total tidak pernah digabungkan, jadi pengendali memegang b_dag dua kali sehingga simplify() menggabungkannya. Kaedah ini menjumlahkan pekali sebutan yang sama dan membuang sebutan yang magnitud jumlahnya di bawah toleransi. Ia ialah operasi struktur: ia membandingkan faktor yang ditulis dan tidak mengaplikasikan hubungan kekomutan. Kaedah berkaitan ichop() membuang pekali kecil di tempat tanpa menggabungkan dahulu, jadi panggil simplify() sebelum ichop() apabila pengendali boleh memegang sebutan pendua.
Hasil darab menggunakan @ atau & seperti yang diterangkan di atas, dan ** menaikkan pengendali kepada kuasa integer:
>>> x = b_dag + b
>>> print(format((x ** 2).simplify()))
1.000000e0 +0.000000e0j * (-0 -0)
1.000000e0 +0.000000e0j * (-0 +0)
1.000000e0 +0.000000e0j * (+0 -0)
1.000000e0 +0.000000e0j * (+0 +0)
Keputusan menyenaraikan empat susunan seperti yang ditulis. Memudahkannya kepada satu bentuk memerlukan hubungan kekomutan, yang diaplikasikan oleh bahagian seterusnya.
Adjoin membalikkan tertib faktor dalam setiap sebutan, menukar pengendali penciptaan dan pemusnahan, serta mengkonjugat pekali. Kaedah is_hermitian() menguji sama ada pengendali sama dengan adjoinnya:
>>> hop = BosonOperator.from_dict({(cre(0), ann(1)): 1j})
>>> print(format(hop.adjoint()))
0.000000e0 -1.000000e0j * (+1 -0)
>>> hop.is_hermitian()
False
>>> (hop + hop.adjoint()).is_hermitian()
True
Sebutan lompatan tunggal bukan Hermitian, yang penting apabila anda mengumpulkan sebutan untuk evolusi masa: setiap kumpulan mestilah Hermitian dengan sendirinya (lihat Mengumpulkan sebutan pengendali: gunakan struktur pengendali).
Tertib normal dan membandingkan pengendali
Dua hasil darab pengendali tangga boleh menjadi pengendali yang sama namun kelihatan berbeza, kerana dan adalah sama dalam aljabar. Pengendali yang sama dalam erti kata ini tidak semestinya disimpan sama, jadi membandingkannya memerlukan hubungan kekomutan. Kaedah normal_ordered() mengaplikasikannya: ia memindahkan setiap pengendali penciptaan ke sebelah kiri setiap pengendali pemusnahan dan menambah sebutan setiap kali pasangan pada mod yang sama ditukar.
>>> product = BosonOperator.from_dict({(ann(0), cre(0), ann(0), cre(0)): 1.0})
>>> ordered = product.normal_ordered()
>>> len(ordered)
5
>>> print(format(ordered.simplify()))
1.000000e0 +0.000000e0j * ()
3.000000e0 +0.000000e0j * (+0 -0)
1.000000e0 +0.000000e0j * (+0 +0 -0 -0)
Pengendali bertertib dikembalikan sebagai hasil tambah tidak bergabung, dengan lima sebutan tersimpan yang menjadi tiga selepas simplify() menggabungkannya. Tertib normal tidak pernah menyebabkan sebutan lenyap: dan setiap faktor berulang yang lain kekal.
Argumen sandwich menetapkan cara mod di dalam dua kumpulan faktor disusun. Secara lalai kedua-dua kumpulan diisih secara menurun. Dengan sandwich=True, pengendali penciptaan menaik dan pengendali pemusnahan menurun, yang meletakkan indeks lebih besar ke arah tengah:
>>> pair = BosonOperator.from_dict({(cre(0), cre(1), ann(0), ann(1)): 1.0})
>>> list(pair.normal_ordered().iter_terms())
[([(True, 1), (True, 0), (False, 1), (False, 0)], (1+0j))]
>>> list(pair.normal_ordered(sandwich=True).iter_terms())
[([(True, 0), (True, 1), (False, 1), (False, 0)], (1+0j))]
Pengendali == menguji kesamaan tepat bagi sebutan tersimpan, tanpa toleransi dan tanpa pemudahan, jadi ia boleh melaporkan perbezaan antara dua pengendali yang sama secara matematik. Kaedah equiv() membawa perbezaan kedua-dua pengendali ke tertib normal terlebih dahulu kemudian menguji bahawa setiap pekali berada di bawah toleransi. Ia ialah perbandingan yang hampir selalu anda mahukan:
>>> reordered = BosonOperator.from_dict({(cre(0), ann(0)): 1.0, (): 1.0})
>>> contracted = BosonOperator.from_dict({(ann(0), cre(0)): 1.0})
>>> contracted == reordered
False
>>> contracted.simplify() == reordered.simplify()
False
>>> contracted.equiv(reordered)
True
Kaedah simplify sahaja membiarkan kedua-dua bentuk berasingan, kerana ia hanya menggabungkan sebutan tertulis yang serupa. Begitu juga, is_hermitian() membandingkan pengendali dengan adjoinnya dalam tertib normal, jadi ia mengenali sebagai Hermitian tanpa kerja tambahan.
Tertib normal ialah identiti tepat dalam aljabar tidak terpangkas, dan equiv bergantung padanya. Ia bukan identiti selepas pemangkasan. Pengendali terpeta bergantung pada bentuk yang anda petakan, jadi imej terpeta bagi contracted dan reordered di atas berbeza, pada aras terpelihara tertinggi sahaja. Lihat Memangkas ruang Fock untuk saiz perbezaan dan cara memilih bentuk.
Menyusun dan menapis sebutan
Menyusun semula sebutan pengendali, berbeza daripada faktor dalam sebutan, tidak mengubah pengendali tetapi boleh mengubah tingkah laku algoritma yang membaca sebutan mengikut tertib, seperti formula hasil darab rawak. Fungsi qiskit_bosons.operators.terms mengembalikan pengendali baharu dan mengekalkan sebarang indeks kumpulan yang dilampirkan pada sebutannya.
canonical_order() mengisih sebutan ke dalam tertib tetap yang hanya bergantung pada faktor tertulis, jadi pengendali kelihatan sama walau bagaimana ia dipasang. order_terms() mengisih mengikut kunci yang anda berikan, yang menerima setiap sebutan sebagai tuple yang dihasilkan oleh iter_terms():
>>> from qiskit_bosons.operators.terms import canonical_order, order_terms
>>> mixed = BosonOperator.from_dict({(cre(1), ann(0)): 1.0, (cre(0), ann(1)): 3.0, (): 2.0})
>>> list(canonical_order(mixed).iter_terms())
[([], (2+0j)), ([(True, 0), (False, 1)], (3+0j)), ([(True, 1), (False, 0)], (1+0j))]
>>> by_weight = order_terms(mixed, key=lambda term: abs(term[1]), reverse=True)
>>> [coeff for _, coeff in by_weight.iter_terms()]
[(3+0j), (2+0j), (1+0j)]
Jika pengendali membawa indeks kumpulan (lihat Mengumpulkan sebutan pengendali: gunakan struktur pengendali), fungsi group_order() menjadikan setiap kumpulan satu rentetan sebutan bersebelahan:
>>> from qiskit_bosons.operators.terms import group_order
>>> grouped = BosonOperator.from_dict({(cre(1), ann(0)): 1.0, (cre(0), ann(1)): 3.0, (): 2.0})
>>> grouped.groups = [1, 0, 1]
>>> group_order(grouped).groups
[0, 1, 1]
Pembantu terakhir membuang sebutan dan bukannya menyusun semula. Sebutan yang merupakan hasil darab pengendali nombor, , hanya mendarab setiap keadaan Fock dengan fasa, jadi filter_diagonal_terms() boleh membuang pemalar, pengendali nombor, dan hasil darabnya apabila fasa itu tidak relevan bagi apa yang anda kira. Ia mengubah pengendali di tempat dan memerlukan pengendali bertertib normal, yang tidak disemaknya:
>>> from qiskit_bosons.operators.terms import filter_diagonal_terms
>>> model = BosonOperator.from_dict(
... {
... (cre(0), ann(1)): -1.0,
... (cre(1), ann(0)): -1.0,
... (cre(0), cre(0), ann(0), ann(0)): 0.5,
... (cre(0), ann(0)): 0.2,
... (): 0.1,
... }
... )
>>> filter_diagonal_terms(model)
>>> len(model)
2
>>> model.is_hermitian()
True
Hanya dua sebutan lompatan yang tinggal. Interaksi dalam-tapak dan pengendali nombor adalah pepenjuru dalam asas penghunian, jadi ia dibuang bersama pemalar. Mod berulang dalam interaksi dikendalikan dengan betul: ialah pepenjuru, manakala bukan.
Langkah seterusnya
-
Mengumpulkan sebutan pengendali: gunakan struktur pengendali menunjukkan cara menanda sebutan dengan indeks kumpulan.
-
Memetakan perwakilan pengendali menukar pengendali kepada pengendali qubit, termasuk pilihan pengekodan dan kesan pemangkasan.
-
Bekerja dengan litar bosonik meletakkan pengendali ke dalam litar sebagai gate
Evolution. -
Mensimulasikan dinamik Bose-Hubbard dengan pilihan pengekodan membina Hamiltonian bagi rantai Bose-Hubbard dan mensimulasikannya dari hujung ke hujung.
-
Modul
qiskit_bosons.operatorsdanqiskit_bosons.operators.termsmendokumenkan antara muka lengkap.