El ansatz
Observa cómo Victoria Lipinska explica qué es un ansatz y por qué es importante en el contexto de un solucionador variacional de valores propios cuánticos (VQE).
Referencias
Los siguientes artículos se mencionan en el video anterior.
- 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 del ansatz
En la lección anterior, creaste un hamiltoniano que describe la energía de la molécula de interés y lo convertiste a un formato adecuado para un computador cuántico. VQE utiliza un circuito variacional para preparar estados cuánticos. Luego usamos estos estados para determinar el valor esperado del hamiltoniano (la energía). Los parámetros del circuito variacional se varían hasta que el cálculo converge a un mínimo del valor esperado. En el contexto de la química cuántica, este debería ser la energía del estado fundamental. Esta lección se centra en el circuito variacional, también llamado ansatz (una palabra alemana que significa "enfoque" o "método"). En esta lección aprenderás:
- el conjunto de ansätze prediseñados en la biblioteca de circuitos
- cómo establecer o modificar las propiedades de un ansatz
- cómo crear tu propio ansatz
- ejemplos de buenos y malos ansätze
La biblioteca de circuitos de Qiskit contiene muchas categorías de circuitos que pueden usarse como ansatz. Aquí limitamos nuestra discusión a circuitos dos-locales (circuitos que consisten en puertas que actúan sobre como máximo dos qubits simultáneamente). Efficient SU2 es un ansatz de uso frecuente.
Un circuito efficient_su_2 consiste en capas de operaciones de un solo qubit generadas por SU(2) (el grupo unitario especial de grado 2, como puertas de rotación de Pauli) y entrelazamientos CX. Este es un patrón heurístico que puede ser útil en algoritmos cuánticos variacionales como VQE y circuitos de clasificación en aprendizaje automático cuántico (QML).
Comenzamos con un ejemplo de circuito efficient_su2 de cuatro qubits con dos tipos de puertas SU(2), concretamente rx e y. También especificamos un esquema de entrelazamiento y el número de repeticiones. Si simplemente dibujas los circuitos con .draw(), obtendrás una representación bastante abstracta. Una representación del circuito más comprensible se obtiene con .decompose().draw(), aquí usamos output = "mpl".
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit
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 ├
└──────────┘└───┘ └───┘ └──────────┘└───┘
Las puertas SU(2) aparecen al principio y al final en el orden y con los elementos especificados en su2_gates = [...]. El esquema de entrelazamiento linear significa que las puertas CX avanzan secuencialmente a través de los qubits numerados, entrelazando 0 y 1, luego 1 y 2, y así sucesivamente, diagonalmente a través del circuito. Como era de esperar, reps = 2 simplemente añade otra capa de entrelazamiento y una capa final de SU(2). La configuración reps = n corresponde a n capas de entrelazamiento, con capas de SU(2) entre ellas y en cada extremo.
SU2_ansatz2 = efficient_su2(
4, su2_gates=["rx", "y", "z"], entanglement="linear", reps=2
)
SU2_ansatz2.decompose().draw(output="mpl")
Existen más esquemas de entrelazamiento. Dos de ellos merecen mención: circular y full. El entrelazamiento circular es idéntico al entrelazamiento lineal, pero añade una puerta CX adicional que entrelaza el primer y el último qubit. El esquema de entrelazamiento completo contiene una puerta CX entre cada par de qubits. Ten en cuenta: en un circuito de N qubits, esto supone puertas , lo que puede volverse computacionalmente intensivo.
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")
Puedes monitorizar la profundidad de tu circuito con .depth() o a veces .decompose().depth().
print(SU2_ansatz4.decompose().depth())
11
Una generalización del efficient_su2 es el circuito dos-local, que a su vez es un caso especial de los circuitos n-locales. Los circuitos dos-locales también contienen bloques SU(2) (o bloques de rotación) y bloques de entrelazamiento. Aquí podemos especificar libremente qué tipo de puertas de entrelazamiento queremos usar, por ejemplo, puertas CRX. En este ejemplo, todas las puertas aceptan un parámetro, pero esto no es obligatorio. Se podrían usar, por ejemplo, puertas de rotación Y y puertas de entrelazamiento CX.
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")
El último ansatz que discutiremos por nombre es el diseño de Pauli dos. Este circuito contiene una rotación inicial de , y las capas de rotación consisten en rotaciones de Pauli de un solo qubit, donde el eje se elige uniformemente al azar como X, Y o Z. Las capas de entrelazamiento consisten en puertas CZ por pares con una profundidad total de dos. Observa la diferencia en la profundidad de entrelazamiento (y del circuito total) entre este pauli_two_design y, por ejemplo, el 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")
Estos circuitos variacionales prediseñados son heurísticas útiles, tanto para alcanzar un nivel deseado de entrelazamiento como para limitar la profundidad del circuito. Pero no tienen nada de mágico. Puedes crear tu propio circuito variacional. Esto puede incluso ser ventajoso si sabes algo sobre el entrelazamiento del estado objetivo de tu sistema.
Para crear tu propio ansatz, simplemente construyes un circuito cuántico donde parte de las puertas son funciones de los elementos de un vector de parámetros ("theta" en el ejemplo de tres qubits a continuación).
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
En general, la selección del mejor ansatz es un arte; el mejor ansatz es aquel que te ayuda a alcanzar tu objetivo en la menor cantidad de pasos de optimización. Es más fácil identificar ansätze que probablemente sean malos. Una mayor profundidad del circuito tiende, por ejemplo, a acumular errores. La mitigación de errores puede ayudar, pero es una buena práctica mantener la profundidad del circuito lo más baja posible. Sin embargo, no omitas el entrelazamiento que sea necesario. Tu estado objetivo podría requerir un esquema de entrelazamiento completo. A continuación se muestran dos ejemplos de decisiones probablemente malas. La selección de un buen ansatz se retomará en secciones posteriores en el contexto de pruebas de convergencia.
Este primer circuito es probablemente una mala elección porque el último qubit no está entrelazado en absoluto con los demás. De hecho, no hay ninguna acción computacionalmente significativa sobre el último qubit. Lo más probable es que el último qubit deba entrelazarse con los demás o eliminarse del 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 es probablemente una mala elección porque la profundidad de puertas es muy grande y repetir la capa de entrelazamiento cuatro veces probablemente no proporcionará una aproximación significativamente mejor al estado objetivo en comparación con dos o tres repeticiones.
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
Estos circuitos no están "completos" en el sentido de que aún deben insertarse parámetros desconocidos y variables en muchas de las puertas. Estos parámetros se eligen y actualizan mediante estimaciones sucesivas para reducir el valor esperado de la función de costo (en el contexto de la química, típicamente la energía del estado fundamental). En una o incluso pocas dimensiones, esto es trivial. Sin embargo, el circuito anterior tiene 20 parámetros variacionales, lo que significa que la búsqueda del estado objetivo con la energía mínima requiere explorar un espacio de 20 dimensiones (otra razón para evitar puertas de circuito innecesarias). Aquí es donde entran los algoritmos de optimización clásica — ese es el tema de la próxima lección.