SparseObservable
class qiskit.quantum_info.SparseObservable(data, /, num_qubits=None)
Bases: object
Un observable sobre bases Pauli que almacena sus datos en un formato de qubit disperso.
mathematics
Este observable representa una suma sobre cadenas de operadores de Pauli y proyectores de estados propios de Pauli, con cada término ponderado por algún número complejo. Es decir, el observable completo es
para números complejos y operadores single-qubit que actúan sobre qubit a partir de un alfabeto restringido . La suma sobre es la suma de los términos individuales, y el producto tensorial produce las cadenas de operadores.
El alfabeto de operadores de un solo qubit permitido del que se extraen los son los operadores de Pauli y los operadores de proyección de Pauli-eigen-estado. Explícitamente, estos son:
Etiqueta | Operador | Valor numérico | BitTerm atributo |
|---|---|---|---|
"I" | (identidad) | No guardado. | No guardado. |
"X" | (Pauli X) | 0b0010 (2) | X |
"Y" | (Pauli Y) | 0b0011 (3) | Y |
"Z" | (Pauli Z) | 0b0001 (1) | Z |
"+" | (proyector a estado propio positivo de X) | 0b1010 (10) | PLUS |
"-" | (proyector a estado propio negativo de X) | 0b0110 (6) | MINUS |
"r" | (proyector a estado propio positivo de Y) | 0b1011 (11) | RIGHT |
"l" | (proyector a estado propio negativo de Y) | 0b0111 (7) | LEFT |
"0" | (proyector a estado propio positivo de Z) | 0b1001 (9) | ZERO |
"1" | (proyector a estado propio negativo de Z) | 0b0101 (5) | ONE |
El alfabeto permitido forma una base sobrecompleta del espacio de operadores. Esto significa que no existe una suma única para representar un observable dado. En comparación, SparsePauliOp utiliza una base precisa del espacio de operadores, por lo que (tras combinar términos de la misma cadena de Pauli, eliminar ceros y ordenar los términos según algún orden canónico ) sólo hay una representación de cualquier operador.
SparseObservable utiliza su particular base sobrecompleta con el objetivo de hacer equivalente la "eficacia de la medición" a la "eficacia de la representación". Por ejemplo, el observable puede medirse eficientemente en el hardware con simples mediciones de , pero sólo puede ser representado por SparsePauliOp como , lo que requiere términos almacenados en . SparseObservable requiere un único término para almacenarlo.
El inconveniente de esto es que resulta poco práctico tomar una matriz arbitraria o SparsePauliOp y encontrar la mejor SparseObservable representación. Por lo general, querrá construir un SparseObservable directamente, en lugar de intentar descomponerlo en uno.
Representación
La representación interna de a SparseObservable almacena sólo los operadores qubit no identitarios. Esto hace que sea significativamente más eficiente representar observables como ; SparseObservable requiere una cantidad de memoria lineal en el número total de qubits, mientras que SparsePauliOp se escala cuadráticamente.
Los términos se almacenan comprimidos, de forma similar al formato de filas dispersas comprimido de las matrices dispersas. En esta analogía, los términos de la suma son las "filas", y los términos del qubit son las "columnas", donde una entrada ausente representa la identidad en lugar de un cero. Más explícitamente, la representación se compone de cuatro matrices contiguas:
Atributo | Longitud | Descripción |
|---|---|---|
coeffs | El multiplicador escalar complejo para cada término. | |
bit_terms | Cada uno de los términos single-qubit no idénticos para todos los operadores, en orden. Corresponden a la no-identidad en la descripción de la suma, donde las entradas se almacenan en orden creciente primero, y en orden creciente dentro de cada término. | |
indices | El qubit correspondiente ( ) para cada uno de los operadores en bit_terms. SparseObservable requiere que esta lista esté ordenada por términos, y los algoritmos pueden confiar en que esta invariante se mantenga. | |
boundaries | Los índices que dividen bit_terms y indices en términos completos. Para el término número , su coeficiente complejo es coeffs[i], y sus operadores single-qubit no-identidad y sus qubits correspondientes son el corte boundaries[i] : boundaries[i+1] en bit_terms y indices respectivamente. boundaries siempre tiene un 0 explícito como primer elemento. |
El parámetro de longitud es el número de términos en la suma, y el parámetro es el número total de términos single-qubit no idénticos.
Como ejemplos ilustrativos:
- en el caso de un operador cero,
boundarieses de longitud 1 (un único 0) y todos los demás vectores están vacíos. - en el caso de un operador de identidad totalmente simplificado,
boundarieses[0, 0],coeffstiene una única entrada, ybit_termsyindicesestán vacíos. - para el operador ,
boundarieses[0, 2, 4],coeffses[1.0, -1.0],bit_termses[BitTerm.Z, BitTerm.Z, BitTerm.Y, BitTerm.X]yindiceses[0, 2, 1, 3]. El operador puede actuar sobre más de cuatro qubits, dependiendo delnum_qubitsparámetro. Losbit_termsson valores enteros, a cuyos números mágicos se puede acceder mediante la clase de atributoBitTermclase de atributo. Obsérvese que los términos e índices de un bit se ordenan por términos. Este es un requisito de la clase.
Estos casos no son especiales, son totalmente coherentes con las normas y no deberían necesitar un tratamiento especial.
El elemento escalar de la matriz bit_terms se almacena como un byte numérico. Los valores numéricos están relacionados con la representación Pauli simpléctica que SparsePauliOp utiliza, y son accesibles con acceso nominativo mediante una enumeración:
BitTerm
class BitTerm
Un IntEnum que proporciona acceso con nombre a los valores numéricos utilizados para representar cada uno de los términos del alfabeto de un solo qubit enumerados en Alfabeto de términos de un solo qubit utilizados en SparseObservable.
Esta clase se adjunta a SparseObservable. Acceder como SparseObservable.BitTerm. Si esto es teclear demasiado, y sólo está tratando con objetos :class:¬SparseObservable` y el nombre BitTerm no es ambiguo, puede acortarlo como:
>>> ops = SparseObservable.BitTerm
>>> assert ops.X is SparseObservable.BitTerm.XPuede acceder a todos los valores de la enumeración por su nombre completo en mayúsculas o por su etiqueta de una sola letra. Las etiquetas de una sola letra no suelen ser identificadores válidos de Python, por lo que debe utilizar la notación de indexación para acceder a ellas:
>>> assert SparseObservable.BitTerm.ZERO is SparseObservable.BitTerm["0"]La estructura numérica de éstos es que son todos valores de cuatro bits de los cuales los dos bits inferiores son la representación simpléctica (sin fase) del operador de Pauli relacionado con el objeto, donde el bit inferior denota una contribución por y el segundo inferior una contribución por , mientras que los dos bits superiores son 00 para un operador de Pauli, 01 para el proyector de estado propio negativo, y 10 para el proyector de estado propio positivo.
Valores
X
Valor predeterminado: 2
El operador Pauli . Utiliza la etiqueta de una sola letra "X".
PLUS
Valor predeterminado: 10
El proyector al estado propio positivo del operador : . Utiliza la etiqueta de una sola letra "+".
MINUS
Valor predeterminado: 6
El proyector al estado propio negativo del operador : . Utiliza la etiqueta de una sola letra "-".
Y
Valor predeterminado: 3
El operador Pauli . Utiliza la etiqueta de una sola letra "Y".
DERECHA
Valor predeterminado: 11
El proyector al estado propio positivo del operador : . Utiliza la etiqueta de una sola letra "r".
IZQUIERDA
Valor predeterminado: 7
El proyector al estado propio negativo del operador : . Utiliza la etiqueta de una sola letra "l".
Z
Valor predeterminado: 1
El operador Pauli . Utiliza la etiqueta de una sola letra "Z".
CERO
Valor predeterminado: 9
El proyector al estado propio positivo del operador : . Utiliza la etiqueta de una sola letra "0".
UNO
Valor predeterminado: 5
El proyector al estado propio negativo del operador : . Utiliza la etiqueta de una sola letra "1".
Atributos
label
La etiqueta de cadena de un solo carácter utilizada para representar este término en el SparseObservable alfabeto.
Cada uno de los atributos tipo array se comporta como una secuencia Python. Puede indexarlos y cortarlos con la semántica list-estándar. El corte de un atributo devuelve un Numpy ndarray que contiene una copia de los datos relevantes con la dirección natural dtype del campo; esto le permite realizar fácilmente operaciones matemáticas con los resultados, como operaciones bit a bit con bit_terms. Puede asignar a índices o rebanadas de cada uno de los atributos, pero tenga en cuenta que al hacerlo debe respetar las normas de coherencia de datos. Por ejemplo:
>>> 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 reducción anterior a las bases de Pauli también se puede conseguir con pauli_bases().
Orden canónico
Para cualquier observable matemático dado, hay varias formas de representarlo con SparseObservable. Por ejemplo, el mismo conjunto de términos de un solo bit y sus índices correspondientes pueden aparecer varias veces en el observable. Matemáticamente, esto equivale a tener un único término con todos los coeficientes sumados. Del mismo modo, los términos de la suma en a SparseObservable pueden estar en cualquier orden mientras representan el mismo observable, ya que la suma es conmutativa (aunque la suma en coma flotante no es asociativa, SparseObservable no garantiza el orden de la suma).
Estas dos categorías de degeneración de la representación pueden hacer que el == operador afirme que dos observables no son iguales, a pesar de que representan el mismo objeto. En estos casos, puede resultar conveniente definir una forma canónica que permita comparar los observables desde el punto de vista estructural.
Puede poner un SparseObservable en forma canónica utilizando el método simplify() método. El orden preciso de los términos en el orden canónico no está especificado, y puede cambiar entre versiones de Qiskit. Sin embargo, dentro de la misma versión de Qiskit, puedes comparar dos observables estructuralmente comparando sus formas simplificadas.
Si desea tener en cuenta la tolerancia de coma flotante en la comparación, lo más seguro es utilizar una receta como:
def equivalent(left, right, tol):
return (left - right).simplify(tol) == SparseObservable.zero(left.num_qubits)La forma canónica producida por simplify() por sí sola no detectará universalmente todos los observables que son equivalentes debido al alfabeto de bases sobrecompletas. Para obtener una expresión única, primero puede representar el observable utilizando sólo términos de Pauli llamando a as_paulis()seguido de simplify(). Tenga en cuenta que la expansión del proyector (p. ej. + en I y X) no es computacionalmente factible a escala.
Indexación
SparseObservable se comporta como una secuencia Python (la forma estándar, no la expandida collections.abc.Sequence). El observable puede indexarse por enteros e iterarse para obtener términos individuales.
Cada término aparece como una instancia de una clase autónoma. Los términos individuales se copian del observable base; las mutaciones en ellos no afectarán al observable.
Plazo
class Term(num_qubits, coeff, bit_terms, indices)
Bases: object
Un solo término de un SparseObservable.
Normalmente se crean indexando o iterando a través de un archivo SparseObservable.
bit_labels
bit_labels()
Devuelve las etiquetas de bits del término como cadena.
Las etiquetas de los bits coincidirán con el orden de SparseTerm.indices, de forma que el carácter i-ésimo de la cadena se aplique al índice del qubit en term.indices[i].
Devuelve
Los términos de bits de no identidad como cadena concatenada.
bit_terms
Vista de sólo lectura de los términos single-qubit individuales.
Los únicos valores válidos en la matriz son los que tienen un valor BitTerm.
coeff
El coeficiente del término.
copy
copy()
Obtenga una copia de este término.
indices
Vista de sólo lectura de los índices de cada término single-qubit no identitario.
Los índices estarán siempre ordenados.
num_qubits
El número de qubits en los que se define el término.
pauli_base
pauli_base()
Obtener un Pauli objeto que representa la base de medida necesaria para este término.
Por ejemplo, el proyector 0l+ devolverá un Pauli ZYX. El resultado Pauli es denso, en el sentido de que se almacenan identidades explícitas. Una identidad en la salida de Pauli no requiere una medida concreta.
Devuelve: Pauli: el operador de Pauli que representa lo necesario
base de medición.
SparseObservable.pauli_bases()
Un método similar para todo un observable a la vez.
to_observable
to_observable()
Convertir este término en un SparseObservable.
Construcción
SparseObservable define varios constructores. El constructor por defecto intentará delegar en uno de los constructores más específicos, basándose en el tipo de la entrada. Siempre puedes utilizar los constructores específicos para tener más control sobre la construcción.
Método | Resumen |
|---|---|
from_label() | Convertir una etiqueta de cadena densa en un único término SparseObservable. |
from_list() | Suma una lista de tuplas de etiquetas de cadenas densas y los coeficientes asociados en un observable. |
from_sparse_list() | Suma una lista de tuplas de etiquetas de cadenas dispersas, los qubits a los que se aplican y sus coeficientes en un observable. |
from_pauli() | Convierte un Pauli en un único término SparseObservable. |
from_sparse_pauli_op() | Elevar un SparsePauliOp a SparseObservable. |
from_terms() | Suma explícita de Term instancias. |
from_raw_parts() | Construir el observable a partir de las matrices de datos brutos. |
__new__
__new__(data, /, num_qubits=None)
El constructor por defecto de SparseObservable.
Esto delega en uno de los métodos constructores de conversión explícitos, basándose en el tipo del argumento data . Si se suministra num_qubits y el constructor implicado por el tipo de data no acepta un número, el entero dado debe coincidir con la entrada.
Parámetros
- data - Tipo de datos de la entrada. Puede ser otro
SparseObservableen cuyo caso se copia la entrada, unPaulioSparsePauliOpen cuyo casofrom_pauli()ofrom_sparse_pauli_op()según corresponda, o puede ser una lista en un formato válido parafrom_list()ofrom_sparse_list(). - num_qubits (int|None) – Número opcional de qubits para el operador. Para la mayoría de las entradas de datos, esto se puede deducir y no es necesario pasarlo. Sólo es necesario para las listas vacías o el formato sparse-list. Si se da innecesariamente, debe coincidir con los datos introducidos.
Además de los constructores basados en conversiones, también existen métodos de ayuda que construyen formas especiales de observables.
Método | Resumen |
|---|---|
zero() | El operador cero en un número determinado de qubits. |
identity() | El operador de identidad en un número determinado de qubits. |
Conversiones
Una SparseObservable puede convertirse en otros quantum_info operadores o formatos genéricos. Tenga en cuenta que otros objetos pueden no ser capaces de representar el mismo observable tan eficientemente como SparseObservableincluyendo la posibilidad de necesitar exponencialmente más memoria.
Método | Resumen |
|---|---|
as_paulis() | Crear un nuevo SparseObservableexpandiéndose sólo en términos de operadores de Pauli. |
to_sparse_list() | Exprese el observable en formato de lista dispersa con elementos (bit_terms, indices, coeff). |
Además, SparsePauliOp.from_sparse_observable() está disponible para la conversión de esta clase a SparsePauliOp. Tenga en cuenta que este método sufre los mismos problemas de uso exponencial de memoria que el método as_paulis().
Manipulación matemática
SparseObservable admite el conjunto estándar de operadores matemáticos Python, al igual que otros quantum_info operadores.
En aritmética básica, puedes:
- añadir dos observables utilizando
+ - restar dos observables mediante
- - multiplicar o dividir por un
int,floatocomplexutilizando*y/ - negar todos los coeficientes de un observable unario
-
Cada uno de los operadores aritméticos binarios básicos tiene su correspondiente método in-place especializado, que muta el lado izquierdo in-place. Su uso suele ser más eficiente que el de los operadores infijos, especialmente para construir un observable en un bucle.
El producto tensorial se calcula con tensor() (para el orden estándar, de yuxtaposición de etiquetas de Pauli) o expand() (para el orden inverso). El operador ^ se sobrecarga para que sea equivalente a tensor().
Al utilizar los operadores binarios ^ (tensor()) y & (compose()), tenga en cuenta que las reglas de precedencia de operadores de Python pueden hacer que el orden de evaluación sea diferente al esperado. En concreto, el operador + se une más estrechamente que ^ o &, al igual que * se une más estrechamente que +.
Cuando se utilizan los operadores en expresiones mixtas, lo más seguro es utilizar paréntesis para agrupar los operandos de los productos tensoriales.
A SparseObservable tiene un adjoint(). Las nociones de conjugación compleja escalar (conjugate()) y transposición de valor real (transpose()) se definen de forma análoga a la representación matricial de otros operadores de Pauli en Qiskit.
Notas sobre eficiencia
Internamente, SparseObservable es mutable in-place, incluyendo el uso de vectores crecibles sobre-asignados para extender el número de términos. Esto significa que el coste de añadir a un observable utilizando += se amortiza linealmente en el número total de términos añadidos, en lugar de la complejidad cuadrática que requeriría el binario + .
Las sumas y restas se ejecutan mediante una operación de apilamiento de términos; no hay "simplificación" automática (suma de términos similares), porque la mayoría de las sumas para construir un observable generan sólo un pequeño número de duplicaciones, y la detección de términos similares tiene costes adicionales. Si esto no se ajusta a sus casos de uso, puede llamar periódicamente a simplify()o discutir otras APIs con nosotros para una mejor construcción de observables.
Atributos
bit_terms
Una lista plana de términos single-qubit. Esto es más naturalmente una lista de listas, pero se almacena plana por razones de uso de memoria y localización, con las sublistas denotadas por límites.
boundaries
Índices que particionan bit_terms y indices en sublistas para cada término individual de la suma. boundaries[0] : boundaries[1] es el rango de índices en bit_terms y indices que corresponden al primer término de la suma. Todos los índices de qubit no especificados son implícitamente la identidad. Es un elemento más largo que coeffsya que boundaries[0] es siempre un cero explícito (para facilitar el algoritmo).
coeffs
Los coeficientes de cada término abstracto en en la suma. Tiene tantos elementos como términos tiene la suma.
indices
Una lista plana de los índices de qubits sobre los que actúan las entradas correspondientes en bit_terms actúan. Esta lista debe estar siempre ordenada por términos, donde un término es una sublista denotada por boundaries.
Si escribe en este atributo desde el espacio Python, debe asegurarse de que sólo escribe en índices ordenados por términos.
num_qubits
Número de qubits sobre los que actúa el operador.
Esto no puede deducirse de ninguna otra forma o valor, ya que las identidades no se almacenan explícitamente.
num_terms
El número de términos en la suma que este operador está rastreando.
Métodos
adjoint
adjoint()
Calcular el adjunto de este observable.
Esto está bien definido en el sentido matemático abstracto. Todos los términos del alfabeto single-qubit son autoadjuntos, por lo que el resultado de esta operación es el mismo observable, excepto que sus coeficientes son todos sus conjugados complejos.
Ejemplos
>>> left = SparseObservable.from_list([("XY+-", 1j)])
>>> right = SparseObservable.from_list([("XY+-", -1j)])
>>> assert left.adjoint() == rightapply_layout
apply_layout(layout, num_qubits=None)
Aplique un diseño de transpilador a esto SparseObservable.
Normalmente habrás definido tu observable en términos de los qubits virtuales de los circuitos que utilizarás para preparar estados. Tras la transpilación, los qubits virtuales se asignan a qubits físicos concretos en un dispositivo, que puede ser más amplio que tu circuito. Esa asignación también puede cambiar a lo largo del circuito. Este método transforma el observable de entrada en qubits virtuales en un observable adecuado para aplicar inmediatamente después del circuito físico totalmente transpilado.
Parámetros
- layout (TranspileLayout |list[int] | None) – La disposición a aplicar. La mayoría de los usos de esta función deben pasar el
QuantumCircuit.layoutcampo de un circuito transpilado para hardware. Además, puede pasar una lista de nuevos índices de qubits. Si se da explícitamenteNone, no se aplica ninguna reasignación (pero puede seguir utilizandonum_qubitspara ampliar el observable). - num_qubits (int | None) – El número de qubits al que expandir el observable. Si no se proporciona, la salida será tan ancha como la entrada
TranspileLayouto la misma anchura que la entrada silayoutse proporciona de otra forma.
Devuelve
Un nuevo SparseObservable con el diseño proporcionado aplicado.
as_paulis
as_paulis()
Expresar el observable sólo en términos de Pauli, escribiendo cada proyector como suma de términos de Pauli.
Nótese que no se garantiza el orden de los términos de Pauli resultantes. Utilice SparseObservable.simplify() además para obtener una representación canónica.
Tenga en cuenta que esto utilizará al menos términos si hay proyectores single-qubit presentes, lo que puede conducir a un número exponencial de términos.
Devuelve
El mismo observable, pero expresado sólo en términos de Pauli.
Ejemplos
Reescribir un observable en términos de proyectores en operadores de 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()Para operadores pequeños, esto se puede utilizar con simplify() como forma canónica única:
>>> 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 constructor de SparsePauliOp que puede convertir un SparseObservable en la SparsePauliOp representación densa de Pauli.
clear
clear()
Borra todos los términos de este operador, haciéndolo de nuevo igual al operador cero.
Esto no modifica la capacidad de las asignaciones internas, por lo que es posible que las operaciones posteriores de suma o resta no requieran una reasignación.
Ejemplos
>>> 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)
Comprueba si el observable conmuta con otro.
Parámetros
- other (SparseObservable) – El otro observable con el que comprobar la conmutación.
- tol (float) – Si los coeficientes en el producto de uno mismo y otro están por debajo de la tolerancia (en magnitud), los términos se ignoran.
Devuelve
True si los términos conmutan, hasta la tolerancia, False de lo contrario.
Aumentos
TypeError – Si no other pudiera ser coaccionado para SparseObservable.
compose
compose(other, /, qargs=None, *, front=False)
Componga otro SparseObservable sobre éste.
En términos de álgebras de operadores, la composición corresponde a la multiplicación por la izquierda: c = a.compose(b) corresponde a $C = B A$. En otras palabras, a.compose(b) devuelve un operador que "realiza a, y luego realiza b sobre el resultado". En cambio, el argumento front=True lo convierte en una multiplicación por la derecha.
self y other deben tener el mismo tamaño, a menos que se indique qargs , en cuyo caso other puede ser menor que self, siempre que el número de qubits de other y la longitud de qargs coincidan. qargs nunca puede contener duplicados o índices de qubits que no existan en self.
Tenga en cuenta que esta función puede provocar una explosión exponencial del uso de memoria del observable, ya que el alfabeto de SparseObservable no es cerrado bajo composición; la composición de dos términos de un solo bit puede ser una suma, que multiplica el número total de términos. Este uso de memoria no es necesariamente inherente al observable resultante, pero encontrar una refactorización eficiente de la suma suele ser igualmente difícil desde el punto de vista computacional. Es mejor utilizar el conocimiento del dominio de tus observables para minimizar el número de términos que existen, en lugar de intentar simplificarlos a posteriori.
Parámetros
-
other - el observable utilizado para multiplicar por la izquierda
self. -
qargs -
si se indica, los qubits de
selfque deben asociarse a los qubits deother. Dicho de otro modo: si esto se da, es similar a una implementación más eficiente de:self.compose(other.apply_layout(qargs, self.num_qubits))ya que no se crea ningún observable temporal para almacenar la forma de disposición aplicada de
other. -
front - si
True, entonces se multiplica a la derecha porotheren lugar de a la izquierda (por defectoFalse). Losqargsse siguen aplicando aother. Esto es muy útil cuandoqargsestá establecido, ootherpuede ser un objeto que debe ser coaccionado aSparseObservable.
conjugate
conjugate()
Calcular la conjugación compleja de este observable.
Esta operación se define en términos de las convenciones estándar de la matriz de Qiskit, en que la forma de la matriz se toma para estar en la base computacional $Z$. Los términos del alfabeto relacionados con $X$- y $Z$ no se ven afectados por la conjugación compleja, pero los términos relacionados con $Y$ modifican sus términos del alfabeto. Precisamente:
- se conjuga con
- conjugados con
- conjugados con
Además, todos los coeficientes están conjugados.
Ejemplos
>>> obs = SparseObservable([("III", 1j), ("Yrl", 0.5)])
>>> assert obs.conjugate() == SparseObservable([("III", -1j), ("Ylr", -0.5)])copy
copy()
Obtener una copia de este observable.
Ejemplos
>>> 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)
Desarrolla este observable mediante un término de Pauli.
Una evolución de la observable mediante un de Pauli corresponde a .
A diferencia de una implementación literal mediante dos composiciones completas, este método realiza la conjugación directamente a nivel de un solo qubit utilizando una tabla de consulta fija. De este modo se evita materializar cualquier término intermedio SparseObservable y se calcula la observable evolucionada en una sola pasada por los términos. self y other debe tener el mismo número de qubits, a menos que qargs se especifique, en cuyo caso other puede ser menor que self, siempre que el número de qubits en other y la longitud de qargs coincidan. qargs especifica qué qubits de self son evolucionados por other.
Actualmente, este método solo admite la evolución mediante un operador de un solo término, lo que significa que other debe ser un operador de Pauli representado por Pauli.
Parámetros
- otro : el operador de Pauli, que se utiliza para conjugar
self. - qargs : si se proporciona, los qubits de
selfque se van a evolucionar medianteother. La longitud debe coincidir con el número de qubits enother.
Devuelve
Una nueva versión mejorada SparseObservable con conjugaciones aplicadas.
Aumentos
- TypeError – si no
otheres de tipoPauli. - ValueError – si
selfyothertienen un número diferente de qubits (y noqargsse especifica). - ValueError – si
qargsla longitud no coincide conotherel número de qubits. - ValueError – si
qargscontiene duplicados o índices fuera de rango. - ValueError – si
othercontiene más de un término.
expand
expand(other, /)
Producto tensorial de orden inverso.
Esto es equivalente a other.tensor(self), excepto que other será primero convertido a SparseObservable si no lo es ya (llamando al constructor por defecto).
Parámetros
otro - el observable a poner en el lado izquierdo del producto tensorial.
Ejemplos
Esto equivale a tensor() con el orden de los argumentos invertido:
>>> left = SparseObservable.from_label("XYZ")
>>> right = SparseObservable.from_label("+-IIrl")
>>> assert left.tensor(right) == right.expand(left)from_label
static from_label(label, /)
Construir un observable de un solo término a partir de una etiqueta de cadena densa.
El operador resultante tendrá un coeficiente de 1. La etiqueta debe ser una secuencia del alfabeto 'IXYZ+-rl01'. La etiqueta se interpreta de forma análoga a una cadena de bits. En otras palabras, la letra más a la derecha está asociada al qubit 0, y así sucesivamente. Esto es lo mismo que las etiquetas para Pauli y SparsePauliOp.
Parámetros
label (str) – la etiqueta densa.
Ejemplos
>>> 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 generalización de este método que construye un operador de suma a partir de múltiples etiquetas y sus coeficientes correspondientes.
from_list
static from_list(iter, /, *, num_qubits=None)
Construir un observable a partir de una lista de etiquetas densas y coeficientes.
Es análogo a SparsePauliOp.from_list()salvo que utiliza el alfabeto ampliado de SparseObservable. En este formulario denso, debe suministrar todas las identidades explícitamente en cada etiqueta.
La etiqueta debe ser una secuencia del alfabeto 'IXYZ+-rl01'. La etiqueta se interpreta de forma análoga a una cadena de bits. En otras palabras, la letra más a la derecha está asociada al qubit 0, y así sucesivamente. Esto es lo mismo que las etiquetas para Pauli y SparsePauliOp.
Parámetros
- iter (list[tuple[str, complex]]) – Pares de etiquetas y sus coeficientes asociados para sumar. Las etiquetas se interpretan del mismo modo que en
from_label(). - num_qubits (int | None) – No es necesario especificarlo si está seguro de que
iterno es una secuencia vacía, ya que puede deducirse de las longitudes de las etiquetas. Siiterpuede estar vacío, debe especificar este argumento para desambiguar para cuántos qubits es el observable. Si se indica yiterno está vacío, el valor debe coincidir con la longitud de las etiquetas.
Ejemplos
Construir un observable a partir de una lista de etiquetas de la misma longitud:
>>> 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)>Utilice num_qubits para desambiguar entradas potencialmente vacías:
>>> SparseObservable.from_list([], num_qubits=10)
<SparseObservable with 0 terms on 10 qubits: 0.0>Este método es equivalente a las llamadas a from_sparse_list() con el campo explícito qubit-arguments establecido en enteros decrecientes:
>>> 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 constructor similar, pero sólo toma una etiqueta y siempre tiene su coeficiente fijado en 1.0.
Construir el observable a partir de una lista de etiquetas sin identidades explícitas, pero con los qubits a los que se aplica cada término single-qubit enumerados explícitamente.
from_pauli
static from_pauli(pauli, /)
Construir un SparseObservable a partir de una única Pauli instancia.
El observable de salida tendrá un único término, con un coeficiente unitario dependiente de la fase.
Parámetros
pauli (Pauli) – el único Pauli para convertir.
Ejemplos
>>> 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)
Construye un SparseObservable a partir de matrices Numpy sin procesar que coincidan con la representación de datos requerida descrita en la documentación de nivel de clase.
Los datos de cada matriz se copian en asignaciones de espacio Rust nuevas y ampliables.
Parámetros
-
num_qubits - número de qubits en el observable.
-
coeffs - coeficientes complejos de cada término del observable. Debería ser un array Numpy con dtype
complex128. -
bit_terms - lista aplanada de los términos single-qubit que comprende todos los términos completos. Debe ser un array Numpy con dtype
uint8(que es compatible conBitTerm). -
índices - lista ordenada por términos de los qubits a los que corresponde cada término de un qubit. Debería ser un array Numpy con dtype
uint32. -
límites : los índices que dividen
bit_termsyindicesen términos. Debería ser un array Numpy con dtypeuintp. -
comprobar -
si
True(por defecto), valida que los datos satisfacen todas las garantías de coherencia. SiFalse, no se realiza ninguna comprobación.AvisoSi
check=False, losbit_termsdeben ser absolutamente todos valores válidos deSparseObservable.BitTerm. Si no lo son, puede producirse un comportamiento indefinido en el espacio Rust, invalidando por completo la ejecución del programa.
Ejemplos
Construye una suma de en cada qubit individual:
>>> 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)
Construye un observable a partir de una lista de etiquetas, los qubits a los que se aplica cada elemento y el coeficiente del término completo.
Es análogo a SparsePauliOp.from_sparse_list()salvo que utiliza el alfabeto ampliado de SparseObservable.
Los campos "labels" e "indices" de las triplas se asocian comprimiéndolos. Por ejemplo, esto significa que una llamada a from_list() puede convertirse a la forma utilizada por este método estableciendo el campo "índices" de cada triple en (num_qubits-1, ..., 1, 0).
Parámetros
- iter (list[tuple[str, Sequence[int], complex]]) – triples de etiquetas, los qubits a los que se aplica cada término single-qubit y el coeficiente del término completo.
- num_qubits (int) – el número de qubits del operador.
Ejemplos
Construye un operador simple:
>>> 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)>Construir el observable de identidad (aunque en realidad, basta con utilizar identity()):
>>> SparseObservable.from_sparse_list([("", (), 1.0)], num_qubits=100)
<SparseObservable with 1 term on 100 qubits: (1+0j)()>Este método puede reproducir el comportamiento de from_list()si el campo qubit-arguments de la tripleta se establece en enteros decrecientes:
>>> 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_listEl reverso de este método.
from_sparse_pauli_op
static from_sparse_pauli_op(op, /)
Construir un SparseObservable a partir de una SparsePauliOp instancia.
Esta será una traducción en gran medida directa del SparsePauliOpen particular, no hay ninguna suma sobre la marcha de términos similares, ni ningún intento de refactorizar sumas de términos de Pauli en operadores de proyección equivalentes.
Parámetros
op (SparsePauliOp) – el operador a convertir.
Ejemplos
>>> 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)
Construya un SparseObservable a partir de términos individuales.
Todos los términos deben tener el mismo número de qubits. Si se proporciona, el argumento num_qubits debe coincidir con los términos.
No se realiza ninguna simplificación como parte de la creación observable.
Parámetros
- obj (Iterable[Term]) – Iterable de términos individuales a partir de los cuales se construye el observable.
- num_qubits (int | None) – El número de qubits sobre los que debe actuar el observable. Normalmente se deduce de la entrada, pero puede indicarse explícitamente para manejar el caso de un iterable vacío.
Devuelve
El observable correspondiente.
identity
static identity(num_qubits)
Obtiene el operador de identidad sobre el número dado de qubits.
Ejemplos
Obtén el operador de identidad para 100 qubits:
>>> SparseObservable.identity(100)
<SparseObservable with 1 term on 100 qubits: (1+0j)()>pauli_bases
pauli_bases()
Obtener un PauliList que representa la base de medida necesaria para cada término (en orden) en este observable.
Por ejemplo, el proyector 0l+ devolverá un Pauli ZXY. El resultado Pauli es denso, en el sentido de que se almacenan identidades explícitas. Una identidad en la salida de Pauli no requiere una medida concreta.
Esto devolverá una entrada en la lista Pauli para cada término de la suma.
Devuelve
la lista de operadores de Pauli que representa las bases de medida necesarias.
Tipo de retorno
simplify
simplify(tol=1e-08)
Suma todos los términos semejantes en este operador, eliminándolos si el coeficiente complejo resultante tiene un valor absoluto dentro de la tolerancia de cero.
Como efecto secundario, esto ordena el operador en orden canónico.
Al utilizar esto para comparaciones de igualdad, hay que tener en cuenta que el redondeo de los números de coma flotante y la no asociatividad de la suma de números de coma flotante pueden hacer que los coeficientes distintos de cero de los términos sumados se consideren desiguales. Para comparar dos observables dentro de un margen de tolerancia, lo más seguro es comparar la diferencia canonicizada de ambas con cero.
Parámetros
tol (float) – después de sumar términos similares, se suprimirán de la salida los coeficientes cuyo valor absoluto sea inferior a la tolerancia absoluta dada.
Ejemplos
Utilizando simplify() para comparar dos operadores que representan el mismo observable, pero que se compararían de forma desigual debido a las pruebas estructurales por defecto:
>>> 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()Tenga en cuenta que en el ejemplo anterior, los coeficientes se eligen de tal manera que todos los cálculos de punto flotante son exactos, y no hay redondeo intermedio o problemas de asociatividad. Si no se puede garantizar que sea así, la forma más segura es:
>>> 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, /)
Producto tensorial de dos observables.
La ordenación de bits se define de tal manera que los índices de qubits del argumento seguirán siendo los mismos, y los índices de self se desplazarán por el número de qubits en other. Esta es la misma convención utilizada por el resto de los operadores de Qiskit quantum_info de Qiskit.
Esta función se utiliza para el operador infijo ^ . Si utiliza este operador, tenga en cuenta que las reglas de precedencia de operadores de Python pueden hacer que el orden de evaluación sea diferente al esperado. En concreto, el operador + se une más estrechamente que ^, al igual que * se une más estrechamente que +. Utilice paréntesis para fijar el orden de evaluación, si es necesario.
El argumento se convertirá en SparseObservable utilizando su constructor por defecto, si no está ya en la forma correcta.
Parámetros
otro - el observable que hay que poner en el lado derecho del producto tensorial.
Ejemplos
El orden de bits es tal que el producto tensorial de dos observables hechos a partir de una sola etiqueta "se parece" a un observable hecho concatenando las dos cadenas:
>>> left = SparseObservable.from_label("XYZ")
>>> right = SparseObservable.from_label("+-IIrl")
>>> assert left.tensor(right) == SparseObservable.from_label("XYZ+-IIrl")También se puede utilizar el operador infijo ^ para productos tensoriales, que de forma similar lanzará el lado derecho de la operación si no es ya un SparseObservable:
>>> assert SparseObservable("rl") ^ Pauli("XYZ") == SparseObservable("rlXYZ")La misma función, pero con el orden de los argumentos invertido. Esto puede ser útil si te gusta usar el comportamiento de fundición para el argumento, pero quieres que tu
SparseObservableesté en el lado derecho de la ordenación tensorial.
to_sparse_list
to_sparse_list()
Expresar el observable en términos de un formato de lista dispersa.
Esto puede verse como una contraoperación de SparseObservable.from_sparse_list()sin embargo, no se garantiza que el orden de los términos sea el mismo después de un viaje de ida y vuelta a una lista dispersa.
Ejemplos
>>> obs = SparseObservable.from_list([("IIXIZ", 2j), ("IIZIX", 2j)])
>>> reconstructed = SparseObservable.from_sparse_list(obs.to_sparse_list(), obs.num_qubits)El constructor que puede interpretar estas listas.
transpose
transpose()
Calcular la transposición matricial de este observable.
Esta operación se define en términos de las convenciones estándar de la matriz de Qiskit, en que la forma de la matriz se toma para estar en la base computacional $Z$. Los términos del alfabeto relacionados con $X$- y $Z$ no se ven afectados por la transposición, pero los términos relacionados con $Y$ modifican sus términos del alfabeto. Precisamente:
- se transpone a
- se transpone a
- se transpone a
Ejemplos
>>> obs = SparseObservable([("III", 1j), ("Yrl", 0.5)])
>>> assert obs.transpose() == SparseObservable([("III", 1j), ("Ylr", -0.5)])zero
static zero(num_qubits)
Obtiene el operador cero sobre el número dado de qubits.
El operador cero es el operador cuyo valor de expectativa es cero para todos los estados cuánticos. No tiene condiciones. Es el elemento de identidad para la suma de dos SparseObservable instancias; cualquier cosa añadida al operador cero es igual a sí misma.
Si desea que el proyector en el estado de todos los ceros, utilice:
>>> num_qubits = 10
>>> all_zeros = SparseObservable.from_label("0" * num_qubits)Ejemplos
Obtén el operador cero para 100 qubits:
>>> SparseObservable.zero(100)
<SparseObservable with 0 terms on 100 qubits: 0.0>