Note di rilascio di Qiskit Fermions
0.2.0
Preludio
Questa versione amplia il controllo che questo pacchetto offre sulla sintesi dei circuiti. Un Evolution gate ora accetta un synthesis metodo esplicito, un nuovo synthesis modulo fornisce le formule del prodotto fermionico di primo e di ordine superiore, e il FermionicTrotterization pass seleziona e applica una di esse contemporaneamente a tutte le evoluzioni presenti in un circuito. Inoltre, il mapper Jordan-Wigner ha ottenuto implementazioni dirette per tutte e quattro le rappresentazioni degli operatori, mentre i nuovi adattatori consentono di semplificare l'output del mapper o di emetterlo gruppo per gruppo. L'API C ha recuperato parecchio terreno. Le rappresentazioni "edge-vertex" e "transfer-vertex", i relativi mappatori e la loro algebra sono ora accessibili da C, così come il supporto degli operatori, la conservazione dei settori, l'aritmetica in-place e scalata e l'ordinamento dei gruppi. Entrambe le API hanno inoltre ottenuto l'accesso agli integrali elettronici analizzati da un FCIDump, che in precedenza erano accessibili solo convertendo l'intera struttura dei dati in un operatore. Inoltre, questa versione definisce in modo più netto l'ambito di applicazione di questo pacchetto: gli operatori fermionici, i mapper che li convertono, il circuito indipendente dal mapper e la sua transpilazione. La simulazione è di competenza di ffsim, pertanto il kernel FCI nativo e le utilità di algebra lineare che lo duplicavano sono stati rimossi per delegare tali funzioni a ffsim, e i UCJ gate UCC e ora avvolgono gli operatori di ffsim invece di reimplementarne le parametrizzazioni. La simulazione richiede quindi il pacchetto aggiuntivo ffsim e, a causa della dipendenza di ffsim da PySCF,, non è disponibile su Windows; la creazione degli operatori, la loro mappatura e la transpilazione dei circuiti risultanti rimangono supportate su tutte le piattaforme. Trattandosi ancora di una versione " pre-1.0 ", tutte queste modifiche sono state apportate tramite rimozione anziché tramite deprecazione: le note di aggiornamento riportate di seguito abbinano ogni API rimossa alla sua sostituta, ed è consigliabile leggerle per intero prima di procedere all'aggiornamento.
Python Funzionalità dell'API
-
Il modo in cui un gate
Evolutionviene scomposto nello spazio fermionico è ora configurabile tramite il suo nuovo argomentosynthesis, specificato solo tramite parola chiave, che rispecchia l’argomentosynthesisdi di QiskitPauliEvolutionGate. I metodi disponibili si trovano nel nuovoqiskit_fermions.circuit.library.synthesismodulo, che fornisce l'interfacciaFermionicEvolutionSynthesise la sua implementazioneFermionicLieTrotterdi primo ordine.Questo passaggio da fermione a fermione è facoltativo: un
Evolutiongate può essere inviato direttamente alla fase di conversione da fermione a qubit, indipendentemente dal numero di termini contenuti nel suo operatore. Scomporla in anticipo è una scelta che si compie per ottenere fattori più economici o per mettere in luce caratteristiche strutturali (come la commutatività reciprocagroups) che la fase successiva potrà sfruttare. Poiché entrambe le parti della riscrittura rimangono nello spazio fermionico, tale struttura rimane inalterata, ed è proprio questo aspetto che questi metodi possono sfruttare, mentre quelli di Qiskit nonEvolutionSynthesissono in grado di farlo.L'impostazione predefinita rimane invariata: lasciando
synthesissu vieneNoneutilizzatoFermionicLieTrotter, il che riproduce esattamente la scomposizione cheEvolutioneffettuava in precedenza, quindi i circuiti esistenti vengono sintetizzati in modo identico. -
I pesi di campionamento di possono
QDriftTrotterizationora essere specificati tramite il suo nuovo argomentoweights, accessibile solo tramite parola chiave, invece di essere ricavati dall'operatore evoluto ad ogni chiamata. Se si lascia il valore predefinito,Noneil comportamento è esattamente lo stesso di prima, quindi i circuiti esistenti vengono campionati allo stesso modo.Si tratta di un'opzione di ottimizzazione delle prestazioni: il percorso predefinito riduce il numero di valori per ogni termine dell'operatore da uno a uno per gruppo ad ogni chiamata a
run(), il che comporta un lavoro ripetitivo quando si genera un insieme a partire da un singolo hamiltoniano, anche se il risultato è ogni volta identico. Derivalo invecegroup_coeff_means()una volta con, insieme agroup_order(), che sposta la ricerca nel gruppo fuori dallo stesso ciclo:hamiltonian = group_order(hamiltonian) weights = group_coeff_means(hamiltonian) pm.optimization = FermionicPassManager( [QDriftTrotterization(num_groups, weights=weights)] )I valori inseriti devono essere non negativi, poiché un peso è il modulo della decomposizione di qDRIFT , in cui il segno di un coefficiente appartiene a e si ricava direttamente dall’operatore evoluto.
Un array fornito comporta due vincoli. La sua scala non è libera: la somma dei pesi determina anche il tempo di evoluzione condiviso, quindi moltiplicando ogni voce per si ottengono
gammacampioni identici, ma l’evoluzione avviene pergamma * t. E poiché l'array descrive i termini (o gruppi) di un operatore specifico, la sua lunghezza viene verificata rispetto a ogniEvolutionporta a cui viene applicato, e un circuito che contiene più di una porta di questo tipo viene scartato; lasciando il campo deselezionatoweights, tale circuito rimane supportato, poiché ogni porta ne deriva autonomamente la propria. -
FCIDumpora rende visibili gli integrali elettronici che ha analizzato, i quali in precedenza erano accessibili solo convertendo l'intera struttura dei dati in unFermionOperator. I nuovi metodi restituiscono copie di sola lettura sotto forma di array di tipoNumPy, con lo stesso layout appiattito utilizzato dai costruttorielectronic-integral:get_one_body_tril_a(),get_one_body_tril_b(),get_two_body_tril_aa(), eget_two_body_tril_ab()get_two_body_tril_bb(). Gli array beta-spin restituiscono i risultati relativi aNoneun file con restrizioni di spin; il nuovois_unrestrictedattributo riporta i dati relativi a tutti e tre contemporaneamente. Il nuovo attributoconstantfornisce l'energia costante (di repulsione nucleare), oppure,Nonese il file non ne contiene alcuna:fcidump = FCIDump.from_file("molecule.fcidump") one_body = fcidump.get_one_body_tril_a() two_body = fcidump.get_two_body_tril_aa()Si noti che
get_two_body_tril_ab()è strutturato in modo diverso rispetto agli altri array a due corpi: presenta solo una simmetria quadrupla, quindi contiene l’intera matrice(npair, npair), in cui le coppie di righe indicano le specie con spin alfa e le coppie di colonne quelle con spin beta. Per conoscere la formula di indicizzazione di ogni array, consultare la documentazione relativa alla classe. L'espansione di questi in tensori densi o(norb, norb)tensori(norb,) * 4, come richiesto ad esempio dagli strumenti di diagonalizzazione basati su campioni, è lasciata a chi chiama la funzione. -
È stata
FermionicSuzukiTrotteraggiunta una formula di prodotto di ordine superiore per scomporre una portaEvolutionnello spazio fermionico. Laddove ogni fattore viene applicatoFermionicLieTrotteruna sola volta, ciò li combina simmetricamente in modo da annullare i termini di errore di ordine inferiore, e il suorepsargomento suddivide l'evoluzione in diversi passi più brevi:Evolution(num_modes, operator, time=1.0, synthesis=FermionicSuzukiTrotter(order=2, reps=4))Poiché agisce sull'operatore fermionico, lo scinde lungo le sue componenti, dove
groupssono assegnate – una suddivisione che non è più disponibile per una formula di prodotto applicata dopo la mappatura da fermione a qubit. Su una catena di Fermi-Hubbard a sei modalità raggruppata in tre insiemi di flusso,order=2si è ottenutoreps=4un errore di Trotter circa due volte inferiore rispetto a una formula di secondo ordine dal lato dei qubit, con un numero leggermente inferiore di porte a due qubit.FermionicLieTrotterè l'elemento di primo ordine di questa famiglia ed è ora implementato come tale, il che gli conferisce anche l'argomentorepsche in precedenza gli mancava. I due sono intercambiabili a parità direps.Si noti che un ordine più elevato garantisce una maggiore precisione grazie alla profondità: una formula di
kordine emette all’incirca5**((k-2)/2)volte più fattori rispetto a una formula di ordine order-2. Si noti inoltre che la precisione di un circuito scomposto è determinata dalla formula più debole tra quella fermionica e quella da fermione a qubit; pertanto, aumentare l’ordine in questo caso, mentre quest’ultimo rimane di primo ordine, non apporta grandi vantaggi. -
FermionicTrotterizationÈ stato aggiunto un passaggio del transpiler che applica l’algoritmo di Trotter a ogni gateEvolutiondi un circuito utilizzando un metodo di sintesi da fermione a fermione:pm.optimization = FermionicPassManager( [FermionicTrotterization(FermionicSuzukiTrotter(order=2, reps=4))] )Scegliere un metodo di sintesi per ogni gate significa applicarlo a tutti gli elementi che compongono un
Evolution– compresi eUCCUCJ, che lo generano internamente. Questo passaggio consente invece di effettuare la scelta una sola volta per l'intera pipeline. Un predicatofilteropzionale lo limita a un sottoinsieme delle porte; è opportuno utilizzarlo quando un circuito combina evoluzioni che traggono vantaggio da un ordine superiore con altre che invece non ne traggono vantaggio, come nel caso degli operatori di Coulomb diagonali tutti commutativi di unUCJ.Il pass espande ogni gate selezionato nei fattori emessi dal proprio metodo, quindi non è necessaria alcuna fase di espansione separata. Utilizzare
passperapply=Falseselezionare solo il metodo e lasciare l'espansione a qualcos'altro, come ad esempio QiskitDecompose; si noti che un gate che non viene mai espanso raggiunge la fase "da fermione a qubit" nella sua interezza, dove viene mappato senza essere maiEvolution.synthesisletto. -
Il modulo
qiskit_fermions.operators.terms.groupingtratta ora l' analisi di un raggruppamento esistente, oltre all'assegnazione degli indici di gruppo. Gli indici di raggruppamento non hanno alcun significato intrinseco, quindi nessuna delle nuove funzioni indica se un raggruppamento sia “corretto”; ciascuna risponde a una domanda specifica relativa a un raggruppamento, in modo che un’ipotesi formulata da un utente a valle possa essere verificata in anticipo, anziché dover essere ricalcolata ad ogni chiamata:groups_are_hermitian()indica, per ciascun gruppo, se l'operatore formato dai suoi elementi è ermitiano. Questa è la proprietà su cui si basa una formula di prodotto randomizzata quando campiona gruppi interi, poiché solo un gruppo hermitiano presenta un’evoluzione temporale unitaria.groups_have_uniform_coeffs()indica, per ciascun gruppo, se i suoi coefficienti sono numericamente uguali (in termini di grandezza, per impostazione predefinita).
Vedi i termini relativi agli operatori di gruppo: utilizza la struttura degli operatori per un esempio pratico.
-
Aggiunto
group_order(), che restituisce una copia di un operatore con i suoi termini ordinati in base all’indice di gruppo. Ogni gruppo diventa una sequenza contigua di termini e gli indici dei gruppi diventano non decrescenti; l'ordinamento è stabile, quindi i termini all'interno di un gruppo mantengono il loro ordine relativo. Un operatore che non tiene traccia di alcun indice di gruppo non ha alcun criterio di ordinamento e viene restituito come copia invariata.Gli indici di gruppo indicano solo quali termini vanno raggruppati, quindi ciò modifica la disposizione dei termini di un operatore, ma non il suo valore. È proprio la struttura a rendere la ricerca dei gruppi poco onerosa: in genere
split_out_groups()è necessario scansionare tutti i termini per trovare i gruppi richiesti, ma con un operatore ordinato per gruppi si effettua invece una ricerca binaria sui confini dei gruppi, quindi una ricerca costa quanto i gruppi richiesti, anziché quanto i termini contenuti. Effettuare un unico ordine iniziale, quindi, si rivela vantaggioso nel caso di ricerche ripetute:from qiskit_fermions.operators.terms.ordering import group_order hamil = group_order(hamil) # every subsequent hamil.split_out_groups(group_indices=...) is now a binary searchÈ consigliabile farlo prima di eseguire ripetute transpilazioni con
QDriftTrotterization, che a ogni chiamata effettua la ricerca dei gruppi campionati. Per un esempio pratico, consultare la sezione “Generazione di circuiti d’ SqDRIFT ”. -
Sono stati aggiunti mappatori diretti di Jordan-Wigner per le tre restanti strutture di dati degli operatori:
majorana_jordan_wigner(), eedge_vertex_jordan_wigner()transfer_vertex_jordan_wigner(). -
jordan_wigner()ora esegue operazioni su tutti e quattro i tipi di operatore anziché solo su unoFermionOperator, delegando all'implementazione diretta che corrisponde all'operatore specificato. Il passaggio a uno degli altri tre tipi di operatore menzionati in precedenza generava unTypeError. -
Il metodo
is_hermitian()fa ora parte del protocolloOperatorTrait. Ogni classe operatore lo forniva già, quindi ora può essere richiamato su qualsiasi valore di tipoOperatorTraitanziché solo su una classe operatore concreta.Si noti che l’efficacia di questa verifica varia a seconda del tipo di operatore; il protocollo la definisce come la sua garanzia più debole: un risultato
Trueè sempre affidabile, mentre un risultatoFalseè conservativo per i tipi di operatore la cui forma normale non è una vera e propria forma canonica. Si vedais_hermitian()per un esempio di questo tipo. -
Sono stati aggiunti due adattatori che avvolgono una funzione di mappatura per controllare l'ordine del termine di Pauli da essa generato, che
MapperFnEvolutionSynthesisora viene preservato durante la sintesi.simplify()semplifica l'operatore mappato, unendo i termini di Pauli duplicati e fissando un ordine canonico dei termini.group_wise()assegna un operatore a una vocegroupsalla volta e somma i risultati. L'operatore rimane invariato, ma i termini di ciascun gruppo vengono disposti in modo adiacente anziché intercalati, il che consente a quelli che agiscono su qubit disgiunti di condividere uno stesso livello del circuito:MapperFnEvolutionSynthesis(group_wise(jordan_wigner)) -
QDriftTrotterizationora registra il numero di estrazioni scartate dalla suafilter_trivialmodalità. Quando almeno una portaEvolutionveniva effettivamente filtrata, le trasportatemetadatadel circuito restituito efilter_trivial.discardedfilter_trivial.emitted, contenevano ciascuna un conteggio per ogni porta filtrata nell’ordine del circuito. Il loro rapporto stima la probabilità di accettazione, ovvero il fattore con cui il filtro ha amplificato i coefficienti dei termini che ha mantenuto. Nessuno dei due campi è presente quando non è stato filtrato alcun gate, quindi leggili con.get(). -
Evolutionora può essere simulato per qualsiasi tipo di operatore di questo pacchetto, non soloFermionOperator. Un operatore di altro tipo viene convertito tramite la sua immagine fermionica (ilSupportsFermionOperatorprotocollo) prima di essere simulato, pertanto l’evoluzione diMajoranaOperator, oEdgeVertexOperatorTransferVertexOperatornon genera piùNotImplementedError. -
FermionOperatorora viene implementatoffsim.SupportsTracetramite un nuovo_trace_()metodo, in modo cheffsim.trace()sia possibile calcolare la traccia di un operatore su un settore(norb, nelec)fisso. Ecco quali sono i presupposti lungoscipy.sparse.linalg.expm_multiply()il percorso evolutivo.
Funzionalità dell'API C
-
È stata aggiunta
qf_ferm_op_conserves_sector()una condizione che verifica che ogni termine di unQfFermionOperatormantenga il numero di particelle all'interno di ciascun blocco di modi. Va ad integrare la funzione esistenteqf_ferm_op_conserves_particle_number(), che verifica solo il totale. Per una struttura spin-orbitale degli orbitali spazialinorb, il passaggio attraverso i due blocchi{norb, norb}richiede che i settori alfa e beta siano conservati separatamente, ovvero che vi sia una conservazione rigorosa sia del numero di particelle sia della componente z dello spin:uint32_t spin_blocks[2] = {norb, norb}; bool conserves = qf_ferm_op_conserves_sector(op, spin_blocks, 2);L'esecuzione
NULLcon un numero di blocchi pari a0considera tutte le modalità come un unico blocco, il che equivale a chiamareqf_ferm_op_conserves_particle_number(). -
È stato aggiunto il supporto per gli operatori all'API C, che in precedenza era disponibile solo all'indirizzo Python tramite
get_support. Ciascuna delle quattro rappresentazioni dell'operatore ha acquisito una coppia di funzioni:qf_ferm_op_num_support()eqf_ferm_op_get_support(), oltre agli equivalentiqf_transfer_opqf_maj_op,qf_edge_ope.A differenza degli altri getter, questi non restituiscono un puntatore all’operatore, poiché il supporto viene calcolato al momento della richiesta anziché essere memorizzato. Chiamare prima la funzione
num_supportper dimensionare il buffer di output, quindi passare tale buffer aget_support:uint32_t num_support = qf_ferm_op_num_support(op); uint32_t *support = malloc(num_support * sizeof(uint32_t)); qf_ferm_op_get_support(op, support);Le funzioni in C scrivono gli indici delle modalità in ordine crescente, mentre il metodo Python restituisce un elenco non ordinato
set. -
È stata aggiunta l'aritmetica con operatori in loco all'API C, che evita l'allocazione di un operando di risultato quando l'operando di sinistra può essere sovrascritto. Ciascuna delle quattro rappresentazioni dell'operatore ha ottenuto
add_inplace,scaled_add_inplaceemul_inplace, ad esempioqf_ferm_op_add_inplace(),qf_ferm_op_scaled_add_inplace()eqf_ferm_op_mul_inplace():// Accumulate `factor * right` into `left` without building an intermediate. QkComplex64 factor = {-1.0, 0.0}; qf_ferm_op_scaled_add_inplace(left, right, &factor);Si noti che i tre differiscono nel modo in cui trattano gli indici di gruppo. Le due aggiunte appendono i termini dell'operando destro e quindi reimpostano l'attributo
groupsaNULL, proprio comeqf_ferm_op_add()fa.mul_inplaceridimensiona solo i coefficienti, lasciando invariato il numero dei termini e preservando così il raggruppamento. -
È stata aggiunta un’API C per le rappresentazioni degli operatori “edge-vertex” e “transfer-vertex”, che in precedenza erano disponibili solo all’indirizzo Python. Ciò include due nuove strutture "opaque",
QfEdgeVertexOperatoreQfTransferVertexOperator, ciascuna delle quali dispone di un insieme di funzioni equivalente a quello delle strutture "operator" esistenti.Entrambi gli operatori memorizzano due array di indici paralleli, e
left_indicesright_indices, al posto del singolomodesarray utilizzato dall'operatore di Majorana. Poiché un generatore è sempre identificato da un'unica coppia(left, right), i due array hanno necessariamente la stessa lunghezza e i costruttori accettano quindi un unico argomentonum_indicesche li comprende entrambi. -
Sono state aggiunte
qf_edge_op_canonical_order()eqf_transfer_op_canonical_order(), insieme alle funzioni di commutatore, anticommutatore e doppio commutatore per entrambi i nuovi tipi di operatore (qf_edge_op_commutator()e simili). -
Sono state aggiunte le funzioni API C per i mappatori edge-vertex e transfer-vertex:
qf_edge_vertex_to_fermion(),qf_edge_vertex_to_majorana(),qf_transfer_vertex_to_fermion(),qf_transfer_vertex_to_majorana(), eqf_transfer_vertex_to_edge_vertex(). -
Aggiunte
scaled_addall’API C per tutte e quattro le rappresentazioni degli operatori:qf_ferm_op_scaled_add(),qf_maj_op_scaled_add(),qf_edge_op_scaled_add()eqf_transfer_op_scaled_add(). Ciascuna restituisceleft + factor * right, incorporando il fattore di scala nell'operazione di addizione, in modo che non venga creata alcuna copia scalata dell'operando di destra durante il calcolo.È così che anche l'API C definisce la sottrazione, poiché il fattore inverte
-1il segno dei coefficienti che seguono:QkComplex64 minus_one = {-1.0, 0.0}; QfFermionOperator *difference = qf_ferm_op_scaled_add(left, right, &minus_one);Per quanto riguarda
qf_ferm_op_add(), i termini dell'operando di destra vengono aggiunti a quelli di sinistra anziché combinati con essi, quindi il risultato non tiene traccia di alcun indice di gruppo. Chiamare la funzionesimplifycorrispondente per raggruppare i termini uguali. -
QfFCIDumpora rende visibili gli integrali elettronici che ha analizzato, i quali in precedenza erano accessibili solo convertendo l'intera struttura dei dati in unQfFermionOperator. I nuovi accessoriqf_fcidump_get_one_body_tril_a(),qf_fcidump_get_one_body_tril_b(),qf_fcidump_get_two_body_tril_aa(), eqf_fcidump_get_two_body_tril_ab()utilizzanoqf_fcidump_get_two_body_tril_bb()il buffer interno, scrivendo un puntatore e una lunghezza tramite i parametri di uscita:double *integrals; uint64_t len; qf_fcidump_get_one_body_tril_a(fcidump, &integrals, &len);Il puntatore restituito non deve essere liberato e rimane valido solo fino a quando non
QfFCIDumpviene liberato. Proteggi i tre getters beta-spin con il nuovoqf_fcidump_is_unrestricted(), eqf_fcidump_constant()con il nuovoqf_fcidump_has_constant(); chiamare uno dei due senza la relativa protezione provoca un panic. Si rimanda alla documentazione relativa alle intestazioni per la formula di indicizzazione di ogni matrice; si noti in particolare cheqf_fcidump_get_two_body_tril_ab()è strutturata in modo diverso dalle altre matrici a due corpi, poiché contiene la(npair, npair)matrice completa in quanto è simmetrica solo di quarto ordine. -
Sono stati aggiunti e
qf_ferm_op_groups_are_hermitian()qf_ferm_op_groups_have_uniform_coeffs(), che indicano per ciascun gruppo se l'operatore formato dai suoi termini è ermitiano e se i suoi coefficienti sono numericamente uguali, insieme alle funzioni equivalenti per i tipi di operatori di Majorana, di bordo-vertice e di trasferimento-vertice. -
È stata aggiunta la funzione
qf_ferm_op_group_order(), che restituisce una copia di un operatore con i suoi termini ordinati in base all'indice di gruppo, insieme alle funzioni equivalenti per i tipi di operatore Majorana (qf_maj_op_group_order()), bordo-vertice (qf_edge_op_group_order()) e trasferimento-vertice (qf_transfer_op_group_order()). Ogni gruppo diventa una sequenza contigua di termini, il che consente di effettuare una ricercaqf_ferm_op_split_out_groups()binaria dei confini del gruppo invece di scansionare ogni singolo termine. -
Sono stati aggiunti mappatori diretti Jordan-Wigner per le tre restanti strutture di dati degli operatori, oltre a quelli già esistenti
qf_ferm_op_jordan_wigner():qf_maj_op_jordan_wigner(),qf_edge_op_jordan_wigner()eqf_transfer_op_jordan_wigner().
Python Note sull'aggiornamento dell'API
-
Le
qiskit_fermions.linalg.linear_operatorfunzioniqiskit_fermions.linalg.apply_unitarye sono state rimosse. Erano semplici involucri attorno ai metodi e_apply_unitary_del protocollo_linear_operator_, che duplicavano effsim.apply_unitary()ffsim.linear_operator(). Utilizza invece le funzioni di ffsim oppure, se ffsim non è installato, il metodo protocol direttamente sull’oggetto:# before from qiskit_fermions.linalg import apply_unitary, linear_operator vec = apply_unitary(vec, gate, norb, nelec) linop = linear_operator(operator, norb, nelec) # after (with ffsim) vec = ffsim.apply_unitary(vec, gate, norb=norb, nelec=nelec) linop = ffsim.linear_operator(operator, norb=norb, nelec=nelec) # after (without ffsim) vec = gate._apply_unitary_(vec, norb, nelec, copy=True) linop = operator._linear_operator_(norb, nelec)I metodi in sé rimangono invariati.
-
I
qiskit_fermions.protocols.SupportsLinearOperatorprotocolli eqiskit_fermions.protocols.SupportsApplyUnitarysono stati rimossi per lo stesso motivo: riprendevano i contratti definiti da ffsim. Utilizzare i protocolli propri di ffsim, che sono stati replicati alla lettera:qiskit_fermions.protocols.SupportsApplyUnitarydiventaffsim.SupportsApplyUnitaryqiskit_fermions.protocols.SupportsLinearOperatordiventaffsim.SupportsLinearOperator
I metodi del protocollo (
_apply_unitary_e_linear_operator_) rimangono invariati, quindi gli oggetti di questo pacchetto che li implementavano continuano a farlo; è stata eliminata solo la ridefinizione locale dell’interfaccia.SupportsApplyUnitaryPlacedrimane, poiché si estendeffsim.SupportsApplyUnitarycon un posizionamento di modalità e non ha una controparte ffsim. -
from_file()ora genera un'eccezione " Python " gestibile quando non riesce ad analizzare un file, invece di propagare un panic di Rust. Un percorso che non può essere aperto o letto genera un'eccezioneOSError, mentre un file che non rispetta il formato FCIDump (mancanza dell'elenco dei nomi nell'intestazione, mancanza del campoNELECoNORB, oppure un campoMS2non valido) genera un'eccezioneValueError. Anche un FCIDump contenente un valore di energia MO, che non è ancora supportato, genera un erroreValueError. -
Il metodo
group_weightsè stato rimosso da tutte le classi operatore e dal protocolloOperatorTrait. Viene sostituita dagroup_coeff_means(), una funzione libera nel moduloqiskit_fermions.operators.terms.grouping, il cui comportamento rimane invariato:# before weights = op.group_weights() # after from qiskit_fermions.operators.terms.grouping import group_coeff_means weights = group_coeff_means(op) -
L'assegnazione di un array
groupsla cui lunghezza differisce dal numero di termini dell'operatore genera ora un'eccezioneValueErrorinvece di essere accettata senza avvisare. L'assegnazioneNoneper azzerare gli indici di gruppo non subisce alcuna modifica. -
MapperFnEvolutionSynthesisnon semplifica più l'operatore restituito dal suomapper_fn. La semplificazione ordina i termini di Pauli secondo un ordine canonico, mentre una formula di prodotto li sintetizza nell’ordine in cui li riceve; pertanto, il comportamento precedente ignorava l’ordine scelto dal mapper.Ciò modifica il circuito sintetizzato per le pipeline esistenti: il numero di porte rimane invariato, ma l'ordine in cui vengono emesse le rotazioni (e quindi la profondità a due qubit) può variare. L'evoluzione che si sta approssimando rimane invariata.
Si noti inoltre che non è garantito che l’ordine dei termini generato da un mapper rimanga stabile tra un’esecuzione e l’altra, poiché gli operatori del core di Rust non preservano l’ordine in cui i termini sono stati aggiunti. Inserisci il mapper all’interno del nuovo
simplify()per ripristinare il comportamento precedente e fissare un ordine canonico:MapperFnEvolutionSynthesis(simplify(jordan_wigner)) -
Le utilità di doppia fattorizzazione sono state rimosse da
qiskit_fermions.linalg:double_factorized_t2,double_factorized_t2_alpha_beta,reconstruct_t2, ereconstruct_t2_alpha_betadouble_factorized_2body. Le variantit2esistevano per servireUCJ.from_t_amplitudes, che ora si trova in ffsim, e nondouble_factorized_2bodyavevano alcun chiamante. Utilizza inveceffsim.linalg.double_factorized()ffsim.linalg.double_factorized_t2(), effsim.linalg.double_factorized_t2_alpha_beta(); quest’ultimo è un superset della routine a due corpi rimossa e offre inoltre una decomposizione ottimizzata (optimize=True). -
Per eseguire la simulazione è ora necessario ffsim. Il kernel nativo Rust FCI (Full Configuration Interaction) che in precedenza fungeva da supporto
_linear_operator_è stato rimosso; il metodo è ora implementato convertendo unFermionOperatorin un effsim.FermionOperatordelegando affsim.linear_operator().La simulazione è l'ambito di competenza di ffsim: questo pacchetto si concentra sui mapper fermionici, sul circuito indipendente dal mapper e sulla sua transpilazione. Poiché ffsim dipende da PySCF,, che non supporta Windows, la simulazione non è quindi disponibile su Windows; il resto del pacchetto (creazione degli operatori, mappatura degli stessi e transpilazione dei circuiti risultanti) non subisce alcuna modifica su nessuna piattaforma. Gli utenti Windows che hanno bisogno di eseguire simulazioni possono farlo tramite il sottosistema Windows per l’ Linux (WSPL).
L'esecuzione di un punto di ingresso di simulazione senza ffsim installato ora genera un errore
MissingOptionalLibraryError. -
Il modulo
qiskit_fermions.linalg.fciè stato rimosso insieme al kernel, comprese la suaFciLinearOperatorclasse e le funzionioccupation_axis_maskeslater_determinant_statevector. ffsim fornisce gli equivalenti: effsim.slater_determinant()ffsim.addresses_to_strings()rispettivamente. -
OrbitalRotationnon ricorre più a un percorso "generatore più esponenziale" in assenza di ffsim; delega sempre affsim.apply_orbital_rotation(). I risultati rimangono invariati quando si installa ffsim. -
Il
UCCgate è ora costituito da uno degli operatori UCCSD di ffsim, che rappresenta il suo unico argomento del costruttore. La variante di spin e il numero di modi si ricavano da quell'operatore, quindi l'argomentovariante l'enumerazioneUCC.Variantnon sono più necessari:# before ansatz = UCC("restricted", t1, t2) # after ansatz = UCC(ffsim.UCCSDOpRestrictedReal(t1=t1, t2=t2))Di conseguenza,
UCC.from_t_amplitudes,UCC.num_parameters, eUCC.from_parametersUCC.to_parameterssono stati rimossi, così come laspinlessvariante e la parametrizzazioneantisymmetricopt-in, che non hanno un equivalente in ffsim. Gli operatori di ffsim utilizzanon_params(), efrom_parameters()to_parameters()con convenzioni identiche. Per costruire un ansatz al di fuori di quella famiglia (uno senza spin o un “ ” antisimmetrizzato), si costruisce direttamente un operatoreEvolutionsul proprio cluster, il che consente anche di controllare l’ordinamento di Trotter dei suoi termini.L'operatore "wrapped" è disponibile come
UCC.uccsd_op, quindi le sue ampiezze sono accessibili come egate.uccsd_op.t1gate.uccsd_op.t2; il gate non le riflette più come propri attributi.UCC.cluster_operator()rimane invariato. Un trasportatofinal_orbital_rotationdall'operatore ffsim viene ora aggiunto come chiusuraOrbitalRotation.Poiché ffsim è ora il tipo di input del gate anziché un acceleratore opzionale, la creazione di un
UCCrichiede il (pip install "qiskit-fermions[ffsim]") aggiuntivoffsime genera un se questoMissingOptionalLibraryErrormanca. ffsim non supporta Windows, quindi in quel sistema operativo il gate non è disponibile; utilizzare WSL. -
Il
UCJgate è ora costituito da uno degli operatori UCJ di ffsim, che rappresenta il suo unico argomento del costruttore. La variante di spin, il numero di orbitali spaziali e il numero di modi si ricavano da quell’operatore, quindi l’argomentovariante l’enumerazioneUCJ.Variantnon sono più necessari:# before ansatz = UCJ.from_t_amplitudes(nelec, t2, t1=t1, n_reps=2) ansatz = UCJ("balanced", diag_coulomb_mats, orbital_rotations) # after ansatz = UCJ(ffsim.UCJOpSpinBalanced.from_t_amplitudes(t2, t1=t1, n_reps=2)) ansatz = UCJ(ffsim.UCJOpSpinBalanced(diag_coulomb_mats, orbital_rotations))Di conseguenza,
UCJ.from_t_amplitudes,UCJ.num_parameters, eUCJ.from_parametersUCJ.to_parameterssono stati rimossi: gli operatori di ffsim fornisconofrom_t_amplitudes(),n_params(),from_parameters()eto_parameters()con convenzioni identiche e offrono inoltre la fattorizzazione doppia compressa (optimize=True) efrom_cisd_vec(), che questo pacchetto non ha mai implementato. L'operatore avvolto è disponibile comeUCJ.ucj_op, quindi i suoi tensori sono accessibili comegate.ucj_op.diag_coulomb_matse così via; il gate non li rispecchia più come propri attributi.Poiché ffsim è ora il tipo di input del gate anziché un acceleratore opzionale, la creazione di un
UCJrichiede il (pip install "qiskit-fermions[ffsim]") aggiuntivoffsime genera un se questoMissingOptionalLibraryErrormanca. ffsim non supporta Windows, quindi in quel sistema operativo il gate non è disponibile; utilizzare WSL. -
Il "dispickling" di un operatore i cui indici di gruppo "pickled" non partono da uno per ogni termine genera ora un
ValueErroranziché produrre un operatore i cui indici di gruppo non corrispondono più ai suoi termini. Ciò è possibile solo con un payload creato manualmente o generato da una versione incompatibile; i file pickle generati da questa versione vengono restituiti senza modifiche.
Note sull'aggiornamento dell'API C
-
qf_fcidump_from_file()ora restituisce un eQfExitCodepassa il analizzato tramiteQfFCIDumpun nuovo parametro di uscita, in linea con la convenzione già utilizzata dalle funzioni di mappatura:QfFCIDump *fcidump = NULL; QfExitCode exit = qf_fcidump_from_file("molecule.fcidump", &fcidump);Viene restituito quando
QfExitCode_ValueErrorun file non rispetta il formato FCIDump (mancanza dell'elenco dei nomi nell'intestazione, mancanza delNELECcampoNORBo, campoMS2non valido oppure valore di energia MO, che non è ancora supportato), lasciando inalterato il parametro di output. Questo sostituisce la firma precedente, che restituiva direttamente il puntatore e non prevedeva alcun modo per segnalare un errore (un file non analizzabile causava un panic in Rust oltre il confine FFI, il che costituisce un comportamento indefinito). -
qf_ferm_op_group_weightse le relative controparti per tipo sono state rinominate inqf_ferm_op_group_coeff_means()e simili. Il loro comportamento è rimasto immutato. -
qf_ferm_op_set_groups()e le sue controparti per tipo ora restituiscono unQfExitCodeanzichévoid. Il passaggio di un array di gruppi la cui lunghezza differisce dal numero di termini dell’operatore restituisce un errore inveceQfExitCode_ValueErrordi essere accettato silenziosamente; il passaggio di un valoreNULLper azzerare gli indici dei gruppi non subisce alcuna modifica.
Modifiche al sistema di compilazione
-
La dipendenza opzionale
simulationè stata rinominata inffsim, quindi diventapip install "qiskit-fermions[simulation]"pip install "qiskit-fermions[ffsim]". Il nome aggiuntivo indica la dipendenza che installa, piuttosto che una funzionalità. L'installazione nonqiskit-fermions[all]subisce alcuna modifica. -
La dipendenza opzionale
optimizationè stata rinominata inpyomo, quindi diventapip install "qiskit-fermions[optimization]"pip install "qiskit-fermions[pyomo]". Il nome aggiuntivo indica la dipendenza che installa, piuttosto che una funzionalità, cosa che il vecchio nome faceva intendere in modo eccessivo: Pyomo è un linguaggio di modellazione, quindi crea il programma a numeri interi misti alla base di eRelabelModesbuild_excitation_span_minimization_model()ma non è in grado di risolverlo. La scelta di un risolutore è un passaggio a sé stante e deliberato (RelabelModesche richiede di specificarne l’argomentosolver, senza che questo pacchetto imponga quale scegliere), e il fatto di chiamare il componente aggiuntivo “Pyomo” rende evidente tale distinzione al momento dell’installazione. L'installazione nonqiskit-fermions[all]subisce alcuna modifica.
Correzioni di bug
-
È stato risolto il problema dell'eccessivo consumo di memoria da parte di e
fermion_jordan_wigner()diqf_ferm_op_jordan_wigner()(e quindi anche dijordan_wigner()quando applicato a unFermionOperator). Il mapper ha accumulato i termini mappati utilizzando un'operazione di addizione che concatena, anziché unire, i termini di Pauli duplicati; di conseguenza, l'osservabile intermedio è cresciuto in base al numero di termini di Pauli emessi anziché al numero di termini distinti. La mappatura di un grande hamiltoniano potrebbe quindi esaurire la memoria disponibile, anche se il risultato semplificato era di ordini di grandezza inferiore. Gli accumulatori vengono ora canonicalizzati non appena raggiungono una dimensione superiore di un certo fattore rispetto a quella risultante dalla precedente fusione, il che limita la memoria da essi occupata in base all’operatore che effettivamente rappresentano.La memoria di picco continua ad aumentare con il numero di thread di lavoro: i termini vengono assegnati al primo thread disponibile anziché essere suddivisi in base alle stringhe di Pauli che generano, quindi ogni accumulatore finisce per contenere all’incirca una copia completa dell’operatore mappato. Ciò che è cambiato è il fattore che ciascuno di essi comporta: in precedenza era proporzionale al numero di termini di Pauli emessi, mentre ora è proporzionale al numero di termini distinti. Riducendo il numero di thread tramite la variabile d'ambiente
RAYON_NUM_THREADSdel rayon si abbassa quindi il picco, in modo approssimativamente proporzionale, e questa è l'opzione da utilizzare quando la memoria è limitata.L'operatore mappato rimane invariato: la canonizzazione unisce solo i termini duplicati, quindi il risultato rappresenta esattamente la stessa grandezza osservabile di prima. Tuttavia, non è più garantito che sia completamente semplificato e il numero di termini in esso contenuti può variare a seconda del numero di fili. Chiama
simplify()(qk_obs_canonicalizein C) se vuoi che tutti i duplicati vengano uniti. -
Risolto il problema relativo all'eliminazione
QDriftTrotterizationdel metodoEvolution.synthesisdei gate che vengono sottoposti alla trasformazione di Trotter e all'emissione di gate campionati che potevano essere scomposti in fattori non unitari. I gate campionati ora ereditano il metodo di sintesi del gate che sostituiscono e sono contrassegnati conEvolution.atomic. -
Risolto il problema della ricerca
RelabelModesdi una permutazione per un hamiltoniano errato quando le porteEvolutiondi un circuito erano già state scomposte. Un restringeFermionicEvolutionSynthesisogni fattore che emette al supporto di quel fattore, per cui l’operatore del fattore porta indici di modalità locali al gate, mentre la sua posizione nel registro è data dagli argomenti qubit del nodo. L'ottimizzazione automatica ha considerato solo l'operatore, che raggruppava tali indici locali: ogni fattore bimodale ristretto diventava l'eccitazione,(0, 1)indipendentemente dalle modalità con cui era effettivamente accoppiato, mentre un accoppiamento a lungo raggio veniva completamente escluso dal modello.La permutazione restituita era valida, ma ottimizzata per un hamiltoniano diverso da quello del circuito, e non è stato generato alcun errore, poiché gli indici locali sono essi stessi indici di modalità validi. I circuiti le cui evoluzioni hanno raggiunto il passaggio completo non sono stati interessati. Se ti sei basato sulla permutazione di un circuito scomposto, riesegui il passaggio: ora potrebbe restituirne una diversa (e migliore).
-
È stato risolto un problema di decomposizione del gate
Evolutionche non terminava mai. L'evoluzione di un termine a un solo operatore si scomponeva in un termine identico a un solo operatoreEvolution, per cui l'espansione ripetuta non portava a nessun risultato. Ciò ha comportato, in modo particolarmente evidente, un aumentoinverse()diRecursionError, poiché la definizione del gate viene elaborata in modo ricorsivo.Un fattore emesso da a
FermionicEvolutionSynthesisviene ora contrassegnato con eEvolution.atomicviene lasciato al suo posto dadecompose()anziché essere nuovamente sviluppato. La decomposizione ripetutaEvolutiondi raggiunge quindi un punto fisso. -
Risolto il problema della generazione
decompose()di fattori non unitari quando l'operazione veniva applicata più di due volte a un operatore che trasportava gruppi. Ogni gruppo è stato ulteriormente suddiviso, termine per termine, ma un singolo termine non è generalmente ermitiano anche quando il gruppo che lo contiene lo è – come dimostra l’esempio delle coppie coniugate di un generatore di clusterUCC. L'esponenziale di un fattore di questo tipo non è unitario.La fase di conversione da fermioni a qubit ha scartato tali fattori relativi
ValueErrorai coefficienti complessi, ma la simulazione del vettore di stato li ha applicati senza problemi e ha restituito uno stato non normalizzato. Poiché un gruppo è ora atomico, la scissione che lo ha generato non si verifica più. -
Evolution.inverse()è ora implementato direttamente, restituendo unEvolutionche evolve lo stesso operatore per il tempo negato e mantiene il metodosynthesisdel gate. In precedenza, l'implementazione ereditata eseguiva una ricorsione attraverso la definizione del gate e restituiva un sempliceGate, scartando sia l'operatore che il metodo di sintesi.
Miglioramenti delle prestazioni
-
È stato migliorato il condizionamento numerico della simulazione del vettore di stato per le
OrbitalRotationporteEvolutione fornendo la traccia esatta dell'operatore a settore fisso alla routine esponenziale matriciale di SciPy’s. -
fermion_jordan_wigner()non copia più l'operatore che gli viene fornito. Il bindingPythonha interpretato il suo argomento per valore, il che ha comportato la duplicazione di ogni buffer di termini dell'input al soloFermionOperatorscopo di leggerlo.
0.1.0
Preludio
Questa è la prima versione di Qiskit Fermions, un’estensione di Qiskit che fornisce strumenti per i sistemi fermionici. Fornisce strutture dati per gli operatori, un framework e una libreria per la conversione di tali operatori in forma di qubit (mapper), nonché un framework e una libreria per la sintesi dei circuiti quantistici corrispondenti. In linea con la filosofia di progettazione di Qiskit, il nucleo di questo pacchetto è scritto in Rust ed è reso accessibile tramite binding proprietari sia per Python che per C.