I-optimize ang paghahanda ng Slater determinant
Ang mga konsepto sa gabay na ito ay kasalukuyang available lang sa Python API. Ang katumbas na functionality ay gagawing available sa C API sa isang susunod na release.
Ang isang InitializeModes gate ay nagdedeklara ng reference na mode occupation; ang isang OrbitalRotation gate ay nagro-rotate ng single-particle basis. Kapag magkasunod na inilagay, ang dalawang gate ay naghahanda ng isang Slater determinant; nagsisimula ang mga mode sa reference determinant at saka iro-rotate sa mga target na orbital. Kinakatawan ng PrepareSlaterDeterminant gate ang kompositong iyon, at dahil dala nito ang reference occupation, magagamit ng pag-transpile nito ang rectangular na givens_decomposition_slater(), na isinasakatuparan lang ang mga okupadong orbital, sa halip na ang buong square decomposition na kailangan ng isang OrbitalRotation nang mag-isa. Mas mura talaga ang synthesis na iyon (tingnan ang pakinabang sa dulo ng gabay na ito).
Ang MergeSlaterDeterminantPreparation transpiler pass ay nakikilala ang pattern na InitializeModes-tapos-OrbitalRotation sa isang FermionicCircuit at isinusulat muli ito bilang mga PrepareSlaterDeterminant gate, kaya awtomatikong nakukuha ng susunod na synthesis stage ang pinaliit na decomposition. Sa simulation, walang epekto ang rewrite sa state: ang PrepareSlaterDeterminant ay validate-then-rotate (bina-validate nito ang reference occupation at saka inilalapat ang rotation, tulad ng ginagawa ng dalawang hiwalay na gate), kaya ang merge ay nagbubukas ng mas murang synthesis nang hindi binabago ang inihandang state.
Sa buong gabay na ito, bawat circuit ay iginuguhit bago at pagkatapos ng pass, magkatabi: ang input sa kaliwa, at ang resulta ng pagpapatakbo ng pass dito sa kanan. Dalawang helper ang gumagawa nito. Pinapatakbo ng merge ang pass sa isang kopya ng circuit (sa pamamagitan ng FermionicDAGCircuit nito) at kino-convert ang resulta pabalik sa isang maiguguhit na FermionicCircuit, at iginuguhit ng draw_merge ang dalawang kalahati sa iisang before/after na figure:
>>> import matplotlib.pyplot as plt
>>> import numpy as np
>>>
>>> from qiskit_fermions.circuit import FermionicCircuit
>>> from qiskit_fermions.circuit.library import InitializeModes, OrbitalRotation
>>> from qiskit_fermions.transpiler import FermionicCircuitToDAG
>>> from qiskit_fermions.transpiler.converters import FermionicDAGToCircuit
>>> from qiskit_fermions.transpiler.passes import MergeSlaterDeterminantPreparation
>>>
>>> def merge(circuit):
... dag = FermionicCircuitToDAG().run(circuit)
... merged = MergeSlaterDeterminantPreparation().run(dag)
... return FermionicDAGToCircuit().run(merged)
>>>
>>> def natural_size(circuit):
... # the figure size Qiskit picks for a circuit on its own (discard the probe figure)
... probe = circuit.draw("mpl")
... size = probe.get_size_inches()
... plt.close(probe)
... return size
>>>
>>> def draw_merge(circuit):
... merged = merge(circuit)
... # let Qiskit size each half naturally, then lay them out side by side
... (bw, bh), (aw, ah) = natural_size(circuit), natural_size(merged)
... fig, (before, after) = plt.subplots(1, 2, figsize=(bw + aw, max(bh, ah)))
... circuit.draw("mpl", ax=before)
... merged.draw("mpl", ax=after)
... before.set_title("before")
... after.set_title("after")
... return fig
Ano ang minemerge
Tatlong pattern ang kinikilala ng pass, lahat ay nakabatay sa block-spin mode convention na ginagamit ng InitializeModes at OrbitalRotation. Para sa isang sistemang may norb na spatial orbital, ang mga mode na 0..norb ay ang spin-alpha na sektor, at ang norb..2*norb ay ang spin-beta na sektor. Ginagawa ang paghahanda ng Slater determinant bawat spin sector dahil may sariling hugis na (electrons, orbitals) ang bawat sektor.
Buong register (o spinless)
Ang pinakasimpleng pattern ay ang paggamit ng InitializeModes, na agad sinusundan ng isang OrbitalRotation sa parehong mga mode. Ito ang spinless na kaso (isang determinant sa lahat ng mode) at gayundin ang isang spinful na circuit na ang rotation ay sumasaklaw sa parehong sektor nang sabay. Nagsasanib ang dalawang gate sa iisang PrepareSlaterDeterminant:
>>> circuit = FermionicCircuit(4)
>>> circuit.append(InitializeModes([1, 1, 0, 0]), circuit.modes)
>>> circuit.append(OrbitalRotation(np.eye(4)), circuit.modes)
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>
Bawat spin sector
Pareho ito ng hugis, pero limitado sa isang spin half. Dito, dalawang independiyenteng pares ng initialize-tapos-rotate, isa sa bawat sektor, ang bawat isa ay nagsasanib nang mag-isa sa isang PrepareSlaterDeterminant:
>>> circuit = FermionicCircuit(6) # norb = 3: alpha modes 0..3, beta modes 3..6
>>> circuit.append(InitializeModes([1, 1, 0]), circuit.modes[:3])
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[:3])
>>> circuit.append(InitializeModes([1, 0, 0]), circuit.modes[3:])
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[3:])
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>
Global na initialization, mga rotation bawat spin
Nangangahulugan ito ng iisang full-register (2 * norb) na InitializeModes na sinusundan ng dalawang OrbitalRotationaction, isa sa bawat magkadikit na spin half. Hinahati ng pass ang global na occupation sa hangganan ng sektor at naglalabas ng dalawang PrepareSlaterDeterminant gate. Maaaring lumabas ang dalawang rotation sa anumang pagkakasunod-sunod.
Ito ang hugis na nililikha ng local unitary cluster Jastrow (LUCJ) workflow (tingnan ang LUCJ guide); naglalagay ang user ng InitializeModes (karaniwan ang Hartree-Fock reference gamit ang from_hartree_fock()) sa unahan ng circuit at nagdaragdag ng UCJ ansatz, na ang unang dalawang per-spin na orbital rotation ay direktang sumusunod sa initialization kapag na-decompose na ang ansatz.
>>> circuit = FermionicCircuit(6) # norb = 3, nelec = (2, 1)
>>> circuit.append(InitializeModes.from_hartree_fock(3, (2, 1)), circuit.modes)
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[:3])
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[3:])
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>
Gumagana rin ang pattern na ito kapag isang spin half lang ang ni-rotate ng gate na direktang sumusunod sa initialization, ang hugis na nalilikha kapag ang orbital rotation ng isang spin sector lang ang nasa unahan ng circuit. Ang na-rotate na kalahati ay nagiging PrepareSlaterDeterminant kasama ang rotation nito, at ang kabilang kalahati ay inihahanda gamit ang identity na rotation. Ang identity preparation ay nagsi-synthesize sa mga reference na X gate na ilalabas din ng InitializeModes para sa kalahating iyon, kaya ang pag-pad dito ay walang dagdag na gate habang binubuksan pa rin ang pinaliit na Slater synthesis sa na-rotate na kalahati:
>>> circuit = FermionicCircuit(6)
>>> circuit.append(InitializeModes.from_hartree_fock(3, (2, 1)), circuit.modes)
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[:3])
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>
Ang naiiwang hindi ginagalaw
Sinadyang konserbatibo ang pass. Nagsasanib lang ito kapag ang dalawang gate ay talagang bumubuo ng paghahanda ng Slater determinant, at kinokopya ang lahat ng iba pa nang walang pagbabago. Ang “agad na sinusundan” ay nauunawaan sa data-flow graph ng circuit. Nagsasanib lang ang OrbitalRotation kapag ang InitializeModes ay ang tanging nauna nito sa lahat ng mode nito. Sa mga figure sa ibaba, magkapareho ang before at after: walang nagsanib.
Isang operasyon sa pagitan
Kung may anumang gate na humahawak sa mga mode sa pagitan ng initialization at ng rotation, hindi na sila bumubuo ng payak na preparation, kaya walang minemerge:
>>> from qiskit_fermions.circuit.library import Evolution
>>> from qiskit_fermions.operators import FermionOperator, ann, cre
>>>
>>> circuit = FermionicCircuit(4)
>>> circuit.append(InitializeModes([1, 1, 0, 0]), circuit.modes)
>>> number_op = FermionOperator.from_dict({(cre(0), ann(0)): 1.0})
>>> circuit.append(Evolution(4, number_op, time=0.5), circuit.modes)
>>> circuit.append(OrbitalRotation(np.eye(4)), circuit.modes)
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>
Isang bahagyang rotation na hindi spin half
Ang rotation na sumasaklaw sa eksaktong isang magkadikit na spin half ng isang full-register initialization ay nagsasanib (tingnan ang nakaraang seksyon). Pero ang rotation sa anumang iba pang bahagyang set ng mode (mas kaunting mode kaysa sa initialization at hindi isa sa dalawang spin half nito, tulad ng sub-range na tumatawid sa hangganan ng sektor) ay hindi per-sector na preparation. Ang pag-merge nito ay tahimik na mag-aalis ng papel ng initialization sa mga mode na hindi nito sakop, kaya iniiwan ng pass ang parehong gate sa lugar:
>>> circuit = FermionicCircuit(4) # norb = 2: the only spin halves are modes [0, 1] and [2, 3]
>>> circuit.append(InitializeModes([1, 1, 0, 0]), circuit.modes)
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[:3])
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>
Bakit ito mahalaga: ang pakinabang sa synthesis
Sulit ang merge dahil ang PrepareSlaterDeterminant ay nagsi-synthesize sa mas kaunting gate kaysa sa OrbitalRotation na sinasaklaw nito. Ang isang determinant na may electron sa orbital ay nangangailangan ng hindi hihigit sa na two-qubit rotation at walang diagonal phase gate, kumpara sa na rotation at na phase ng buong square orbital rotation.
Pinapatakbo ng preset na Jordan-Wigner pass manager ang MergeSlaterDeterminantPreparation sa optimization stage nito, kaya awtomatikong nangyayari ang pagbawas na ito. I-transpile ang isang two-electron, six-orbital na preparation hanggang sa isang QuantumCircuit at bilangin ang mga gate nito:
Pinapatakbo ng preset ang MergeOrbitalRotations bago mismo ang MergeSlaterDeterminantPreparation. Mahalaga ito kapag ang isang hanay ng magkakasunod na OrbitalRotation gate ay sumusunod sa initialization. Unang pinagsasama ng naunang pass ang hanay sa iisang rotation, na saka kino-contract ng MergeSlaterDeterminantPreparation kasama ang initialization sa iisang PrepareSlaterDeterminant. Kung wala ang pagkakasunod-sunod na iyon, ang unang rotation lang ng hanay ang masasakop at ang iba ay magsi-synthesize gamit ang buong square decomposition.
>>> from qiskit_fermions.transpiler.presets import generate_preset_jw_pass_manager
>>>
>>> # a reproducible random 6 x 6 orbital rotation
>>> rng = np.random.default_rng(1)
>>> z = rng.standard_normal((6, 6)) + 1j * rng.standard_normal((6, 6))
>>> q, r = np.linalg.qr(z)
>>> rotation = q * (np.diagonal(r) / np.abs(np.diagonal(r)))
>>>
>>> circuit = FermionicCircuit(6)
>>> circuit.append(InitializeModes([1, 1, 0, 0, 0, 0]), circuit.modes)
>>> circuit.append(OrbitalRotation(rotation), circuit.modes)
>>>
>>> pm = generate_preset_jw_pass_manager()
>>> prepared = pm.run(circuit)
>>> print(dict(sorted(prepared.count_ops().items())))
{'x': 2, 'xx_plus_yy': 8}
>>> prepared.draw("mpl", fold=-1)
<Figure size ... with 1 Axes>
Ihambing iyan sa pag-synthesize ng parehong orbital rotation nang mag-isa. Walang naunang initialization kaya walang mamemerge, kaya bumabalik ang preset sa buong square decomposition, na mas malalim at may dalang mga diagonal phase gate:
>>> reference = FermionicCircuit(6)
>>> reference.append(OrbitalRotation(rotation), reference.modes)
>>> reference_prepared = pm.run(reference)
>>> print(dict(sorted(reference_prepared.count_ops().items())))
{'rz': 6, 'xx_plus_yy': 15}
>>> reference_prepared.draw("mpl", fold=-1)
<Figure size ... with 1 Axes>

Inaalis ng pinaliit na decomposition ang lahat ng anim na phase gate at halos hinahati ang bilang ng two-qubit rotation. Tama ang inihandang determinant hanggang sa isang global phase (hindi mahalaga sa pisika para sa state preparation), na siyang aayusin sana ng mga phase gate.
Dalawang paraan para hatiin ang gate
Ang isang PrepareSlaterDeterminant ay maaaring hatiin sa dalawang magkaibang paraan, at hindi sila mapagpapalit:
decompose()
Pinalalawak ang gate sa depinisyon nito: ang InitializeModes ng reference na sinusundan ng buong OrbitalRotation. Katumbas ito ng gate bilang isang operator, para sa anumang input state.
PrepareSlaterDeterminant.synthesis, inilapat ng F2FSynthesis
Pinapalitan ang gate ng pinaliit na decomposition ng gabay na ito. Katumbas ito ng gate bilang state preparation lang: nagbubunga ito ng parehong determinant, hanggang sa isang global phase, kapag nagsisimula ang mga mode sa vacuum. Mas mura ito dahil mismo sa pag-aakalang iyon.
Gamitin ang synthesis kapag ang gate ay naghahanda ng state sa vacuum, na siyang layunin ng gate at ginagawa ng transpiler para sa iyo. Gamitin ang decompose() kapag kailangang patuloy na kumilos ang gate bilang operator, halimbawa dahil may ibang gate na nauuna rito sa parehong mga mode, o kapag gusto mong siyasatin o i-simulate ang mga payak na gate. Hindi malalaman ng F2FSynthesis pass kung aling kaso ang tinitingnan nito, kaya nasa circuit na ibibigay mo ang responsibilidad na ihanda ang determinant mula sa vacuum.
Ang pag-simulate ng circuit gamit ang ffsim ay hindi nangangailangan ng alinman: inilalapat ng bawat gate ang buong unitary nito, kaya ang circuit na may PrepareSlaterDeterminant ay sinisimulate kung ano ito, at nananatili sa mabilis na landas para sa mga OrbitalRotation gate sa paligid nito. Ang paghahati sa Slater gate lang gamit ang circuit.decompose("PrepareSlaterDeterminant") ay nag-iiwan din ng buo sa mga rotation na iyon.
PrepareSlaterDeterminant, InitializeModes, OrbitalRotation, MergeOrbitalRotations, F2FSynthesis, FermionicSlaterGivensDecomposition, at ang LUCJ guide para sa workflow na lumilikha ng global-initialization na pattern.
