Skip to main content
IBM Quantum Platform

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 Evolution viene scomposto nello spazio fermionico è ora configurabile tramite il suo nuovo argomento synthesis , specificato solo tramite parola chiave, che rispecchia l’argomento synthesis di di Qiskit PauliEvolutionGate. I metodi disponibili si trovano nel nuovo qiskit_fermions.circuit.library.synthesis modulo, che fornisce l'interfaccia FermionicEvolutionSynthesis e la sua implementazione FermionicLieTrotter di primo ordine.

    Questo passaggio da fermione a fermione è facoltativo: un Evolution gate 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à reciproca groups) 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 non EvolutionSynthesis sono in grado di farlo.

    L'impostazione predefinita rimane invariata: lasciando synthesis su viene None utilizzato FermionicLieTrotter, il che riproduce esattamente la scomposizione che Evolution effettuava in precedenza, quindi i circuiti esistenti vengono sintetizzati in modo identico.

  • I pesi di campionamento di possono QDriftTrotterization ora essere specificati tramite il suo nuovo argomento weights , accessibile solo tramite parola chiave, invece di essere ricavati dall'operatore evoluto ad ogni chiamata. Se si lascia il valore predefinito, None il 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 invece group_coeff_means() una volta con, insieme a group_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 hjh_j della decomposizione di qDRIFT H=∑jhjHjH = \sum_j h_j H_j, in cui il segno di un coefficiente appartiene a HjH_j 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 gamma campioni identici, ma l’evoluzione avviene per gamma * t. E poiché l'array descrive i termini (o gruppi) di un operatore specifico, la sua lunghezza viene verificata rispetto a ogni Evolution porta a cui viene applicato, e un circuito che contiene più di una porta di questo tipo viene scartato; lasciando il campo deselezionato weights , tale circuito rimane supportato, poiché ogni porta ne deriva autonomamente la propria.

  • FCIDump ora rende visibili gli integrali elettronici che ha analizzato, i quali in precedenza erano accessibili solo convertendo l'intera struttura dei dati in un FermionOperator. I nuovi metodi restituiscono copie di sola lettura sotto forma di array di tipo NumPy, con lo stesso layout appiattito utilizzato dai costruttori electronic-integral: get_one_body_tril_a(), get_one_body_tril_b(), get_two_body_tril_aa(), e get_two_body_tril_ab() get_two_body_tril_bb(). Gli array beta-spin restituiscono i risultati relativi a None un file con restrizioni di spin; il nuovo is_unrestricted attributo riporta i dati relativi a tutti e tre contemporaneamente. Il nuovo attributo constant fornisce l'energia costante (di repulsione nucleare), oppure, None se 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 FermionicSuzukiTrotter aggiunta una formula di prodotto di ordine superiore per scomporre una porta Evolution nello spazio fermionico. Laddove ogni fattore viene applicato FermionicLieTrotter una sola volta, ciò li combina simmetricamente in modo da annullare i termini di errore di ordine inferiore, e il suo reps argomento 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 groups sono 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=2 si è ottenuto reps=4 un 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'argomento reps che in precedenza gli mancava. I due sono intercambiabili a parità di reps.

    Si noti che un ordine più elevato garantisce una maggiore precisione grazie alla profondità: una formula dik ordine emette all’incirca 5**((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 gate Evolution di 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 e UCC UCJ, che lo generano internamente. Questo passaggio consente invece di effettuare la scelta una sola volta per l'intera pipeline. Un predicato filter opzionale 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 un UCJ.

    Il pass espande ogni gate selezionato nei fattori emessi dal proprio metodo, quindi non è necessaria alcuna fase di espansione separata. Utilizzare pass per apply=False selezionare solo il metodo e lasciare l'espansione a qualcos'altro, come ad esempio Qiskit Decompose; 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 mai Evolution.synthesis letto.

  • Il modulo qiskit_fermions.operators.terms.grouping tratta 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(), e edge_vertex_jordan_wigner() transfer_vertex_jordan_wigner().

  • jordan_wigner() ora esegue operazioni su tutti e quattro i tipi di operatore anziché solo su uno FermionOperator, delegando all'implementazione diretta che corrisponde all'operatore specificato. Il passaggio a uno degli altri tre tipi di operatore menzionati in precedenza generava un TypeError.

  • Il metodo is_hermitian() fa ora parte del protocollo OperatorTrait . Ogni classe operatore lo forniva già, quindi ora può essere richiamato su qualsiasi valore di tipo OperatorTrait anziché 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 risultato False è conservativo per i tipi di operatore la cui forma normale non è una vera e propria forma canonica. Si veda is_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 MapperFnEvolutionSynthesis ora 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 voce groups alla 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))
  • QDriftTrotterization ora registra il numero di estrazioni scartate dalla sua filter_trivial modalità. Quando almeno una porta Evolution veniva effettivamente filtrata, le trasportate metadata del circuito restituito e filter_trivial.discarded filter_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().

  • Evolution ora può essere simulato per qualsiasi tipo di operatore di questo pacchetto, non solo FermionOperator. Un operatore di altro tipo viene convertito tramite la sua immagine fermionica (il SupportsFermionOperator protocollo) prima di essere simulato, pertanto l’evoluzione di MajoranaOperator, o EdgeVertexOperator TransferVertexOperator non genera più NotImplementedError.

  • FermionOperator ora viene implementato ffsim.SupportsTrace tramite un nuovo _trace_() metodo, in modo che ffsim.trace() sia possibile calcolare la traccia di un operatore su un settore (norb, nelec) fisso. Ecco quali sono i presupposti lungo scipy.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 un QfFermionOperator mantenga il numero di particelle all'interno di ciascun blocco di modi. Va ad integrare la funzione esistente qf_ferm_op_conserves_particle_number(), che verifica solo il totale. Per una struttura spin-orbitale degli orbitali spaziali norb , 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 NULL con un numero di blocchi pari a 0 considera tutte le modalità come un unico blocco, il che equivale a chiamare qf_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() e qf_ferm_op_get_support(), oltre agli equivalenti qf_transfer_op qf_maj_op, qf_edge_op e.

    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_support per dimensionare il buffer di output, quindi passare tale buffer a get_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_inplace e mul_inplace, ad esempio qf_ferm_op_add_inplace(), qf_ferm_op_scaled_add_inplace() e qf_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 groups a NULL, proprio come qf_ferm_op_add() fa. mul_inplace ridimensiona 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", QfEdgeVertexOperator e QfTransferVertexOperator, 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_indices right_indices, al posto del singolo modes array 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 argomento num_indices che li comprende entrambi.

  • Sono state aggiunte qf_edge_op_canonical_order() e qf_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(), e qf_transfer_vertex_to_edge_vertex().

  • Aggiunte scaled_add all’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() e qf_transfer_op_scaled_add(). Ciascuna restituisce left + 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 -1 il 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 funzione simplify corrispondente per raggruppare i termini uguali.

  • QfFCIDump ora rende visibili gli integrali elettronici che ha analizzato, i quali in precedenza erano accessibili solo convertendo l'intera struttura dei dati in un QfFermionOperator. I nuovi accessori qf_fcidump_get_one_body_tril_a(), qf_fcidump_get_one_body_tril_b(), qf_fcidump_get_two_body_tril_aa(), e qf_fcidump_get_two_body_tril_ab() utilizzano qf_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 QfFCIDump viene liberato. Proteggi i tre getters beta-spin con il nuovo qf_fcidump_is_unrestricted(), e qf_fcidump_constant() con il nuovo qf_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 che qf_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 ricerca qf_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() e qf_transfer_op_jordan_wigner().

Python Note sull'aggiornamento dell'API

  • Le qiskit_fermions.linalg.linear_operator funzioni qiskit_fermions.linalg.apply_unitary e sono state rimosse. Erano semplici involucri attorno ai metodi e _apply_unitary_ del protocollo _linear_operator_ , che duplicavano e ffsim.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.SupportsLinearOperator protocolli e qiskit_fermions.protocols.SupportsApplyUnitary sono stati rimossi per lo stesso motivo: riprendevano i contratti definiti da ffsim. Utilizzare i protocolli propri di ffsim, che sono stati replicati alla lettera:

    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. SupportsApplyUnitaryPlaced rimane, poiché si estende ffsim.SupportsApplyUnitary con 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'eccezione OSError, mentre un file che non rispetta il formato FCIDump (mancanza dell'elenco dei nomi nell'intestazione, mancanza del campo NELEC o NORB , oppure un campo MS2 non valido) genera un'eccezione ValueError. Anche un FCIDump contenente un valore di energia MO, che non è ancora supportato, genera un errore ValueError.

  • Il metodo group_weights è stato rimosso da tutte le classi operatore e dal protocollo OperatorTrait . Viene sostituita da group_coeff_means(), una funzione libera nel modulo qiskit_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 groups la cui lunghezza differisce dal numero di termini dell'operatore genera ora un'eccezione ValueError invece di essere accettata senza avvisare. L'assegnazione None per azzerare gli indici di gruppo non subisce alcuna modifica.

  • MapperFnEvolutionSynthesis non semplifica più l'operatore restituito dal suo mapper_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, e reconstruct_t2_alpha_beta double_factorized_2body. Le varianti t2 esistevano per servire UCJ.from_t_amplitudes, che ora si trova in ffsim, e non double_factorized_2body avevano alcun chiamante. Utilizza invece ffsim.linalg.double_factorized() ffsim.linalg.double_factorized_t2(), e ffsim.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 un FermionOperator in un e ffsim.FermionOperator delegando a ffsim.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 sua FciLinearOperator classe e le funzioni occupation_axis_mask e slater_determinant_statevector . ffsim fornisce gli equivalenti: e ffsim.slater_determinant() ffsim.addresses_to_strings() rispettivamente.

  • OrbitalRotation non ricorre più a un percorso "generatore più esponenziale" in assenza di ffsim; delega sempre a ffsim.apply_orbital_rotation(). I risultati rimangono invariati quando si installa ffsim.

  • Il UCC gate è 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'argomento variant e l'enumerazione UCC.Variant non 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, e UCC.from_parameters UCC.to_parameters sono stati rimossi, così come la spinless variante e la parametrizzazione antisymmetric opt-in, che non hanno un equivalente in ffsim. Gli operatori di ffsim utilizzano n_params(), e from_parameters() to_parameters() con convenzioni identiche. Per costruire un ansatz al di fuori di quella famiglia (uno senza spin o un “ t2t_2 ” antisimmetrizzato), si costruisce direttamente un operatore Evolution sul 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 e gate.uccsd_op.t1 gate.uccsd_op.t2; il gate non le riflette più come propri attributi. UCC.cluster_operator() rimane invariato. Un trasportato final_orbital_rotation dall'operatore ffsim viene ora aggiunto come chiusura OrbitalRotation.

    Poiché ffsim è ora il tipo di input del gate anziché un acceleratore opzionale, la creazione di un UCC richiede il (pip install "qiskit-fermions[ffsim]") aggiuntivo ffsim e genera un se questo MissingOptionalLibraryError manca. ffsim non supporta Windows, quindi in quel sistema operativo il gate non è disponibile; utilizzare WSL.

  • Il UCJ gate è 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’argomento variant e l’enumerazione UCJ.Variant non 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, e UCJ.from_parameters UCJ.to_parameters sono stati rimossi: gli operatori di ffsim forniscono from_t_amplitudes(), n_params(), from_parameters() e to_parameters() con convenzioni identiche e offrono inoltre la fattorizzazione doppia compressa (optimize=True) e from_cisd_vec(), che questo pacchetto non ha mai implementato. L'operatore avvolto è disponibile come UCJ.ucj_op, quindi i suoi tensori sono accessibili come gate.ucj_op.diag_coulomb_mats e 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 UCJ richiede il (pip install "qiskit-fermions[ffsim]") aggiuntivo ffsim e genera un se questo MissingOptionalLibraryError manca. 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 ValueError anziché 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 e QfExitCode passa il analizzato tramite QfFCIDump un 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_ValueError un file non rispetta il formato FCIDump (mancanza dell'elenco dei nomi nell'intestazione, mancanza del NELEC campo NORB o, campo MS2 non 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_weights e le relative controparti per tipo sono state rinominate in qf_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 un QfExitCode anziché void. Il passaggio di un array di gruppi la cui lunghezza differisce dal numero di termini dell’operatore restituisce un errore invece QfExitCode_ValueError di essere accettato silenziosamente; il passaggio di un valore NULL per azzerare gli indici dei gruppi non subisce alcuna modifica.

Modifiche al sistema di compilazione

  • La dipendenza opzionale simulation è stata rinominata in ffsim, quindi diventa pip install "qiskit-fermions[simulation]" pip install "qiskit-fermions[ffsim]". Il nome aggiuntivo indica la dipendenza che installa, piuttosto che una funzionalità. L'installazione non qiskit-fermions[all] subisce alcuna modifica.

  • La dipendenza opzionale optimization è stata rinominata in pyomo, quindi diventa pip 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 e RelabelModes build_excitation_span_minimization_model() ma non è in grado di risolverlo. La scelta di un risolutore è un passaggio a sé stante e deliberato (RelabelModes che richiede di specificarne l’argomento solver , 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 non qiskit-fermions[all] subisce alcuna modifica.

Correzioni di bug

  • È stato risolto il problema dell'eccessivo consumo di memoria da parte di e fermion_jordan_wigner() di qf_ferm_op_jordan_wigner() (e quindi anche di jordan_wigner() quando applicato a un FermionOperator). 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_THREADS del 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_canonicalize in C) se vuoi che tutti i duplicati vengano uniti.

  • Risolto il problema relativo all'eliminazione QDriftTrotterization del metodo Evolution.synthesis dei 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 con Evolution.atomic.

  • Risolto il problema della ricerca RelabelModes di una permutazione per un hamiltoniano errato quando le porte Evolution di un circuito erano già state scomposte. Un restringe FermionicEvolutionSynthesis ogni 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 Evolution che non terminava mai. L'evoluzione di un termine a un solo operatore si scomponeva in un termine identico a un solo operatore Evolution, per cui l'espansione ripetuta non portava a nessun risultato. Ciò ha comportato, in modo particolarmente evidente, un aumento inverse() di RecursionError, poiché la definizione del gate viene elaborata in modo ricorsivo.

    Un fattore emesso da a FermionicEvolutionSynthesis viene ora contrassegnato con e Evolution.atomic viene lasciato al suo posto da decompose() anziché essere nuovamente sviluppato. La decomposizione ripetuta Evolution di 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 cluster UCC . L'esponenziale di un fattore di questo tipo non è unitario.

    La fase di conversione da fermioni a qubit ha scartato tali fattori relativi ValueError ai 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 un Evolution che evolve lo stesso operatore per il tempo negato e mantiene il metodo synthesis del gate. In precedenza, l'implementazione ereditata eseguiva una ricorsione attraverso la definizione del gate e restituiva un semplice Gate, 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 OrbitalRotation porte Evolution e 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 binding Python ha interpretato il suo argomento per valore, il che ha comportato la duplicazione di ogni buffer di termini dell'input al solo FermionOperator scopo 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.

Questa pagina è stata utile?
Segnala un bug, un errore di battitura o richiedi contenuti su GitHub.