O ansatz
Assista enquanto Victoria Lipinska explica o que é um ansatz e por que ele é importante no contexto de um solucionador quântico variacional (VQE).
Referências
Os artigos a seguir são citados no vídeo acima.
- The theory of variational hybrid quantum-classical algorithms, McClean, et al.
- Quantum Chemistry in the Age of Quantum Computing, Cao, et al.
- Noisy intermediate-scale quantum (NISQ) algorithms, Bharti, et al.
- The Variational Quantum Eigensolver: A review of methods and best practices, Tilly, et al.
- Hardware-efficient Variational Quantum Eigensolver for Small Molecules and Quantum Magnets, Kandala, et al.
- Quantum computational chemistry, McArdle, et al.
Código do ansatz
Na lição anterior, você criou um Hamiltoniano que descreve a energia da molécula de interesse e o mapeou para um formato útil para um computador quântico. O VQE usa um circuito variacional para preparar estados quânticos. Em seguida, usamos esses estados para determinar o valor esperado do Hamiltoniano (a energia). Os parâmetros no circuito variacional são variados até que o cálculo convirja para um valor esperado mínimo. No contexto da química quântica, esse deve ser a energia do estado fundamental. Esta lição foca no circuito variacional, também chamado de ansatz (uma palavra alemã que significa "abordagem" ou "método"). Nesta lição você vai aprender
- o conjunto de ansätze pré-construídos disponíveis na biblioteca de circuitos
- Como especificar ou modificar as características de um ansatz
- Como construir seu próprio ansatz
- exemplos de ansätze bons e ruins
A biblioteca de circuitos do Qiskit tem muitas categorias de circuitos que podem ser usados como ansatz. Aqui, vamos restringir nossa discussão aos circuitos two-local (circuitos compostos por portas que atuam em, no máximo, dois qubits por vez). O Efficient SU2 é um ansatz comumente utilizado.
Um circuito efficient_su_2 consiste em camadas de operações de qubit único abrangidas por SU(2) (grupo unitário especial de grau 2, como portas de rotação de Pauli) e emaranhamentos CX. Este é um padrão heurístico que pode ser útil em algoritmos quânticos variacionais como VQE e circuitos de classificação em aprendizado de máquina quântico (QML).
Começaremos com um exemplo de circuito efficient_su2 de quatro qubits com dois tipos de portas SU(2), digamos rx e y. Também especificamos um esquema de emaranhamento e o número de repetições. Se você simplesmente .draw() os circuitos, obterá uma representação bastante abstrata. Um diagrama de circuito mais compreensível é obtido usando .decompose().draw(), e aqui usaremos `output = "mpl"'.
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"qiskit": "qiskit"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
from qiskit.circuit.library import efficient_su2
SU2_ansatz = efficient_su2(4, su2_gates=["rx", "y"], entanglement="linear", reps=1)
print(SU2_ansatz.draw())
SU2_ansatz.decompose().draw(output="mpl")
┌──────────┐┌───┐ ┌──────────┐ ┌───┐
q_0: ┤ Rx(θ[0]) ├┤ Y ├──■──┤ Rx(θ[4]) ├───┤ Y ├─────────────────────
├──────────┤├───┤┌─┴─┐└──────────┘┌──┴───┴───┐ ┌───┐
q_1: ┤ Rx(θ[1]) ├┤ Y ├┤ X ├─────■──────┤ Rx(θ[5]) ├───┤ Y ├─────────
├──────────┤├───┤└───┘ ┌─┴─┐ └──────────┘┌──┴───┴───┐┌───┐
q_2: ┤ Rx(θ[2]) ├┤ Y ├────────┤ X ├─────────■──────┤ Rx(θ[6]) ├┤ Y ├
├──────────┤├───┤ └───┘ ┌─┴─┐ ├──────────┤├───┤
q_3: ┤ Rx(θ[3]) ├┤ Y ├────────────────────┤ X ├────┤ Rx(θ[7]) ├┤ Y ├
└──────────┘└───┘ └───┘ └──────────┘└───┘
As portas SU(2) aparecem no início e no fim com a ordem e os elementos conforme especificado em su2_gates = [...]. O esquema de emaranhamento linear significa que as portas CX percorrem os qubits numerados, emaranhando 0 e 1, depois 1 e 2, e assim por diante, ao longo de uma linha diagonal no circuito. Como você poderia esperar, definir reps = 2 simplesmente adiciona uma camada de emaranhamento e uma camada final de SU(2). A configuração reps = n corresponde a n camadas de emaranhamento, com camadas de SU(2) entre elas e em cada extremidade.
SU2_ansatz2 = efficient_su2(
4, su2_gates=["rx", "y", "z"], entanglement="linear", reps=2
)
SU2_ansatz2.decompose().draw(output="mpl")
Existem vários outros esquemas de emaranhamento. Dois que valem destacar são circular e full. O emaranhamento circular é idêntico ao emaranhamento linear, mas com uma porta CX adicional emaranhando o primeiro e o último qubit. O esquema de emaranhamento full inclui uma porta CX entre cada par de qubits. Observe que, para um circuito de N qubits, isso é portas , e pode se tornar computacionalmente caro.
SU2_ansatz3 = efficient_su2(
4, su2_gates=["rx", "y", "z"], entanglement="circular", reps=1
)
SU2_ansatz3.decompose().draw(output="mpl")
SU2_ansatz4 = efficient_su2(4, su2_gates=["rx", "y", "z"], entanglement="full", reps=1)
SU2_ansatz4.decompose().draw(output="mpl")
Você pode monitorar as profundidades do seu circuito usando .depth(), ou às vezes .decompose().depth().
print(SU2_ansatz4.decompose().depth())
11
Uma generalização do efficient_su2 é o circuito two-local, que é, por sua vez, um caso especial dos circuitos n-local. Os circuitos two-local também contêm blocos SU(2) (ou blocos de rotação) e blocos de emaranhamento. Aqui, temos liberdade para especificar o tipo de portas de emaranhamento que queremos usar, por exemplo, portas CRX. Neste exemplo, todas as portas aceitam um parâmetro, mas isso não precisa ser o caso. Poder-se-ia usar portas de rotação Y e portas de emaranhamento CX, por exemplo.
from qiskit.circuit.library import n_local
rotation_blocks = ["ry"]
entanglement_blocks = ["crx"]
two_ansatz = n_local(
4, rotation_blocks, entanglement_blocks, "linear", insert_barriers=True, reps=2
)
two_ansatz.decompose().draw(output="mpl")
O último ansatz que discutiremos pelo nome é o Pauli-two-design. Este circuito contém uma rotação inicial de , e as camadas de rotação contêm rotações de Pauli de qubit único, onde o eixo é escolhido uniformemente ao acaso entre X, Y ou Z. As camadas de emaranhamento são compostas por portas CZ em pares com profundidade total de dois. Observe a diferença na profundidade de emaranhamento (e do circuito total) entre este pauli_two_design e, por exemplo, o efficient_su2.
from qiskit.circuit.library import pauli_two_design
PtwoD_ansatz = pauli_two_design(5, reps=1, seed=10599, insert_barriers=True)
PtwoD_ansatz.decompose().draw(output="mpl")
Esses circuitos variacionais pré-fabricados são heurísticas úteis tanto em termos de alcançar um nível desejado de emaranhamento quanto de limitar a profundidade do circuito. Mas não há nada de mágico neles. Você é livre para criar seu próprio circuito variacional. De fato, isso pode ser vantajoso em casos em que você sabe algo sobre o emaranhamento do estado alvo do seu sistema.
Para criar seu próprio ansatz, você simplesmente constrói um circuito quântico em que algum subconjunto de portas é função de elementos de um vetor de parâmetros ("theta" no exemplo de três qubits abaixo).
from qiskit import QuantumCircuit
from qiskit.circuit import ParameterVector
n = 3
theta = ParameterVector("θ", length=n)
qc = QuantumCircuit(n)
qc.h(0)
qc.h(2)
for i in range(n - 1):
qc.cx(i, i + 1)
qc.cz(0, n - 1)
qc.barrier()
for i in range(n):
qc.ry(theta[i], i)
qc.barrier()
qc.cz(0, n - 1)
for i in reversed(range(n - 1)):
qc.cx(i, i + 1)
qc.h(0)
qc.h(1)
own_ansatz = qc
print(own_ansatz.depth())
qc.draw("mpl")
9
Em geral, selecionar o melhor ansatz é uma arte; o melhor ansatz é qualquer um que ajude você a alcançar seu alvo no menor número de etapas de otimização. É mais fácil identificar ansätze que provavelmente serão ruins. Por exemplo, uma maior profundidade de circuito tende a resultar em acúmulo de erros. A mitigação de erros pode ajudar com isso, mas é uma boa prática manter a profundidade do circuito o mais baixa possível dentro do razoável. Mas não pule o emaranhamento que é necessário. Você pode ter um estado alvo que requer um esquema de emaranhamento completo. Dois exemplos são mostrados abaixo que provavelmente são escolhas ruins por razões bastante óbvias. A escolha de um bom ansatz será revisitada em seções posteriores no contexto de testes de convergência.
É provável que este primeiro circuito seja uma má escolha, porque o último qubit não está de forma alguma emaranhado com os demais. De fato, não há nenhuma ação computacionalmente significativa sobre o último qubit. Muito provavelmente, o último qubit deveria ser emaranhado com os demais ou removido do cálculo.
n = 4
theta = ParameterVector("θ", length=n)
qc = QuantumCircuit(n)
qc.h(0)
qc.h(2)
for i in range(n - 2):
qc.cx(i, i + 1)
qc.cz(0, n - 2)
qc.barrier()
for i in range(n):
qc.ry(theta[i], i)
qc.barrier()
qc.cz(0, n - 2)
for i in reversed(range(n - 2)):
qc.cx(i, i + 1)
qc.h(0)
qc.h(1)
own_ansatz2 = qc
print(own_ansatz2.depth())
qc.draw("mpl")
9
Este último circuito provavelmente é uma má escolha, porque a profundidade das portas é muito alta, e repetir a camada de emaranhamento quatro vezes provavelmente não produzirá uma correspondência substancialmente melhor com o estado alvo do que duas ou três repetições.
su2_ansatz_long = efficient_su2(
4, su2_gates=["rx", "y", "z"], entanglement="linear", reps=4
)
print(su2_ansatz_long.decompose().depth())
su2_ansatz_long.decompose().draw(output="mpl")
24
Esses circuitos não são "completos" no sentido de que ainda há parâmetros desconhecidos e variáveis a serem inseridos em muitas das portas. Esses parâmetros são escolhidos fazendo sucessivas estimativas e atualizando os parâmetros para diminuir o valor esperado da função de custo (no contexto da química, tipicamente a energia do estado fundamental). Em uma, ou mesmo em algumas dimensões, isso é trivial. Mas o circuito acima tem 20 parâmetros variacionais, o que significa que encontrar o estado alvo com a energia mínima significa pesquisar um estado de 20 dimensões (outra razão para não incluir portas de circuito desnecessárias). É aqui que entram os algoritmos de otimização clássicos, e esse é o assunto da próxima lição.