L'ansatz
Regarde Victoria Lipinska expliquer ce qu'est un ansatz et pourquoi il est important dans le contexte d'un solveur propre quantique variationnel (VQE).
Références
Les articles suivants sont cités dans la vidéo ci-dessus.
- 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.
Code de l'ansatz
Dans la leçon précédente, tu as créé un Hamiltonien qui décrit l'énergie de la molécule qui t'intéresse, et tu l'as mis sous un format exploitable par un ordinateur quantique. Le VQE utilise un circuit variationnel pour préparer des états quantiques. On se sert ensuite de ces états pour déterminer la valeur attendue de l'Hamiltonien (l'énergie). Les paramètres du circuit variationnel sont ajustés jusqu'à ce que le calcul converge vers une valeur attendue minimale. Dans le contexte de la chimie quantique, cette valeur doit correspondre à l'énergie de l'état fondamental. Cette leçon se concentre sur le circuit variationnel, également appelé ansatz (un mot allemand signifiant « approche » ou « méthode »). Dans cette leçon, tu apprendras :
- l'ensemble des ansaetze prêts à l'emploi disponibles dans la bibliothèque de circuits
- comment spécifier ou modifier les caractéristiques d'un ansatz
- comment construire ton propre ansatz
- des exemples de bons et de mauvais ansaetze
La bibliothèque de circuits Qiskit propose de nombreuses catégories de circuits pouvant servir d'ansatz. Ici, nous limiterons notre discussion aux circuits deux-locaux (circuits composés de portes agissant sur au plus deux qubits à la fois). Efficient SU2 est un ansatz couramment utilisé.
Un circuit efficient_su_2 est constitué de couches d'opérations sur un seul qubit couvertes par SU(2) (groupe unitaire spécial de degré 2, comme les portes de rotation de Pauli) et d'enchevêtrements CX. Il s'agit d'un schéma heuristique qui peut être utile dans des algorithmes quantiques variationnels comme le VQE et les circuits de classification en apprentissage automatique quantique (QML).
Commençons par un exemple de circuit efficient_su2 à quatre qubits avec deux types de portes SU(2), disons rx et y. On spécifie également un schéma d'enchevêtrement et le nombre de répétitions. Si tu te contentes de .draw() les circuits, tu obtiendras une représentation assez abstraite. On obtient un schéma de circuit plus lisible avec .decompose().draw(), et ici on utilisera 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 ├
└──────────┘└───┘ └───┘ └──────────┘└───┘
Les portes SU(2) apparaissent au début et à la fin, dans l'ordre et avec les éléments spécifiés dans su2_gates = [...]. Le schéma d'enchevêtrement linear signifie que les portes CX parcourent les qubits numérotés en séquence, enchevêtrant 0 & 1, puis 1 & 2, et ainsi de suite en diagonale dans le circuit. Comme tu peux t'y attendre, définir reps = 2 ajoute simplement une couche d'enchevêtrement et une couche SU(2) finale. Définir reps = n correspond à n couches d'enchevêtrement, avec des couches SU(2) entre elles et à chaque extrémité.
SU2_ansatz2 = efficient_su2(
4, su2_gates=["rx", "y", "z"], entanglement="linear", reps=2
)
SU2_ansatz2.decompose().draw(output="mpl")
Il existe plusieurs autres schémas d'enchevêtrement. Deux méritent d'être mentionnés : circular et full. L'enchevêtrement circulaire est identique à l'enchevêtrement linéaire, mais avec une porte CX supplémentaire enchevêtrant le premier et le dernier qubit. Le schéma d'enchevêtrement complet inclut une porte CX entre chaque paire de qubits. Note que pour un circuit à N qubits, cela représente portes , ce qui peut devenir coûteux en termes de calcul.
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")
Tu peux surveiller la profondeur de tes circuits en utilisant .depth(), ou parfois .decompose().depth().
print(SU2_ansatz4.decompose().depth())
11
Une généralisation de l'efficient_su2 est le circuit deux-local, qui est lui-même un cas particulier des circuits n-locaux. Les circuits deux-locaux contiennent également des blocs SU(2) (ou blocs de rotation) et des blocs d'enchevêtrement. Ici, on est libre de spécifier le type de portes d'enchevêtrement à utiliser, par exemple des portes CRX. Dans cet exemple, toutes les portes acceptent un paramètre, mais ce n'est pas forcément le cas. On pourrait par exemple utiliser des portes de rotation Y et des portes d'enchevêtrement 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")
Le dernier ansatz que nous aborderons par son nom est le Pauli-two-design. Ce circuit contient une rotation initiale de , et les couches de rotation contiennent des rotations de Pauli sur un seul qubit, où l'axe est choisi uniformément au hasard parmi X, Y ou Z. Les couches d'enchevêtrement sont composées de portes CZ par paires avec une profondeur totale de deux. Remarque la différence de profondeur d'enchevêtrement (et de circuit total) entre ce pauli_two_design et, par exemple, l'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")
Ces circuits variationnels prêts à l'emploi sont des heuristiques utiles, tant pour atteindre un niveau d'enchevêtrement souhaité que pour limiter la profondeur du circuit. Mais il n'y a rien de magique en eux. Tu es libre de créer ton propre circuit variationnel. C'est même potentiellement avantageux dans les cas où tu connais quelque chose sur l'enchevêtrement de l'état cible de ton système.
Pour créer ton propre ansatz, il te suffit de construire un circuit quantique dans lequel un sous-ensemble de portes est une fonction d'éléments d'un vecteur de paramètres (« theta » dans l'exemple à trois qubits ci-dessous).
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 règle générale, choisir le meilleur ansatz est un art ; le meilleur ansatz est celui qui t'aide à atteindre ta cible en le moins d'étapes d'optimisation possible. Il est plus facile d'identifier les ansaetze susceptibles d'être mauvais. Par exemple, une profondeur de circuit plus grande tend à entraîner une accumulation d'erreurs. La mitigation des erreurs peut aider, mais c'est une bonne pratique de maintenir la profondeur du circuit aussi faible que raisonnable. N'omets cependant pas l'enchevêtrement nécessaire. Tu pourrais avoir un état cible qui requiert un schéma d'enchevêtrement complet. Deux exemples ci-dessous sont susceptibles d'être de mauvais choix pour des raisons assez évidentes. Le choix d'un bon ansatz sera revisité dans les sections ultérieures dans le contexte des tests de convergence.
Ce premier circuit est probablement un mauvais choix car le dernier qubit n'est pas du tout enchevêtré avec les autres. En fait, aucune action computationnellement significative n'est effectuée sur le dernier qubit. Selon toute vraisemblance, ce dernier qubit devrait soit être enchevêtré avec les autres, soit être retiré du calcul.
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
Ce dernier circuit est probablement un mauvais choix, car la profondeur de portes est très élevée, et répéter la couche d'enchevêtrement quatre fois ne donnera vraisemblablement pas un résultat sensiblement meilleur que deux ou trois répétitions.
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
Ces circuits ne sont pas « complets » au sens où il reste des paramètres inconnus et variables à insérer dans bon nombre des portes. Ces paramètres sont choisis en formulant des hypothèses successives et en mettant à jour les paramètres pour réduire la valeur attendue de la fonction de coût (dans le contexte de la chimie, typiquement l'énergie de l'état fondamental). Dans une ou même quelques dimensions, c'est trivial. Mais le circuit ci-dessus comporte 20 paramètres variationnels, ce qui signifie que trouver l'état cible avec l'énergie minimale revient à parcourir un espace à 20 dimensions (une raison de plus de ne pas inclure des portes de circuit inutiles). C'est là qu'interviennent les algorithmes d'optimisation classiques, et c'est le sujet de la prochaine leçon.