SparseObservable
class qiskit.quantum_info.SparseObservable(data, /, num_qubits=None)
Bases: object
Um observável sobre bases Pauli que armazena seus dados em um formato qubit-esparso.
matemática
Esse observável representa uma soma sobre cadeias de operadores de Pauli e projetores de estado próprio de Pauli, com cada termo ponderado por algum número complexo. Ou seja, o observável completo é
para números complexos e operadores de um único qubit atuando no qubit de um alfabeto restrito . A soma sobre é a soma dos termos individuais, e o produto tensorial produz as cadeias de operadores.
O alfabeto de operadores de um único qubit permitido do qual o é extraído são os operadores de Pauli e os operadores de projeção de estado próprio de Pauli. Explicitamente, são eles:
Rótulo | Operador | Valor numérico | BitTerm atributo |
|---|---|---|---|
"I" | (identidade) | Não armazenado. | Não armazenado. |
"X" | (Pauli X) | 0b0010 (2) | X |
"Y" | (Pauli Y) | 0b0011 (3) | Y |
"Z" | (Pauli Z) | 0b0001 (1) | Z |
"+" | (projetor para o estado próprio positivo de X) | 0b1010 (10) | PLUS |
"-" | (projetor para o estado próprio negativo de X) | 0b0110 (6) | MINUS |
"r" | (projetor para o estado próprio positivo de Y) | 0b1011 (11) | RIGHT |
"l" | (projetor para o estado próprio negativo de Y) | 0b0111 (7) | LEFT |
"0" | (projetor para o estado próprio positivo de Z) | 0b1001 (9) | ZERO |
"1" | (projetor para o estado próprio negativo de Z) | 0b0101 (5) | ONE |
O alfabeto permitido forma uma base supercompleta do espaço do operador. Isso significa que não há um somatório único para representar um determinado observável. Em comparação, SparsePauliOp usa uma base precisa do espaço do operador, portanto (depois de combinar termos da mesma cadeia de Pauli, remover zeros e classificar os termos em alguma ordem canônica ) há apenas uma representação de qualquer operador.
SparseObservable usa sua base particular supercompleta com o objetivo de tornar a "eficiência da medição" equivalente à "eficiência da representação". Por exemplo, o observável pode ser medido com eficiência no hardware com medições simples , mas só pode ser representado por SparsePauliOp como , o que requer termos armazenados em . SparseObservable requer apenas um único termo para armazenar isso.
A desvantagem disso é que não é prático pegar uma matriz arbitrária ou SparsePauliOp e encontrar a melhor SparseObservable representação. Normalmente, você desejará construir um SparseObservable diretamente, em vez de tentar decompor em um.
Representação
A representação interna de a SparseObservable armazena apenas os operadores de qubit não idênticos. Isso torna significativamente mais eficiente a representação de observáveis, como ; SparseObservable requer uma quantidade de memória linear no número total de qubits, enquanto SparsePauliOp é escalonado quadraticamente.
Os termos são armazenados compactados, com espírito semelhante ao formato de linha esparsa compactada de matrizes esparsas. Nessa analogia, os termos da soma são as "linhas", e os termos do qubit são as "colunas", em que uma entrada ausente representa a identidade em vez de um zero. De forma mais explícita, a representação é composta por quatro matrizes contíguas:
Atributo | Duração | Descrição |
|---|---|---|
coeffs | O multiplicador escalar complexo para cada termo. | |
bit_terms | Cada um dos termos de um único qubit sem identidade para todos os operadores, em ordem. Elas correspondem à não identidade na descrição da soma, em que as entradas são armazenadas na ordem crescente de primeiro, e na ordem crescente de dentro de cada termo. | |
indices | O qubit correspondente ( ) para cada um dos operadores em bit_terms. SparseObservable exige que essa lista seja classificada por termos, e os algoritmos podem contar com a manutenção desse invariante. | |
boundaries | Os índices que dividem bit_terms e indices em termos completos. Para o termo número , seu coeficiente complexo é coeffs[i], e seus operadores de qubit único não idênticos e seus qubits correspondentes são a fatia boundaries[i] : boundaries[i+1] em bit_terms e indices respectivamente. boundaries sempre tem um 0 explícito como seu primeiro elemento. |
O parâmetro de comprimento é o número de termos na soma, e o parâmetro é o número total de termos de um único qubit sem identidade.
Como exemplos ilustrativos:
- no caso de um operador zero,
boundariestem comprimento 1 (um único 0) e todos os outros vetores são vazios. - no caso de um operador de identidade totalmente simplificado,
boundariesé[0, 0],coeffstem uma única entrada, ebit_termseindicesestão vazios. - para o operador ,
boundariesé[0, 2, 4],coeffsé[1.0, -1.0],bit_termsé[BitTerm.Z, BitTerm.Z, BitTerm.Y, BitTerm.X]eindicesé[0, 2, 1, 3]. O operador pode atuar em mais de quatro qubits, dependendo do parâmetronum_qubitsparâmetro. Osbit_termssão valores inteiros, cujos números mágicos podem ser acessados por meio da classe de atributoBitTermclasse de atributo. Observe que os termos e índices de um único bit são classificados em ordem ordenada termwise. Esse é um requisito da aula.
Esses casos não são especiais, são totalmente consistentes com as regras e não devem precisar de tratamento especial.
O item escalar da matriz bit_terms é armazenado como um byte numérico. Os valores numéricos estão relacionados à representação Pauli simétrica que SparsePauliOp usa, e podem ser acessados com acesso nomeado por uma enumeração:
BitTerm
class BitTerm
Um IntEnum que fornece acesso nomeado aos valores numéricos usados para representar cada um dos termos do alfabeto de qubit único enumerados em Alfabeto de termos de qubit único usados em SparseObservable.
Essa classe é anexada a SparseObservable. Acesse-o como SparseObservable.BitTerm. Se isso for muito difícil de digitar e você estiver lidando apenas com objetos :class:¬SparseObservable` e o nome BitTerm não for ambíguo, talvez seja melhor encurtá-lo como:
>>> ops = SparseObservable.BitTerm
>>> assert ops.X is SparseObservable.BitTerm.XVocê pode acessar todos os valores da enumeração pelo nome completo em letras maiúsculas ou pelo rótulo de uma única letra. Os rótulos de letra única geralmente não são identificadores válidos no site Python, portanto, é necessário usar a notação de indexação para acessá-los:
>>> assert SparseObservable.BitTerm.ZERO is SparseObservable.BitTerm["0"]A estrutura numérica deles é que todos são valores de quatro bits, dos quais os dois bits inferiores são a representação simpática (sem fase) do operador de Pauli relacionado ao objeto, em que o bit inferior denota uma contribuição de e o segundo inferior uma contribuição de , enquanto os dois bits superiores são 00 para um operador de Pauli, 01 para o projetor de estado próprio negativo e 10 para o projetor de estado próprio positivo.
Valores
X
Valor Padrão: 2
O operador Pauli . Usa o rótulo de letra única "X".
Plus
Valor Padrão: 10
O projetor para o estado próprio positivo do operador : . Usa o rótulo de letra única "+".
MINUS
Valor Padrão: 6
O projetor para o estado próprio negativo do operador : . Usa o rótulo de letra única "-".
Y
Valor Padrão: 3
O operador Pauli . Usa o rótulo de letra única "Y".
DIREITA
Valor Padrão: 11
O projetor para o estado próprio positivo do operador : . Usa o rótulo de letra única "r".
ESQUERDA
Valor Padrão: 7
O projetor para o estado próprio negativo do operador : . Usa o rótulo de letra única "l".
Z
Valor Padrão: 1
O operador Pauli . Usa o rótulo de letra única "Z".
ZERO
Valor Padrão: 9
O projetor para o estado próprio positivo do operador : . Usa o rótulo de letra única "0".
ONE
Valor Padrão: 5
O projetor para o estado próprio negativo do operador : . Usa o rótulo de letra única "1".
Atributos
label
O rótulo da cadeia de caracteres de um único caractere usado para representar esse termo no SparseObservable alfabeto.
Cada um dos atributos do tipo matriz se comporta como uma sequência Python. Você pode indexar e fatiar esses dados com a semântica padrão list-como a semântica padrão. O corte de um atributo retorna um Numpy ndarray contendo uma cópia dos dados relevantes com o dtype natural do campo; isso permite que você faça matemática facilmente nos resultados, como operações bit a bit em bit_terms. Você pode atribuir a índices ou fatias de cada um dos atributos, mas lembre-se de que é necessário manter as regras de coerência de dados ao fazer isso. Por exemplo:
>>> 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)])A redução acima para as bases de Pauli também pode ser obtida com pauli_bases().
Ordenação canônica
Para qualquer observável matemático dado, há várias maneiras de representá-lo com SparseObservable. Por exemplo, o mesmo conjunto de termos de um único bit e seus índices correspondentes podem aparecer várias vezes no observável. Matematicamente, isso é equivalente a ter apenas um único termo com todos os coeficientes somados. Da mesma forma, os termos da soma em a SparseObservable podem estar em qualquer ordem e representar o mesmo observável, já que a adição é comutativa (embora a adição de ponto flutuante não seja associativa), SparseObservable não garante a ordem da soma).
Essas duas categorias de degeneração de representação podem levar o == operador a afirmar que duas observáveis não são iguais, apesar de representarem o mesmo objeto. Nesses casos, pode ser conveniente definir uma forma canônica, que permita comparar os observáveis estruturalmente.
Você pode colocar um SparseObservable na forma canônica usando o método simplify() método. A ordenação precisa dos termos na ordenação canônica não é especificada e pode mudar entre as versões do Qiskit. Na mesma versão do Qiskit, no entanto, você pode comparar dois observáveis estruturalmente, comparando suas formas simplificadas.
Se você quiser levar em conta a tolerância de ponto flutuante na comparação, é mais seguro usar uma receita como:
def equivalent(left, right, tol):
return (left - right).simplify(tol) == SparseObservable.zero(left.num_qubits)A forma canônica produzida por simplify() por si só não detectará universalmente todos os observáveis que são equivalentes devido ao alfabeto de base supercompleto. Para obter uma expressão única, você pode primeiro representar o observável usando apenas termos de Pauli, chamando as_paulis(), seguido de simplify(). Observe que a expansão do projetor (por exemplo + em I e X) não é computacionalmente viável em escala.
Indexando
SparseObservable comporta-se como uma sequência Python (a forma padrão, não a expandida collections.abc.Sequence). O observável pode ser indexado por números inteiros e iterado para gerar termos individuais.
Cada termo aparece como uma instância de uma classe autônoma. Os termos individuais são copiados do observável básico; as mutações neles não afetarão o observável.
Termo
class Term(num_qubits, coeff, bit_terms, indices)
Bases: object
Um único termo de um SparseObservable.
Normalmente, eles são criados por meio de indexação ou iteração em um arquivo SparseObservable.
bit_labels
bit_labels()
Retorna os rótulos de bits do termo como string.
Os rótulos dos bits corresponderão à ordem de SparseTerm.indices, de modo que o i-ésimo caractere da string seja aplicado ao índice do qubit em term.indices[i].
Retorna
Os termos de bits sem identidade como uma cadeia concatenada.
bit_terms
Visualização somente de leitura dos termos individuais de um único qubit.
Os únicos valores válidos na matriz são aqueles com um BitTerm.
coeff
O coeficiente do termo.
copy
copy()
Obtenha uma cópia deste termo.
indices
Visualização somente de leitura dos índices de cada termo de qubit único sem identidade.
Os índices sempre estarão em ordem ordenada.
num_qubits
O número de qubits em que o termo é definido.
pauli_base
pauli_base()
Obtenha um Pauli objeto que representa a base de medição necessária para esse termo.
Por exemplo, o projetor 0l+ retornará um Pauli ZYX. O resultado Pauli resultante é denso, no sentido de que as identidades explícitas são armazenadas. Uma identidade na saída de Pauli não requer uma medida concreta.
Retorna: Pauli: o operador Pauli que representa o necessário
base de medição.
SparseObservable.pauli_bases()
Um método semelhante para um observável inteiro de uma só vez.
to_observable
to_observable()
Converta esse termo em um SparseObservable.
Construção
SparseObservable define vários construtores. O construtor padrão tentará delegar a um dos construtores mais específicos, com base no tipo de entrada. Você sempre pode usar os construtores específicos para ter mais controle sobre a construção.
Método | Resumo |
|---|---|
from_label() | Converter um rótulo de string denso em um único termo SparseObservable. |
from_list() | Soma uma lista de tuplas de rótulos de cadeia densa e os coeficientes associados em um observável. |
from_sparse_list() | Soma uma lista de tuplas de rótulos de cadeia de caracteres esparsos, os qubits aos quais eles se aplicam e seus coeficientes em um observável. |
from_pauli() | Elevar um único Pauli em um único termo SparseObservable. |
from_sparse_pauli_op() | Elevar um SparsePauliOp em um SparseObservable. |
from_terms() | Soma de instâncias individuais explícitas Term instâncias. |
from_raw_parts() | Crie o observável a partir das matrizes de dados brutos. |
__new__
__new__(data, /, num_qubits=None)
O construtor padrão do SparseObservable.
Isso delega a um dos métodos de construtor de conversão explícitos, com base no tipo do argumento data . Se num_qubits for fornecido e o construtor implícito pelo tipo de data não aceitar um número, o número inteiro fornecido deverá corresponder à entrada.
Parâmetros
- data - O tipo de dados da entrada. Isso pode ser outro
SparseObservablee, nesse caso, a entrada é copiada, umPauliouSparsePauliOp, caso em quefrom_pauli()oufrom_sparse_pauli_op()são chamados conforme apropriado, ou pode ser uma lista em um formato válido parafrom_list()oufrom_sparse_list(). - num_qubits (int|None) – Número opcional de qubits para o operador. Para a maioria das entradas de dados, isso pode ser inferido e não precisa ser passado. Isso só é necessário para listas vazias ou para o formato de lista esparsa. Se for fornecido desnecessariamente, deverá corresponder à entrada de dados.
Além dos construtores baseados em conversão, há também métodos auxiliares que constroem formas especiais de observáveis.
Método | Resumo |
|---|---|
zero() | O operador zero em um determinado número de qubits. |
identity() | O operador de identidade em um determinado número de qubits. |
Conversões
Um arquivo existente SparseObservable existente pode ser convertido em outros operadores quantum_info operadores ou formatos genéricos. Observe que outros objetos podem não ser capazes de representar o mesmo observável de forma tão eficiente quanto SparseObservableincluindo a necessidade potencial de uma memória exponencialmente maior.
Método | Resumo |
|---|---|
as_paulis() | Crie um novo SparseObservableexpandindo apenas em termos de operadores Pauli. |
to_sparse_list() | Expresse o observável em um formato de lista esparsa com elementos (bit_terms, indices, coeff). |
Além disso, SparsePauliOp.from_sparse_observable() está disponível para conversão dessa classe para SparsePauliOp. Esteja ciente de que esse método sofre das mesmas preocupações de uso de memória exponencial que o as_paulis().
Manipulação matemática
SparseObservable suporta o conjunto padrão de operadores matemáticos do Python, como outros quantum_info operadores.
Na aritmética básica, você pode:
- adicionar dois observáveis usando
+ - subtrair dois observáveis usando
- - multiplicar ou dividir por um
int,floatoucomplexusando*e/ - negar todos os coeficientes em um observável com um unário
-
Cada um dos operadores aritméticos binários básicos tem um método in-place especializado correspondente, que altera o lado esquerdo in-place. O uso desses operadores é normalmente mais eficiente do que o dos operadores infixos, especialmente para criar um observável em um loop.
O produto tensorial é calculado com tensor() (para a ordem padrão de justaposição dos rótulos de Pauli) ou expand() (para a ordem inversa). O operador ^ é sobrecarregado para ser equivalente a tensor().
Ao usar os operadores binários ^ (tensor()) e & (compose()), lembre-se de que as regras de precedência de operador do Python podem fazer com que a ordem de avaliação seja diferente da sua expectativa. Em particular, o operador + se vincula mais firmemente do que ^ ou &, assim como * se vincula mais firmemente do que +.
Ao usar os operadores em expressões mistas, é mais seguro usar parênteses para agrupar os operandos dos produtos tensoriais.
A SparseObservable tem uma forma bem definida de adjoint(). As noções de conjugação complexa escalar (conjugate()) e transposição de valor real (transpose()) são definidas de forma análoga à representação matricial de outros operadores Pauli no Qiskit.
Notas sobre eficiência
Internamente, SparseObservable é mutável no local, incluindo o uso de vetores expansíveis com alocação excessiva para estender o número de termos. Isso significa que o custo de anexar a um observável usando += é amortizado linearmente no número total de termos adicionados, em vez da complexidade quadrática que o + binário exigiria.
As adições e subtrações são implementadas por uma operação de empilhamento de termos; não há "simplificação" automática (soma de termos semelhantes), porque a maioria das adições para construir um observável gera apenas um pequeno número de duplicações, e a detecção de termos semelhantes tem custos adicionais. Se isso não se adequar aos seus casos de uso, você pode chamar periodicamente simplify()ou discutir outras APIs conosco para melhorar a criação de observáveis.
Atributos
bit_terms
Uma lista simples de termos de um único qubit. Isso é mais naturalmente uma lista de listas, mas é armazenada de forma plana por motivos de uso de memória e localidade, com as sublistas denotadas por limites.
boundaries
Índices que dividem bit_terms e indices em sublistas para cada termo individual na soma. boundaries[0] : boundaries[1] é o intervalo de índices em bit_terms e indices que correspondem ao primeiro termo da soma. Todos os índices de qubit não especificados são implicitamente a identidade. Esse é um item a mais do que coeffsjá que boundaries[0] é sempre um zero explícito (para facilitar o algoritmo).
coeffs
Os coeficientes de cada termo abstrato na soma. Isso tem tantos elementos quanto termos na soma.
indices
Uma lista simples dos índices de qubit que as entradas correspondentes em bit_terms atuam. Essa lista deve ser sempre classificada por termo, em que um termo é uma sublista, conforme indicado por boundaries.
Se estiver escrevendo nesse atributo a partir do espaço Python, você deve garantir que só escreva em índices que estejam ordenados por termo.
num_qubits
O número de qubits em que o operador atua.
Isso não pode ser inferido de nenhuma outra forma ou valor, pois as identidades não são armazenadas explicitamente.
num_terms
O número de termos na soma que esse operador está rastreando.
Métodos
adjoint
adjoint()
Calcule o adjunto desse observável.
Isso é bem definido no sentido matemático abstrato. Todos os termos do alfabeto de um único qubit são autoadjuntos, portanto, o resultado dessa operação é o mesmo observável, exceto que seus coeficientes são todos conjugados complexos.
Exemplos
>>> left = SparseObservable.from_list([("XY+-", 1j)])
>>> right = SparseObservable.from_list([("XY+-", -1j)])
>>> assert left.adjoint() == rightapply_layout
apply_layout(layout, num_qubits=None)
Aplique um layout de transpilador a isso SparseObservable.
Normalmente, você terá definido seu observável em termos dos qubits virtuais dos circuitos que usará para preparar os estados. Após a transpilação, os qubits virtuais são mapeados para qubits físicos específicos em um dispositivo, que pode ser maior do que o seu circuito. Esse mapeamento também pode mudar no decorrer do circuito. Esse método transforma o observável de entrada em qubits virtuais em um observável que é adequado para ser aplicado imediatamente após o circuito físico totalmente transpilado.
Parâmetros
- layout (TranspileLayout |list[int] | None) – O layout a ser aplicado. A maioria dos usos dessa função deve passar o campo
QuantumCircuit.layoutde um circuito que foi transpilado para hardware. Além disso, você pode passar uma lista de novos índices de qubit. Se for fornecido explicitamente comoNone, nenhum remapeamento será aplicado (mas você ainda poderá usarnum_qubitspara expandir o observável). - num_qubits (int | None) – O número de qubits para expandir o observável. Se não for fornecido, a saída terá a mesma largura da entrada fornecida
TranspileLayoutou a mesma largura da entrada se o endereçolayoutfor fornecido em outro formato.
Retorna
Um novo SparseObservable com o layout fornecido aplicado.
as_paulis
as_paulis()
Expresse o observável somente em termos de Pauli, escrevendo cada projetor como uma soma de termos de Pauli.
Observe que não há garantia da ordem dos termos de Pauli resultantes. Use SparseObservable.simplify() além disso, para obter uma representação canônica.
Observe que isso usará pelo menos termos se houver projetores de um único qubit presentes, o que pode levar a um número exponencial de termos.
Retorna
O mesmo observável, mas expresso apenas em termos de Pauli.
Exemplos
Reescreva um observável em termos de projetores em operadores 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 pequenos, isso pode ser usado com simplify() como uma 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()
Um construtor de SparsePauliOp que pode converter um SparseObservable na SparsePauliOp representação densa de Pauli.
clear
clear()
Limpe todos os termos desse operador, tornando-o igual ao operador zero novamente.
Isso não altera a capacidade das alocações internas; portanto, operações posteriores de adição ou subtração podem não exigir uma realocação.
Exemplos
>>> 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)
Verifique se o observável comuta com outro.
Parâmetros
- other (SparseObservable) – O outro observável para verificar a comutação.
- tol (float) – Se os coeficientes no produto de si mesmo e do outro estiverem abaixo da tolerância (em magnitude), os termos são ignorados.
Retorna
True se os termos se compensarem, até a tolerância, False caso contrário.
Aumentos
TypeError – Se não other pudesse ser coagido a SparseObservable.
compose
compose(other, /, qargs=None, *, front=False)
Componha outro SparseObservable sobre este.
Em termos de álgebras de operadores, a composição corresponde à multiplicação à esquerda: c = a.compose(b) corresponde a $C = B A$. Em outras palavras, a.compose(b) retorna um operador que "executa a e, em seguida, executa b no resultado". Em vez disso, o argumento front=True torna essa multiplicação correta.
self e other devem ter o mesmo tamanho, a menos que qargs seja fornecido, caso em que other pode ser menor que self, desde que o número de qubits em other e o comprimento de qargs sejam iguais. qargs nunca pode conter duplicatas ou índices de qubits que não existam em self.
Observe que essa função pode causar uma explosão exponencial do uso de memória do observável, pois o alfabeto de SparseObservable não é fechado sob composição; a composição de dois termos de bit único pode ser uma soma, que multiplica o número total de termos. Esse uso de memória não é _necessariamente_ inerente ao observável resultante, mas encontrar uma refatoração eficiente da soma geralmente é igualmente difícil do ponto de vista computacional. É melhor usar o conhecimento do domínio de seus observáveis para minimizar o número de termos que existem, em vez de tentar simplificá-los após o fato.
Parâmetros
-
outro - o observável usado para multiplicar à esquerda
self. -
qargs -
se fornecido, os qubits em
selfa serem associados aos qubits emother. Em outras palavras: se isso for dado, é semelhante a uma implementação mais eficiente de:self.compose(other.apply_layout(qargs, self.num_qubits))pois nenhum observável temporário é criado para armazenar a forma de layout aplicado de
other. -
front - se
True, então multiplicar à direita porotherem vez de multiplicar à esquerda (padrãoFalse). Oqargsainda é aplicado aoother. Isso é mais útil quandoqargsestá definido, ouotherpode ser um objeto que deve ser coagido aSparseObservable.
conjugate
conjugate()
Calcule a conjugação complexa desse observável.
Essa operação é definida em termos das convenções de matriz padrão do Qiskit, em que a forma da matriz é considerada na base computacional $Z$. Os termos do alfabeto relacionados a $X$- e $Z$ não são afetados pela conjugação complexa, mas os termos relacionados a $Y$ modificam seus termos do alfabeto. Exatamente:
- conjugue com
- conjugados para
- conjugados para
Além disso, todos os coeficientes são conjugados.
Exemplos
>>> obs = SparseObservable([("III", 1j), ("Yrl", 0.5)])
>>> assert obs.conjugate() == SparseObservable([("III", -1j), ("Ylr", -0.5)])copy
copy()
Obtenha uma cópia desse observável.
Exemplos
>>> 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)
Desenvolva essa observável por meio de um termo de Pauli.
Uma evolução da observável por um de Pauli corresponde a .
Ao contrário de uma implementação literal por meio de duas composições completas, este método realiza a conjugação diretamente no nível de um único qubit, utilizando uma tabela de consulta fixa. Isso evita a materialização de quaisquer termos intermediários SparseObservable e calcula a observável evoluída em uma única passagem pelos termos. self e other deve ter o mesmo número de qubits, a menos que qargs seja especificado; nesse caso, other pode ser menor que self, desde que o número de qubits em other e o comprimento de qargs sejam iguais. qargs especifica quais qubits de self são evoluídos por other.
Atualmente, esse método suporta a evolução apenas por meio de um operador de termo único, o que significa que other deve ser um operador de Pauli representado por Pauli.
Parâmetros
- outro – o operador de Pauli, usado para conjugar
self. - qargs – se fornecido, os qubits em
selfa serem evoluídos porother. O comprimento deve corresponder ao número de qubits emother.
Retorna
Uma nova forma evoluiu SparseObservable com as conjugações aplicadas.
Aumentos
- TypeError – se não
otherfor do tipoPauli. - ValueError – se
selfeothertiverem números diferentes de qubits (e nãoqargsfor especificado). - ValueError – se
qargso comprimento não corresponderotherao número de qubits. - ValueError – se
qargscontiver duplicatas ou índices fora do intervalo. - ValueError – se
othercontiver mais de um termo.
expand
expand(other, /)
Produto tensor de ordem inversa.
Isso é equivalente a other.tensor(self), exceto pelo fato de que other será primeiro convertido para SparseObservable se ainda não for um (chamando o construtor padrão).
Parâmetros
outro - o observável a ser colocado no lado esquerdo do produto tensorial.
Exemplos
Isso é equivalente a tensor() com a ordem dos argumentos invertida:
>>> left = SparseObservable.from_label("XYZ")
>>> right = SparseObservable.from_label("+-IIrl")
>>> assert left.tensor(right) == right.expand(left)from_label
static from_label(label, /)
Construa um observável de termo único a partir de um rótulo de cadeia densa.
O operador resultante terá um coeficiente de 1. O rótulo deve ser uma sequência do alfabeto 'IXYZ+-rl01'. O rótulo é interpretado de forma análoga a uma cadeia de bits. Em outras palavras, a letra mais à direita está associada ao qubit 0, e assim por diante. Isso é o mesmo que os rótulos de Pauli e SparsePauliOp.
Parâmetros
label (str) – o rótulo denso.
Exemplos
>>> 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)Uma generalização desse método que constrói um operador de soma a partir de vários rótulos e seus coeficientes correspondentes.
from_list
static from_list(iter, /, *, num_qubits=None)
Construa um observável a partir de uma lista de rótulos e coeficientes densos.
Isso é análogo ao SparsePauliOp.from_list()exceto que ele usa o alfabeto estendido de SparseObservable. Nesse formulário denso, você deve fornecer todas as identidades explicitamente em cada rótulo.
O rótulo deve ser uma sequência do alfabeto 'IXYZ+-rl01'. O rótulo é interpretado de forma análoga a uma cadeia de bits. Em outras palavras, a letra mais à direita está associada ao qubit 0, e assim por diante. Isso é o mesmo que os rótulos de Pauli e SparsePauliOp.
Parâmetros
- iter (list[tuple[str, complex]]) – Pares de rótulos e seus coeficientes associados a serem somados. Os rótulos são interpretados da mesma forma que em
from_label(). - num_qubits (int | None) – Não é necessário especificar isso se você tiver certeza de que
iternão é uma sequência vazia, pois isso pode ser inferido a partir dos comprimentos dos rótulos. Se oiterpuder estar vazio, você deverá especificar esse argumento para não ambiguidade de quantos qubits o observável representa. Se isso for fornecido eiternão estiver vazio, o valor deverá corresponder aos comprimentos do rótulo.
Exemplos
Construa um observável a partir de uma lista de rótulos do mesmo tamanho:
>>> 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)>Use num_qubits para eliminar a ambiguidade de entradas potencialmente vazias:
>>> SparseObservable.from_list([], num_qubits=10)
<SparseObservable with 0 terms on 10 qubits: 0.0>Esse método é equivalente a chamadas para from_sparse_list() com o campo explícito qubit-arguments definido como números inteiros decrescentes:
>>> 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_listUm construtor semelhante, mas que recebe apenas um único rótulo e sempre tem seu coeficiente definido como 1.0.
Construa o observável a partir de uma lista de rótulos sem identidades explícitas, mas com os qubits aos quais cada termo de qubit único se aplica listado explicitamente.
from_pauli
static from_pauli(pauli, /)
Construir um SparseObservable a partir de uma única Pauli instância.
O observável de saída terá um único termo, com um coeficiente unitário dependente da fase.
Parâmetros
pauli (Pauli) – a única Pauli para converter.
Exemplos
>>> 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)
Construa um SparseObservable a partir de matrizes Numpy brutas que correspondem à representação de dados necessária descrita na documentação em nível de classe.
Os dados de cada matriz são copiados em alocações de espaço Rust novas e crescentes.
Parâmetros
-
num_qubits - número de qubits no observável.
-
coeffs - coeficientes complexos de cada termo do observável. Esse deve ser um array Numpy com dtype
complex128. -
bit_terms - lista achatada dos termos de um único qubit que inclui todos os termos completos. Esse deve ser um array Numpy com dtype
uint8(que é compatível comBitTerm). -
índices - lista ordenada por termos achatados dos qubits aos quais cada termo de qubit único corresponde. Esse deve ser um array Numpy com dtype
uint32. -
limites - os índices que dividem
bit_termseindicesem termos. Esse deve ser um array Numpy com dtypeuintp. -
verificação -
se
True(o padrão), validar se os dados satisfazem todas as garantias de coerência. SeFalse, nenhuma verificação é feita.AvisoSe
check=False, osbit_termsdevem ser absolutamente todos valores válidos deSparseObservable.BitTerm. Se não forem, poderá ocorrer um comportamento indefinido no espaço Rust, invalidando totalmente a execução do programa.
Exemplos
Construa uma soma de em 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)
Construa um observável a partir de uma lista de rótulos, os qubits aos quais cada item se aplica e o coeficiente de todo o termo.
Isso é análogo ao SparsePauliOp.from_sparse_list()exceto que ele usa o alfabeto estendido de SparseObservable.
Os campos "labels" (rótulos) e "indices" (índices) das triplas são associados por meio de um zíper. Por exemplo, isso significa que uma chamada para from_list() pode ser convertida para a forma usada por esse método, definindo o campo "indices" de cada tripla como (num_qubits-1, ..., 1, 0).
Parâmetros
- iter (list[tuple[str, Sequence[int], complex]]) – triplas de rótulos, os qubits aos quais cada termo de qubit único se aplica e o coeficiente de todo o termo.
- num_qubits (int) – o número de qubits no operador.
Exemplos
Construa um operador simples:
>>> 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)>Construa o observável de identidade (embora, na verdade, basta usar identity()):
>>> SparseObservable.from_sparse_list([("", (), 1.0)], num_qubits=100)
<SparseObservable with 1 term on 100 qubits: (1+0j)()>Esse método pode replicar o comportamento de from_list()se o campo qubit-arguments da tripla for definido como números inteiros decrescentes:
>>> 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_listO inverso desse método.
from_sparse_pauli_op
static from_sparse_pauli_op(op, /)
Construir um SparseObservable a partir de uma SparsePauliOp instância.
Essa será uma tradução direta da versão SparsePauliOpem particular, não há soma imediata de termos semelhantes, nem qualquer tentativa de refatorar as somas de termos de Pauli em operadores de projeção equivalentes.
Parâmetros
op (SparsePauliOp) – o operador a ser convertido.
Exemplos
>>> 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)
Construa um SparseObservable a partir de termos individuais.
Todos os termos devem ter o mesmo número de qubits. Se fornecido, o argumento num_qubits deve corresponder aos termos.
Nenhuma simplificação é feita como parte da criação observável.
Parâmetros
- obj (Iterable[Term]) – Iterável de termos individuais para construir o observável.
- num_qubits (int | None) – O número de qubits em que o observável deve atuar. Isso geralmente é inferido a partir da entrada, mas pode ser explicitamente fornecido para lidar com o caso de um iterável vazio.
Retorna
O observável correspondente.
identity
static identity(num_qubits)
Obtém o operador de identidade sobre o número determinado de qubits.
Exemplos
Obtenha o operador de identidade para 100 qubits:
>>> SparseObservable.identity(100)
<SparseObservable with 1 term on 100 qubits: (1+0j)()>pauli_bases
pauli_bases()
Obter um PauliList que representa a base de medição necessária para cada termo (em ordem) nesse observável.
Por exemplo, o projetor 0l+ retornará um Pauli ZXY. O resultado Pauli resultante é denso, no sentido de que as identidades explícitas são armazenadas. Uma identidade na saída de Pauli não requer uma medida concreta.
Isso retornará uma entrada na lista de Pauli para cada termo na soma.
Retorna
a lista de operadores Pauli que representa as bases de medição necessárias.
Tipo de retorno
simplify
simplify(tol=1e-08)
Some todos os termos semelhantes nesse operador, removendo-os se o coeficiente complexo resultante tiver um valor absoluto dentro da tolerância de zero.
Como efeito colateral, isso classifica o operador em ordem canônica.
Ao utilizar isso para comparações de igualdade, observe que o arredondamento de números de ponto flutuante e a não associatividade da adição de números de ponto flutuante podem fazer com que coeficientes diferentes de zero dos termos somados sejam considerados desiguais. Para comparar duas grandezas observáveis dentro de um limite de tolerância, o método mais seguro é comparar a diferença canonicizada entre as duas grandezas observáveis com o valor zero.
Parâmetros
tol (float) – depois de somar termos semelhantes, todos os coeficientes cujo valor absoluto for menor do que a tolerância absoluta fornecida serão suprimidos da saída.
Exemplos
Usando simplify() para comparar dois operadores que representam o mesmo observável, mas que seriam comparados de forma desigual devido aos testes estruturais por padrão:
>>> 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()Observe que, no exemplo acima, os coeficientes são escolhidos de forma que todos os cálculos de ponto flutuante sejam exatos e não haja problemas intermediários de arredondamento ou associatividade. Se não for possível garantir que esse seja o caso, a forma mais segura é:
>>> 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, /)
Produto tensorial de dois observáveis.
A ordenação de bits é definida de forma que os índices de qubit do argumento permaneçam os mesmos e os índices de self sejam deslocados pelo número de qubits em other. Essa é a mesma convenção usada pelo restante dos operadores do Qiskit quantum_info operadores.
Essa função é usada para o operador infixo ^ . Se estiver usando esse operador, esteja ciente de que as regras de precedência de operador do Python podem fazer com que a ordem de avaliação seja diferente da sua expectativa. Em particular, o operador + se vincula mais firmemente do que ^, assim como * se vincula mais firmemente do que +. Use parênteses para corrigir a ordem de avaliação, se necessário.
O argumento será convertido para SparseObservable usando seu construtor padrão, se ainda não estiver no formato correto.
Parâmetros
outro - o observável a ser colocado no lado direito do produto tensorial.
Exemplos
A ordem dos bits é tal que o produto tensorial de dois observáveis feitos a partir de um único rótulo "se parece" com um observável feito pela concatenação das duas cadeias:
>>> left = SparseObservable.from_label("XYZ")
>>> right = SparseObservable.from_label("+-IIrl")
>>> assert left.tensor(right) == SparseObservable.from_label("XYZ+-IIrl")Você também pode usar o operador infixo ^ para produtos tensoriais, que converterá de forma semelhante o lado direito da operação se ele ainda não for um SparseObservable:
>>> assert SparseObservable("rl") ^ Pauli("XYZ") == SparseObservable("rlXYZ")A mesma função, mas com a ordem dos argumentos invertida. Isso pode ser útil se você gostar de usar o comportamento de conversão para o argumento, mas quiser que seu
SparseObservableexistente esteja no lado direito da ordenação do tensor.
to_sparse_list
to_sparse_list()
Expresse o observável em termos de um formato de lista esparsa.
Isso pode ser visto como uma contraoperação de SparseObservable.from_sparse_list()no entanto, não é garantido que a ordem dos termos seja a mesma depois de uma viagem de ida e volta para uma lista esparsa.
Exemplos
>>> obs = SparseObservable.from_list([("IIXIZ", 2j), ("IIZIX", 2j)])
>>> reconstructed = SparseObservable.from_sparse_list(obs.to_sparse_list(), obs.num_qubits)O construtor que pode interpretar essas listas.
transpose
transpose()
Calcule a transposição da matriz desse observável.
Essa operação é definida em termos das convenções de matriz padrão do Qiskit, em que a forma da matriz é considerada na base computacional $Z$. Os termos do alfabeto relacionados a $X$- e $Z$ não são afetados pela transposição, mas os termos relacionados a $Y$ modificam seus termos do alfabeto. Exatamente:
- transpõe para
- transpõe para
- transpõe para
Exemplos
>>> obs = SparseObservable([("III", 1j), ("Yrl", 0.5)])
>>> assert obs.transpose() == SparseObservable([("III", 1j), ("Ylr", -0.5)])zero
static zero(num_qubits)
Obtém o operador zero sobre o número dado de qubits.
O operador zero é o operador cujo valor de expectativa é zero para todos os estados quânticos. Ele não tem termos. É o elemento de identidade para a adição de duas instâncias SparseObservable qualquer coisa adicionada ao operador zero é igual a ele mesmo.
Se você quiser que o projetor fique no estado de todos os zeros, use:
>>> num_qubits = 10
>>> all_zeros = SparseObservable.from_label("0" * num_qubits)Exemplos
Obtenha o operador zero para 100 qubits:
>>> SparseObservable.zero(100)
<SparseObservable with 0 terms on 100 qubits: 0.0>