Como este pacote se relaciona com o ffsim
Os conceitos deste guia estão disponíveis apenas na API Python.
O ffsim e este pacote dividem o trabalho em vez de se sobrepor, ao longo de uma única linha:
| O ffsim é responsável por | O qiskit-fermions é responsável por |
|---|---|
Os operadores de ansatz de alto nível (UCJOpSpinBalanced, UCCSDOpRestrictedReal e seus similares), construídos a partir de amplitudes de coupled-cluster ou de um vetor de parâmetros | O circuito fermiônico (FermionicCircuit) em que esses operadores se transformam, e sua transpilação para um circuito de Qubits sob qualquer codificação de férmion para Qubit |
| Simulação rápida em um espaço compacto de número fixo de partículas | O framework de mapeadores e as estruturas de dados de operadores a partir dos quais o circuito é construído |
A consequência prática vale nos dois sentidos. A matemática do ansatz vive no ffsim, então UCJ e UCC recebem um operador do ffsim diretamente como entrada. Por outro lado, as portas Qiskit próprias do ffsim são específicas da transformação de Jordan-Wigner, então reduzir um de seus operadores de ansatz por meio de uma codificação diferente é o que trazê-lo para cá oferece a você.
A simulação é onde os dois se encontram, e este guia explica como. O ffsim é o backend de simulação deste pacote, acessado por meio de seus próprios protocolos em vez de um wrapper: o guia do SKQD chama ffsim.apply_unitary(), ffsim.linear_operator() e ffsim.sample_state_vector() diretamente sobre objetos FermionicCircuite objetos FermionOperator, sem etapa de conversão.
Conectando-se à interface de simulação do ffsim
O ffsim é um simulador de alto desempenho para circuitos quânticos fermiônicos que explora a conservação do número de partículas e do spin-Z para representar vetores de estado de forma mais compacta que um statevector genérico de Qubits de dimensão . Ele define protocolos que qualquer objeto pode implementar para participar de sua maquinaria de simulação (veja qiskit_fermions.protocols para os protocolos próprios deste pacote, que seguem o mesmo design):
-
ffsim.SupportsApplyUnitary, pelo método_apply_unitary_(vec, norb, nelec, copy), que aplica o objeto como um unitário a um vetor de estado de número fixo de partículas -
ffsim.SupportsLinearOperator, pelo método_linear_operator_(norb, nelec), que retorna uma visão do objeto comoscipy.sparse.linalg.LinearOperatornesse mesmo setor -
ffsim.SupportsTrace, pelo método_trace_(norb, nelec), que retorna o traço do objeto nesse setor.
Este pacote implementa essa interface: toda porta fermiônica em qiskit_fermions.circuit.library fornece _apply_unitary_, e FermionOperator fornece _linear_operator_ e _trace_. Isso é o que torna os operadores e circuitos deste pacote compatíveis com o ffsim, de modo que suas ferramentas funcionam neles nativamente, sem etapa de conversão. ffsim.apply_unitary() pode simular um FermionicCircuit de ponta a ponta, ffsim.linear_operator() pode diagonalizar um FermionOperator, e utilitários de amostragem como ffsim.sample_state_vector() (usado no guia do SKQD para transformar um statevector simulado em contagens de medição) funcionam sem modificação.
O que _linear_operator_ retorna é um LinearOperator comum do SciPy, então scipy.sparse.linalg.eigsh() e companhia também funcionam nele.
>>> import ffsim
>>> import numpy as np
>>>
>>> from qiskit_fermions.circuit import FermionicCircuit
>>> from qiskit_fermions.circuit.library import Evolution
>>> from qiskit_fermions.operators import FermionOperator, ann, cre
>>>
>>> norb, nelec = 2, (1, 1)
>>> hamiltonian = FermionOperator.from_terms([
... ([cre(0), ann(1)], 0.5),
... ([cre(1), ann(0)], 0.5),
... ])
>>>
>>> circuit = FermionicCircuit(2 * norb)
>>> circuit.append(Evolution(2 * norb, hamiltonian, time=1.0), circuit.modes)
>>>
>>> reference = ffsim.hartree_fock_state(norb, nelec)
>>> state = ffsim.apply_unitary(reference, circuit, norb=norb, nelec=nelec) # native ffsim call
Acoplar-se por meio dos protocolos do ffsim é o que proporciona isso: seu ecossistema de utilitários de simulação e amostragem, em desenvolvimento ativo, se aplica sem alterações aos circuitos e operadores deste pacote, e usuários que já trabalham com o ffsim podem misturar as portas e operadores deste pacote sem aprender outra API de simulação.
O ffsim é uma dependência opcional
O ffsim é declarado como um extra opcional (pip install "qiskit-fermions[ffsim]", ou transitivamente por [all]), protegido em tempo de execução por HAS_FFSIM (como mostrado acima). Instalá-lo desbloqueia a simulação: ffsim.SupportsLinearOperator e ffsim.SupportsTrace são implementados convertendo o FermionOperator deste pacote em um ffsim.FermionOperator, então sem o ffsim eles levantam MissingOptionalLibraryError. Tudo o que não simula (construir operadores, mapeá-los e transpilar os circuitos resultantes) não precisa de nada disso.
A simulação fermiônica vive em um setor de número fixo de partículas
Tanto _apply_unitary_ quanto _linear_operator_ recebem um argumento nelec e representam o vetor de estado sobre a base de determinantes de número fixo de partículas para esse setor (norb, nelec), espelhando a configuração do espaço FCI do ffsim (e, transitivamente, do PySCF), em vez do espaço de dimensão completo que um simulador de Qubits geral usaria. Este é um espaço muito menor (seu tamanho é um produto de coeficientes binomiais em vez de uma potência de dois), mas vem com uma restrição rígida. Apenas operadores e portas que preservam o número de partículas (e, no caso com spin, o número de partículas de cada espécie de spin individualmente) podem ser representados nele. Um operador cuja ação moveria amplitude para um número de partículas diferente não tem onde colocá-la.
O ffsim resolve isso rejeitando tal operador de imediato: convertê-lo em um operador linear levanta um ValueError que nomeia qual lei de conservação falha. Esse é o padrão correto, porque aplicar para um Hamiltoniano com um termo que não conserva partículas transformaria, de outra forma, um suposto unitário em um mapa não unitário e sem significado físico.
-
Evolutionrepassa essa rejeição a partir do operador que ele evolui. -
OrbitalRotationverifica se sua matriz de rotação (embutida) é diagonal em blocos na divisão alfa/beta no caso com spin, e lança umValueErrorse ela misturar setores de spin.
>>> from qiskit_fermions.circuit.library import Evolution
>>>
>>> non_conserving = FermionOperator.from_terms([([cre(0), cre(1)], 1.0)]) # creates 2 particles
>>> gate = Evolution(2 * norb, non_conserving, time=1.0)
>>>
>>> try:
... gate._apply_unitary_(reference, norb, nelec, copy=True)
... except ValueError as exc:
... print("rejected:", exc)
rejected: The given FermionOperator could not be converted to a LinearOperator because it does not conserve particle number and the z component of spin. Conserves particle number: False Conserves spin z: False
Portanto, a simulação que não preserva o número de partículas está fora de alcance no espaço fermiônico, pela própria construção da representação de setor fixo, e esse é o preço do espaço FCI compacto que torna a simulação fermiônica tratável logo de início. Se o seu algoritmo precisar de operadores que violam o número de partículas (por exemplo, um canal de erro nativo de qubits, ou um operador construído para um Hamiltoniano mapeado que não conserva o número de partículas termo a termo), transpile primeiro para qubits e simule o QuantumCircuit resultante com um simulador em nível de qubits. Qualquer rota de transpilação serve para isso; os guias de circuito fermiônico e de transpilação entram em mais detalhes, mas generate_preset_jw_pass_manager() é uma escolha padrão razoável.
A convenção de spin em blocos para sistemas com spin
nelec tem o tipo int | tuple[int, int], e é o seu tipo, não um sinalizador separado, que seleciona um dos layouts de modos suportados:
-
Um int seleciona a interpretação sem spin. Os
norbmodos são tratados diretamente como orbitais sem spin, enelecé o número total de partículas. -
Um par
(n_alpha, n_beta)seleciona a interpretação com spin de2 * norbmodos sob uma convenção de spin em blocos fixa. Os modos0 .. norbsão os orbitais alfa (spin para cima) e os modosnorb .. 2 * norbsão os orbitais beta (spin para baixo). Cada espécie de spin é conservada independentemente; um termo somente alfa pode mover um elétron entre modos alfa, mas nunca para um modo beta, e vice-versa.
Esse despacho acontece em todo ponto de entrada do protocolo ffsim neste pacote (construtores de portas, implementações de _apply_unitary_placed_ e a compilação de setores do kernel nativo em Rust), e é a convenção usada em todos os guias LUCJ e SKQD (por exemplo, InitializeModes.from_hartree_fock() preenche as ocupações alfa nos modos 0 .. n_alpha e as ocupações beta nos modos norb .. norb + n_beta). Vale contrastar isso com o módulo qiskit_fermions.operators e com o FermionicCircuit em geral, que usam índices de modos genéricos sem semântica de spin inerente (veja o guia de circuito fermiônico). O significado de spin em blocos só é imposto quando um nelec com spin é fornecido a uma chamada de simulação, e não está embutido na própria representação do operador ou do circuito.
>>> from qiskit_fermions.circuit.library import InitializeModes
>>>
>>> norb, nelec = 3, (2, 1)
>>> init = InitializeModes.from_hartree_fock(norb, nelec)
>>> print([bool(occ) for occ in init.occupation]) # alpha modes 0,1; beta mode 3 (= norb + 0)
[True, True, False, True, False, False]
Próximos passos
-
Percorra o guia SKQD para ver
ffsim.apply_unitary()effsim.sample_state_vector()usados juntos para amostrar circuitos construídos com as portas deste pacote, e para avaliar o valor esperado de um Hamiltoniano por meio deffsim.linear_operator()(ele próprio apoiado no mesmo método de protocolo_linear_operator_descrito aqui). -
Percorra o guia LUCJ para a outra metade da história: levar um operador de ansatz do ffsim pelo pipeline de transpilação deste pacote até o mapa de acoplamento de um dispositivo.
-
Leia o guia de circuito fermiônico para conhecer a indexação de modos genérica, sem spin, usada fora das chamadas de simulação.
-
Leia o guia de transpilação para saber como sair do espaço fermiônico
e simular em qubits, o que é necessário para operadores que não conservam partículas.
-
Explore
qiskit_fermions.protocolspara ver o catálogo completo de protocolos que este pacote define, incluindo os protocolos de conversão (SupportsFermionOperator,SupportsMajoranaOperator) que não têm relação com o ffsim. -
Leia os guias do próprio ffsim para os fluxos de trabalho que ele cobre, em particular construir um ansatz UCJ e otimizá-lo de forma variacional. Os vetores de parâmetros que esses guias manipulam são os que
UCJeUCCaceitam, por meio dos operadores que eles constroem.