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?

Transpile litar bosonik

Panduan ini menerangkan cara transpile BosonicCircuit kepada QuantumCircuit standard. Litar bosonik tidak membawa pemangkasan, pengekodan, mahupun bilangan qubit (lihat Bekerja dengan litar bosonik), jadi Transpiler ialah tempat pilihan tersebut dibuat. Pemangkasan dan pengekodan diterangkan oleh LevelSpec (lihat Menerangkan pemangkasan dengan LevelSpec), dan pemeta yang menukar pengendali kepada pengendali qubit diterangkan dalam Memetakan perwakilan pengendali. Hasilnya ialah QuantumCircuit biasa, jadi mana-mana simulator yang serasi dengan Qiskit boleh menjalankannya.

Panduan ini menggunakan satu contoh kecil sepanjang masa: dua mod bosonik, keadaan dengan satu boson dalam setiap satu, dan satu evolusi masa di bawah Hamiltonian Bose-Hubbard yang sebutan ikatan dan dalam-tapaknya berada dalam kumpulan berasingan (lihat Mengumpulkan sebutan pengendali: gunakan struktur pengendali):

>>> from qiskit_bosons.circuit import BosonicCircuit
>>> from qiskit_bosons.circuit.library import Evolution, InitializeModes
>>> from qiskit_bosons.operators import BosonOperator, ann, cre
>>>
>>> terms = {(cre(0), ann(1)): -1.0, (cre(1), ann(0)): -1.0}
>>> terms.update({(cre(mode), cre(mode), ann(mode), ann(mode)): 0.5 for mode in range(2)})
>>> hamiltonian = BosonOperator.from_dict(terms)
>>> # the bond is group 0 and the on-site interaction is group 1; derive the index from the term,
>>> # because the stored order of a dictionary is not specified
>>> hamiltonian.groups = [
... 0 if len({mode for _, mode in term}) == 2 else 1 for term, _ in hamiltonian.iter_terms()
... ]
>>>
>>> circuit = BosonicCircuit(2)
>>> circuit.append(InitializeModes([1, 1]), circuit.modes)
>>> circuit.append(Evolution(2, hamiltonian, time=0.1), circuit.modes)
>>> dict(circuit.count_ops())
{'InitializeModes': 1, 'Evolution': 1}

Peringkat transpilasi​

Seperti yang diterangkan dalam qiskit_bosons.transpiler, pengurus laluan pratetap dalam pakej ini membahagikan transpilasi kepada peringkat berikut:

Masukan​

Menukar litar kepada struktur data graf berarah tanpa kitaran (DAG), perwakilan yang digunakan oleh setiap laluan kemudian.

Pengoptimuman​

Mengekalkan litar dalam ruang bosonik. Laluan ini menggunakan struktur pengendali bosonik, khususnya kumpulannya, sementara belum ada maklumat qubit. B2BSynthesis ialah laluan yang menguraikan gate di sini, dengan pemalam bagi setiap jenis gate: formula hasil darab atau qDRIFT untuk Evolution, dan mesh Clements untuk ModeRotation (lihat Mensintesis gate Evolution dalam ruang bosonik untuk cara kaedah dipilih). RelabelModes menyusun semula mod. Pratetap menguraikan putaran mod dalam peringkat ini dan membiarkan yang lain seperti sedia ada.

Susun atur​

Memetakan setiap register mod bosonik kepada register kuantum. Satu mod bosonik lazimnya menduduki beberapa qubit (dd qubit di bawah pengekodan unari, ⌈log⁡2d⌉\lceil \log_2 d \rceil di bawah pengekodan padat), jadi register kuantum lebih lebar daripada register mod. OccupationB2QLayout memperoleh lebar register daripada LevelSpec.

Sintesis​

Menukar gate bosonik kepada operasi qubit, satu pemalam bagi setiap jenis gate. Pengekodan berlaku dalam langkah ini: pemalam untuk gate Evolution memetakan pengendalinya kepada pengendali qubit, dan pemalam untuk gate InitializeModes menetapkan qubit yang mengekod setiap penghunian. Penghadang dibawa ke atas setiap qubit bagi setiap register yang disentuhnya.

Qubit​

Transpile litar qubit yang terhasil dengan kaedah Qiskit sendiri, contohnya kepada set gate asas atau peranti. Pratetap sentiasa menyertakannya; secara lalai ia ialah pengurus laluan pratetap Qiskit, yang mensintesis setiap evolusi kepada gate (lihat Meneruskan pada aras qubit).

Keluaran​

Menukar struktur data DAG dalaman kembali kepada QuantumCircuit.

Pemangkasan dan pengekodan tidak disimpan dalam litar. Ia diberikan kepada Transpiler, dan susun atur, penyediaan keadaan, dan pemeta mesti bersetuju mengenainya. Pratetap memastikannya dengan menyerahkan LevelSpec yang sama kepada ketiga-tiganya.

Contoh praktikal​

generate_preset_occupation_pass_manager() membina saluran paip daripada spesifikasi. Menurunkan contoh di bawah dua pengekodan hanya mengubah spesifikasi. Pratetap berakhir dengan transpilasi aras qubit Qiskit sendiri, yang akan mensintesis evolusi kepada gate. Untuk melihat litar tepat seperti yang dihasilkan oleh peringkat bosonik, kosongkan peringkat itu dahulu:

>>> from qiskit.transpiler import PassManager
>>> from qiskit_bosons.mappers import LevelSpec
>>> from qiskit_bosons.transpiler.presets import generate_preset_occupation_pass_manager
>>>
>>> for encoding in ("unary_unprojected", "gray"):
... pass_manager = generate_preset_occupation_pass_manager(LevelSpec(2, encoding))
... pass_manager.qubit = PassManager()
... lowered = pass_manager.run(circuit)
... print(f"{encoding:18s} qubits={lowered.num_qubits} ops={dict(lowered.count_ops())}")
unary_unprojected qubits=6 ops={'x': 2, 'PauliEvolution': 1}
gray qubits=4 ops={'x': 2, 'PauliEvolution': 1}

Kedua-dua pengekodan masing-masing menggunakan enam dan empat qubit (tiga aras bagi setiap mod di bawah pengekodan unari, dua qubit bagi setiap mod di bawah kod Gray), tetapi struktur litar adalah sama: dua operasi XGate menyediakan penghunian, dan satu PauliEvolutionGate membawa evolusi.

Bandingkan dengan aliran kerja tradisional​

Panduan litar bosonik menerangkan aliran kerja tradisional, yang memetakan Hamiltonian kepada pengendali qubit sebelum membina QuantumCircuit. Kedua-dua laluan menghasilkan litar yang setara. Perbezaannya ialah apa yang perlu diulang apabila pemangkasan berubah. Litar bosonik dibina sekali dan diturunkan pada setiap had, manakala laluan peta-dahulu memerlukan pengendali dipetakan semula dan litar dibina semula:

>>> from qiskit import QuantumCircuit
>>> from qiskit.circuit.library import PauliEvolutionGate
>>> from qiskit_bosons.mappers.library import occupation_encoding
>>>
>>> basis = ["rz", "sx", "x", "cx"]
>>> cx_counts = []
>>> for max_occupation in (1, 2, 3):
... spec = LevelSpec(max_occupation, "gray")
... # the bosonic circuit is reused as it is
... lowered = generate_preset_occupation_pass_manager(
... spec, optimization_level=0, basis_gates=basis
... ).run(circuit)
... # the map-first route maps the operator and builds a new circuit for every cutoff
... qubit_hamiltonian = occupation_encoding(hamiltonian, spec)
... by_hand = QuantumCircuit(qubit_hamiltonian.num_qubits)
... _ = by_hand.append(PauliEvolutionGate(qubit_hamiltonian, time=0.1), by_hand.qubits)
... cx_counts.append(lowered.count_ops().get("cx", 0))
... print(
... f"d={max_occupation + 1}: {lowered.num_qubits} qubits; map-first: "
... f"{len(qubit_hamiltonian)} Pauli terms to place by hand"
... )
d=2: 2 qubits; map-first: 2 Pauli terms to place by hand
d=3: 4 qubits; map-first: 10 Pauli terms to place by hand
d=4: 4 qubits; map-first: 22 Pauli terms to place by hand

Bilangan gate berbelit bergantung pada cara Qiskit mensintesis gate evolusi, yang berubah antara keluaran, jadi plotkannya:

CX count of the lowered circuit against the number of levels d, growing quickly with d.

Bilangan meningkat dengan pantas mengikut had, dan itulah sebab untuk menjadikan had sebagai parameter transpilasi dan bukan litar. Kedua-dua laluan adalah setara di sini, tetapi hanya laluan bosonik yang juga boleh menyelang-nyelikan pengumpulan pengendali dengan formula hasil darab (lihat Mensintesis gate Evolution dalam ruang bosonik) atau menyerahkan pengekodan kepada spesifikasi berbeza tanpa mengubah satu baris pun litar.

Spesifikasi dengan satu had membiarkan bilangan mod terbuka, jadi satu pengurus laluan melayani litar dengan sebarang lebar. Spesifikasi dengan satu had bagi setiap mod menetapkan lebar:

>>> wide = BosonicCircuit(3)
>>> wide.append(InitializeModes([1, 0, 1]), wide.modes)
>>> shared = generate_preset_occupation_pass_manager(LevelSpec(2, "gray"))
>>> shared.run(wide).num_qubits
6
>>> per_mode = generate_preset_occupation_pass_manager(LevelSpec([2, 1], "gray"))
>>> per_mode.run(circuit).num_qubits
3
>>> try:
... per_mode.run(wide)
... except ValueError as error:
... print(error)
the spec covers 2 modes, but 3 were expected

Membina saluran paip secara manual​

Pratetap ialah MultiStagePassManager dengan satu entri bagi setiap peringkat. Anda boleh memasang saluran paip yang sama daripada bahagian-bahagiannya, iaitu titik permulaan bagi setiap penyesuaian di bawah:

>>> from qiskit.passmanager import MultiStagePassManager
>>> from qiskit_bosons.mappers.library import occupation_encoding
>>> from qiskit_bosons.transpiler import BosonicCircuitToDAG, QuantumDAGToCircuit
>>> from qiskit_bosons.transpiler.passes import (
... B2QSynthesis,
... MapperFnEvolutionSynthesis,
... OccupationB2QLayout,
... OccupationInitializeModesSynthesis,
... )
>>>
>>> spec = LevelSpec(2, "gray")
>>>
>>> def mapper_fn(operator, num_qubits):
... return occupation_encoding(operator, spec, num_qubits)
>>>
>>> synthesis = B2QSynthesis()
>>> synthesis.methods["Evolution"] = MapperFnEvolutionSynthesis(mapper_fn)
>>> synthesis.methods["InitializeModes"] = OccupationInitializeModesSynthesis(spec)
>>>
>>> by_hand = MultiStagePassManager(
... input=BosonicCircuitToDAG(),
... layout=OccupationB2QLayout(spec),
... synthesis=synthesis,
... output=QuantumDAGToCircuit(),
... )
>>> preset = generate_preset_occupation_pass_manager(spec)
>>> preset.qubit = PassManager()
>>> by_hand.run(circuit) == preset.run(circuit)
True

Fungsi pemeta menerima operator dan lebar daftar yang diperoleh daripada reka letak, lalu mengembalikan SparseObservable. Oleh sebab lebar diberikan, menghantar num_qubits kepada occupation_encoding() menjadikan pemeta menyemak bahawa spesifikasi muat dalam daftar, supaya ketidakpadanan antara reka letak dan pengekodan gagal dengan mesej yang jelas.

Aliran kerja lanjutan​

Sediakan reka letak yang eksplisit​

CustomB2QLayout menggantikan peringkat reka letak dengan pemetaan yang anda sediakan, daripada setiap daftar boson bagi litar kepada daftar kuantum. Ia berguna apabila daftar mesti lebih lebar daripada yang diperlukan oleh spesifikasi, contohnya untuk membiarkan qubit melahu bagi tujuan lain:

>>> from qiskit.circuit import QuantumRegister
>>> from qiskit_bosons.transpiler.passes import CustomB2QLayout
>>>
>>> (mode_register,) = BosonicCircuitToDAG().run(circuit).qregs.values()
>>> custom = MultiStagePassManager(
... input=BosonicCircuitToDAG(),
... layout=CustomB2QLayout({mode_register: QuantumRegister(6, "q")}),
... synthesis=synthesis,
... output=QuantumDAGToCircuit(),
... )
>>> custom.run(circuit).num_qubits
6
Amaran

Reka letak tersuai ialah tanggungjawab anda. Pemalam sintesis masih menuju qubit melalui spesifikasi, jadi daftar yang tidak sepadan dengan lebar spesifikasi menghasilkan litar yang salah atau ralat jauh di dalam pemalam. Reka letak tersuai juga melangkau semakan yang dilakukan oleh OccupationB2QLayout selepas RelabelModes (lihat bahagian seterusnya).

Susun semula mod​

RelabelModes ialah pas peringkat pengoptimuman yang menyusun semula mod. Pilih atur dibaca sebagai pemetaan daripada indeks mod asal kepada indeks mod baharu, dan ia direkodkan dalam metadata litar. Penyusunan semula mengubah qubit yang diduduki oleh setiap mod, dan oleh itu ia boleh mengurangkan kedalaman litar yang diturunkan.

Oleh sebab hasilnya dinyatakan dalam susunan mod baharu, rentetan bit yang diukur mesti dipetakan semula sebelum ditafsirkan. Dengan beberapa qubit bagi setiap mod, pemetaan bertindak pada blok qubit dan bukan pada bit tunggal. Contoh berikut melabel semula tiga mod, setiap satu menduduki dua qubit di bawah kod Gray, dan memulihkan penghunian asal daripada kiraan yang diukur:

>>> # BasicSimulator is a convenient pure-Python choice; any simulator works
>>> from qiskit.providers.basic_provider import BasicSimulator
>>> from qiskit_bosons.transpiler.passes import RelabelModes
>>>
>>> state = BosonicCircuit(3)
>>> state.append(InitializeModes([2, 0, 1]), state.modes)
>>> spec = LevelSpec(2, "gray") # three levels, two qubits per mode
>>> width = spec.resolve(3).widths[0]
>>> permutation = [2, 0, 1]
>>>
>>> synthesis = B2QSynthesis()
>>> synthesis.methods["InitializeModes"] = OccupationInitializeModesSynthesis(spec)
>>> relabeled = MultiStagePassManager(
... input=BosonicCircuitToDAG(),
... optimization=RelabelModes(permutation),
... layout=OccupationB2QLayout(spec),
... synthesis=synthesis,
... output=QuantumDAGToCircuit(),
... ).run(state)
>>> relabeled.metadata
{'permutation': [2, 0, 1]}
>>>
>>> def restore(bitstring, permutation, width):
... """Map a bitstring in the new mode order back to the original mode order."""
... qubits = bitstring[::-1] # character q is now the value of qubit q
... blocks = [qubits[mode * width : (mode + 1) * width] for mode in range(len(permutation))]
... return "".join(blocks[new] for new in permutation)[::-1]
>>>
>>> relabeled.measure_all()
>>> (measured,) = BasicSimulator().run(relabeled, shots=1).result().get_counts()
>>> measured
'110100'
>>> restore(measured, permutation, width)
'010011'

Rentetan bit yang dipulihkan ialah apa yang dihasilkan oleh litar tanpa pelabelan semula, dibaca dalam susunan little-endian Qiskit: mod 0 memegang kod Gray bagi aras dua (11), mod 1 memegang aras sifar (00), dan mod 2 memegang aras satu (01).

>>> plain = MultiStagePassManager(
... input=BosonicCircuitToDAG(),
... layout=OccupationB2QLayout(spec),
... synthesis=synthesis,
... output=QuantumDAGToCircuit(),
... ).run(state)
>>> plain.measure_all()
>>> (reference,) = BasicSimulator().run(plain, shots=1).result().get_counts()
>>> restore(measured, permutation, width) == reference
True
Amaran

Spesifikasi yang memberikan mod potongan berbeza tidak boleh digabungkan dengan RelabelModes. Potongan dilampirkan pada mod mengikut kedudukannya, jadi menyusun semula mod akan meletakkan potongan pada mod yang salah. OccupationB2QLayout menolak litar yang dilabel semula dalam kes itu:

>>> mixed = LevelSpec([2, 1, 2], "gray")
>>> synthesis = B2QSynthesis()
>>> synthesis.methods["InitializeModes"] = OccupationInitializeModesSynthesis(mixed)
>>> refused = MultiStagePassManager(
... input=BosonicCircuitToDAG(),
... optimization=RelabelModes(permutation),
... layout=OccupationB2QLayout(mixed),
... synthesis=synthesis,
... output=QuantumDAGToCircuit(),
... )
>>> try:
... refused.run(state)
... except ValueError as error:
... print(str(error).split(",")[0])
the circuit has been relabeled by RelabelModes

Pilih pemalam sintesis​

B2QSynthesis tidak mempunyai pemalam lalai; pratetap mendaftarkan satu bagi setiap jenis gate. Pemalam yang tersedia diurus oleh B2QSynthesisPluginManager, yang menyenaraikannya mengikut nama gate yang menjadi sasaran tindakannya:

>>> from qiskit_bosons.transpiler.passes import B2QSynthesisPluginManager
>>> manager = B2QSynthesisPluginManager()
>>> for gate in manager.op_names():
... print(gate, manager.method_names(gate))
Evolution ['MapperFn']
InitializeModes ['Occupation']

Dua kaedah terbina dalam ialah Evolution.MapperFn dan InitializeModes.Occupation. Yang pertama mensintesis evolusi daripada mana-mana fungsi pemeta, iaitu cara pengekodan tersuai dipasang (lihat Map operator representations). Pustaka ini tidak menyediakan pemalam untuk gate ModeRotation. Pratetap memecahkannya dalam ruang boson kepada gate Evolution terlebih dahulu, yang kemudian dikendalikan oleh kaedah pertama, tetapi pemalam yang didaftarkan untuk ModeRotation boleh menurunkan gate itu secara langsung; lihat Rotate bosonic modes dan Lower a rotation without decomposing it. Anda boleh mendaftarkan kaedah mengikut nama melalui argumen config bagi B2QSynthesis, atau menulis objek pemalam terus ke dalam B2QSynthesis.methods, seperti contoh sebelum ini.

Teruskan pada peringkat qubit​

Pratetap sentiasa berakhir dengan peringkat qubit, iaitu pengurus pas pratetap milik Qiskit sendiri. Argumen kata kunci bagi generate_preset_occupation_pass_manager() dihantar kepada qiskit.transpiler.generate_preset_pass_manager(), jadi anda boleh menterjemah kepada set gate asas atau menyasarkan peranti secara langsung. Utamakan optimization_level=0 untuk mengekalkan litar hampir dengan apa yang dihasilkan oleh peringkat boson:

>>> from qiskit.transpiler import generate_preset_pass_manager
>>>
>>> spec = LevelSpec(2, "gray")
>>> translated = generate_preset_occupation_pass_manager(
... spec, optimization_level=0, basis_gates=["rz", "sx", "x", "cx"]
... ).run(circuit)
>>> sorted(translated.count_ops())
['cx', 'rz', 'sx', 'x']

Dua pilihan lain yang lazim. Tetapkan PassManager kosong kepada peringkat qubit apabila anda mahu menunjukkan litar tepat seperti yang disintesis oleh saluran paip, seperti contoh di atas. Atau pasang saluran paip sendiri dan tambahkan mana-mana pengurus pas Qiskit sebagai peringkat qubit bagi MultiStagePassManager. Peringkat dijalankan mengikut susunan yang diberikan, jadi peringkat qubit mesti datang sebelum output, kerana pas Qiskit menjangkakan DAG dan bukan litar yang telah siap:

>>> synthesis = B2QSynthesis()
>>> synthesis.methods["Evolution"] = MapperFnEvolutionSynthesis(
... lambda operator, num_qubits: occupation_encoding(operator, spec, num_qubits)
... )
>>> synthesis.methods["InitializeModes"] = OccupationInitializeModesSynthesis(spec)
>>> staged = MultiStagePassManager(
... input=BosonicCircuitToDAG(),
... layout=OccupationB2QLayout(spec),
... synthesis=synthesis,
... qubit=generate_preset_pass_manager(
... optimization_level=0, basis_gates=["rz", "sx", "x", "cx"]
... ),
... output=QuantumDAGToCircuit(),
... )
>>> sorted(staged.run(circuit).count_ops())
['cx', 'rz', 'sx', 'x']

Langkah seterusnya​