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 ( qubit di bawah pengekodan unari, 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:
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
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
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
-
Map operator representations untuk pengekodan dan untuk menulis pemeta anda sendiri.
-
Synthesize an Evolution gate in bosonic space untuk memilih formula hasil darab bagi gate evolusi.
-
Rotate bosonic modes untuk menurunkan putaran mod.
-
Simulate Bose-Hubbard dynamics with a choice of encoding untuk simulasi lengkap yang menyemak litar yang diturunkan berbanding dinamik tepat.
-
qiskit_bosons.transpileruntuk huraian penuh tentang peringkat dan pas yang tersedia.