Lucrează cu circuite fermionice
Conceptele din acest ghid sunt disponibile momentan doar în API-ul Python. Funcționalitatea echivalentă va fi pusă la dispoziție în API-ul C într-o versiune viitoare.
Acest ghid explică cum să folosești FermionicCircuit pentru a implementa algoritmi cuantici direct în spațiul fermionic, în loc să faci mai întâi maparea pe qubiți.
De ce să folosești circuite fermionice
Să luăm fluxul de lucru tradițional pentru implementarea evoluției în timp a unui Hamiltonian fermionic pe o arhitectură bazată pe qubiți:
-
Definește Hamiltonianul fermionic (de exemplu, ).
-
Mapează-l la un operator pe qubiți (de exemplu, folosind maparea Jordan-Wigner).
-
Implementează evoluția în timp în spațiul qubiților (folosind trotterizarea sau alte metode).
Această abordare are o limitare fundamentală: maparea timpurie pe qubiți pierde cunoștințele despre problemă legate de structura fermionică. Trecerile de optimizare standard nu pot exploata simetriile fermionice sau reordonările permise ale termenilor, ceea ce duce la circuite suboptime, cu adâncime și număr de porți inutil de mari.
FermionicCircuit permite un flux de lucru mai bun:
-
Definește Hamiltonianul fermionic (de exemplu, ).
-
Implementează evoluția în timp direct în spațiul fermionic, folosind porți fermionice.
-
Mapează pe spațiul qubiților ca parte a procesului de transpilare.
Această abordare rezolvă limitarea păstrând cunoștințele despre problemă în spațiul fermionic, unde trecerile de optimizare pot exploata structura fermionică și relațiile de comutare pentru a reduce adâncimea circuitului. Circuitul fermionic descrie ce calcul trebuie efectuat, iar transpilarea se ocupă de cum să fie mapat pe qubiți, oferind o separare mai clară a responsabilităților.
Indexarea generică a modurilor
Atât modulul operators, cât și FermionicCircuit folosesc o indexare generică bazată pe moduri, care nu face nicio presupunere despre natura modurilor. Un mod este pur și simplu un index abstract care etichetează un grad de libertate fermionic, fără semantică inerentă. Această alegere de design reflectă modulul operators și asigură flexibilitate maximă. De exemplu, poți implementa evoluția în timp pentru orice operator care implementează protocolul OperatorTrait, indiferent de reprezentarea sa matematică.
Construiește un circuit fermionic
Exemplul de mai jos implementează evoluția în timp descrisă în secțiunea anterioară. Construiește un FermionicCircuit specificând numărul de moduri fermionice, apoi adaugă porți fermionice din qiskit_fermions.circuit.library pentru a implementa evoluția în timp.
Exemplul arată și cum să încorporezi cunoștințe de domeniu într-un operator folosind gruparea termenilor operatorului. Atribuind indici de grup termenilor Hamiltonianului, păstrezi informații structurale pe care trecerile de optimizare le pot exploata pe tot parcursul stivei de transpilare.
>>> 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)
Observă cum gruparea termenilor operatorului se păstrează chiar și în operații simple precum descompunerea. Aceasta demonstrează cum circulă informația structurală prin stiva de circuite. Pentru a înțelege transpilarea completă pe qubiți, consultă ghidul Transpilarea circuitelor fermionice.
Ține porțile separate cu o barieră
Trecerile de optimizare fuzionează porțile vecine acolo unde pot. De exemplu, MergeOrbitalRotations combină o succesiune de porți OrbitalRotation consecutive într-una singură, iar MergeSlaterDeterminantPreparation integrează un InitializeModes în rotația care îl urmează.
Folosește FermionicCircuit.barrier() când vrei să previi acest lucru. O barieră nu are niciun efect unitar, deci nu modifică starea pregătită de circuitul tău; ea marchează doar un punct peste care trecerile nu fuzionează. Apeleaz-o fără argumente pentru a acoperi toate modurile sau transmite modurile la care vrei să o restrângi:
>>> 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)
Barierele supraviețuiesc transpilării fermion-qubit, deci restricționează și optimizarea la nivel de qubiți care urmează. Ghidul Transpilarea circuitelor fermionice tratează această etapă.
Repetă un circuit
Mulți algoritmi aplică de multe ori același bloc de porți. Evoluția în timp este cazul comun: un pas Trotter se construiește o singură dată, apoi se repetă pentru a atinge timpul țintă. Folosește repeat() pentru a construi circuitul repetat și compose() pentru a-l uni cu un prefix de pregătire a stării.
Exemplul de mai jos construiește un pas Trotter de ordinul doi pentru un model Fermi-Hubbard. Pasul este simetric: împarte termenul de salt în două porți OrbitalRotation de jumătate de durată, care înconjoară interacțiunea locală.
>>> 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)
Repetarea pasului și compunerea lui peste o referință Hartree-Fock dă evoluția completă:
>>> 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
Repetările sunt aplatizate în circuitul returnat, în loc să fie învelite într-o singură poartă, așa că count_ops() numără două rotații pe pas. Această aplatizare face redundanța de la limitele pașilor vizibilă pentru transpiler: rotația finală de jumătate de durată a unui pas și cea inițială a următorului sunt adiacente, așa că MergeOrbitalRotations le combină într-o singură rotație.
>>> from qiskit_fermions.transpiler import FermionicPassManager
>>> from qiskit_fermions.transpiler.passes import MergeOrbitalRotations
>>>
>>> merged = FermionicPassManager([MergeOrbitalRotations()]).run(evolution)
>>> merged.count_ops()["OrbitalRotation"]
4
Rezultatul costă o rotație pe pas plus una, exact ce obții fuzionând manual acei semi-pași. Scrierea pasului în forma lui simetrică naturală și repetarea lui produce același circuit, deci fuziunea este lăsată în seama etapei de optimizare.
Când ai nevoie ca repetările să rămână separate, transmite insert_barriers=True pentru a plasa o barrier() între ele. Trecerile de optimizare nu fuzionează porțile peste o barieră, așa că fiecare rotație se păstrează:
>>> separated = trotter_step.repeat(reps, insert_barriers=True)
>>> FermionicPassManager([MergeOrbitalRotations()]).run(separated).count_ops()[
... "OrbitalRotation"
... ]
6
Argumentul reps al FermionicSuzukiTrotter este un concept diferit. El subdivide o singură poartă Evolution în mai mulți factori, mai scurți, în timp ce repeat() duplică circuite întregi. Repetarea unui circuit care conține o singură Evolution doar înmulțește timpul total de evoluție.
Măsoară moduri fermionice
measure_all() citește fiecare mod într-un bit clasic propriu, asociind modul i cu bitul i. Folosește measure() cu propriul ClassicalRegister pentru a citi doar o parte dintre ele.
>>> measured = trotter_step.measure_all(inplace=False)
>>> measured.count_ops()["measure"]
4
Transmiterea inplace=False returnează o copie măsurată și o lasă neatinsă pe cea originală, ceea ce contează deoarece o măsurătoare nu este unitară: un circuit care conține una nu mai poate fi aplicat unui vector de stare, așa că păstrează un circuit nemăsurat dacă vrei să îl și simulezi.
Măsurarea aici, și nu pe QuantumCircuit-ul transpilat, este ceea ce păstrează șirurile de biți eșantionate ușor de citit. O măsurătoare fermionică numește modul pe care îl citește, așa că fiecare etapă care mută moduri (RelabelModes la nivel fermionic, aranjarea și rutarea la nivel de qubiți) duce măsurătoarea cu ea, iar șirul de biți rămâne indexat după modul despre care ai întrebat.
Spre deosebire de measure_all(), nu se inserează nicio barrier() înaintea măsurătorilor. Apelează tu însuți mai întâi barrier() dacă vrei una acolo.
Dacă un bit măsurat înseamnă ceea ce te aștepți depinde de codificarea fermion-qubit, care se alege în timpul transpilării, nu aici. Într-o codificare în baza de ocupare, precum Jordan-Wigner, fiecare mod este purtat de un qubit, iar bitul este ocuparea acelui mod; vezi FermionicMeasure.
Transpilează circuite fermionice
Pentru a implementa algoritmul cuantic reprezentat de FermionicCircuit-ul tău, acesta trebuie transpilat într-un QuantumCircuit. Poți afla cum se face acest lucru în ghidul Transpilează circuite fermionice.
Pașii următori
-
Află despre porțile fermionice disponibile în documentația
qiskit_fermions.circuit.library. -
Explorează ghidul explicativ despre operatori pentru a înțelege cum să construiești Hamiltoniene fermionici pe care îi poți folosi cu circuite fermionice.
-
Parcurge ghidul de introducere SQDRIFT pentru un exemplu practic complet.
-
Citește ghidul despre relația cu ffsim pentru a înțelege cum poate fi simulat direct un
FermionicCircuitși cum aceşti indici generici de moduri capătă semantică de spin abia în momentul simulării.

