Langkau ke kandungan utama
Projek ini dalam fasa beta. Jumpa pepijat? Buat isu GitHub. Ada soalan atau idea? Tanya dalam Discussions — kami suka dengar maklum balas anda!
Bagaimana kualiti terjemahan pada halaman ini?

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,

[bi,bj†]=δij,[bi,bj]=[bi†,bj†]=0 .[b_i, b_j^\dagger] = \delta_{ij}, \qquad [b_i, b_j] = [b_i^\dagger, b_j^\dagger] = 0 \, .

Dua akibat membentuk antara muka ini. Indeks mod boleh berulang dalam satu sebutan, kerana bi†bi†b_i^\dagger b_i^\dagger 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, bibi†=bi†bi+1b_i b_i^\dagger = b_i^\dagger b_i + 1.

Storan​

Pengendali menyimpan sebutannya dalam empat tatasusunan rata, serupa dengan format baris jarang termampat bagi matriks jarang:

coeffsSatu pekali kompleks bagi setiap sebutan.
actionsSatu boolean bagi setiap faktor: True untuk penciptaan dan False untuk pengendali pemusnahan.
modesSatu indeks mod bagi setiap faktor.
boundariesKedudukan 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 2b0†b12 b_0^\dagger b_1, −ib1†b1†-i b_1^\dagger b_1^\dagger, dan pemalar 0.50.5, 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.

Note

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 b0†b_0^\dagger dan -0 mewakili b0b_0. 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,

q^=b†+b2,p^=i(b†−b)2 ,\hat{q} = \frac{b^\dagger + b}{\sqrt{2}}, \qquad \hat{p} = \frac{i \left(b^\dagger - b\right)}{\sqrt{2}} \, ,

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 ABAB. Pengendali & ialah gubahan, yang mengikut konvensyen Qiskit iaitu membaca dari kiri ke kanan: a & b mengaplikasikan a dahulu kemudian b, jadi ia ialah BABA. Fungsi komutator menggunakan @, jadi commutator(a, b) ialah [A,B][A, B]. 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.

Note

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 b b†b\,b^\dagger dan b†b+1b^\dagger b + 1 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: b†b†b^\dagger b^\dagger 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 b b†+b†bb\,b^\dagger + b^\dagger b sebagai Hermitian tanpa kerja tambahan.

Note

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, bi†bib_i^\dagger b_i, 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: b0†b0†b0b0b_0^\dagger b_0^\dagger b_0 b_0 ialah pepenjuru, manakala b0†b0†b0b_0^\dagger b_0^\dagger b_0 bukan.

Langkah seterusnya​