Sari la conținutul principal
Acest proiect este în versiune beta. Ai găsit o eroare? Deschide un issue pe GitHub. Ai o întrebare sau o idee? Întreabă în Discussions — ne-ar plăcea să aflăm părerea ta!
Cum este calitatea traducerii pe această pagină?

Principiile de design ale reprezentărilor de operatori

Acest ghid explică principiile de design comune și conceptele de bază împărtășite de toate reprezentările de operatori din modulul operators.

Prezentare generală​

Reprezentările de operatori oferite de acest modul împărtășesc mai multe principii fundamentale de design:

  • Structură de date rară (sparse): operatorii codifică doar operațiile non-identitate. Structura internă a datelor este în general inspirată de formatele de date pentru matrice rare, permițând stocare și calcul eficiente pentru sisteme cu multe moduri, dar relativ puține contribuții semnificative.

  • Iterarea și reconstruirea termenilor: indiferent de stocarea internă rară, operatorii oferă o interfață de iterare consecventă, astfel încât poți inspecta, filtra și transforma termenii fără a înțelege structura de date subiacentă, apoi poți reconstrui operatori noi din termenii modificați.

  • Indexare bazată pe moduri: operatorii folosesc indici de mod abstracți pentru a eticheta gradele de libertate fermionice, permițând o mapare flexibilă de la sistemele fizice la reprezentarea operatorului.

  • Gruparea termenilor și relațiile de comutare: operatorii acceptă nativ informații de grupare care asociază termenii cu indici de grup. Aceasta permite optimizări și păstrarea structurii fizice fără structuri de date separate. Consultă ghidul de grupare pentru utilizare practică.

  • Operații aritmetice și matematice: toți operatorii implementează un set consecvent de operații aritmetice (precum adunarea, înmulțirea și compunerea) și de funcții matematice prin protocolul OperatorTrait, permițând cod uniform pentru diferite tipuri de operatori.

  • Ordonarea termenilor operatorului și formele normale: operatori echivalenți matematic pot avea reprezentări și comportamente foarte diferite în algoritmii cuantici. Toate reprezentările de operatori acceptă diverse forme normale (bazate pe relații de comutare specifice algebrei) pentru a obține reprezentări canonice, predictibile și optimizabile ale operatorilor.

Structură de date rară​

Toți operatorii folosesc o reprezentare rară în care sunt urmărite doar operațiile non-identitate. Fiecare operație non-identitate constă dintr-un coeficient (număr complex) și o secvență de acțiuni pe moduri specifice.

Această abordare reduce dramatic consumul de memorie și timpul de calcul, mai ales pentru sisteme cu multe moduri, dar relativ puține contribuții semnificative. Prin codificarea doar a operațiilor non-identitate, operațiile se concentrează doar pe ceea ce contează, permițând lucrul cu sisteme mari, imposibil de tratat cu reprezentări dense. În plus, operatorii se scalează firesc la orice număr de moduri; un operator care acționează pe modurile {0, 1} funcționează neschimbat în sisteme cu mult mai multe moduri, deoarece modurile neafectate sunt implicit identitate.

Termenii identici sunt păstrați separat în timpul operațiilor aritmetice și trebuie combinați explicit dacă este nevoie.

Format de stocare internă​

Intern, operatorii sunt stocați în matrice inspirate de formatele de date pentru matrice rare:

  • Matricea coeficienților: coeficientul complex al fiecărui termen

  • Matricea indicilor de mod: modurile fermionice pe care acționează fiecare acțiune

  • Matricea limitelor: indici care marchează unde începe și unde se termină modurile fiecărui termen în matricea de moduri

Important

În funcție de tipul operatorului, pot exista matrice suplimentare. De exemplu, instanțele FermionOperator includ o matrice de acțiuni de valori booleene care specifică tipul de acțiune fermionică ce acționează asupra indicelui de mod respectiv. În schimb, clasa MajoranaOperator nu necesită această distincție, deoarece codifică acea informație în paritatea indicelui de mod. Consultă documentația API pentru tipul tău de operator pentru a înțelege formatul complet de stocare.

Următoarele exemple arată cum sunt organizate aceste matrice. Mai întâi, o construcție directă folosind matricele rare:

>>> from qiskit_fermions.operators import FermionOperator
>>>
>>> # Construct operators directly using sparse arrays
>>> # First operator: 1.0 * +0 -1
>>> op1 = FermionOperator(
... coeffs=[1.0],
... actions=[True, False],
... modes=[0, 1],
... boundaries=[0, 2],
... )
>>>
>>> # Second operator: 1.0 * +2 -3
>>> op2 = FermionOperator(
... coeffs=[1.0],
... actions=[True, False],
... modes=[2, 3],
... boundaries=[0, 2],
... )
>>>
>>> # Combine the sparse operators
>>> op1 += op2
>>> print(format(op1))
1.000000e0 +0.000000e0j * (+0 -1)
1.000000e0 +0.000000e0j * (+2 -3)

Metode de construcție convenabile​

Pentru dezvoltatorii Python sunt disponibile mai multe metode de construcție convenabile, care ascund detaliile stocării rare. Ele fac mai ușoară construirea operatorilor, fără să te preocupe gestionarea vectorilor de coeficienți, moduri și limite:

>>> from qiskit_fermions.operators import FermionOperator, cre, ann
>>>
>>> # Construct operators using operator algebra notation
>>> op1 = FermionOperator.from_dict({(cre(0), ann(1)): 1.0})
>>> op2 = FermionOperator.from_dict({(cre(2), ann(3)): 1.0})
>>>
>>> # The result is sparse even when combining them
>>> op1 += op2
>>> print(format(op1))
1.000000e0 +0.000000e0j * (+0 -1)
1.000000e0 +0.000000e0j * (+2 -3)
Sugestie

Implementările individuale ale operatorilor pot oferi metode de construcție suplimentare, adaptate cazului lor specific de utilizare. Consultă documentația API a tipului tău de operator pentru a vedea toate opțiunile de construcție disponibile.

Iterarea termenilor și reconstrucția​

Operatorii oferă o interfață de iterare consecventă prin OperatorTrait.iter_terms(), indiferent de reprezentarea lor internă rară. Astfel, poți inspecta, filtra sau transforma termenii fără să fie nevoie să înțelegi structura de date de dedesubt. Apoi poți reconstrui un operator nou din termenii transformați folosind OperatorTrait.from_terms().

>>> from qiskit_fermions.operators import FermionOperator, cre, ann
>>>
>>> # Construct an operator with terms of different orders
>>> op = FermionOperator.from_dict({
... (): 0.5, # constant term (order 0)
... (cre(0), ann(1)): 1.0, # two-body term (order 2)
... (cre(0), cre(1), ann(1), ann(0)): 0.25 # four-body term (order 4)
... })
>>>
>>> # Filter to keep only terms of order 2
>>> order_two_terms = [
... (term, coeff) for term, coeff in op.iter_terms()
... if len(term) == 2
... ]
>>>
>>> # Reconstruct operator from filtered terms
>>> filtered_op = FermionOperator.from_terms(order_two_terms)
>>> print(f"Original operator has {len(op)} terms")
Original operator has 3 terms
>>> print(f"Filtered operator has {len(filtered_op)} term")
Filtered operator has 1 term

Indexarea pe moduri​

Toate reprezentările operatorilor numesc moduri indicii asupra cărora acționează termenii lor. Un mod este pur și simplu un indice care identifică un grad de libertate fermionic din sistemul tău. Corespondența dintre gradele de libertate fizice (cum ar fi orbitalii spațiali, stările de spin sau alte numere cuantice) și indicii modurilor rămâne la latitudinea utilizatorului, ceea ce oferă flexibilitate maximă.

Această abstractizare este prezentă și în modulul qiskit_fermions.circuit, unde FermionicCircuit operează pe un registru de moduri fermionice. Atât în reprezentarea operatorilor, cât și în cea a circuitelor, modurile oferă o modalitate consecventă, independentă de algebră, de a specifica ce grade de libertate participă la o operație dată.

Important

Implementările curente folosesc moduri fără spin: toate reprezentările operatorilor oferite în prezent de acest modul tratează modurile ca grade de libertate fermionice fără spin. Aceasta înseamnă că, dacă sistemul tău are atât electroni sau fermioni cu spin în sus, cât și cu spin în jos, trebuie să îi asociezi explicit unor moduri distincte (de exemplu, modurile 0-3 pentru spinul în sus al celor patru orbitali spațiali, modurile 4-7 pentru spinul în jos, sau orice altă convenție aleasă de tine).

Această proiectare păstrează reprezentările de bază simple și generale, evitând impunerea unei convenții specifice de ordonare a spinului. Module utilitare precum qiskit_fermions.operators.library oferă funcții de conveniență (de exemplu, FCIDump.from_file()) care se ocupă de astfel de asocieri în locul tău atunci când încarci date de structură electronică.

Sugestie

Pe măsură ce pachetul evoluează, ar putea fi adăugate reprezentări de operatori cu spin, care să susțină nativ gradele de libertate de spin în modelul lor de date. Acestea vor fi clar deosebite de implementările curente fără spin și vor coexista cu ele în modul.

Gruparea termenilor și relațiile de comutare​

La fel ca pentru coeficienți și indicii modurilor, operatorii pot stoca opțional un vector de grupuri care asociază fiecare termen cu un indice de grup. Integrând gruparea direct în reprezentarea operatorului, ca parte a structurii de date rare, informația de grupare însoțește în mod natural operatorul prin transformări. Acest lucru permite folosirea sistematică a structurii, fie că provine din proprietăți fizice, relații algebrice sau simetrii specifice problemei. Informația structurată poate fi apoi folosită în operații ulterioare, precum sinteza și descompunerea circuitelor, prin metode ca OperatorTrait.split_out_groups().

Pentru îndrumări detaliate despre cum să grupezi termenii operatorilor în fluxurile tale de lucru, consultă ghidul de grupare.

>>> from qiskit_fermions.operators import MajoranaOperator, gamma
>>> op = MajoranaOperator.from_dict({
... (gamma(0, False),): 1.0,
... (gamma(1, False),): 1.0,
... (gamma(2, False), gamma(3, False)): 1.0
... })
>>> # Assign group indices to terms
>>> op.groups = [0, 0, 1]
>>> # Partition operator by groups
>>> grouped_ops = op.split_out_groups()

Operații aritmetice și matematice​

Toți operatorii implementează protocolul OperatorTrait, care oferă un set unificat de operații pentru diferite tipuri de operatori. Astfel, codul scris pentru o reprezentare a operatorilor funcționează uniform și cu celelalte. Consultă qiskit_fermions.protocols pentru a vedea cum se leagă acest protocol de celelalte definite de acest pachet.

Protocolul include operații aritmetice (precum adunarea, înmulțirea și compunerea), operații structurale (iterarea termenilor, analiza suportului modurilor, redenumirea), funcții matematice (ordonare normală, simplificare, verificarea echivalenței) și altele. Consultă documentația OperatorTrait pentru o referință completă a tuturor operațiilor disponibile.

>>> from qiskit_fermions.operators import FermionOperator, cre, ann
>>>
>>> # Construct a Hermitian operator: H = +0 -1 + +1 -0
>>> op = FermionOperator.from_dict({
... (cre(0), ann(1)): 1.0,
... (cre(1), ann(0)): 1.0
... })
>>>
>>> # Check if the operator is Hermitian
>>> print(f"Operator is Hermitian: {op.is_hermitian(atol=1e-10)}")
Operator is Hermitian: True
Notă

is_hermitian() verifică faptul că H−H†=0H - H^\dagger = 0, reducând această diferență la o formă normală și comparând-o cu zero(). Exprimată prin celelalte operații ale protocolului, ea efectuează următoarele:

>>> difference = (op - op.adjoint()).normal_ordered().simplify(atol=1e-10)
>>> difference.equiv(FermionOperator.zero(), atol=1e-10)
True

Puterea acestei verificări depinde de tipul operatorului, deoarece depinde de cât de complet contractă forma normală a acelui tip generatorii adiacenți. Un rezultat True este întotdeauna de încredere, dar pentru unele tipuri un rezultat False este conservator: consultă is_hermitian() pentru detalii.

Important

Exemplul folosește atol=1e-10. Parametrul atol (toleranță absolută) specifică un prag. Coeficienții cu magnitudine mai mică decât atol sunt tratați ca zero și eliminați. Acest lucru este esențial pentru stabilitatea numerică la compararea operatorilor, deoarece aritmetica în virgulă mobilă poate introduce mici erori de rotunjire care, altfel, ar împiedica recunoașterea operatorilor echivalenți ca atare.

Sugestie

Deși protocolul OperatorTrait oferă o interfață comună, implementările individuale ale operatorilor pot oferi metode de conveniență suplimentare care nu fac parte din protocol. De exemplu, FermionOperator și MajoranaOperator oferă o metodă max_rank() care raportează lungimea celui mai lung termen al lor. Consultă întotdeauna documentația API a tipului tău de operator pentru a descoperi toate funcționalitățile disponibile.

Ordonarea termenilor operatorilor și formele normale​

O provocare fundamentală în algebra operatorilor cuantici este că operatorii echivalenți matematic pot fi reprezentați în multe moduri diferite, fiecare cu implicații diferite pentru algoritmii cuantici. Același operator poate fi scris în forme echivalente algebric (de exemplu, a†ba^\dagger b poate fi exprimat ca ba†+[a†,b]ba^\dagger + [a^\dagger,b]), însă aceste reprezentări duc la comportamente diferite în sinteza circuitelor, în simplificare și în algoritmii numerici.

Reprezentările operatorilor susțin operații de ordonare normală, care transformă operatorii în forme canonice specifice algebrei, folosind relațiile de comutare. Aceasta permite comparații fiabile (doi operatori echivalenți au forme identice în ordine normală), scoate la iveală simplificări (relațiile de comutare fac ca termenii să se anuleze sau să se combine) și susține algoritmii care necesită forme specifice ale operatorilor pentru corectitudine și eficiență.

Important

Reprezentări diferite ale operatorilor pot implementa ordonarea normală pe baza unor relații de comutare diferite, potrivite algebrei lor. De exemplu, ordonarea normală fermionică folosește relații de anticomutare ({ci,cj†}=δij\{c_i, c_j^\dagger\} = \delta_{ij}), în timp ce ordonarea normală Majorana folosește alte convenții algebrice ({γi,γj}=2δij\{\gamma_i, \gamma_j\} = 2\delta_{ij}). Consultă întotdeauna documentația tipului tău de operator pentru a înțelege cum este implementată ordonarea normală.

>>> from qiskit_fermions.operators import FermionOperator, cre, ann
>>>
>>> # Two different representations of the same operator
>>> op1 = FermionOperator.from_dict({(ann(0), cre(0)): 1.0})
>>> op2 = FermionOperator.from_dict({(): 1.0, (cre(0), ann(0)): -1.0})
>>>
>>> # Direct comparison fails due to different forms
>>> op1.equiv(op2, atol=1e-10)
False
>>>
>>> # Normal-order both and compare again
>>> op1_normal = op1.normal_ordered()
>>> op2_normal = op2.normal_ordered()
>>> op1_normal.equiv(op2_normal, atol=1e-10)
True
Sugestie

Metoda de protocol OperatorTrait.normal_ordered() lasă în mod deliberat nespecificate argumentele sale poziționale și cele cu nume, permițând implementărilor concrete ale operatorilor să definească parametri ajustabili care controlează forma canonică exactă produsă. Acest lucru permite optimizări specifice operatorului și variante de forme normale adaptate algebrei sau cazului lor de utilizare. Consultă documentația API a tipului tău de operator pentru a vedea ce parametri sunt disponibili.