SparseObservable
class qiskit.quantum_info.SparseObservable(data, /, num_qubits=None)
Basi: object
Un'osservabile su basi di Pauli che memorizza i suoi dati in un formato qubit-sparse.
Matematica
Questa osservabile rappresenta una somma di stringhe degli operatori di Pauli e dei proiettori di autovalori di Pauli, con ogni termine ponderato per un numero complesso. Cioè, l'osservabile completo è
per i numeri complessi e gli operatori single-qubit che agiscono sui qubit da un alfabeto ristretto . La somma su è la somma dei singoli termini e il prodotto tensoriale produce le stringhe di operatori.
L'alfabeto degli operatori a singolo bit consentiti da cui è tratto è costituito dagli operatori di Pauli e dagli operatori di proiezione degli autostati di Pauli. Esplicitamente, questi sono:
Etichetta | Operatore | Valore numerico | BitTerm attributo |
|---|---|---|---|
"I" | (identità) | Non conservato. | Non conservato. |
"X" | (Pauli X) | 0b0010 (2) | X |
"Y" | (Pauli Y) | 0b0011 (3) | Y |
"Z" | (Pauli Z) | 0b0001 (1) | Z |
"+" | (proiettore all'autostato positivo di X) | 0b1010 (10) | PLUS |
"-" | (proiettore all'autostato negativo di X) | 0b0110 (6) | MINUS |
"r" | (proiettore all'autostato positivo di Y) | 0b1011 (11) | RIGHT |
"l" | (proiettore all'autostato negativo di Y) | 0b0111 (7) | LEFT |
"0" | (proiettore verso un autostato positivo di Z) | 0b1001 (9) | ZERO |
"1" | (proiettore all'autostato negativo di Z) | 0b0101 (5) | ONE |
L'alfabeto consentito forma una base sovra-completa dello spazio degli operatori. Ciò significa che non esiste un'unica sommatoria per rappresentare una determinata osservabile. In confronto, SparsePauliOp utilizza una base precisa dello spazio degli operatori, per cui (dopo aver combinato i termini della stessa stringa di Pauli, rimosso gli zeri e ordinato i termini secondo un ordine canonico ) esiste una sola rappresentazione di qualsiasi operatore.
SparseObservable utilizza la sua particolare base sovracompleta con l'obiettivo di rendere l'"efficienza della misurazione" equivalente all'"efficienza della rappresentazione". Ad esempio, l'osservabile può essere misurata in modo efficiente sull'hardware con semplici misure , ma può essere rappresentata solo da SparsePauliOp come , che richiede termini memorizzati . SparseObservable richiede un solo termine per memorizzarlo.
L'aspetto negativo di questa soluzione è che non è pratico prendere una matrice arbitraria o SparsePauliOp e trovare la migliore SparseObservable rappresentazione. In genere si vuole costruire un elemento SparseObservable direttamente, piuttosto che cercare di decomporlo.
Rappresentazione
La rappresentazione interna di un SparseObservable memorizza solo gli operatori dei qubit non identici. Questo rende molto più efficiente la rappresentazione di osservabili come ; SparseObservable richiede una quantità di memoria lineare rispetto al numero totale di qubit, mentre SparsePauliOp scala quadraticamente.
I termini vengono memorizzati compressi, in modo simile al formato compresso delle matrici rade. In questa analogia, i termini della somma sono le "righe" e i termini dei qubit sono le "colonne", dove una voce assente rappresenta l'identità piuttosto che uno zero. Più esplicitamente, la rappresentazione è costituita da quattro array contigui:
Attributo | Lunghezza | Descrizione |
|---|---|---|
coeffs | Il moltiplicatore scalare complesso per ogni termine. | |
bit_terms | Ognuno dei termini non identici a singolo qubit per tutti gli operatori, nell'ordine. Questi corrispondono alla non identità nella descrizione della somma, dove le voci sono memorizzate in ordine crescente prima, e in ordine crescente all'interno di ogni termine. | |
indices | Il qubit corrispondente ( ) per ciascuno degli operatori in bit_terms. SparseObservable richiede che questo elenco sia ordinato per termini e gli algoritmi possono fare affidamento sul mantenimento di questo invariante. | |
boundaries | Gli indici che suddividono bit_terms e indices in termini completi. Per il termine numero , il suo coefficiente complesso è coeffs[i], e i suoi operatori single-qubit non identici e i loro qubit corrispondenti sono la fetta boundaries[i] : boundaries[i+1] in bit_terms e indices rispettivamente. boundaries ha sempre uno 0 esplicito come primo elemento. |
Il parametro di lunghezza è il numero di termini della somma e il parametro è il numero totale di termini single-qubit non identici.
Come esempi illustrativi:
- nel caso di un operatore zero,
boundariesè di lunghezza 1 (un singolo 0) e tutti gli altri vettori sono vuoti. - nel caso di un operatore identità completamente semplificato,
boundariesè[0, 0],coeffsha una sola voce ebit_termseindicessono vuoti. - per l'operatore ,
boundariesè[0, 2, 4],coeffsè[1.0, -1.0],bit_termsè[BitTerm.Z, BitTerm.Z, BitTerm.Y, BitTerm.X]eindicesè[0, 2, 1, 3]. L'operatore potrebbe agire su più di quattro qubit, a seconda deinum_qubitsparametro. Ibit_termssono valori interi, i cui numeri magici sono accessibili tramite la classe di attributiBitTermdell'attributo. Si noti che i termini e gli indici a bit singolo sono ordinati in modo termale. Questo è un requisito del corso.
Questi casi non sono speciali, sono pienamente coerenti con le regole e non dovrebbero richiedere una gestione speciale.
L'elemento scalare dell'array bit_terms viene memorizzato come byte numerico. I valori numerici sono relativi alla rappresentazione di Pauli simplectica che SparsePauliOp utilizza e sono accessibili con un accesso con nome tramite un'enumerazione:
BitTerm
class BitTerm
Un IntEnum che fornisce un accesso con nome ai valori numerici utilizzati per rappresentare ciascuno dei termini dell'alfabeto single-qubit enumerati in Alfabeto dei termini single-qubit utilizzati in SparseObservable.
Questa classe è collegata a SparseObservable. Accedere come SparseObservable.BitTerm. Se la digitazione è eccessiva e si ha a che fare solo con gli oggetti :class:¬SparseObservable` e il nome BitTerm non è ambiguo, si potrebbe abbreviare come:
>>> ops = SparseObservable.BitTerm
>>> assert ops.X is SparseObservable.BitTerm.XÈ possibile accedere a tutti i valori dell'enumerazione sia con il loro nome completo in tutte le lettere maiuscole, sia con la loro etichetta di una sola lettera. Le etichette di una sola lettera non sono generalmente identificatori validi per Python, quindi è necessario utilizzare la notazione di indicizzazione per accedervi:
>>> assert SparseObservable.BitTerm.ZERO is SparseObservable.BitTerm["0"]La struttura numerica di questi è che sono tutti valori a quattro bit di cui i due bit più bassi sono la rappresentazione simplettica (senza fase) dell'operatore di Pauli relativo all'oggetto, dove il bit più basso denota un contributo di e il secondo più basso un contributo di , mentre i due bit superiori sono 00 per un operatore di Pauli, 01 per il proiettore di autovalori negativi e 10 per il proiettore di autovalori positivi.
Valori
X
Valore predefinito: 2
L'operatore di Pauli . Utilizza l'etichetta a lettera singola "X".
Plus
Valore predefinito: 10
Il proiettore all'autostato positivo dell'operatore : . Utilizza l'etichetta a lettera singola "+".
meno
Valore predefinito: 6
Il proiettore all'autostato negativo dell'operatore : . Utilizza l'etichetta a lettera singola "-".
Y
Valore predefinito: 3
L'operatore di Pauli . Utilizza l'etichetta a lettera singola "Y".
DESTRO
Valore predefinito: 11
Il proiettore all'autostato positivo dell'operatore : . Utilizza l'etichetta a lettera singola "r".
SINISTRA
Valore predefinito: 7
Il proiettore all'autostato negativo dell'operatore : . Utilizza l'etichetta a lettera singola "l".
Z
Valore predefinito: 1
L'operatore di Pauli . Utilizza l'etichetta a lettera singola "Z".
ZERO
Valore predefinito: 9
Il proiettore all'autostato positivo dell'operatore : . Utilizza l'etichetta a lettera singola "0".
ONE
Valore predefinito: 5
Il proiettore all'autostato negativo dell'operatore : . Utilizza l'etichetta a lettera singola "1".
Attributi
label
L'etichetta a stringa di un solo carattere usata per rappresentare questo termine nell'alfabeto SparseObservable alfabeto.
Ogni attributo di tipo array si comporta come una sequenza Python. È possibile indicizzarli e tagliarli con una semantica list-con la semantica standard. Affettare un attributo restituisce un Numpy ndarray contenente una copia dei dati rilevanti con il dtype naturale del campo; questo permette di fare facilmente matematica sui risultati, come operazioni bitwise su bit_terms. È possibile assegnare agli indici o alle fette di ciascun attributo, ma è necessario rispettare le regole di coerenza dei dati. Ad esempio:
>>> obs = SparseObservable.from_list([("XZY", 1.5j), ("+1r", -0.5)])
>>> assert isinstance(obs.coeffs[:], np.ndarray)
>>> # Reduce all single-qubit terms to the relevant Pauli operator, if they are a projector.
>>> obs.bit_terms[:] = obs.bit_terms[:] & 0b00_11
>>> assert obs == SparseObservable.from_list([("XZY", 1.5j), ("XZY", -0.5)])La riduzione di cui sopra alle basi di Pauli può essere ottenuta anche con pauli_bases().
Ordinamento canonico
Per ogni osservabile matematico, esistono diversi modi di rappresentarlo con SparseObservable. Ad esempio, lo stesso insieme di termini a bit singolo e i loro indici corrispondenti possono comparire più volte nell'osservabile. Dal punto di vista matematico, ciò equivale ad avere un solo termine con tutti i coefficienti sommati. Analogamente, i termini della somma in a SparseObservable possono essere in qualsiasi ordine pur rappresentando la stessa osservabile, poiché l'addizione è commutativa (anche se l'addizione in virgola mobile non è associativa), SparseObservable non garantisce l'ordine della somma).
Queste due categorie di degenerazione della rappresentazione possono indurre == l'operatore a ritenere che due osservabili non siano uguali, nonostante rappresentino lo stesso oggetto. In questi casi, può essere utile definire una forma canonica che consenta di confrontare strutturalmente le grandezze osservabili.
È possibile inserire un elemento SparseObservable in forma canonica utilizzando il metodo simplify() metodo. L'ordine preciso dei termini nell'ordinamento canonico non è specificato e può cambiare da una versione all'altra di Qiskit. All'interno della stessa versione di Qiskit, tuttavia, è possibile confrontare due osservabili dal punto di vista strutturale, confrontando le loro forme semplificate.
Se si desidera tenere conto della tolleranza in virgola mobile nel confronto, è più sicuro utilizzare una ricetta come:
def equivalent(left, right, tol):
return (left - right).simplify(tol) == SparseObservable.zero(left.num_qubits)La forma canonica prodotta da simplify() da sola non è in grado di rilevare universalmente tutte le osservabili equivalenti a causa dell'alfabeto di basi troppo completo. Per ottenere un'espressione univoca, si può innanzitutto rappresentare l'osservabile usando solo i termini di Pauli chiamando as_paulis(), seguito da simplify(). Si noti che l'espansione del proiettore (ad es. + in I e X) non è computazionalmente fattibile in scala.
Indicizzazione
SparseObservable si comporta come una sequenza Python (la forma standard, non quella espansa collections.abc.Sequence). L'osservabile può essere indicizzato da numeri interi e iterato per ottenere i singoli termini.
Ogni termine appare come un'istanza di una classe autonoma. I singoli termini sono copiati dall'osservabile di base; le mutazioni ad essi apportate non influiscono sull'osservabile.
Termine
class Term(num_qubits, coeff, bit_terms, indices)
Basi: object
Un singolo termine di un'opera completa SparseObservable.
In genere vengono creati indicizzando o iterando in un file SparseObservable.
bit_labels
bit_labels()
Restituisce le etichette dei bit del termine come stringa.
Le etichette dei bit corrisponderanno all'ordine di SparseTerm.indices, in modo che l'i-esimo carattere della stringa sia applicato all'indice del qubit in term.indices[i].
Risultati
I termini di bit non identici come stringa concatenata.
bit_terms
Visualizzazione in sola lettura dei singoli termini a singolo qubit.
Gli unici valori validi nell'array sono quelli con un corrispondente valore BitTerm.
coeff
Il coefficiente del termine.
copy
copy()
Ottenere una copia di questo termine.
indices
Visualizzazione in sola lettura degli indici di ogni termine non identitario a singolo qubit.
Gli indici saranno sempre in ordine sparso.
num_qubits
Il numero di qubit su cui è definito il termine.
pauli_base
pauli_base()
Ottenere un Pauli che rappresenta la base di misura necessaria per questo termine.
Ad esempio, il proiettore 0l+ restituirà un Pauli ZYX. Il risultato Pauli è denso, nel senso che vengono memorizzate identità esplicite. Un'identità nell'uscita di Pauli non richiede una misura concreta.
Restituisce: Pauli: l'operatore di Pauli che rappresenta il necessario
base di misurazione.
SparseObservable.pauli_bases()
Un metodo simile per un'intera osservabile in una sola volta.
to_observable
to_observable()
Convertire questo termine in un termine completo SparseObservable.
Costruzioni
SparseObservable definisce diversi costruttori. Il costruttore predefinito tenterà di delegare a uno dei costruttori più specifici, in base al tipo di input. Si possono sempre usare i costruttori specifici per avere un maggiore controllo sulla costruzione.
Metodo | Riepilogo |
|---|---|
from_label() | Convertire un'etichetta stringa densa in un termine singolo SparseObservable. |
from_list() | Somma un elenco di tuple di etichette di stringhe dense e i coefficienti associati in un osservabile. |
from_sparse_list() | Somma un elenco di tuple di etichette di stringhe sparse, i qubit a cui si applicano e i loro coefficienti in un osservabile. |
from_pauli() | Trasformare un singolo Pauli in un termine singolo SparseObservable. |
from_sparse_pauli_op() | Sollevare un SparsePauliOp in un SparseObservable. |
from_terms() | Somma esplicita di singole Term istanze. |
from_raw_parts() | Costruire l'osservabile dagli array di dati grezzi. |
__new__
__new__(data, /, num_qubits=None)
Il costruttore predefinito di SparseObservable.
Delega a uno dei metodi espliciti del costruttore di conversione, in base al tipo dell'argomento data . Se viene fornito num_qubits e il costruttore implicito nel tipo di data non accetta un numero, il numero intero fornito deve corrispondere all'input.
Parametri
- data - Il tipo di dati dell'ingresso. Questo può essere un altro
SparseObservable, nel qual caso l'input viene copiato, unPaulioSparsePauliOp, nel qual casofrom_pauli()ofrom_sparse_pauli_op()vengono richiamati a seconda dei casi, oppure può essere un elenco in un formato valido per i due metodifrom_list()ofrom_sparse_list(). - num_qubits (int|None) – Numero opzionale di qubit per l'operatore. Per la maggior parte degli input di dati, questo può essere dedotto e non è necessario passarlo. È necessario solo per gli elenchi vuoti o per il formato sparse-list. Se viene dato inutilmente, deve corrispondere ai dati immessi.
Oltre ai costruttori basati sulla conversione, esistono anche metodi di aiuto che costruiscono forme speciali di osservabili.
Metodo | Riepilogo |
|---|---|
zero() | L'operatore zero su un dato numero di qubit. |
identity() | L'operatore di identità su un dato numero di qubit. |
Conversioni
Un file esistente SparseObservable può essere convertito in altri quantum_info formati o in formati generici. SparseObservableSi tenga presente che altri oggetti potrebbero non essere in grado di rappresentare la stessa grandezza osservabile con la stessa efficienza, e potrebbero richiedere una quantità di memoria esponenzialmente maggiore.
Metodo | Riepilogo |
|---|---|
as_paulis() | Creare un nuovo SparseObservableche si espande solo in termini di operatori di Pauli. |
to_sparse_list() | Esprimere l'osservabile in un formato di lista rada con elementi (bit_terms, indices, coeff). |
Inoltre, SparsePauliOp.from_sparse_observable() è disponibile per la conversione da questa classe a SparsePauliOp. Si noti che questo metodo soffre degli stessi problemi di utilizzo esponenziale della memoria di as_paulis().
Manipolazione matematica
SparseObservable supporta l'insieme standard di operatori matematici Python, proprio come gli altri quantum_info operatori.
In aritmetica di base, è possibile:
- aggiungere due osservabili usando
+ - sottrarre due osservabili utilizzando
- - moltiplicare o dividere per un
int,floatocomplexutilizzando*e/ - negare tutti i coefficienti di un'osservabile con unary
-
Ciascuno degli operatori aritmetici binari di base ha un metodo in-place specializzato corrispondente, che muta il lato sinistro in-place. L'uso di questi operatori è in genere più efficiente degli operatori infix, soprattutto per costruire un osservabile in un ciclo.
Il prodotto tensoriale è calcolato con tensor() (per l'ordinamento standard di giustapposizione delle etichette di Pauli) oppure expand() (per l'ordinamento inverso). L'operatore ^ è sovraccaricato per essere equivalente a tensor().
Quando si utilizzano gli operatori binari ^ (tensor()) e & (compose()), si tenga presente che le regole di precedenza degli operatori di Python possono far sì che l'ordine di valutazione sia diverso da quello previsto. In particolare, l'operatore + si lega più strettamente di ^ o &, così come * si lega più strettamente di +.
Quando si utilizzano gli operatori nelle espressioni miste, è preferibile utilizzare le parentesi per raggruppare gli operandi dei prodotti tensoriali.
A SparseObservable ha un valore ben definito adjoint(). Le nozioni di coniugazione complessa scalare (conjugate()) e trasposizione a valori reali (transpose()) sono definite in modo analogo alla rappresentazione matriciale di altri operatori di Pauli in Qiskit.
Note sull'efficienza
Internamente, SparseObservable è mutabile in-place, anche utilizzando vettori crescenti sovra-allocati per estendere il numero di termini. Ciò significa che il costo dell'aggiunta a un'osservabile utilizzando += viene ammortizzato in modo lineare rispetto al numero totale di termini aggiunti, invece della complessità quadratica che richiederebbe + binario.
Le aggiunte e le sottrazioni sono implementate da un'operazione di impilamento dei termini; non c'è una "semplificazione" automatica (somma di termini simili), perché la maggior parte delle aggiunte per costruire un osservabile generano solo un piccolo numero di duplicazioni e il rilevamento dei termini simili ha costi aggiuntivi. Se questo non si adatta ai casi d'uso, si può chiamare periodicamente simplify()o discutere con noi di altre API per migliorare la costruzione degli osservabili.
Attributi
bit_terms
Un elenco piatto di termini a singolo qubit. Si tratta più naturalmente di un elenco di elenchi, ma viene memorizzato in modo piatto per motivi di utilizzo della memoria e di localizzazione, con i sottoelenchi indicati dai confini.
boundaries
Indici che dividono bit_terms e indices in sottoelenchi per ogni singolo termine della somma. boundaries[0] : boundaries[1] è l'intervallo di indici in bit_terms e indices che corrispondono al primo termine della somma. Tutti gli indici di qubit non specificati sono implicitamente l'identità. Questo è un elemento più lungo di coeffs, dato che boundaries[0] è sempre uno zero esplicito (per semplicità algoritmica).
coeffs
I coefficienti di ciascun termine astratto nella somma. Questo ha tanti elementi quanti sono i termini della somma.
indices
Un elenco piatto degli indici dei qubit su cui agiscono le voci corrispondenti in bit_terms agiscono su di essi. Questo elenco deve essere sempre ordinato per termini, dove un termine è un sottoelenco indicato da boundaries.
Se si scrive in questo attributo dallo spazio Python, è necessario assicurarsi di scrivere solo indici ordinati per termine.
num_qubits
Il numero di qubit su cui agisce l'operatore.
Questo non è deducibile da altre forme o valori, poiché le identità non sono memorizzate esplicitamente.
num_terms
Il numero di termini della somma che l'operatore sta tracciando.
Metodi
adjoint
adjoint()
Calcolare l'adjoint di questa osservabile.
Questo è ben definito in senso matematico astratto. Tutti i termini dell'alfabeto a singolo qubit sono auto-aggiunti, quindi il risultato di questa operazione è lo stesso osservabile, con la differenza che i suoi coefficienti sono tutti i loro coniugati complessi.
Esempi
>>> left = SparseObservable.from_list([("XY+-", 1j)])
>>> right = SparseObservable.from_list([("XY+-", -1j)])
>>> assert left.adjoint() == rightapply_layout
apply_layout(layout, num_qubits=None)
Applicare un layout di transpiler a questo SparseObservable.
In genere si definisce l'osservabile in termini di qubit virtuali dei circuiti da utilizzare per preparare gli stati. Dopo la transpilazione, i qubit virtuali vengono mappati su particolari qubit fisici di un dispositivo, che può essere più ampio del vostro circuito. Questa mappatura può anche cambiare nel corso del circuito. Questo metodo trasforma l'osservabile di ingresso sui qubit virtuali in un'osservabile adatta ad essere applicata immediatamente dopo il circuito fisico completamente transpilato.
Parametri
- layout (TranspileLayout |list[int] | None) – Il layout da applicare. Nella maggior parte dei casi, questa funzione dovrebbe ricevere il
QuantumCircuit.layoutcampo proveniente da un circuito che è stato transpilato per l'hardware. Inoltre, è possibile passare un elenco di indici dei nuovi qubit.NoneSe specificato in modo esplicito, non viene applicata alcuna rimappatura (ma è comunque possibile utilizzarenum_qubitsper espandere l'osservabile). - num_qubits (int | None) – Il numero di qubit a cui espandere l'osservabile. Se non viene fornito, l'output sarà largo quanto il dato
TranspileLayouto della stessa larghezza dell'input selayoutè fornito in un'altra forma.
Risultati
Un nuovo SparseObservable con l'applicazione del layout fornito.
as_paulis
as_paulis()
Esprimere l'osservabile solo in termini di Pauli, scrivendo ogni proiettore come somma di termini di Pauli.
Si noti che non è garantito l'ordine dei termini di Pauli risultanti. Utilizzare SparseObservable.simplify() per ottenere una rappresentazione canonica.
Si noti che questo utilizzerà almeno termini se sono presenti proiettori a singolo qubit, il che può portare a un numero esponenziale di termini.
Risultati
La stessa osservabile, ma espressa solo in termini di Pauli.
Esempi
Riscrivere un'osservabile in termini di proiettori in operatori di Pauli:
>>> obs = SparseObservable("+")
>>> obs.as_paulis()
<SparseObservable with 2 terms on 1 qubit: (0.5+0j)() + (0.5+0j)(X_0)>
>>> direct = SparseObservable.from_list([("I", 0.5), ("Z", 0.5)])
>>> assert direct.simplify() == obs.as_paulis().simplify()Per piccoli operatori, questo può essere usato con simplify() come forma canonica unica:
>>> left = SparseObservable.from_list([("+", 0.5), ("-", 0.5)])
>>> right = SparseObservable.from_list([("r", 0.5), ("l", 0.5)])
>>> assert left.as_paulis().simplify() == right.as_paulis().simplify()SparsePauliOp.from_sparse_observable()
Un costruttore di SparsePauliOp che può convertire un oggetto SparseObservable nella SparsePauliOp rappresentazione densa di Pauli.
clear
clear()
Cancella tutti i termini da questo operatore, rendendolo di nuovo uguale all'operatore zero.
Ciò non modifica la capacità delle allocazioni interne, pertanto le successive operazioni di aggiunta o sottrazione potrebbero non richiedere una riallocazione.
Esempi
>>> obs = SparseObservable.from_list([("IX+-rl", 2.0), ("01YZII", -1j)])
>>> obs.clear()
>>> assert obs == SparseObservable.zero(obs.py_num_qubits())commutes
commutes(other, tol=1e-12)
Verifica se l'osservabile commuta con un altro.
Parametri
- other (SparseObservable) – L'altro osservabile con cui verificare la commutazione.
- tol (float) – Se i coefficienti nel prodotto di sé e dell'altro sono inferiori alla tolleranza (in grandezza), i termini vengono ignorati.
Risultati
True se i termini sono commutativi, fino alla tolleranza, False altrimenti.
Aumenti
TypeError – Se non other potesse essere costretto a SparseObservable.
compose
compose(other, /, qargs=None, *, front=False)
Comporre un altro SparseObservable su questo.
In termini di algebre di operatori, la composizione corrisponde alla moltiplicazione a sinistra: c = a.compose(b) corrisponde a $C = B A$. In altre parole, a.compose(b) restituisce un operatore che "esegue a e poi esegue b sul risultato". L'argomento front=True rende invece questa moltiplicazione giusta.
self e other devono avere la stessa dimensione, a meno che non venga dato qargs , nel qual caso other può essere più piccolo di self, purché il numero di qubit in other e la lunghezza di qargs corrispondano. qargs non può mai contenere duplicati o indici di qubit che non esistono in self.
Attenzione: questa funzione può causare un'esplosione esponenziale dell'utilizzo della memoria dell'osservabile, poiché l'alfabeto di SparseObservable non è chiuso sotto composizione; la composizione di due termini a singolo bit può essere una somma, che moltiplica il numero totale di termini. Questo utilizzo di memoria non è ´necessariamente´ inerente all'osservabile risultante, ma trovare una rifattorizzazione efficiente della somma è generalmente altrettanto difficile dal punto di vista computazionale. È meglio utilizzare la conoscenza del dominio delle osservabili per ridurre al minimo il numero di termini esistenti, piuttosto che cercare di semplificarli a posteriori.
Parametri
-
altro - l'osservabile utilizzato per la moltiplicazione a sinistra
self. -
qargs -
se dato, i qubit in
selfda associare ai qubit inother. Detto altrimenti: se questo è dato, è simile a un'implementazione più efficiente di:self.compose(other.apply_layout(qargs, self.num_qubits))poiché non viene creato un osservabile temporaneo per memorizzare la forma di layout applicato di
other. -
front - se
True, allora moltiplica a destra perotherinvece di moltiplicare a sinistra (defaultFalse). Il sitoqargsè ancora applicato aother. Questo è molto utile quandoqargsè impostato, ootherpotrebbe essere un oggetto che deve essere forzato aSparseObservable.
conjugate
conjugate()
Calcolare la coniugazione complessa di questa osservabile.
Questa operazione è definita in termini di convenzioni matriciali standard di Qiskit, in quanto la forma della matrice è considerata nella base computazionale $Z$. I termini dell'alfabeto legati a ´X´ e ´Z´ non sono influenzati dalla coniugazione complessa, ma i termini legati a ´Y´ modificano i loro termini dell'alfabeto. Precisamente:
- si coniuga con
- coniugati a
- coniugati a
Inoltre, tutti i coefficienti sono coniugati.
Esempi
>>> obs = SparseObservable([("III", 1j), ("Yrl", 0.5)])
>>> assert obs.conjugate() == SparseObservable([("III", -1j), ("Ylr", -0.5)])copy
copy()
Ottenere una copia di questo osservabile.
Esempi
>>> obs = SparseObservable.from_list([("IXZ+lr01", 2.5), ("ZXI-rl10", 0.5j)])
>>> assert obs == obs.copy()
>>> assert obs is not obs.copy()evolve
evolve(other, /, qargs=None)
Evolvi questa osservabile mediante un termine di Pauli.
Un'evoluzione dell' e osservabile mediante un' e di Pauli corrisponde a .
A differenza di un'implementazione letterale tramite due composizioni complete, questo metodo esegue la coniugazione direttamente a livello di singolo qubit utilizzando una tabella di consultazione fissa. In questo modo si evita di materializzare qualsiasi grandezza intermedia SparseObservable e si calcola l'osservabile risultante in un unico passaggio sui termini. self e other deve avere lo stesso numero di qubit, a meno che qargs non sia specificato, nel qual caso other può essere inferiore a self, a condizione che il numero di qubit in other e la lunghezza di qargs corrispondano. qargs specifica quali qubit di self vengono fatti evolvere da other.
Attualmente, questo metodo supporta l'evoluzione solo tramite un operatore a termine singolo, il che significa che other deve essere un operatore di Pauli rappresentato da Pauli.
Parametri
- altro – l'operatore di Pauli utilizzato per coniugare
self. - qargs – se specificato, i qubit in
selfda sottoporre all'evoluzione tramiteother. La lunghezza deve corrispondere al numero di qubit inother.
Risultati
Una nuova versione evoluta SparseObservable con coniugazioni applicate.
Aumenti
- TypeError – se non
otherè di tipoPauli. - ValueError – se
selfeotherhanno un numero diverso di qubit (e nonqargsè specificato). - ValueError – se
qargsla lunghezza non corrispondeotheral numero di qubit. - ValueError – se
qargscontiene duplicati o indici fuori intervallo. - ValueError – se
othercontiene più di un termine.
expand
expand(other, /)
Prodotto tensoriale di ordine inverso.
Questo è equivalente a other.tensor(self), tranne che per il fatto che other verrà prima trasformato in SparseObservable se non lo è già (richiamando il costruttore predefinito).
Parametri
altro - l'osservabile da mettere sul lato sinistro del prodotto tensoriale.
Esempi
Questo è equivalente a tensor() con l'ordine degli argomenti invertito:
>>> left = SparseObservable.from_label("XYZ")
>>> right = SparseObservable.from_label("+-IIrl")
>>> assert left.tensor(right) == right.expand(left)from_label
static from_label(label, /)
Costruire un osservabile a termine singolo da un'etichetta di stringa densa.
L'operatore risultante avrà un coefficiente pari a 1. L'etichetta deve essere una sequenza dell'alfabeto 'IXYZ+-rl01'. L'etichetta viene interpretata in modo analogo a una stringa di bit. In altre parole, la lettera più a destra è associata al qubit 0 e così via. Questa è la stessa etichetta per Pauli e SparsePauliOp.
Parametri
label (str) – l'etichetta densa.
Esempi
>>> SparseObservable.from_label("IIII+ZI")
<SparseObservable with 1 term on 7 qubits: (1+0j)(+_2 Z_1)>
>>> label = "IYXZI"
>>> pauli = Pauli(label)
>>> assert SparseObservable.from_label(label) == SparseObservable.from_pauli(pauli)Una generalizzazione di questo metodo che costruisce un operatore di somma a partire da più etichette e dai loro coefficienti corrispondenti.
from_list
static from_list(iter, /, *, num_qubits=None)
Costruire un osservabile da un elenco di etichette e coefficienti densi.
È analogo a SparsePauliOp.from_list()con la differenza che utilizza l'alfabeto esteso di SparseObservable. In questa forma densa, è necessario fornire esplicitamente tutte le identità in ogni etichetta.
L'etichetta deve essere una sequenza dell'alfabeto 'IXYZ+-rl01'. L'etichetta viene interpretata in modo analogo a una stringa di bit. In altre parole, la lettera più a destra è associata al qubit 0 e così via. Questa è la stessa etichetta per Pauli e SparsePauliOp.
Parametri
- iter (list[tuple[str, complex]]) – Coppie di etichette e coefficienti associati da sommare. Le etichette vengono interpretate come in
from_label(). - num_qubits (int | None) – Non è necessario specificarlo se si è sicuri che
iternon è una sequenza vuota, poiché può essere dedotta dalle lunghezze delle etichette. Seiterpuò essere vuoto, è necessario specificare questo argomento per disambiguare il numero di qubit dell'osservabile. Se viene dato eiternon è vuoto, il valore deve corrispondere alle lunghezze delle etichette.
Esempi
Costruire un osservabile da un elenco di etichette della stessa lunghezza:
>>> SparseObservable.from_list([
... ("III++", 1.0),
... ("II--I", 1.0j),
... ("I++II", -0.5),
... ("--III", -0.25j),
... ])
<SparseObservable with 4 terms on 5 qubits:
(1+0j)(+_1 +_0) + (0+1j)(-_2 -_1) + (-0.5+0j)(+_3 +_2) + (-0-0.25j)(-_4 -_3)>Utilizzare num_qubits per disambiguare gli input potenzialmente vuoti:
>>> SparseObservable.from_list([], num_qubits=10)
<SparseObservable with 0 terms on 10 qubits: 0.0>Questo metodo è equivalente alle chiamate a from_sparse_list() con il campo esplicito qubit-arguments impostato su numeri interi decrescenti:
>>> labels = ["XY+Z", "rl01", "-lXZ"]
>>> coeffs = [1.5j, 2.0, -0.5]
>>> from_list = SparseObservable.from_list(list(zip(labels, coeffs)))
>>> from_sparse_list = SparseObservable.from_sparse_list([
... (label, (3, 2, 1, 0), coeff)
... for label, coeff in zip(labels, coeffs)
... ])
>>> assert from_list == from_sparse_listUn costruttore simile, ma che accetta solo una singola etichetta e ha sempre il suo coefficiente impostato su 1.0.
Costruire l'osservabile a partire da una lista di etichette senza identità esplicite, ma con i qubit a cui si applica ogni termine a singolo qubit elencati esplicitamente.
from_pauli
static from_pauli(pauli, /)
Costruire un SparseObservable da una singola Pauli istanza.
L'osservabile di uscita avrà un unico termine, con un coefficiente unitario dipendente dalla fase.
Parametri
pauli (Pauli) – il singolo Pauli da convertire.
Esempi
>>> label = "IYXZI"
>>> pauli = Pauli(label)
>>> SparseObservable.from_pauli(pauli)
<SparseObservable with 1 term on 5 qubits: (1+0j)(Y_3 X_2 Z_1)>
>>> assert SparseObservable.from_label(label) == SparseObservable.from_pauli(pauli)from_raw_parts
static from_raw_parts(num_qubits, coeffs, bit_terms, indices, boundaries, check=True)
Costruire una classe SparseObservable da array Numpy grezzi che corrispondono alla rappresentazione dei dati richiesta, descritta nella documentazione a livello di classe.
I dati di ogni array vengono copiati in allocazioni fresche e crescenti dello spazio Rust.
Parametri
-
num_qubit - numero di qubit nell'osservabile.
-
coeff - coefficienti complessi di ciascun termine dell'osservabile. Dovrebbe essere una matrice Numpy con il tipo dtype
complex128. -
bit_terms - elenco appiattito dei termini single-qubit che comprende tutti i termini completi. Dovrebbe essere un array Numpy con dtype
uint8(che è compatibile conBitTerm). -
indici - elenco appiattito ordinato per termini dei qubit a cui corrisponde ogni termine a singolo qubit. Dovrebbe essere una matrice Numpy con il tipo dtype
uint32. -
confini - gli indici che suddividono
bit_termseindicesin termini. Dovrebbe essere un array Numpy con il tipo dtypeuintp. -
controllo -
se
True(l'impostazione predefinita), convalida che i dati soddisfino tutte le garanzie di coerenza. SeFalse, non viene effettuato alcun controllo.AvvertenzaSe
check=False, ibit_termsdevono assolutamente essere tutti valori validi diSparseObservable.BitTerm. In caso contrario, potrebbe verificarsi un comportamento non definito nello spazio di Rust, invalidando completamente l'esecuzione del programma.
Esempi
Costruire una somma di su ogni singolo qubit:
>>> num_qubits = 100
>>> terms = np.full((num_qubits,), SparseObservable.BitTerm.Z, dtype=np.uint8)
>>> indices = np.arange(num_qubits, dtype=np.uint32)
>>> coeffs = np.ones((num_qubits,), dtype=complex)
>>> boundaries = np.arange(num_qubits + 1, dtype=np.uintp)
>>> SparseObservable.from_raw_parts(num_qubits, coeffs, terms, indices, boundaries)
<SparseObservable with 100 terms on 100 qubits: (1+0j)(Z_0) + ... + (1+0j)(Z_99)>from_sparse_list
static from_sparse_list(iter, /, num_qubits)
Costruire un osservabile da un elenco di etichette, i qubit a cui si applica ogni elemento e il coefficiente dell'intero termine.
È analogo a SparsePauliOp.from_sparse_list()con la differenza che utilizza l'alfabeto esteso di SparseObservable.
I campi "label" e "indices" delle triple sono associati tra loro mediante lo zippaggio. Per esempio, ciò significa che una chiamata a from_list() può essere convertita nella forma utilizzata da questo metodo impostando il campo "indici" di ogni tripla a (num_qubits-1, ..., 1, 0).
Parametri
- iter (list[tuple[str, Sequence[int], complex]]) – triple di etichette, i qubit a cui si applica ogni termine a singolo qubit e il coefficiente dell'intero termine.
- num_qubits (int) – il numero di qubit nell'operatore.
Esempi
Costruire un operatore semplice:
>>> SparseObservable.from_sparse_list(
... [("ZX", (1, 4), 1.0), ("YY", (0, 3), 2j)],
... num_qubits=5,
... )
<SparseObservable with 2 terms on 5 qubits: (1+0j)(X_4 Z_1) + (0+2j)(Y_3 Y_0)>Costruire l'osservabile identità (anche se, in realtà, basta usare identity()):
>>> SparseObservable.from_sparse_list([("", (), 1.0)], num_qubits=100)
<SparseObservable with 1 term on 100 qubits: (1+0j)()>Questo metodo può replicare il comportamento di from_list()se il campo qubit-arguments della tripla è impostato su numeri interi decrescenti:
>>> labels = ["XY+Z", "rl01", "-lXZ"]
>>> coeffs = [1.5j, 2.0, -0.5]
>>> from_list = SparseObservable.from_list(list(zip(labels, coeffs)))
>>> from_sparse_list = SparseObservable.from_sparse_list([
... (label, (3, 2, 1, 0), coeff)
... for label, coeff in zip(labels, coeffs)
... ])
>>> assert from_list == from_sparse_listL'inverso di questo metodo.
from_sparse_pauli_op
static from_sparse_pauli_op(op, /)
Costruire un SparseObservable da un'istanza SparsePauliOp istanza.
Si tratta di una traduzione in gran parte diretta del modello SparsePauliOpin particolare, non c'è alcuna somma al volo di termini simili, né alcun tentativo di rifattorizzare le somme di termini di Pauli in operatori di proiezione equivalenti.
Parametri
op (SparsePauliOp) – l'operatore da convertire.
Esempi
>>> spo = SparsePauliOp.from_list([("III", 1.0), ("IIZ", 0.5), ("IZI", 0.5)])
>>> SparseObservable.from_sparse_pauli_op(spo)
<SparseObservable with 3 terms on 3 qubits: (1+0j)() + (0.5+0j)(Z_0) + (0.5+0j)(Z_1)>from_terms
static from_terms(obj, /, num_qubits=None)
Costruire un SparseObservable con i singoli termini.
Tutti i termini devono avere lo stesso numero di qubit. Se fornito, l'argomento num_qubits deve corrispondere ai termini.
Non viene effettuata alcuna semplificazione nell'ambito della creazione dell'osservabile.
Parametri
- obj (Iterable[Term]) – Iterabile di termini individuali da cui costruire l'osservabile.
- num_qubits (int | None) – Il numero di qubit su cui deve agire l'osservabile. Di solito viene dedotto dall'input, ma può essere dato esplicitamente per gestire il caso di un iterabile vuoto.
Risultati
L'osservabile corrispondente.
identity
static identity(num_qubits)
Ottiene l'operatore di identità sul numero di qubit dato.
Esempi
Ottenere l'operatore di identità per 100 qubit:
>>> SparseObservable.identity(100)
<SparseObservable with 1 term on 100 qubits: (1+0j)()>pauli_bases
pauli_bases()
Ottenere un PauliList che rappresenta la base di misura necessaria per ogni termine (in ordine) di questa osservabile.
Ad esempio, il proiettore 0l+ restituirà un Pauli ZXY. Il risultato Pauli è denso, nel senso che vengono memorizzate identità esplicite. Un'identità nell'uscita di Pauli non richiede una misura concreta.
Questo restituirà una voce nell'elenco Pauli per ogni termine della somma.
Risultati
l'elenco degli operatori di Pauli che rappresentano le basi di misura necessarie.
Tipo di restituzione
simplify
simplify(tol=1e-08)
Somma tutti i termini simili in questo operatore, eliminandoli se il coefficiente complesso risultante ha un valore assoluto entro la tolleranza di zero.
Come effetto collaterale, questo ordina l'operatore in ordine canonico.
Quando si utilizza questa operazione per confronti di uguaglianza, occorre tenere presente che l'arrotondamento dei valori in virgola mobile e la non associatività dell'addizione in virgola mobile possono far sì che coefficienti diversi da zero dei termini sommati risultino disuguali al confronto. Per confrontare due grandezze osservabili entro un certo margine di tolleranza, il metodo più sicuro consiste nel verificare se la differenza canonizzata delle due grandezze è pari a zero.
Parametri
tol (float) – dopo aver sommato i termini simili, i coefficienti il cui valore assoluto è inferiore alla tolleranza assoluta indicata saranno eliminati dall'uscita.
Esempi
Utilizzando simplify() per confrontare due operatori che rappresentano lo stesso osservabile, ma che si confronterebbero in modo diseguale a causa dei test strutturali predefiniti:
>>> base = SparseObservable.from_sparse_list([
... ("XZ", (2, 1), 1e-10), # value too small
... ("+-", (3, 1), 2j),
... ("+-", (3, 1), 2j), # can be combined with the above
... ("01", (3, 1), 0.5), # out of order compared to `expected`
... ], num_qubits=5)
>>> expected = SparseObservable.from_list([("I0I1I", 0.5), ("I+I-I", 4j)])
>>> assert base != expected # non-canonical comparison
>>> assert base.simplify() == expected.simplify()Si noti che nell'esempio precedente i coefficienti sono scelti in modo tale che tutti i calcoli in virgola mobile siano esatti e non vi siano problemi di arrotondamento o associatività intermedia. Se non è possibile garantire che ciò avvenga, la forma più sicura è la seguente:
>>> left = SparseObservable.from_list([("XYZ", 1.0/3.0)] * 3) # sums to 1.0
>>> right = SparseObservable.from_list([("XYZ", 1.0/7.0)] * 7) # doesn't sum to 1.0
>>> assert left.simplify() != right.simplify()
>>> assert (left - right).simplify() == SparseObservable.zero(left.num_qubits)tensor
tensor(other, /)
Prodotto tensoriale di due osservabili.
L'ordinamento dei bit è definito in modo tale che gli indici dei qubit dell'argomento rimangano gli stessi, mentre gli indici di self vengano spostati del numero di qubit presenti in other. Si tratta della stessa convenzione utilizzata dal resto degli operatori di Qiskit quantum_info .
Questa funzione viene utilizzata per l'operatore infix ^ . Se si usa questo operatore, bisogna tenere presente che le regole di precedenza degli operatori di Python ' possono far sì che l'ordine di valutazione sia diverso da quello previsto. In particolare, l'operatore + si lega più strettamente di ^, così come * si lega più strettamente di +. Usare le parentesi per fissare l'ordine di valutazione, se necessario.
L'argomento verrà lanciato in SparseObservable utilizzando il suo costruttore predefinito, se non è già nella forma corretta.
Parametri
altro - l'osservabile da mettere sul lato destro del prodotto tensoriale.
Esempi
L'ordinamento dei bit è tale che il prodotto tensoriale di due osservabili ricavati da una singola etichetta "assomiglia" a un osservabile ricavato dalla concatenazione delle due stringhe:
>>> left = SparseObservable.from_label("XYZ")
>>> right = SparseObservable.from_label("+-IIrl")
>>> assert left.tensor(right) == SparseObservable.from_label("XYZ+-IIrl")Si può anche usare l'operatore infix ^ per i prodotti tensoriali, che in modo analogo casterà il lato destro dell'operazione, se non è già un elemento SparseObservable:
>>> assert SparseObservable("rl") ^ Pauli("XYZ") == SparseObservable("rlXYZ")La stessa funzione, ma con l'ordine degli argomenti invertito. Questo può essere utile se si desidera utilizzare il comportamento di fusione per l'argomento, ma si vuole che il parametro esistente
SparseObservablesia sul lato destro dell'ordinamento del tensore.
to_sparse_list
to_sparse_list()
Esprimere l'osservabile in termini di formato di elenco sparso.
Questa può essere vista come una contro-operazione di SparseObservable.from_sparse_list()ma non è garantito che l'ordine dei termini sia lo stesso dopo un viaggio di andata e ritorno verso un elenco rado.
Esempi
>>> obs = SparseObservable.from_list([("IIXIZ", 2j), ("IIZIX", 2j)])
>>> reconstructed = SparseObservable.from_sparse_list(obs.to_sparse_list(), obs.num_qubits)Il costruttore che può interpretare questi elenchi.
transpose
transpose()
Calcolare la trasposizione matriciale di questa osservabile.
Questa operazione è definita in termini di convenzioni matriciali standard di Qiskit, in quanto la forma della matrice è considerata nella base computazionale $Z$. I termini dell'alfabeto legati a ´X´ e ´Z´ non sono influenzati dalla trasposizione, ma i termini legati a ´Y´ modificano i loro termini dell'alfabeto. Precisamente:
- si traspone in
- si traspone in
- si traspone in
Esempi
>>> obs = SparseObservable([("III", 1j), ("Yrl", 0.5)])
>>> assert obs.transpose() == SparseObservable([("III", 1j), ("Ylr", -0.5)])zero
static zero(num_qubits)
Ottiene l'operatore zero sul numero di qubit indicato.
L'operatore zero è l'operatore il cui valore di aspettativa è zero per tutti gli stati quantistici. Non ha termini. È l'elemento di identità per l'addizione di due SparseObservable qualsiasi cosa venga aggiunta all'operatore zero è uguale a se stesso.
Se si desidera che il proiettore si trovi nello stato di tutti gli zeri, utilizzare:
>>> num_qubits = 10
>>> all_zeros = SparseObservable.from_label("0" * num_qubits)Esempi
Ottenere l'operatore zero per 100 qubit:
>>> SparseObservable.zero(100)
<SparseObservable with 0 terms on 100 qubits: 0.0>