Petakan perwakilan pengendali
Sebuah pemeta menukarkan BosonOperator menjadi pengendali qubit, di sini SparseObservable. Pengendali itu sendiri tidak tahu apa-apa tentang qubit: ia menyimpan pengendali tangga pada mod, dan sesuatu mod boson memuatkan zarah dalam bilangan sembarangan. Segala yang menjadikan pengendali itu terhingga (berapa banyak aras penghunian yang anda simpan, dan bagaimana aras itu disusun pada qubit) ditentukan semasa anda memetakannya.
Pemeta pustaka occupation_encoding() bekerja dalam asas penghunian (Fock): aras bagi sesuatu mod ialah keadaan dengan boson. Ia menerima pengendali dan sebuah LevelSpec, yang menyimpan pemangkasan dan pengekodan. Panduan ini menerangkan maksud kedua-dua pilihan itu dan kos setiap satu. Untuk laluan hujung ke hujung yang menggunakannya, lihat Simulasikan dinamik Bose-Hubbard dengan pilihan pengekodan; untuk cara Transpiler menerapkan pilihan yang sama pada keseluruhan litar, lihat Transpile litar boson.
Pangkas ruang Fock
Sesuatu mod yang dikekalkan pada aras, dengan ialah max_occupationnya, tidak lagi mematuhi hubungan kalis tukar kanonik. Menulis bagi pengendali pemusnah yang dipangkas kepada aras-aras tersebut,
maka hubungan itu berlaku pada setiap aras kecuali yang tertinggi [1]. Anda boleh melihat ini dengan memetakan komutator kedua-dua pengendali tangga dan membaca pepenjurunya pada aras satu mod. Pembantu berikut membina matriks tumpat bagi pengendali yang dipetakan untuk satu mod, yang hanya munasabah untuk daftar yang sangat kecil:
>>> import numpy as np
>>> from qiskit.quantum_info import SparsePauliOp
>>> from qiskit_bosons.mappers import LevelEncoding, LevelSpec
>>> from qiskit_bosons.mappers.library import occupation_encoding
>>> from qiskit_bosons.operators import BosonOperator, ann, cre
>>>
>>> def level_diagonal(operator, max_occupation, encoding):
... """The diagonal of a mapped one-mode operator on the codewords of the levels."""
... spec = LevelSpec(max_occupation, encoding)
... matrix = SparsePauliOp.from_sparse_observable(occupation_encoding(operator, spec)).to_matrix()
... codewords = [spec.encoding.level_to_bits(level) for level in range(max_occupation + 1)]
... return [round(float(matrix[c, c].real), 6) for c in codewords]
>>>
>>> b = BosonOperator.from_dict({(ann(0),): 1.0})
>>> b_dagger = BosonOperator.from_dict({(cre(0),): 1.0})
>>> commutator = (b @ b_dagger) - (b_dagger @ b)
>>> level_diagonal(commutator, 2, "unary_unprojected") # d = 3
[1.0, 1.0, -2.0]
Entri terakhir ialah . Oleh sebab ini bermakna rentang tidak tertutup di bawah komutator, tiada pengekodan yang merupakan isomorfisme aljabar. Sesuatu pengekodan memetakan keadaan, bersama pengendali yang bertindak dalam subruang terpangkas. Dua akibat berikut penting dalam amalan.
Hadnya mesti mengikat penghunian yang boleh dicapai. Tidak cukup untuk mengikat penghunian yang anda mulakan. Pemecah alur yang bertindak pada mengisi dan , jadi had satu secara senyap menghasilkan keadaan yang salah:
>>> from scipy.linalg import expm
>>>
>>> beamsplitter = BosonOperator.from_dict({(cre(0), ann(1)): 1.0, (cre(1), ann(0)): 1.0})
>>>
>>> def stays_in_11(max_occupation):
... """Probability to remain in |1, 1> after a 50:50 beamsplitter."""
... spec = LevelSpec(max_occupation, "gray").resolve(2)
... observable = occupation_encoding(beamsplitter, spec, spec.total_width)
... matrix = SparsePauliOp.from_sparse_observable(observable).to_matrix()
... unitary = expm(-1j * (np.pi / 4) * matrix)
... index = sum(spec.encoding.level_to_bits(1) << offset for offset in spec.offsets)
... return round(float(abs(unitary[index, index]) ** 2), 6)
>>>
>>> stays_in_11(1), stays_in_11(2)
(1.0, 0.0)
Dengan had satu, kebarangkalian kekal dalam ialah satu, kerana kedua-dua keadaan yang akan diisi oleh dinamik itu tidak wujud. Dengan had dua, ia lenyap, iaitu kesan Hong-Ou-Mandel. Inilah sebabnya max_occupation ialah hujah wajib yang tidak pernah mempunyai nilai lalai: ia ialah keputusan pemodelan dan parameter penumpuan yang anda dijangka sapu.
Pemetaan tidak berkomutasi dengan penyusunan normal. Pengendali dan adalah sama sebelum pemangkasan, tetapi keduanya dipetakan kepada pengendali qubit yang berbeza:
>>> product = BosonOperator.from_dict({(ann(0), cre(0)): 1.0})
>>> ordered = BosonOperator.from_dict({(cre(0), ann(0)): 1.0}) + BosonOperator.one()
>>> bool(product.equiv(ordered))
True
>>> level_diagonal(product, 3, "unary_unprojected") # d = 4
[1.0, 2.0, 3.0, 0.0]
>>> level_diagonal(ordered, 3, "unary_unprojected")
[1.0, 2.0, 3.0, 4.0]
Kedua-duanya sepadan pada setiap aras kecuali yang tertinggi. Apabila dikenakan pada aras teratas, tiada tempat untuk dituju dan amplitud dipotong, jadi ialah sifar di situ, manakala memberikan . Tentukan sekali sama ada Hamiltonian anda ditakrifkan dalam bentuk tersusun normal, dan petakan bentuk itu secara konsisten.
Tiga kaedah membantu anda memilih had, dan kesemuanya ialah batas bawah. Pengendali melaporkan bilangan terbesar pengendali penciptaan yang dikenakannya pada setiap mod dalam satu sebutan, dan sesuatu spesifikasi membandingkannya dengan hadnya:
>>> quartic = BosonOperator.from_dict({(cre(0), cre(0), ann(0), ann(0)): 1.0})
>>> quartic.mode_occupations(), quartic.max_occupation()
([2], 2)
>>> LevelSpec(1, "gray").truncation(quartic) # one level short
[1]
>>> LevelSpec(2, "gray").truncation(quartic)
[0]
Entri bukan sifar bermakna had berada di bawah batas, dan nilainya ialah seberapa jauh. Pembantu ini tidak mengetahui tentang dinamik, jadi semakan yang lulus tidak menjadikan sesuatu had mencukupi; pemecah alur di atas mempunyai batas satu tetapi memerlukan had dua. Ia juga boleh melaporkan kekurangan bagi sebutan yang bernilai sifar pada aras yang dikekalkan, seperti bagi had satu.
Huraikan pemangkasan dengan LevelSpec
Sebuah LevelSpec menghimpunkan had setiap mod bersama pengekodan, dan daripadanya ia memperoleh berapa banyak qubit yang diduduki setiap mod serta di mana qubitnya bermula. Segala yang perlu bersetuju tentang itu (pemeta, susun atur Transpiler, penyediaan keadaan) diberikan spesifikasi yang sama. Pengekodan ialah hujah wajib, begitu juga had: kedua-duanya tidak mempunyai nilai lalai.
Had ialah sama ada satu integer yang dikongsi oleh semua mod, atau satu integer bagi setiap mod:
>>> shared = LevelSpec(2, "gray")
>>> per_mode = LevelSpec([4, 2, 2], "gray")
>>> shared.num_modes is None, per_mode.num_modes
(True, 3)
Spesifikasi yang dibina daripada satu integer membiarkan bilangan mod terbuka, jadi anda perlu menyelesaikannya sebelum meminta lebar. Satu jujukan tidak pernah disiarkan, walau apa pun panjangnya, dan ia menetapkan bilangan mod:
>>> per_mode.widths, per_mode.offsets, per_mode.total_width
([3, 2, 2], [0, 3, 5], 7)
>>> shared.resolve(3).widths
[2, 2, 2]
>>> try:
... shared.widths
... except ValueError as error:
... print(error)
this spec was built from a single cutoff, so its number of modes is open; call `resolve(num_modes)` first
Mod menduduki qubit offsets[m] hingga offsets[m] + widths[m] - 1, jadi mod dengan had berbeza boleh mengambil bilangan qubit yang berbeza, dan ofset ialah hasil tambah berjalan bagi lebar. Anda tidak perlu menghantar bilangan mod kepada pemeta. Ia mengambilnya daripada spesifikasi, atau, bagi spesifikasi dengan satu had, daripada pengendali melalui min_num_modes(). Daftar boleh lebih lebar daripada keperluan mod; qubit lebihan tidak aktif dan terletak di hujung:
>>> number = BosonOperator.from_dict({(cre(0), ann(0)): 1.0})
>>> number.min_num_modes()
1
>>> occupation_encoding(number, LevelSpec(1, "gray")).num_qubits
1
>>> occupation_encoding(number, LevelSpec(1, "gray"), 4).num_qubits
4
Pengendali yang bertindak pada mod di luar mod terakhir bagi spesifikasi per-mod akan ditolak:
>>> far = BosonOperator.from_dict({(cre(5), ann(5)): 1.0})
>>> try:
... occupation_encoding(far, LevelSpec([1, 1], "gray"))
... except ValueError as error:
... print(error)
the operator acts on 6 modes, but the spec covers only 2
Beberapa had timbul daripada cara pengekodan mengira qubit. Di bawah pengekodan padat, mod dengan satu aras sahaja (max_occupation = 0) tidak mengambil qubit langsung, kerana satu-satunya arasnya tidak memerlukan apa-apa untuk dibezakan daripada yang lain. Mod sebegini dibenarkan dan tidak menggerakkan qubit mod selepasnya, tetapi spesifikasi yang setiap modnya tidak mengambil qubit akan ditolak. Pengekodan unari memuatkan paling banyak 128 aras bagi setiap mod, dan had yang lebih besar menimbulkan ralat yang mencadangkan pengekodan padat:
>>> LevelSpec([0, 1], "gray").resolve(2).widths
[0, 1]
>>> try:
... LevelSpec(0, "gray")
... except ValueError as error:
... print(str(error).split(":")[0])
the spec would span no qubits
>>> try:
... LevelSpec(128, "unary_projected")
... except ValueError as error:
... print(str(error).split(" (")[0])
max_occupation = 128 is too large for the unary encodings, which hold at most 128 levels per mode
Pilih pengekodan
Sebuah LevelEncoding menentukan keadaan asas bagi blok qubit yang mewakili setiap aras. Terdapat empat, dan tiada satu pun yang terbaik secara umum [2]. Bilangan qubit bagi setiap mod dan corak bit bagi setiap aras mengikut pengekodan:
>>> encodings = (
... LevelEncoding.UNARY_PROJECTED,
... LevelEncoding.UNARY_UNPROJECTED,
... LevelEncoding.STANDARD_BINARY,
... LevelEncoding.GRAY,
... )
>>> rows = ["encoding qubits for d = 1, 2, 3, 4, 5, 8 level 0 to 4 as bit patterns"]
>>> for encoding in encodings:
... widths = [encoding.num_qubits(levels) for levels in (1, 2, 3, 4, 5, 8)]
... patterns = [f"{encoding.level_to_bits(level):05b}" for level in range(5)]
... rows.append(f"{encoding!s:18s} {widths!s:34s} {patterns}")
>>> print("\n".join(rows))
encoding qubits for d = 1, 2, 3, 4, 5, 8 level 0 to 4 as bit patterns
unary_projected [1, 2, 3, 4, 5, 8] ['00001', '00010', '00100', '01000', '10000']
unary_unprojected [1, 2, 3, 4, 5, 8] ['00001', '00010', '00100', '01000', '10000']
standard_binary [0, 1, 2, 2, 3, 3] ['00000', '00001', '00010', '00011', '00100']
gray [0, 1, 2, 2, 3, 3] ['00000', '00001', '00011', '00010', '00110']
Corak bit ialah integer yang bit merupakan nilai qubit . Pengekodan unari menggunakan satu qubit bagi setiap aras dan menetapkan satu daripadanya. Pengekodan padat menggunakan qubit dan menyimpan nombor aras, dalam binari atau sebagai kod Gray yang aras berjirannya berbeza dalam satu bit. Nama rentetan bagi sesuatu pengekodan diterima di mana-mana pengekodan diterima.
Pertukaran ganti ialah qubit berbanding berat pengendali. Pengekodan padat memerlukan qubit yang jauh lebih sedikit secara eksponen, tetapi pengendali tangga menghubungkan aras yang coraknya boleh berbeza dalam beberapa bit, jadi sebutannya bertindak pada lebih banyak qubit. Bahagian berikut meneliti kosnya, bermula dengan dua pengekodan unari, yang mempunyai susun atur yang sama dan hanya berbeza pada pengendali.
Bandingkan dua pengekodan unari
Dalam pengekodan unari, pengendali tangga menggerakkan satu pengujaan antara dua qubit berjiran. Untuk menulisnya sebagai pengendali pada seluruh mod, anda mesti memilih apa yang dilakukan pada qubit lain. Kedua-dua varian memilih secara berbeza:
-
unary_projectedmeletakkan unjuran pada setiap qubit lain dalam mod itu. Itu menguatkuasakan kekangan satu-panas dalam pengendali itu sendiri: sebutan hanya bertindak jika bakinya mod itu kosong. -
unary_unprojectedmembiarkan qubit tersebut, jadi sebutan bertindak sebagai identiti padanya.
Lihat satu pada di bawah kedua-duanya. Pemeta mengembalikan SparseObservable, yang sebutannya ialah rentetan sebutan bit qubit tunggal. Berikut ialah empat sebutan pertama setiap satu, dengan qubit yang dikenakan:
>>> for encoding in ("unary_projected", "unary_unprojected"):
... observable = occupation_encoding(b_dagger, LevelSpec(3, encoding))
... terms = observable.to_sparse_list()
... print(encoding, "->", len(terms), "terms on", observable.num_qubits, "qubits")
... for label, qubits, coefficient in terms[:4]:
... print(f" {label:5s} on qubits {qubits} coefficient {complex(coefficient):.4f}")
unary_projected -> 12 terms on 4 qubits
XX00 on qubits [0, 1, 2, 3] coefficient 0.2500+0.0000j
XY00 on qubits [0, 1, 2, 3] coefficient 0.0000-0.2500j
YX00 on qubits [0, 1, 2, 3] coefficient 0.0000+0.2500j
YY00 on qubits [0, 1, 2, 3] coefficient 0.2500+0.0000j
unary_unprojected -> 12 terms on 4 qubits
XX on qubits [0, 1] coefficient 0.2500+0.0000j
XY on qubits [0, 1] coefficient 0.0000-0.2500j
YX on qubits [0, 1] coefficient 0.0000+0.2500j
YY on qubits [0, 1] coefficient 0.2500+0.0000j
Kedua-duanya mempunyai dua belas sebutan dengan pekali yang sama. Sebutan terunjur membawa sebutan bit 0 pada dua qubit yang tidak disebut oleh sebutan tak terunjur, jadi setiap sebutan terunjur menyentuh keempat-empat qubit dan setiap sebutan tak terunjur menyentuh dua. Secara umum sebutan terunjur menyentuh semua qubit bagi mod itu, kerana ia membawa dua faktor, masing-masing atau , dan satu unjuran pada setiap qubit lain.
Kedua-dua pengendali adalah sama pada keadaan yang dimaksudkan oleh pengekodan, iaitu keadaan asas satu-panas (kata kod), dan berbeza di luarnya. Untuk menyemak, ambil pengendali kecil pada dua mod pada dan bandingkan matriks pada sembilan kata kod:
>>> hamiltonian = BosonOperator.from_dict({
... (cre(0), ann(1)): 1.0, (cre(1), ann(0)): 1.0,
... (cre(0), ann(0)): 0.5, (cre(1), cre(1), ann(1), ann(1)): 0.25,
... })
>>> matrices = {}
>>> for encoding in ("unary_projected", "unary_unprojected"):
... observable = occupation_encoding(hamiltonian, LevelSpec(2, encoding))
... matrices[encoding] = SparsePauliOp.from_sparse_observable(observable).to_matrix()
>>> unary = LevelEncoding.UNARY_PROJECTED
>>> codewords = sorted(
... unary.level_to_bits(first) | (unary.level_to_bits(second) << 3)
... for first in range(3) for second in range(3)
... )
>>> projected, unprojected = matrices["unary_projected"], matrices["unary_unprojected"]
>>> bool(np.allclose(projected[np.ix_(codewords, codewords)], unprojected[np.ix_(codewords, codewords)]))
True
>>> bool(np.allclose(projected, unprojected))
False
Kedua-duanya juga membiarkan subruang kata kod tidak berubah, jadi keadaan yang disediakan pada kata kod kekal pada kata kod. Pilihan ini penting hanya untuk keadaan yang meninggalkan subruang itu, contohnya melalui hingar. Di situ pengendali terunjur menekan evolusi, kerana unjurannya gagal, manakala yang tak terunjur terus bertindak. Tiada satu pun kelakuan itu merupakan keperluan fizikal. Pengendali tak terunjur hanya ditakrifkan pada subruang satu-panas, jadi memilihnya ialah keputusan pemodelan dan bukan pengoptimuman percuma.
Kos unjuran ialah Gate. Evolusikan yang Hermitian pada satu mod untuk masa yang singkat, dan kira Gate dua-qubit selepas transpilasi Qiskit:
>>> from qiskit import QuantumCircuit, transpile
>>> from qiskit.circuit.library import PauliEvolutionGate
>>>
>>> def cx_count(observable):
... circuit = QuantumCircuit(observable.num_qubits)
... circuit.append(PauliEvolutionGate(observable, time=0.1), range(observable.num_qubits))
... basis = ["cx", "rz", "sx", "x"]
... return transpile(circuit, basis_gates=basis, optimization_level=1).count_ops().get("cx", 0)
>>>
>>> hermitian = b + b_dagger
>>> levels = (3, 4, 5, 6)
>>> counts = {
... str(encoding): [
... cx_count(occupation_encoding(hermitian, LevelSpec(d - 1, encoding))) for d in levels
... ]
... for encoding in encodings
... }
Kiraan tepat bergantung pada cara Qiskit mensintesis Gate evolusi, yang berubah antara keluaran, jadi plotkannya:
Kiraan tak terunjur meningkat secara linear dengan , kerana setiap satu daripada peralihan ialah putaran dua-qubit. Kiraan terunjur meningkat jauh lebih pantas, kerana setiap sebutan bertindak pada semua qubit. Pengekodan padat memerlukan lebih sedikit qubit, tetapi berat sebutannya bergantung pada bagaimana corak bit aras berjiran berbeza: kod Gray, yang berbeza dalam satu bit, ialah yang lebih murah antara dua pengekodan padat, dan binari piawai melonjak apabila bawa membalikkan beberapa bit serentak (). Sama ada pengekodan padat juga mengatasi unari tak terunjur bergantung pada dan pada cara Qiskit mensintesis evolusi, jadi bacalah trendnya dan bukan kedudukan pada satu saiz.
Unjuran juga penting apabila anda mengembangkan pengendali yang dipetakan kepada Pauli, yang dilakukan oleh from_sparse_observable() dengan menggantikan setiap unjuran dengan hasil tambah dua pengendali Pauli. Sebutan dengan unjuran menjadi sebutan Pauli. Berikut ialah satu pada kecil, iaitu satu-satunya saiz yang patut anda cuba:
>>> rows = [f"{'d':>2s} {'stored (both)':>14s} {'expanded, projected':>20s} {'expanded, unprojected':>22s}"]
>>> for levels in (3, 4, 5):
... stored = len(occupation_encoding(b_dagger, LevelSpec(levels - 1, "unary_projected")))
... expanded = [
... len(SparsePauliOp.from_sparse_observable(
... occupation_encoding(b_dagger, LevelSpec(levels - 1, encoding))).simplify())
... for encoding in ("unary_projected", "unary_unprojected")
... ]
... rows.append(f"{levels:2d} {stored:14d} {expanded[0]:20d} {expanded[1]:22d}")
>>> print("\n".join(rows))
d stored (both) expanded, projected expanded, unprojected
3 8 16 8
4 12 48 12
5 16 128 16
Kedua-dua varian menyimpan bilangan sebutan yang sama, tetapi pengembangan yang terunjur meningkat secara eksponen dalam . Untuk apa-apa yang lebih besar daripada contoh mainan, baca sebutan SparseObservable secara terus, seperti senarai di atas, dan jangan menukarnya.
Pengekodan padat tidak boleh menggugurkan unjurannya. Di sana bit sifar merupakan sebahagian daripada nombor aras, jadi membuang mengubah aras yang dihubungkan oleh sesuatu sebutan, dan pengendali tidak lagi sama pada kata kod. Inilah sebabnya hanya pengekodan unari mempunyai dua varian.
Hasil darab tangga dipangkas dahulu
Satu lagi pilihan reka bentuk muncul dalam kos sebutan pepenjuru. Pemeta tidak memetakan setiap pengendali tangga dan mendarabkan hasilnya. Ia mengumpulkan pengendali tangga yang dikenakan oleh sesuatu sebutan pada satu mod, mendarabkannya sebagai matriks dalam asas Fock, memangkas hasil darab itu sekali, dan mengekodkan hasilnya [2]. Hasil darab seperti pengendali nombor oleh itu kekal pepenjuru:
>>> number_observable = occupation_encoding(number, LevelSpec(3, "unary_unprojected"))
>>> [(label, qubits) for label, qubits, _ in number_observable.to_sparse_list()]
[('1', [1]), ('1', [2]), ('1', [3])]
>>> cx_count(number_observable)
0
Sebutannya ialah unjuran pada qubit setiap aras di atas sifar, dibebani oleh nombor aras (aras sifar mempunyai berat sifar dan oleh itu tiada sebutan). Evolusi masanya ialah satu set putaran qubit tunggal dan tidak memerlukan Gate dua-qubit. Mendarabkan dua pengendali tangga yang dipetakan sebaliknya memberikan pengendali yang sama pada kata kod, tetapi yang berbeza di luarnya, dengan rentetan luar-pepenjuru yang memerlukan kos Gate:
>>> unprojected = LevelSpec(3, "unary_unprojected")
>>> as_pauli = lambda operator: SparsePauliOp.from_sparse_observable(
... occupation_encoding(operator, unprojected, 4)).simplify()
>>> composed = as_pauli(b_dagger).dot(as_pauli(b)).simplify()
>>> direct = as_pauli(number)
>>> len(direct), len(composed)
(4, 16)
>>> cx_count(direct)
0
>>> bool(cx_count(composed) > 0)
True
>>> codewords = [LevelEncoding.UNARY_UNPROJECTED.level_to_bits(level) for level in range(4)]
>>> bool(np.allclose(direct.to_matrix()[np.ix_(codewords, codewords)],
... composed.to_matrix()[np.ix_(codewords, codewords)]))
True
>>> bool(np.allclose(direct.to_matrix(), composed.to_matrix()))
False
Sebutan pepenjuru seperti interaksi pada-tapak model Bose-Hubbard ialah hasil darab ini, di situlah pilihan ini penting.
Tulis pemeta tersuai
Jika anda mahukan pengekodan yang tidak disediakan oleh pustaka, bina daripada imej satu pengendali tangga. map_boson_action_generators() menerima fungsi yang memetakan satu BosonAction, fungsi yang mengembalikan identiti, dan membina pemeta pengendali keseluruhan daripadanya. Ia mendarabkan imej tindakan setiap sebutan mengikut susunan, menskala dengan pekali, dan menjumlahkan sebutan. Outputnya boleh jenis apa pun yang menyokong pendaraban skalar dan gubahan.
Sebagai contoh, ambil pengekodan untuk yang menyimpan satu mod dalam satu qubit. Pengendali penciptaan ialah , iaitu dalam konvensyen Qiskit, dan pengendali pemusnah ialah adjointnya:
>>> from qiskit_bosons.mappers import map_boson_action_generators
>>> from qiskit_bosons.operators import BosonAction
>>>
>>> num_qubits = 2
>>>
>>> def map_action(action: BosonAction) -> SparsePauliOp:
... is_creation, mode = action
... sign = -1j if is_creation else 1j
... return SparsePauliOp.from_sparse_list(
... [("X", [mode], 0.5), ("Y", [mode], 0.5 * sign)], num_qubits=num_qubits
... )
>>>
>>> def identity() -> SparsePauliOp:
... return SparsePauliOp.from_sparse_list([("", [], 1.0)], num_qubits)
>>>
>>> model = BosonOperator.from_dict({
... (cre(0), ann(1)): -1.0, (cre(1), ann(0)): -1.0, (ann(0), cre(0)): 0.7, (cre(1), ann(1)): 0.3,
... })
>>> custom = map_boson_action_generators(model, map_action, identity)
>>> library = SparsePauliOp.from_sparse_observable(occupation_encoding(model, LevelSpec(1, "gray")))
>>> bool(np.allclose(custom.to_matrix(), library.to_matrix()))
True
Pemeta tulisan tangan ini mendarabkan pengendali tangga yang dipetakan, dan ia sepadan dengan pemeta pustaka di sini kerana setiap mod mempunyai dua aras dan satu qubit memuatkan keseluruhan mod. Pada yang lebih besar, seperti yang ditunjukkan bahagian sebelumnya, menggubah tangga yang dipetakan dan memangkas hasil darab dahulu bukanlah pengendali yang sama di luar kata kod.
Untuk menggunakan pemeta sebegini dalam litar, bungkus dalam fungsi dengan tandatangan (operator, num_qubits) -> SparseObservable dan serahkan kepada MapperFnEvolutionSynthesis, yang memetakan pengendali setiap Gate Evolution dengannya. Saluran paip Transpiler diterangkan dalam Transpile litar boson.
>>> from qiskit.quantum_info import SparseObservable
>>> from qiskit_bosons.transpiler.passes import MapperFnEvolutionSynthesis
>>>
>>> def mapper_fn(operator, num_qubits):
... return SparseObservable.from_sparse_pauli_op(
... map_boson_action_generators(operator, map_action, identity)
... )
>>>
>>> synthesis = MapperFnEvolutionSynthesis(mapper_fn)
Dua butiran wajar diketahui. Pertama, pengendali qubit mesti mempunyai pekali nyata, kerana PauliEvolutionGate menolak bahagian khayal. Pengendali Hermitian dipetakan kepada yang nyata, tetapi hanya selepas sebutan yang saling membatalkan digabungkan. Pemeta tulisan tangan ini tidak menggabungkannya, jadi sebutan lompatan dan adjointnya meninggalkan pekali khayal sehingga anda memudahkan hasilnya. Pemeta pustaka menggabungkan hasilnya sendiri; pemeta tersuai perlu melakukannya, contohnya dengan membungkusnya dalam simplify():
>>> hopping = BosonOperator.from_dict({(cre(0), ann(1)): 1.0})
>>> hermitian_hopping = hopping + hopping.adjoint()
>>> raw = mapper_fn(hermitian_hopping, 2)
>>> bool(np.allclose(np.asarray(raw.coeffs).imag, 0))
False
>>> merged = raw.simplify()
>>> bool(np.allclose(np.asarray(merged.coeffs).imag, 0))
True
>>> sorted(label for label, _, _ in merged.to_sparse_list())
['XX', 'YY']
Kedua, formula hasil darab mensintesis sebutan mengikut susunan ia menerimanya, dan susunan yang dihasilkan pemeta tidak dijamin stabil. Pembantu simplify() dan group_wise() membungkus fungsi pemeta untuk mengawalnya: simplify menggabungkan rentetan pendua dan mengisih sebutan ke dalam susunan kanonik, manakala group_wise memetakan satu kumpulan pengendali pada satu masa supaya sebutan sesuatu kumpulan kekal bersebelahan. Jangan gabungkan kedua-duanya, kerana pengisihan membuang pengumpulan. Panduan pengumpulan menunjukkan cara menetapkan kumpulan, dan panduan sintesis menunjukkan bagaimana susunan menjejaskan litar.
Langkah seterusnya
-
Ikuti Simulasikan dinamik Bose-Hubbard dengan pilihan pengekodan untuk simulasi penuh yang menyapu had dan membandingkan pengekodan dengan dinamik tepat.
-
Lihat Transpile litar boson untuk cara
LevelSpecyang sama diterapkan pada litar boson. -
Baca Prinsip reka bentuk perwakilan pengendali untuk mengetahui cara
BosonOperatormenyimpan dan menyusun sebutannya, termasuk petua pengecutan di sebalik contoh penyusunan normal di atas. -
Baca Putarkan mod boson untuk Gate yang menggerakkan boson antara mod, di situlah amaran penghunian yang boleh dicapai paling penting.
-
Rujuk rujukan API bagi
qiskit_bosons.mappersdanqiskit_bosons.mappers.library.
[1]
R. Somma, G. Ortiz, E. Knill, and J. Gubernatis, Quantum Simulations of Physics Problems (2003), arXiv:quant-ph/0304063, Sec. 4, Eq. 22.
N. P. D. Sawaya, T. Menke, T. H. Kyaw, S. Johri, A. Aspuru-Guzik, and G. G. Guerreschi, Resource-efficient digital quantum simulation of d-level systems for photonic, vibrational, and spin-s Hamiltonians, npj Quantum Information 6, 49 (2020), arXiv:1909.12847.