Lumaktaw sa pangunahing nilalaman
Ang proyektong ito ay nasa beta. Nakakita ng bug? Mag-bukas ng GitHub issue. May tanong o ideya? Magtanong sa Discussions — gusto naming marinig ang iyong feedback!
Gaano kaganda ang kalidad ng pagsasalin sa pahinang ito?

Paggamit ng mga fermionic circuit

Mahalaga

Ang mga konsepto sa gabay na ito ay kasalukuyang available lang sa Python API. Magiging available ang katumbas na functionality sa C API sa isang susunod na release.

Ipinapaliwanag ng gabay na ito kung paano gamitin ang FermionicCircuit para direktang ipatupad ang mga quantum algorithm sa fermionic space, imbes na i-map muna sa mga qubit.

Bakit gagamit ng mga fermionic circuit​

Isaalang-alang ang tradisyonal na workflow para ipatupad ang time evolution ng isang fermionic Hamiltonian sa isang arkitekturang nakabatay sa qubit:

  1. Tukuyin ang fermionic Hamiltonian (halimbawa, H=∑pqhpqcp†cqH = \sum_{pq} h_{pq} c^\dagger_p c_q).

  2. I-map ito sa isang qubit operator (halimbawa, gamit ang Jordan-Wigner mapping).

  3. Ipatupad ang time evolution sa qubit space (gamit ang Trotterization o iba pang paraan).

May pangunahing limitasyon ang pamamaraang ito; ang maagang pag-map sa mga qubit ay nagtatapon ng kaalamang may kinalaman sa problema tungkol sa fermionic na estruktura. Hindi magagamit ng mga karaniwang optimization pass ang mga fermionic symmetry o ang mga pinahihintulutang pag-aayos muli ng mga term, kaya nagreresulta ito sa mga suboptimal na circuit na may hindi kinakailangang depth at bilang ng gate.

Nagbibigay ang FermionicCircuit ng mas magandang workflow:

  1. Tukuyin ang fermionic Hamiltonian (halimbawa, H=∑pqhpqcp†cqH = \sum_{pq} h_{pq} c^\dagger_p c_q).

  2. Ipatupad ang time evolution nang direkta sa fermionic space gamit ang mga fermionic gate.

  3. I-map sa qubit space bilang bahagi ng proseso ng transpilation.

Tinutugunan ng pamamaraang ito ang limitasyon sa pamamagitan ng pagpapanatili ng kaalamang may kinalaman sa problema sa fermionic space, kung saan magagamit ng mga optimization pass ang fermionic na estruktura at mga commutation relation para bawasan ang circuit depth. Inilalarawan ng fermionic circuit kung anong computation ang gagawin, habang ang transpilation naman ang bahala kung paano ito i-map sa mga qubit, kaya mas malinis ang paghihiwalay ng mga responsibilidad.

Generic na mode indexing​

Parehong gumagamit ang operators module at ang FermionicCircuit ng generic na mode-based indexing na walang ipinapalagay tungkol sa likas na katangian ng mga mode. Ang mode ay isa lang abstract na index na nagtatakda sa isang fermionic degree of freedom, na walang likas na semantika. Ginagaya ng desisyong ito sa disenyo ang operators module at tinitiyak ang pinakamataas na flexibility. Halimbawa, maaari mong ipatupad ang time evolution para sa anumang operator na nagpapatupad ng OperatorTrait protocol, anuman ang matematikal nitong representasyon.

Gumawa ng fermionic circuit​

Ipinapatupad ng halimbawa sa ibaba ang time evolution na inilarawan sa nakaraang seksyon. Gumagawa ito ng FermionicCircuit sa pamamagitan ng pagtukoy sa bilang ng mga fermionic mode, pagkatapos ay nagdaragdag ng mga fermionic gate mula sa qiskit_fermions.circuit.library para ipatupad ang time evolution.

Ipinapakita rin ng halimbawa kung paano maglagay ng domain knowledge sa isang operator gamit ang operator term grouping. Sa pagtatalaga ng mga group index sa mga term ng Hamiltonian, napapanatili mo ang estrukturang impormasyon na magagamit ng mga optimization pass sa buong transpilation stack.

>>> from qiskit_fermions.circuit import FermionicCircuit
>>> from qiskit_fermions.circuit.library import Evolution
>>> from qiskit_fermions.operators import FermionOperator, cre, ann
>>>
>>> # Create a circuit with 4 fermionic modes
>>> circuit = FermionicCircuit(4)
>>>
>>> # Define a simple fermionic Hamiltonian
>>> hamiltonian = FermionOperator.from_terms([
... ([cre(0), ann(2)], 0.5),
... ([cre(2), ann(0)], 0.5),
... ([cre(1), ann(3)], 0.5),
... ([cre(3), ann(1)], 0.5),
... ])
>>> # Add some problem structure by grouping our Hamiltonian terms
>>> hamiltonian.groups = [0, 0, 1, 1]
>>>
>>> # Add an evolution gate to implement exp(-i * t * H)
>>> evolution = Evolution(4, hamiltonian, time=1.0)
>>> circuit.append(evolution, circuit.register)

A simple `FermionicCircuit` with a single time evolution gate.

A decomposed `FermionicCircuit` with several time evolution gates.

Pansinin kung paanong napapanatili ang operator term grouping kahit sa mga simpleng operasyon tulad ng decomposition. Ipinapakita nito kung paano dumadaloy ang estrukturang impormasyon sa circuit stack. Para maunawaan ang buong transpilation papunta sa mga qubit, tingnan ang gabay na Transpiling fermionic circuits.

Paghiwalayin ang mga gate gamit ang barrier​

Pinagsasama ng mga optimization pass ang mga magkatabing gate hangga't maaari. Halimbawa, pinagsasama ng MergeOrbitalRotations ang sunud-sunod na OrbitalRotation na mga gate sa isa, at isinasama naman ng MergeSlaterDeterminantPreparation ang isang InitializeModes sa rotation na kasunod nito.

Gamitin ang FermionicCircuit.barrier() kapag gusto mong pigilan iyon. Walang unitary na epekto ang barrier, kaya hindi nito binabago ang state na inihahanda ng circuit mo; nagmamarka lang ito ng punto na hindi tatawirin ng mga pass sa pagsasama. Tawagin ito nang walang argumento para sakupin ang lahat ng mode, o ipasa ang mga mode para limitahan ito sa mga iyon:

>>> from qiskit_fermions.circuit.library import OrbitalRotation
>>> import numpy as np
>>>
>>> rotations = FermionicCircuit(4)
>>> rotations.append(OrbitalRotation(np.eye(4, dtype=complex)), rotations.register)
>>> rotations.barrier()
>>> rotations.append(OrbitalRotation(np.eye(4, dtype=complex)), rotations.register)

A `FermionicCircuit` with a barrier separating two orbital rotations.

Nananatili ang mga barrier sa fermion-to-qubit transpilation, kaya naaapektuhan din nito ang qubit-level na optimization na kasunod. Sinasaklaw ng gabay na Transpile fermionic circuits ang yugtong iyon.

Ulitin ang isang circuit​

Maraming algorithm ang naglalapat ng parehong bloke ng mga gate nang maraming beses. Karaniwang halimbawa ang time evolution: isang beses binubuo ang isang Trotter step, tapos inuulit para maabot ang target na oras. Gamitin ang repeat() para buuin ang inuulit na circuit, at compose() para ikabit ito sa isang state preparation prefix.

Bumubuo ang halimbawa sa ibaba ng second-order na Trotter step para sa isang Fermi-Hubbard model. Simetriko ang step: hinahati nito ang hopping term sa dalawang OrbitalRotation na gate na may kalahating tagal, na pumapalibot sa on-site na interaksyon.

>>> import scipy.linalg
>>> from qiskit_fermions.circuit.library import InitializeModes, OrbitalRotation
>>>
>>> norb, reps, time = 2, 3, 1.0
>>> num_modes = 2 * norb
>>> dt = time / reps
>>>
>>> hopping = np.zeros((norb, norb))
>>> hopping[0, 1] = hopping[1, 0] = -1.0
>>> interaction = FermionOperator.from_dict(
... {(cre(p), ann(p), cre(p + norb), ann(p + norb)): 2.0 for p in range(norb)}
... )
>>>
>>> def rotation(duration):
... propagator = scipy.linalg.expm(-1j * duration * hopping)
... # the same rotation acts on the alpha and beta halves of the register
... return scipy.linalg.block_diag(propagator, propagator)
>>>
>>> trotter_step = FermionicCircuit(num_modes)
>>> trotter_step.append(OrbitalRotation(rotation(dt / 2)), trotter_step.modes)
>>> trotter_step.append(
... Evolution(num_modes, interaction, dt, atomic=True), trotter_step.modes
... )
>>> trotter_step.append(OrbitalRotation(rotation(dt / 2)), trotter_step.modes)

Ang pag-ulit ng step at pag-compose nito sa isang Hartree-Fock reference ang nagbibigay ng buong evolution:

>>> evolution = FermionicCircuit(num_modes)
>>> evolution.append(InitializeModes([True, False, True, False]), evolution.modes)
>>> evolution.compose(trotter_step.repeat(reps), inplace=True)
>>> evolution.count_ops()["OrbitalRotation"]
6

A `FermionicCircuit` with repeated steps.

Pinapatag ang mga pag-ulit sa ibinalik na circuit imbes na ibalot sa iisang gate, kaya binibilang ng count_ops() ang dalawang rotation kada step. Ang pagpapatag na iyon ang nagpapakita sa transpiler ng redundancy sa mga hangganan ng step: magkatabi ang huling half-duration na rotation ng isang step at ang una ng susunod, kaya pinagsasama sila ng MergeOrbitalRotations sa iisang rotation.

>>> from qiskit_fermions.transpiler import FermionicPassManager
>>> from qiskit_fermions.transpiler.passes import MergeOrbitalRotations
>>>
>>> merged = FermionicPassManager([MergeOrbitalRotations()]).run(evolution)
>>> merged.count_ops()["OrbitalRotation"]
4

A `FermionicCircuit` with repeated steps and merged orbital rotations.

Ang resulta ay may gastos na isang rotation kada step, dagdag ang isa, na siya ring makukuha mo kapag mano-mano mong pinagsama ang mga half-step na iyon. Ang pagsulat ng step sa natural nitong simetrikong anyo at pag-ulit nito ay nagbubunga ng parehong circuit, kaya ang pagsasama ay iniiwan sa optimization stage.

Kapag kailangan mong panatilihing hiwalay ang mga pag-ulit, ipasa ang insert_barriers=True para maglagay ng barrier() sa pagitan nila. Hindi pinagsasama ng mga optimization pass ang mga gate sa kabila ng barrier, kaya nananatili ang bawat rotation:

>>> separated = trotter_step.repeat(reps, insert_barriers=True)
>>> FermionicPassManager([MergeOrbitalRotations()]).run(separated).count_ops()[
... "OrbitalRotation"
... ]
6

A `FermionicCircuit` with repeated steps separated by barriers.

Tandaan

Ibang konsepto ang argumentong reps ng FermionicSuzukiTrotter. Hinahati nito ang isang Evolution na gate sa mas marami at mas maiikling factor, samantalang dinudoble naman ng repeat() ang buong mga circuit. Ang pag-ulit ng circuit na may isang Evolution ay nagpaparami lang ng kabuuang evolution time nito.

Sukatin ang mga fermionic mode​

Binabasa ng measure_all() ang bawat mode papunta sa sarili nitong classical bit, na ipinapares ang mode i sa bit i. Gamitin ang measure() kasama ang sarili mong ClassicalRegister para basahin lang ang ilan sa mga ito.

>>> measured = trotter_step.measure_all(inplace=False)
>>> measured.count_ops()["measure"]
4

Ang pagpasa ng inplace=False ay nagbabalik ng sinukat na kopya at hindi ginagalaw ang orihinal, na mahalaga dahil hindi unitary ang isang measurement: hindi na mailalapat sa state vector ang circuit na may measurement, kaya magtabi ng circuit na walang measurement kung gusto mo rin itong i-simulate.

Ang pagsukat dito, imbes na sa na-transpile na QuantumCircuit, ang nagpapanatiling madaling basahin ng mga na-sample na bitstring. Pinangangalanan ng fermionic measurement ang mode na binabasa nito, kaya bawat yugto na naglilipat ng mga mode (RelabelModes sa fermionic na antas, layout at routing sa qubit na antas) ay isinasama ang measurement, at nananatiling naka-index ang bitstring sa mode na itinanong mo.

Tandaan

Hindi tulad ng measure_all(), walang barrier() na inilalagay bago ang mga measurement. Tawagin mo muna mismo ang barrier() kung gusto mo nito roon.

Nakadepende sa fermion-to-qubit encoding kung ang sinukat na bit ay nangangahulugan ng inaasahan mo, na pinipili sa panahon ng transpilation at hindi dito. Sa ilalim ng occupation-basis encoding tulad ng Jordan-Wigner, dala ng isang qubit ang bawat mode at ang bit ay ang occupation ng mode na iyon; tingnan ang FermionicMeasure.

I-transpile ang mga fermionic circuit​

Para maipatupad ang quantum algorithm na kinakatawan ng iyong FermionicCircuit, kailangan itong i-transpile sa isang QuantumCircuit. Matututunan mo kung paano ito gawin sa gabay na Transpile fermionic circuits.

Mga susunod na hakbang​

  • Alamin ang tungkol sa mga available na fermionic gate sa dokumentasyon ng qiskit_fermions.circuit.library.

  • Tuklasin ang gabay sa paliwanag ng operators para maunawaan kung paano bumuo ng mga fermionic Hamiltonian na magagamit mo sa mga fermionic circuit.

  • Repasuhin ang gabay sa pagsisimula ng SQDRIFT para sa isang praktikal na end-to-end na halimbawa.

  • Basahin ang gabay sa ugnayan sa ffsim para maunawaan kung paano direktang masi-simulate ang isang FermionicCircuit, at kung paano nagkakaroon ng spin semantics ang mga generic na mode index na ito sa oras lang ng simulation.