I-transpile ang isang LUCJ ansatz
Ang mga konsepto sa gabay na ito ay available lamang sa Python API.
Ang local unitary cluster Jastrow (LUCJ) ansatz ay isang compact at hardware-efficient na parametrization ng isang correlated na electronic wavefunction. Miyembro ito ng mas pangkalahatang unitary cluster Jastrow (UCJ) na pamilya at may anyong
kung saan ang ay isang reference state (karaniwang Hartree-Fock), ang bawat ay isang orbital rotation, at ang bawat ay isang diagonal Coulomb operator
na ang ay ang number operator sa spatial orbital na may spin .
Binubuo ng ffsim ang ansatz operator; ginagawa ito ng package na ito na circuit at ibinababa ang circuit na iyon sa mga qubit. Ang gabay na ito ay tungkol sa ikalawang kalahati ng pangungusap na iyon. Ang dahilan para gawin ang biyaheng ito ay ang mismong pagbaba: ang sariling mga Qiskit gate ng ffsim ay partikular sa Jordan-Wigner transformation, samantalang ang isang FermionicCircuit ay walang ipinapalagay tungkol sa fermion-to-qubit encoding nito, kaya ang parehong ansatz ay maaaring i-synthesize sa pamamagitan ng alinmang encoding na akma sa target na device.
Kaya ang ansatz operator ay dumarating dito nang handa na, at lahat ng nasa ibaba ay ang nangyayari rito pagkatapos: kung saan nagde-decompose ang gate, paano ito bumababa sa pamamagitan ng Jordan-Wigner, paano ito itinutugma sa coupling map ng device, at ano pa ang kakailanganin ng isang non-Jordan-Wigner na pagbaba. Para sa pagbuo, pag-parametrize, o pag-simulate ng UCJ ansatz sa simula pa lang, tingnan ang sariling mga gabay ng ffsim.
Ang gabay sa ugnayan sa ffsim kung paano naghahati ng trabaho ang dalawang package, at ang gabay sa transpilation para sa mga yugto ng pipeline na ginagamit sa ibaba.
1. Kumuha ng LUCJ operator mula sa ffsim
Ang ansatz ay ini-initialize mula sa mga amplitude ng isang coupled-cluster singles and doubles (CCSD) na kalkulasyon. Magpatakbo ng restricted Hartree-Fock na sinundan ng CCSD para sa isang hydrogen molecule sa 6-31g na basis, gamit ang PySCF para sa quantum chemistry, pagkatapos ay ibigay ang mga amplitude sa from_t_amplitudes() ng ffsim. Nagsasagawa ito ng double factorization ng mga amplitude para makuha ang mga diagonal Coulomb matrix at orbital rotation ng bawat layer, at kumukuha ng opsyonal na huling orbital rotation mula sa mga amplitude. Ang bilang ng mga pag-uulit na ay kung ano man ang ibigay ng factorization na iyon; ang n_reps ay nagpuputol nito, ipinagpapalit ang accuracy para sa mas mababaw na circuit.
>>> import ffsim
>>> import pyscf
>>> import pyscf.cc
>>>
>>> # build the molecule and run Hartree-Fock
>>> mol = pyscf.gto.Mole()
>>> mol.build(
... atom=[["H", (0, 0, 0)], ["H", (0, 0, 0.74)]],
... basis="6-31g",
... symmetry="Dooh",
... verbose=0,
... )
<pyscf.gto.mole.Mole object at ...>
>>> scf = pyscf.scf.RHF(mol).run()
>>>
>>> norb = scf.mo_coeff.shape[1]
>>> nelec = (mol.nelec[0], mol.nelec[1])
>>>
>>> # run CCSD for the t-amplitudes, then factorize them into an ansatz operator
>>> ccsd = pyscf.cc.CCSD(scf).run()
>>> t1, t2 = ccsd.t1, ccsd.t2
>>>
>>> ucj_op = ffsim.UCJOpSpinBalanced.from_t_amplitudes(t2, t1=t1, n_reps=2)
Dito humihinto ang ansatz bilang alalahanin ng ffsim. May mga pagpipilian ang ffsim dito na hindi nakikita o kailangang malaman ng package na ito, gaya ng variationally optimized ("compressed") na factorization sa likod ng optimize=True, o ang pag-pack ng parameter vector na minamaneho ng variational optimizer. Lahat ng ito ay gumagawa ng parehong uri ng operator, at gumagana nang walang pagbabago ang lahat ng nasa ibaba sa alinman sa mga ito.
2. Gawing fermionic circuit ang operator
Binabalot ng UCJ gate ang operator at ine-express ito bilang circuit sa mga fermionic mode. Ang gate ay purong unitary na walang sariling reference, kaya magdagdag sa unahan ng InitializeModes gate (binuo gamit ang from_hartree_fock()) para ibigay ang Hartree-Fock reference na pinagpapatungan ng ansatz.
>>> from qiskit_fermions.circuit import FermionicCircuit
>>> from qiskit_fermions.circuit.library import InitializeModes, UCJ
>>>
>>> ansatz = UCJ(ucj_op)
>>>
>>> circuit = FermionicCircuit(2 * norb)
>>> circuit.append(InitializeModes.from_hartree_fock(norb, nelec), circuit.modes)
>>> circuit.append(ansatz, circuit.modes)
Ipinapakita ng pag-decompose sa circuit ang anatomiya nito, at ito ang representasyong pinagtatrabahuhan ng natitirang bahagi ng gabay. Inihahanda ng InitializeModes gate ang reference determinant, at ang bawat ansatz layer ay nag-aambag ng OrbitalRotation , pagkatapos ay (isang Evolution ng diagonal Coulomb operator na ), pagkatapos ay , na may huling OrbitalRotation sa dulo. Ang mga orbital rotation ay kumikilos sa bawat spin sector, kaya ang bawat isa ay inilalagay sa mga alpha mode na 0..norb at sa mga beta mode na norb..2*norb nang hiwalay.
>>> circuit.decompose().draw("mpl", fold=-1)
<Figure size ... with 1 Axes>

Fermionic pa rin ang bawat gate sa decomposition na iyon: ang OrbitalRotation at Evolution ay naka-define sa mga mode, at wala pang lumilitaw na qubit o Pauli operator. Iyon ang nag-iiwang bukas sa encoding, at iyon ang estado na pananatilihan ng ansatz hanggang sa pumili ng isa ang synthesis stage.
Ang bawat layer ay nagtatapos sa at ang susunod ay nagsisimula sa , kaya ang magkakatabing OrbitalRotation gate ay maaaring pagsamahin sa iisang rotation. Ang isang transpilation pass na gumagawa ng fusion na ito ay planong paunlarin sa hinaharap.
3. Pumili ng fermion-to-qubit encoding
Fermionic pa rin ang circuit, at iyon ang punto kung saan napapatunayan ng package na ito ang halaga nito sa workflow. Ang FermionicCircuit ay walang ipinapalagay kung paano nagiging mga qubit ang mga mode, kaya ang encoding ay pagpiling nasa synthesis stage at hindi isang bagay na nakabaon sa ansatz. Iyon ang naidaragdag ng package na ito kumpara sa sariling mga Qiskit gate ng ffsim, na partikular sa Jordan-Wigner transformation.
Para sa Jordan-Wigner lamang, hindi mo kailangan ang lakad na ito: may kasamang UCJOpSpinBalancedJW ang ffsim at ang mga kapatid nito, na kayang gawing qubit circuit ang UCJ operator nang mag-isa. Ang dahilan para idaan ang ansatz sa isang fermionic circuit ay ang lahat ng iba pang maibibigay ng isang encoding. Halimbawa, ang mga local encoding ay gumagastos ng karagdagang qubit para limitahan ang Pauli weight ng bawat term, na ginagamit ng mga flow-set na gabay sa 1D at 2D para gawing hindi nakadepende sa laki ng sistema ang two-qubit depth ng isang Trotter step.
Para sa isang UCJ ansatz partikular, kailangang saklawin ng synthesis stage ang dalawang uri ng gate:
-
Ang diagonal Coulomb evolution. Ang
Evolutionay encoding-agnostic na: angMapperFnEvolutionSynthesisay tumatanggap ng mapper function, kaya ang mga layer na ay bumababa sa pamamagitan ng anumang encoding na maipapahayag mo bilang isa. Ipinapakita ng mga flow-set na gabay kung paano sumulat ng isa. -
Ang mga orbital rotation at ang paghahanda ng Slater determinant. Ni ang
OrbitalRotationni angPrepareSlaterDeterminantna pinagsasamahan ng nangungunangInitializeModesat rotation (tingnan sa ibaba) ay hindi ma-map sa mga qubit bilang ganoon. Unang binabasag ngF2FSynthesispass ang mga ito, sa fermionic space, tungo sa mgaEvolutiongate sa isa o dalawang mode bawat isa (at, para sa preparation, isangInitializeModesng reference), kaya anumang encoding na kayang mag-map ng isangEvolutionay kayang humawak sa mga ito. Sa ilalim ng Jordan-Wigner, ang mga factor sa pagitan ng magkakatabing mode ay nagiging mgaXXPlusYYGateobject sa qubit stage. -
Ang initialization. Ang plugin para sa
InitializeModesay hindi pina-parametrize ng isang encoding: idinodokumento nito ang palagay nito na occupation-basis na encoding na may 1-to-1 na mode-to-qubit mapping, at direktang naglalabas ng mga gate sa basis na iyon. Ang ibang encoding ay nangangailangan ng sarili nitong plugin para dito, at wala pang kasama.
Ang paraan ng pag-transpile ng mga UCJ circuit sa ilalim ng non-Jordan-Wigner na encoding ay isang bukas na tanong sa pananaliksik at nagbibigay ang package na ito ng balangkas para mabuo ang ganoong pipeline. Ang natitirang bahagi ng gabay na ito ay bumubuo, bilang halimbawa, ng transpiler pipeline para sa Jordan-Wigner na encoding, katumbas ng ginagawa ng generate_preset_jw_pass_manager().
4. Ibaba ito sa pamamagitan ng Jordan-Wigner
Nangyayari ang pagbaba sa F2QSynthesis, na gumagana tulad ng HighLevelSynthesis ng Qiskit: dinadaanan nito ang mga fermionic instruction at ibinibigay ang bawat isa sa plugin na nakarehistro para sa pangalan ng gate na iyon. Ang F2QSynthesis.methods attribute nito ay may hawak na isang F2QSynthesisPlugin bawat pangalan ng gate, kaya ang pagbuo ng stage ay nangangahulugang pagpili ng plugin para sa bawat fermionic gate na nilalaman ng circuit:
>>> from qiskit_fermions.mappers.library import jordan_wigner
>>> from qiskit_fermions.transpiler.passes import F2QSynthesis
>>> from qiskit_fermions.transpiler.passes.synthesis import (
... MapperFnEvolutionSynthesis,
... TrivialOccupationInitializeModesSynthesis,
... )
>>>
>>> synthesis = F2QSynthesis()
>>> synthesis.methods["Evolution"] = MapperFnEvolutionSynthesis(jordan_wigner)
>>> synthesis.methods["InitializeModes"] = TrivialOccupationInitializeModesSynthesis()
Ang pag-assign ng mga instance sa ganitong paraan ay siya ring paraan para makapasok sa pipeline ang plugin na isinulat mo mismo, nang hindi kailangang irehistro muna ito kahit saan.
Dito naisasaayos ang encoding, sa dalawang paraang inilarawan ng seksyon 3: tahasan, sa pamamagitan ng pagbibigay ng jordan_wigner() sa MapperFnEvolutionSynthesis, at hindi tahasan, sa pamamagitan ng isa pang plugin na ipinapalagay na ito.
Ang stage na iyon ay isinasalang sa isang MultiStagePassManager na ang mga stage ay tumatakbo sa nakapirming pagkakasunod-sunod:
>>> from qiskit.passmanager import MultiStagePassManager
>>> from qiskit.transpiler import generate_preset_pass_manager
>>> from qiskit.transpiler.passes.synthesis.high_level_synthesis import HLSConfig
>>>
>>> from qiskit_fermions.transpiler import (
... FermionicCircuitToDAG,
... FermionicPassManager,
... QuantumDAGToCircuit,
... )
>>> from qiskit_fermions.transpiler.passes import (
... F2FSynthesis,
... HopEvolutionSynthesis,
... MergeOrbitalRotations,
... MergeSlaterDeterminantPreparation,
... TrivialF2QLayout,
... )
>>>
>>> pm = MultiStagePassManager(
... input=FermionicCircuitToDAG(),
... optimization=FermionicPassManager(
... [
... MergeOrbitalRotations(),
... MergeSlaterDeterminantPreparation(),
... F2FSynthesis(
... {"OrbitalRotation": "Givens", "PrepareSlaterDeterminant": "Givens"}
... ),
... ]
... ),
... layout=FermionicPassManager(TrivialF2QLayout()),
... synthesis=synthesis,
... qubit=generate_preset_pass_manager(
... hls_config=HLSConfig(PauliEvolution=[HopEvolutionSynthesis(), "default"])
... ),
... output=QuantumDAGToCircuit(),
... )
Ang optimization stage ay tumatakbo bago ang synthesis, sa fermionic circuit, na siyang dahilan kung bakit sulit ito: nire-rewrite nito ang mga gate habang fermionic at encoding-agnostic pa ang mga ito, kaya dala ng tipid ang anumang encoding na susunod. Pinagsasama ng MergeOrbitalRotations ang bawat sunod-sunod na OrbitalRotation gate sa isa, na mahalaga para sa UCJ ansatz dahil ang bawat layer ay nagtatapos sa at ang susunod ay nagsisimula sa . Pagkatapos ay pinagsasama ng MergeSlaterDeterminantPreparation ang nangungunang InitializeModes at ang rotation na kasunod nito sa iisang PrepareSlaterDeterminant, na ibinababa ng synthesis sa pamamagitan ng mas murang reduced Slater decomposition sa halip na buong square rotation. Mahalaga ang pagkakasunod-sunod: ang pagsasama muna ng mga rotation ay naglalantad ng iisang rotation kaagad pagkatapos ng initialization, na saka kinokontrata ng Slater fusion papasok dito. Ang F2FSynthesis ang huli, dahil binabasag nito ang anumang rotation na natira, at kung gagawin ito nang mas maaga ay mawawalan ng pagsasamahin ang dalawang merging pass.
Ang natitirang mga stage ay plumbing lamang. Ang input at output ay nagko-convert sa pagitan ng mga circuit at DAG, ang layout ay nag-a-assign ng mga fermionic mode sa mga qubit (ina-map ng TrivialF2QLayout ang mode i sa qubit i), at ang qubit ay isang ordinaryong Qiskit pass manager na pumapalit kapag wala nang natirang fermionic.
Ang pagpapatakbo nito ay nangangailangan ng isang hakbang sa paghahanda: ang composite na UCJ gate ay kailangang i-decompose muna sa mga primitive na gate nito, para makita ng optimization stage ang mga indibidwal na rotation at evolution.
>>> transpiled = pm.run(circuit.decompose())
>>> print(dict(sorted(transpiled.count_ops().items())))
{'rz': 14, 'rzz': 12, 'x': 2, 'xx_plus_yy': 28}
Kung walang target na device, nagma-map ito sa 2 * norb na mga qubit na may ipinapalagay na all-to-all na connectivity; ang mga orbital rotation ay nasi-synthesize sa mga XXPlusYYGateobject at ang mga diagonal Coulomb evolution sa mga RZZGateobject:
>>> transpiled.draw("mpl", fold=-1)
<Figure size ... with 1 Axes>
Ang pipeline na ito mismo ang binubuo ng generate_preset_jw_pass_manager(), kaya sa praktika ay gamitin ang preset at buuin nang mano-mano ang mga stage kapag kailangan mong baguhin ang isa sa mga ito:
>>> from qiskit_fermions.transpiler.presets import generate_preset_jw_pass_manager
>>>
>>> preset = generate_preset_jw_pass_manager()
>>> preset.run(circuit.decompose()).count_ops() == transpiled.count_ops()
True
5. Itugma ang topology ng device
Ang tunay na device ay may limitadong qubit coupling map, at ang LUCJ ansatz ay dinisenyo para tumugma rito. Ang same-spin (pairs_aa) na mga interaksyon ay bumubuo ng dalawang linear chain at ang alpha-beta (pairs_ab) na mga interaksyon ang tumutulay sa mga ito. Ang generate_lucj_pass_manager() ng ffsim ay bumubuo ng device-aware na qubit pipeline para sa istrukturang ito, at ibinabalik ang subset ng pairs_ab na talagang kayang tanggapin ng hardware. Isalang ang pipeline na iyon sa qubit stage ng preset habang pinapanatili ang sariling fermion-to-qubit synthesis ng package:
>>> from ffsim.qiskit import generate_lucj_pass_manager
>>> from qiskit.providers.fake_provider import GenericBackendV2
>>> from qiskit.transpiler import CouplingMap
>>>
>>> # a heavy-hex device coupling map (any BackendV2 works, e.g. a real fake_provider backend)
>>> coupling_map = CouplingMap.from_heavy_hex(5)
>>> backend = GenericBackendV2(
... num_qubits=coupling_map.size(),
... basis_gates=["cp", "xx_plus_yy", "p", "x", "swap"],
... coupling_map=coupling_map,
... )
>>>
>>> # nearest-neighbor same-spin chain; let the pass manager choose the alpha-beta pairs
>>> pairs_aa = [(p, p + 1) for p in range(norb - 1)]
>>>
>>> pm = generate_preset_jw_pass_manager()
>>> hls_config = HLSConfig(PauliEvolution=[HopEvolutionSynthesis(), "default"])
>>> pm.qubit, allowed_pairs_ab = generate_lucj_pass_manager(
... backend,
... norb,
... "heavy-hex",
... (pairs_aa, None),
... optimization_level=3,
... seed_transpiler=0,
... hls_config=hls_config,
... )
>>>
>>> # the alpha-beta interactions the heavy-hex connectivity can implement
>>> print(allowed_pairs_ab)
[(0, 0)]
Iyon ang punto ng komposisyon: ang qubit stage ay device-aware, habang ang fermion-to-qubit synthesis stage na nauuna rito ay nananatiling sa package na ito, kaya nananatiling napapalitan. Ang pagpapalit sa qubit stage ay nag-aalis ng nirerehistro ng preset doon, na siyang HopEvolutionSynthesis na ginagawang mga XXPlusYYGateobject ang mga Givens factor ng isang orbital rotation. Ipasa ito sa pamamagitan ng hls_config gaya ng nasa itaas, o kailangang maabot ang basis ng device nang wala ito.
Dahil naka-set na ang pm.qubit sa device-aware na pipeline, ang pagpapatakbo ng pass manager ay naglalatag ng circuit sa mga qubit ng backend at nagru-route nito sa coupling map. Para sa unrestricted na ansatz mula sa hakbang 1, na ang diagonal Coulomb operator ay naglalaman pa rin ng mga alpha-beta term na hindi direktang maabot ng hardware, kailangang magpasok ang router ng maraming SWAP gate para tulayan ang mga ito:
>>> naive = pm.run(circuit.decompose())
>>> naive.num_qubits # laid out on the full heavy-hex device register
57
>>> naive_swaps = naive.count_ops()["swap"]
>>> naive_swaps # many SWAPs to bridge the unreachable alpha-beta interactions
33
Limitahan ang ansatz sa mga interaksyong kayang ipatupad ng hardware
Ang solusyon ay ibalik ang allowed_pairs_ab sa pagbuo ng ansatz, sa pamamagitan ng argument na interaction_pairs ng from_t_amplitudes(), para ang diagonal Coulomb operator ay maglaman lamang ng mga alpha-beta term na direktang kayang ipatupad ng coupling map. Tumutugma na ang ansatz sa topology ng device at halos hindi na kailangang galawin ng router:
>>> restricted = UCJ(
... ffsim.UCJOpSpinBalanced.from_t_amplitudes(
... t2, t1=t1, n_reps=2, interaction_pairs=(pairs_aa, allowed_pairs_ab)
... )
... )
>>>
>>> circuit = FermionicCircuit(2 * norb)
>>> circuit.append(InitializeModes.from_hartree_fock(norb, nelec), circuit.modes)
>>> circuit.append(restricted, circuit.modes)
>>>
>>> transpiled = pm.run(circuit.decompose())
>>> restricted_swaps = transpiled.count_ops()["swap"]
>>> restricted_swaps # far fewer routing SWAPs than the unrestricted ansatz
4
Ang pagguhit lamang sa mga aktibong qubit (idle_wires=False) ay nagpapakita ng circuit na limitado sa dalawang spin chain at sa alpha-beta na tulay, na naka-express sa mga basis gate ng device. Ikinakalat ng layout at routing ang mga logical mode sa mga pisikal na qubit ng device, kaya magpasa ng wire_order na kinuha mula sa final layout ng circuit (inililista ng final_index_layout() ang pisikal na qubit kung saan napunta ang bawat input qubit, ayon sa pagkakasunod ng input-qubit) para iguhit muli ang mga wire sa orihinal na pagkakasunod ng mode:
>>> wire_order = transpiled.layout.final_index_layout(filter_ancillas=False)
>>> transpiled.draw("mpl", idle_wires=False, fold=-1, wire_order=wire_order)
<Figure size ... with 1 Axes>

Ang eksaktong bilang ng gate at depth pagkatapos ng layout ay nakadepende sa mga routing/optimization pass at sa napiling device, kaya hindi ito inilalahad dito. Ang pangunahing punto ay ang co-design; ang pag-express ng ansatz gamit ang nearest-neighbor na pairs_aa chain at hardware-filtered na pairs_ab na mga tulay ay nagpapanatiling malapit sa topology ng device ang na-synthesize na circuit, na nagpapaliit sa routing overhead (mga ipinasok na SWAP gate). Tingnan ang FermionicSlaterGivensDecomposition para sa kaugnay na pagpili sa synthesis (minimize_2q_gate_count), na nagpapalit ng bilang ng two-qubit gate laban sa routed depth.
Mga susunod na hakbang
-
Alamin kung paano gumagana ang mga indibidwal na gate sa dokumentasyon ng
qiskit_fermions.circuit.libraryat sa gabay sa fermionic circuit. -
Tuklasin ang gabay na paliwanag sa mga operator para maunawaan kung paano direktang bumuo ng mga fermionic operator at Hamiltonian.
-
Tingnan kung paano ma-map ang isang fermionic circuit sa mga qubit sa gabay sa transpilation.
-
Basahin ang dokumentasyon ng
F2QSynthesispara sa isa pang paraan ng pag-configure ng synthesis stage: isangconfigdictionary na pumipili ng mga plugin ayon sa maiikling pangalang nirerehistro nila sa ilalim ng kanilangqiskit_fermions.transpiler.synthesisna mga entry point, sa halip na ayon sa instance. -
Basahin ang gabay sa ugnayan sa ffsim para maunawaan kung paano naghahati ng trabaho ang dalawang package, at kung aling mga protocol ang nagpapahintulot sa ffsim na mag-simulate ng mga circuit ng package na ito.
