Diagonalisation quantique de Krylov
Dans cette leçon sur la diagonalisation quantique de Krylov (KQD), nous répondrons aux questions suivantes :
- Qu'est-ce que la méthode de Krylov, en général?
- Pourquoi la méthode de Krylov fonctionne-t-elle et dans quelles conditions?
- Quel est le rôle de l'informatique quantique?
La partie quantique des calculs est basée en grande partie sur les travaux de Ref [1].
La vidéo ci-dessous donne un aperçu des méthodes de Krylov dans l'informatique classique, motive leur utilisation et explique comment l'informatique quantique peut jouer un rôle dans ce domaine. Le texte suivant offre plus de détails et met en œuvre une méthode de Krylov à la fois de manière classique et en utilisant un ordinateur quantique.
1. Introduction aux méthodes de Krylov
Une méthode de sous-espace de Krylov peut se référer à n'importe laquelle des méthodes construites autour de ce qu'on appelle le sous-espace de Krylov. Un examen complet de ces derniers dépasse le cadre de cette leçon, mais les références [2-4] peuvent toutes fournir des informations plus détaillées. Nous nous concentrerons ici sur ce qu'est un sous-espace de Krylov, comment et pourquoi il est utile pour résoudre les problèmes de valeurs propres, et enfin comment il peut être mis en œuvre sur un ordinateur quantique.
Définition : Étant donné une matrice symétrique semi-définie positive , l'espace de Krylov d'ordre est l'espace couvert par les vecteurs obtenus en multipliant les puissances supérieures d'une matrice , jusqu'à , avec un vecteur de référence .
Bien que les vecteurs ci-dessus couvrent ce que nous appelons un sous-espace de Krylov, il n'y a aucune raison de penser qu'ils seront orthogonaux. On utilise souvent un processus d'orthonormalisation itératif similaire à l' orthogonalisation de Gram-Schmidt. Ici, le processus est légèrement différent puisque chaque nouveau vecteur est rendu orthogonal aux autres au fur et à mesure qu'il est généré. Dans ce contexte, on parle d' itération d'Arnoldi. A partir du vecteur initial , on génère le vecteur suivant , puis on s'assure que ce second vecteur est orthogonal au premier en soustrayant sa projection sur . C'est-à-dire
On voit aisément que , puisque
Nous procédons de la même manière pour le vecteur suivant, en nous assurant qu'il est orthogonal aux deux vecteurs précédents :
Si nous répétons ce processus pour tous les vecteurs , nous obtenons une base orthonormée complète pour un espace de Krylov. Notez que le processus d'orthogonalisation ici produira zéro une fois , puisque vecteur orthogonal couvre nécessairement tout l'espace. Le processus aboutira également à zéro si un vecteur est un vecteur propre de puisque tous les vecteurs suivants seront des multiples de ce vecteur.
1.1 Un exemple simple : Krylov à la main
Passons en revue la génération d'un sous-espace de Krylov sur une matrice trivialement petite, afin de voir le processus. Nous commençons par une matrice initiale qui nous intéresse :
Pour ce petit exemple, nous pouvons déterminer les vecteurs propres et les valeurs propres facilement, même à la main. Nous présentons ici la solution numérique.
# One might use linalg.eigh here, but later matrices may not be Hermitian. So we use
# linalg.eig in this lesson.
import numpy as np
A = np.array([[4, -1, 0], [-1, 4, -1], [0, -1, 4]])
eigenvalues, eigenvectors = np.linalg.eig(A)
print("The eigenvalues are ", eigenvalues)
print("The eigenvectors are ", eigenvectors)Output:
The eigenvalues are [2.58578644 4. 5.41421356]
The eigenvectors are [[ 5.00000000e-01 -7.07106781e-01 5.00000000e-01]
[ 7.07106781e-01 1.37464400e-16 -7.07106781e-01]
[ 5.00000000e-01 7.07106781e-01 5.00000000e-01]]
Nous les enregistrons ici pour une comparaison ultérieure :
Nous aimerions étudier comment ce processus fonctionne (ou échoue) lorsque nous augmentons la dimension de notre sous-espace de Krylov, . À cette fin, nous allons appliquer ce processus :
- Générer un sous-espace de l'espace vectoriel complet à partir d'un vecteur choisi aléatoirement (l'appeler s'il est déjà normalisé, comme ci-dessus).
- Projetez la matrice complète sur ce sous-espace et trouvez les valeurs propres de cette matrice projetée .
- Augmenter la taille du sous-espace en générant davantage de vecteurs, en veillant à ce qu'ils soient orthonormés, à l'aide d'un processus similaire à l'orthogonalisation de Gram-Schmidt.
- Projetez sur le sous-espace le plus grand et trouvez les valeurs propres de la matrice résultante, .
- Répétez cette opération jusqu'à ce que les valeurs propres convergent (ou, dans le cas présent, jusqu'à ce que vous ayez généré des vecteurs couvrant tout l'espace vectoriel de la matrice originale ).
Une mise en œuvre normale de la méthode de Krylov n'aurait pas besoin de résoudre le problème des valeurs propres de la matrice projetée sur chaque sous-espace de Krylov au fur et à mesure de sa construction. Vous pouvez construire le sous-espace de la dimension souhaitée, projeter la matrice sur ce sous-espace et diagonaliser la matrice projetée. La projection et la diagonalisation à chaque dimension du sous-espace ne sont effectuées que pour vérifier la convergence.
des dimensions :
Nous choisissons un vecteur aléatoire, disons
S'il n'est pas déjà normalisé, normalisez-le.
Nous projetons maintenant notre matrice sur le sous-espace de ce seul vecteur :
Il s'agit de la projection de la matrice sur notre sous-espace de Krylov lorsqu'il ne contient qu'un seul vecteur, . La valeur propre de cette matrice est trivialement 4. Nous pouvons considérer cela comme notre estimation d'ordre zéro des valeurs propres (dans ce cas, une seule) de . Bien qu'il s'agisse d'une estimation médiocre, c'est l'ordre de grandeur correct.
des dimensions :
Nous générons maintenant le vecteur suivant dans notre sous-espace en opérant avec sur le vecteur précédent :
Nous soustrayons maintenant la projection de ce vecteur sur notre vecteur précédent afin de garantir l'orthogonalité.
S'il n'est pas déjà normalisé, normalisez-le. Dans ce cas, le vecteur était déjà normalisé, donc
Nous projetons maintenant notre matrice A sur le sous-espace de ces deux vecteurs :
Il nous reste à déterminer les valeurs propres de cette matrice. Mais cette matrice est légèrement plus petite que la matrice complète. Dans les problèmes impliquant de très grandes matrices, il peut être très avantageux de travailler avec ce sous-espace plus petit.
Bien qu'il ne s'agisse pas encore d'une bonne estimation, elle est meilleure que l'estimation de second ordre. Nous procéderons à une nouvelle itération pour nous assurer que le processus est clair. Cependant, cela réduit l'intérêt de la méthode, puisque nous finirons par diagonaliser une matrice 3x3 lors de l'itération suivante, ce qui signifie que nous n'avons pas gagné de temps ni de puissance de calcul.
des dimensions :
Nous générons maintenant le vecteur suivant dans notre sous-espace en opérant avec A sur le vecteur précédent :
Nous soustrayons maintenant la projection de ce vecteur sur nos deux vecteurs précédents afin de garantir l'orthogonalité.
S'il n'est pas déjà normalisé, normalisez-le. Dans ce cas, le vecteur était déjà normalisé, donc
Nous projetons maintenant notre matrice sur le sous-espace de ces vecteurs :
Nous déterminons maintenant les valeurs propres :
Ces valeurs propres sont exactement les valeurs propres de la matrice originale . Cela doit être le cas, puisque nous avons étendu notre sous-espace de Krylov à l'ensemble de l'espace vectoriel de la matrice originale .
Dans cet exemple, la méthode de Krylov ne semble pas particulièrement plus facile que la diagonalisation directe. En effet, comme nous le verrons dans les sections suivantes, la méthode de Krylov n'est avantageuse qu'à partir d'une certaine dimension de la matrice; ceci a pour but de nous aider à résoudre les problèmes de valeurs propres/vecteurs propres de matrices extrêmement grandes.
C'est le seul exemple que nous montrerons "à la main", mais la section 2 ci-dessous présente des exemples de calcul.
Clarification des termes
Une idée fausse très répandue est qu'il n'existe qu'un seul sous-espace de Krylov pour un problème donné. Mais bien sûr, comme il existe de nombreux vecteurs initiaux auxquels notre matrice peut être appliquée, il existe de nombreux sous-espaces de Krylov possibles. Nous n'utiliserons l'expression "le sous-espace de Krylov" que pour faire référence à un sous-espace de Krylov spécifique déjà défini pour un exemple particulier. Pour les approches générales de résolution de problèmes, nous nous référerons à "a Krylov subspace " (sous-espace de Krylov). Une dernière précision : il est possible de se référer à un " espace de Krylov". Il est souvent appelé " sous-espace de Krylov" en raison de son utilisation dans le contexte de la projection de matrices d'un espace initial dans un sous-espace. Dans ce contexte, nous l'appellerons le plus souvent sous-espace.
Vérifiez votre compréhension
Expliquez pourquoi il n'est pas (a) utile et (b) possible d'étendre la dimension du sous-espace de Krylov au-delà de la dimension de la matrice concernée.
(a) Étant donné que nous orthonormalisons les vecteurs au fur et à mesure que nous les générons, un ensemble d’ s de ces vecteurs formera une base complète, ce qui signifie qu’une combinaison linéaire de ceux-ci permet de créer n’importe quel vecteur de l’espace.
(b) Le processus d'orthogonalisation consiste à soustraire la projection d'un nouveau vecteur sur tous les vecteurs précédents. Si tous les vecteurs précédents génèrent l'espace vectoriel entier, alors la soustraction des projections sur le sous-espace entier nous donnera toujours un vecteur nul.
Imaginons qu'un collègue chercheur fasse une démonstration de la méthode de Krylov appliquée à une petite matrice fictive. Y a-t-il un problème avec leur choix de matrice et de vecteur initial ?
et
Votre collègue a accidentellement choisi un vecteur propre pour son vecteur initial. Agir avec la matrice sur le vecteur initial renverra simplement le même vecteur, mis à l'échelle par la valeur propre. Cela ne génère pas de sous-espace de dimension croissante. Conseillez à votre collègue de choisir un vecteur initial différent, en vous assurant qu'il ne s'agit pas d'un vecteur propre.
Appliquez la méthode de Krylov à la matrice donnée, en choisissant un nouveau vecteur initial approprié. Notez les estimations de la valeur propre minimale aux ordres 0 et 1 de votre sous-espace de Krylov.
De nombreuses réponses sont possibles en fonction du choix du vecteur initial. Nous choisirons :
Pour obtenir , on applique une fois à , puis on rend orthogonal à .
Au 0ème ordre, la projection sur notre sous-espace de Krylov est la suivante
Au premier ordre, la projection sur ce sous-espace de Krylov est la suivante
Cette opération peut être effectuée à la main, mais elle est plus facile à réaliser avec numpy :
import numpy as np vstar = np.array([[1/np.sqrt(3),1/np.sqrt(3),1/np.sqrt(3)],[-1/np.sqrt(6),np.sqrt(2/3),-1/np.sqrt(6)]] ) A = np.array([[1, 1, 0], [1, 1, 1], [0, 1, 1]]) v = np.array([[1/np.sqrt(3),-1/np.sqrt(6)],[1/np.sqrt(3),np.sqrt(2/3)],[1/np.sqrt(3),-1/np.sqrt(6)]]) proj = vstar@A@v print(proj) eigenvalues, eigenvectors = np.linalg.eig(proj) print("The eigenvalues are ", eigenvalues) print("The eigenvectors are ", eigenvectors)outputs:
[[ 2.33333333 0.47140452] [ 0.47140452 -0.33333333]] The eigenvalues are [ 2.41421356 -0.41421356] The eigenvectors are [[ 0.98559856 -0.16910198] [ 0.16910198 0.98559856]]L'estimation de la valeur propre minimale est -0.414.
1.2 Types de méthodes de Krylov
les "méthodes du sous-espace de Krylov" peuvent faire référence à plusieurs techniques itératives utilisées pour résoudre les grands systèmes linéaires et les problèmes de valeurs propres. Toutes ces méthodes ont en commun de construire une solution approximative à partir d'un sous-espace de Krylov
où est l'estimation initiale (voir Ref [5]). Elles diffèrent par la manière dont elles choisissent la meilleure approximation à partir de ce sous-espace, en équilibrant des facteurs tels que le taux de convergence, l'utilisation de la mémoire et le coût de calcul global. L'objectif de cette leçon est d'exploiter l'informatique quantique dans le contexte des méthodes du sous-espace de Krylov; une discussion exhaustive de ces méthodes dépasse le cadre de cette leçon. Les brèves définitions ci-dessous ne sont données qu'à titre indicatif et comprennent quelques références permettant d'approfondir ces méthodes.
La méthode du gradient conjugué (CG) : Cette méthode est utilisée pour résoudre les systèmes linéaires symétriques et définis positifs [6]. Il minimise la norme A de l'erreur à chaque itération, ce qui le rend particulièrement efficace pour les systèmes issus d'EDP elliptiques discrétisées [7]. Nous utiliserons cette approche dans la section suivante pour expliquer pourquoi un sous-espace de Krylov serait un sous-espace efficace pour rechercher des solutions améliorées aux systèmes linéaires.
La méthode du résidu minimal généralisé (GMRES) : Cette méthode est conçue pour résoudre des systèmes linéaires généraux non symétriques. Il minimise la norme résiduelle sur un espace de Krylov à chaque itération, ce qui le rend robuste mais potentiellement gourmand en mémoire pour les grands systèmes [7].
La méthode du résidu minimal (MINRES) : Cette méthode est utilisée pour résoudre les systèmes linéaires indéfinis symétriques. Il est similaire au GMRES mais tire parti de la symétrie de la matrice pour réduire les coûts de calcul [8].
Parmi les autres approches intéressantes, on peut citer la méthode d'orthogonalisation complète (FOM), qui est étroitement liée à la méthode d'Arnoldi pour les problèmes de valeurs propres, la méthode du gradient bi-conjugué ( BiCG ) et la méthode de réduction de la dimension induite (IDR).
1.3 Pourquoi la méthode du sous-espace de Krylov fonctionne-t-elle?
Nous expliquerons ici que la méthode du sous-espace de Krylov devrait être un moyen efficace d'approximer les valeurs propres des matrices par un raffinement itératif des approximations des vecteurs propres, à travers la lentille de la descente la plus abrupte. Nous démontrerons qu'étant donné une supposition initiale d'un état fondamental, l'espace des corrections successives de cette supposition initiale qui produit la convergence la plus rapide est un sous-espace de Krylov. Nous n'apportons pas de preuve rigoureuse du comportement de convergence.
Supposons que notre matrice d'intérêt soit symétrique et définie positive. C'est ce qui rend notre argument le plus pertinent pour la méthode CG ci-dessus. Nous ne faisons ici aucune hypothèse sur l'éparpillement; nous ne prétendons pas non plus que doit être un hermitien (ce qu'il doit être s'il s'agit d'un hamiltonien).
Nous souhaitons généralement résoudre un problème de la forme suivante
On peut imaginer que où est une constante, comme dans un problème de valeurs propres. Mais l'énoncé de notre problème reste plus général pour l'instant.
Nous partons d'un vecteur qui constitue une solution approximative. Bien qu'il existe des similitudes entre cette hypothèse et dans la section 1.1, nous ne nous en servons pas ici. Nous pensons que l' contient une erreur, que nous appelons « » :
Nous définissons également l' résiduelle :
Nous utilisons ici la majuscule pour distinguer le résidu de la dimension de notre sous-espace de Krylov .
Nous voulons maintenant procéder à une correction de la forme suivante
ce qui, nous l'espérons, améliorera notre approximation. Ici, est un vecteur qui reste à déterminer. Soit l'erreur après la correction. Ensuite
Nous nous intéressons à la manière dont notre erreur se comporte lorsqu'elle est transformée par notre matrice. Calculons donc la norme de l'erreur. C'est-à-dire
où nous avons utilisé la symétrie de ainsi que le fait que . Ici, est une constante indépendante de . Comme mentionné dans la section 1.2, la norme de l'erreur n'est pas la seule grandeur que l'on pourrait choisir de minimiser, mais c'est un bon choix. Nous souhaitons observer comment cette grandeur varie en fonction du choix des vecteurs de correction . Nous définissons donc la fonction en posant
est simplement l'erreur en fonction de la correction mesurée dans la norme . Nous voulons donc choisir de telle sorte que soit aussi petit que possible. Pour ce faire, nous calculons le gradient de . En utilisant la symétrie de , nous avons
Le gradient indique la direction de la pente la plus raide, ce qui signifie que sa direction opposée correspond à la direction dans laquelle la fonction diminue le plus : la direction de la pente la plus raide en sens inverse. À partir de notre hypothèse initiale , où , on a que . Ainsi, la fonction diminue le plus fortement dans la direction du résidu . Notre choix initial serait donc le plus favorisé par l'ajout du vecteur pour un certain scalaire .
Dans l'étape suivante, nous choisissons à nouveau un vecteur et ajoutons sa valeur à l'approximation actuelle. En utilisant le même argument que précédemment, nous choisissons pour un certain scalaire . Nous continuons ainsi, de sorte que l'itération de notre vecteur est
De manière équivalente, nous voulons construire l'espace dans lequel nous choisissons nos estimations améliorées en ajoutant , , et ainsi de suite, dans l'ordre. Le vecteur estimé se situe dans
Maintenant, en utilisant la relation que
nous constatons que
Autrement dit, l'espace que nous construisons et qui se rapproche le plus efficacement de la solution correcte est précisément l'espace obtenu par l'application successive de la matrice sur . Un sous-espace de Krylov est l'espace engendré par les vecteurs représentant les directions successives de la descente la plus raide.
Enfin, nous rappelons que nous n'avons fait aucune déclaration numérique sur la mise à l'échelle de cette approche et que nous n'avons pas non plus discuté de l'avantage comparatif pour les matrices éparses. Il s'agit uniquement de motiver l'utilisation des méthodes du sous-espace de Krylov et de leur donner un sens intuitif. Nous allons maintenant explorer le comportement de ces méthodes numériquement.
Vérifiez votre compréhension
Dans le processus ci-dessus, nous avons proposé de minimiser la norme de l'erreur. Quelles autres quantités pourrait-on envisager de minimiser dans la recherche de l'état fondamental et de sa valeur propre?
On pourrait imaginer d'utiliser le vecteur résiduel au lieu de la norme de l'erreur. Dans certains cas, il peut être utile de considérer le vecteur d'erreur lui-même.
2. Méthodes de Krylov dans le calcul classique
Dans cette section, nous mettons en œuvre les itérations d'Arnoldi de manière informatique afin de pouvoir exploiter un sous-espace de Krylov pour résoudre les problèmes de valeurs propres. Nous l'appliquerons d'abord à un exemple à petite échelle, puis nous examinerons comment le temps de calcul s'adapte à l'augmentation de la taille de la matrice concernée. Une idée clé ici est que la génération des vecteurs couvrant l'espace de Krylov contribuera largement au temps de calcul total requis. La mémoire nécessaire varie selon les méthodes de Krylov. Mais les contraintes de mémoire peuvent limiter l'utilisation des méthodes traditionnelles de Krylov.
2.1 Exemple simple à petite échelle
Lors de la création d'un sous-espace de Krylov, nous devons orthonormer les vecteurs de notre sous-espace. Définissons une fonction qui prend un vecteur établi dans notre sous-espace vknown (non supposé être normalisé) et un vecteur candidat à ajouter à notre sous-espace vnext et qui rend vnext orthogonal à vknown et normalisé. Définissons également une fonction qui suit ce processus pour tous les vecteurs établis dans notre sous-espace de Krylov afin de garantir un ensemble totalement orthonormé.
# vknown is some established vector in our subspace. vnext is one we wish to add,
# which must be orthogonal to vknown.
def orthog_pair(vknown, vnext):
vknown = vknown / np.sqrt(vknown.T @ vknown)
diffvec = vknown.T @ vnext * vknown
vnext = vnext - diffvec
return vnext
# v is the candidate vector to be added to our subspace. s is the existing subspace.
def orthoset(v, s):
v = v / np.sqrt(v.T @ v)
temp = v
for i in range(len(s)):
temp = orthog_pair(s[i], temp)
v = temp / np.sqrt(temp.T @ temp)
return vDéfinissons maintenant une fonction qui construit un sous-espace de Krylov de plus en plus grand, jusqu'à ce que l'espace des vecteurs de Krylov couvre l'espace complet de la matrice originale. Cela nous permettra de voir dans quelle mesure les valeurs propres obtenues à l'aide de notre méthode de sous-espace de Krylov correspondent aux valeurs exactes, en fonction de la dimension du sous-espace de Krylov. Il est important de noter que notre fonction krylov_full_build renvoie les vecteurs de Krylov, les hamiltoniens projetés, les valeurs propres et le temps nécessaire.
# Necessary imports and definitions to track time in microseconds
import time
def time_mus():
return int(time.time() * 1000000)
# This function constructs a Krylov subspace that spans the whole space of the original matrix.
# Input:
# v0 : initial vector
# matrix : original matrix to be diagonalized
# Output:
# ks : Krylov vectors
# Hs : projected Hamiltonians
# eigs : eigenvalues
# k_tot_times : time required for the operation
def krylov_full_build(v0, matrix):
t0 = time_mus()
b = v0 / np.sqrt(v0 @ v0.T)
A = matrix
ks = []
ks.append(b)
Hs = []
eigs = []
Hs.append(b.T @ A @ b)
eigs.append(np.array([b.T @ A @ b]))
k_tot_times = []
for j in range(len(A) - 1):
vec = A @ ks[j].T
ortho = orthoset(vec, ks)
ks.append(ortho)
ksarray = np.array(ks)
Hs.append(ksarray @ A @ ksarray.T)
eigs.append(np.linalg.eig(Hs[j + 1]).eigenvalues)
k_tot_times.append(time_mus() - t0)
# Return the Krylov vectors, the projected Hamiltonians, the eigenvalues,
# and the total time required.
return (ks, Hs, eigs, k_tot_times)Nous allons tester cette méthode sur une matrice qui est encore assez petite, mais plus grande que ce que nous pourrions vouloir faire à la main.
# Define our small test matrix
test_matrix = np.array(
[
[4, -1, 0, 1, 0],
[-1, 4, -1, 2, 1],
[0, -1, 4, 3, 3],
[1, 2, 3, 4, 0],
[0, 1, 3, 0, 4],
]
)
# Give the test matrix and an initial guess as arguments in the function defined above.
# Calculate outputs.
test_ks, test_Hs, test_eigs, text_k_tot_times = krylov_full_build(
np.array([0.5, 0.5, 0, 0.5, 0.5]), test_matrix
)Nous pouvons vérifier nos fonctions en nous assurant qu'à la dernière étape (lorsque l'espace de Krylov est l'espace vectoriel complet de la matrice originale), les valeurs propres de la méthode de Krylov correspondent exactement à celles de la diagonalisation numérique exacte :
print(np.linalg.eig(test_matrix).eigenvalues)
print(test_eigs[len(test_matrix) - 1])Output:
[-1.36956923 8.43756009 2.9040308 5.34436028 4.68361806]
[-1.36956923 8.43756009 2.9040308 4.68361806 5.34436028]
C'est un succès. Bien entendu, ce qui compte vraiment, c'est la qualité de notre approximation en fonction de la dimension de notre sous-espace de Krylov. Étant donné que nous sommes souvent préoccupés par la recherche d'états fondamentaux et d'autres valeurs propres minimales (et pour d'autres raisons plus algébriques expliquées ci-dessous), examinons notre estimation de la valeur propre la plus faible en fonction de la dimension du sous-espace de Krylov. C'est-à-dire
def errors(matrix, krylov_eigs):
targ_min = min(np.linalg.eig(matrix).eigenvalues)
err = []
for i in range(len(matrix)):
err.append(min(krylov_eigs[i]) - targ_min)
return errimport matplotlib.pyplot as plt
krylov_error = errors(test_matrix, test_eigs)
plt.plot(krylov_error)
plt.axhline(y=0, color="red", linestyle="--") # Add dashed red line at y=0
plt.xlabel("Order of Krylov subspace") # Add x-axis label
plt.ylabel("Error in minimum eigenvalue") # Add y-axis label
plt.show()Output:
On constate que la valeur propre minimale est atteinte avec une assez grande précision dès que le sous-espace de Krylov a atteint la taille , et qu’elle est parfaite selon .
2.2 Échelle de temps avec dimension matricielle
Convainquons-nous que la méthode de Krylov peut être avantageuse par rapport aux résolveurs numériques exacts de la manière suivante :
- Construire des matrices aléatoires (pas très clairsemées, pas l'application idéale pour la KQD)
- Déterminer les valeurs propres à l'aide de deux méthodes : directement en utilisant NumPy et en utilisant un sous-espace de Krylov.
- Nous choisissons un seuil de précision pour nos valeurs propres, avant d'accepter les estimations de Krylov.
- Comparez le temps nécessaire pour résoudre le problème de ces deux manières.
Mises en garde : Comme nous le verrons en détail ci-dessous, la diagonalisation quantique de Krylov s'applique mieux aux opérateurs dont les représentations matricielles sont peu nombreuses et/ou peuvent être écrites à l'aide d'un petit nombre de groupes d'opérateurs de Pauli commutatifs. Les matrices aléatoires que nous utilisons ici ne correspondent pas à cette description. Celles-ci ne sont utiles que pour sonder l'échelle à laquelle les méthodes de Krylov classiques pourraient être utiles. Deuxièmement, en utilisant la méthode de Krylov, nous calculerons les valeurs propres en utilisant de nombreux sous-espaces de Krylov de tailles différentes. Nous indiquerons le temps nécessaire pour obtenir le sous-espace de Krylov de dimension minimale qui permet d'obtenir la précision requise pour la valeur propre de l'état fondamental. Là encore, il s'agit d'une situation un peu différente de la résolution d'un problème insoluble pour les résolveurs exacts, puisque nous utilisons la solution exacte pour évaluer la dimension nécessaire.
Nous commençons par générer notre ensemble de matrices aléatoires.
import numpy as np
# Set the random seed
np.random.seed(42)
# how many random matrices will we make
num_matrix = 200
matrices = []
for m in range(1, num_matrix):
matrices.append(np.random.rand(m, m))Nous allons maintenant diagonaliser chaque matrice directement, en utilisant numpy. Nous calculons le temps nécessaire à la diagonalisation pour une comparaison ultérieure.
matrix_numpy_times = []
matrix_numpy_eigs = []
for mm in range(num_matrix - 1):
t0 = time_mus()
matrix_numpy_eigs.append(min(np.linalg.eig(matrices[mm]).eigenvalues))
matrix_numpy_times.append(time_mus() - t0)
plt.plot(matrix_numpy_times)
plt.xlabel("Dimension of matrix") # Add x-axis label
plt.ylabel("Time to diagonalize (microsec)") # Add y-axis label
plt.show()Output:
Notez que dans l'image ci-dessus, le temps anormalement élevé autour d'une dimension de 125 peut être dû à la nature aléatoire des matrices ou à l'implémentation sur le processeur classique utilisé, mais il n'est pas reproductible. Une nouvelle exécution du code produira un profil différent avec des pics anormaux différents.
Pour chaque matrice, nous allons maintenant construire un sous-espace de Krylov et calculer les valeurs propres par étapes. À chaque étape, nous vérifions si la valeur propre la plus basse a été obtenue avec l'erreur absolue spécifiée. Le sous-espace qui nous donne en premier des valeurs propres dans les limites de l'erreur spécifiée est le sous-espace pour lequel nous enregistrerons les temps de calcul. L'exécution de cette cellule peut prendre plusieurs minutes, en fonction de la vitesse du processeur. N'hésitez pas à sauter l'évaluation ou à réduire la dimension maximale des matrices diagonalisées. Il suffit de regarder les résultats précalculés.
# Choose the absolute error you can tolerate, and make a list for tracking the Krylov subspace size
# at which that error is achieved.
abserr = 0.05
accept_subspace_size = []
# Lists to store total time spent on the Krylov method, and the subset of that time spent on
# diagonalizing the projected matrix.
matrix_krylov_tot_times = []
matrix_krylov_dim = []
# Step through all our random matrices
for mm in range(0, num_matrix - 1):
test_ks, test_Hs, test_eigs, test_k_tot_times = krylov_full_build(
np.ones(len(matrices[mm])), matrices[mm]
)
# We have not yet found a Krylov subspace that produces our minimum eigenvalue to
# within the required error.
found = 0
for j in range(0, len(matrices[mm]) - 1):
# If we still haven't found the desired subspace...
if found == 0:
# ...but if this one satisfies the requirement, then record everything
if (
abs((min(test_eigs[j]) - matrix_numpy_eigs[mm]) / matrix_numpy_eigs[mm])
< abserr
):
accept_subspace_size.append(j)
matrix_krylov_tot_times.append(test_k_tot_times[j])
matrix_krylov_dim.append(mm)
found = 1Traçons les temps que nous avons obtenus pour ces deux méthodes à titre de comparaison :
plt.plot(matrix_numpy_times, color="blue")
plt.plot(matrix_krylov_dim, matrix_krylov_tot_times, color="green")
plt.xlabel("Dimension of matrix") # Add x-axis label
plt.ylabel("Time to diagonalize (microsec)") # Add y-axis label
plt.show()Output:
Il s'agit des temps réels nécessaires, mais pour les besoins de la discussion, nous allons lisser ces courbes en faisant la moyenne sur quelques points adjacents / dimensions de la matrice. C'est ce qui est fait ci-dessous :
smooth_numpy_times = []
smooth_krylov_times = []
# Choose the number of adjacent points over which to average forward;
# the same will be used backward.
smooth_steps = 10
# We will do this smoothing for all points/matrix dimensions
for i in range(len(matrix_krylov_tot_times)):
# Ensure we don't exceed the boundaries of our lists
start = max(0, i - smooth_steps)
end = min(len(matrix_krylov_tot_times) - 1, i + smooth_steps)
# Dummy variables for accumulating an average over adjacent points. This is done for both Krylov
# and the NumPy calculations.
smooth_count = 0
smooth_numpy_sum = 0
smooth_krylov_sum = 0
for j in range(start, end):
smooth_numpy_sum = smooth_numpy_sum + matrix_numpy_times[j]
smooth_krylov_sum = smooth_krylov_sum + matrix_krylov_tot_times[j]
smooth_count = smooth_count + 1
# Appending the averaged adjacent values to our new smooth lists
smooth_numpy_times.append(smooth_numpy_sum / smooth_count)
smooth_krylov_times.append(smooth_krylov_sum / smooth_count)plt.plot(smooth_numpy_times, color="blue")
plt.plot(smooth_krylov_times, color="green")
plt.xlabel("Dimension of matrix") # Add x-axis label
plt.ylabel("Time to diagonalize (smoothed, microsec)") # Add y-axis label
plt.show()Output:
Notez que le temps nécessaire à la construction d'un sous-espace de Krylov dépasse initialement le temps nécessaire à la diagonalisation complète de numpy. Mais lorsque la taille de la matrice augmente, la méthode de Krylov devient avantageuse. Cela est vrai même si nous abaissons notre erreur acceptable, mais l'avantage apparaît lorsque la taille de la matrice est plus grande. Cela vaut la peine d'être décortiqué.
La complexité temporelle de la diagonalisation numérique est de (avec quelques variations d'un algorithme à l'autre). La complexité temporelle de la génération d'une base orthonormée de vecteurs est également de . L'avantage de la méthode de Krylov ne réside donc pas dans l'utilisation d'une base orthonormée , mais dans l'utilisation d'une base orthonormée particulière qui sélectionne efficacement les valeurs propres d'intérêt. Nous l'avons déjà vu dans l'esquisse d'une preuve dans la première section de cette leçon, et cela est essentiel pour les garanties de convergence dans les méthodes de Krylov.
Passons en revue les progrès réalisés jusqu'à présent :
- Pour les très grandes matrices, la méthode du sous-espace de Krylov peut donner des valeurs propres approximatives dans les tolérances requises plus rapidement que les algorithmes de diagonalisation traditionnels.
- Pour des matrices aussi grandes, la génération d'un sous-espace de Krylov est la partie la plus longue de la méthode du sous-espace de Krylov.
- Il serait donc très utile de disposer d'un moyen efficace de générer un sous-espace de Krylov. C'est enfin là que l'ordinateur quantique entre en scène.
Vérifiez votre compréhension
Reportez-vous au graphique lissé ci-dessus représentant les temps de diagonalisation en fonction de la dimension de la matrice.
(a) D'après ce graphique, à partir de quelle dimension approximative de la matrice la méthode de Krylov est-elle devenue plus rapide?
(b) Quels aspects du calcul pourraient modifier la dimension à partir de laquelle la méthode de Krylov devient plus rapide?
(a) Les résultats peuvent varier si vous relancez le calcul, mais la méthode de Krylov devient plus rapide à partir d'une dimension d'environ 80 à 85.
(b) Il existe de nombreuses réponses possibles. Parmi les facteurs importants, on peut citer la précision requise et la稀疏性 des matrices à diagonaliser.
3. Krylov via l'évolution temporelle
Tout ce que nous avons décrit jusqu'à présent peut être réalisé de manière classique. Comment et quand utiliserions-nous un ordinateur quantique? Pour les très grandes matrices, la méthode de Krylov peut nécessiter de longs temps de calcul et de grandes quantités de mémoire. Le temps nécessaire à l'opération matricielle de sur est équivalent à celui de dans le pire des cas. Même la multiplication de matrices éparses sur un vecteur (le cas typique des solveurs classiques de type Krylov) a une échelle de complexité temporelle de l'ordre de . Ceci est fait pour chaque vecteur que nous voulons dans notre sous-espace. La dimension du sous-espace n'est généralement pas une fraction significative de , et s'échelonne souvent comme . Par conséquent, la génération de tous les vecteurs est comparable à dans le pire des cas. Bien qu'il y ait d'autres étapes, comme l'orthogonalisation, c'est l'échelle dominante qu'il faut garder à l'esprit.
L'informatique quantique nous permet de modifier les attributs du problème qui déterminent l'échelle du temps et des ressources nécessaires. Au lieu de dépendre de la taille de la matrice , nous verrons des éléments tels que le nombre de tirs et le nombre de termes de Pauli non commutatifs qui composent l'hamiltonien. Voyons comment cela fonctionne.
3.1 Évolution dans le temps
Rappelons que l'opérateur qui fait évoluer dans le temps un état quantique est (et il est très courant, en particulier en informatique quantique, de supprimer le de la notation). Une façon de comprendre et même de réaliser une telle fonction exponentielle d'un opérateur est d'examiner son expansion en série de Taylor. Notez que cette opération agissant sur un vecteur initial produit une somme de termes avec des puissances croissantes de appliquées à l'état initial. Il semble que nous puissions créer notre sous-espace de Krylov en faisant évoluer dans le temps l'état de notre supposition initiale!
La mise en garde concerne la réalisation de l'évolution temporelle sur un véritable ordinateur quantique. De nombreux termes de l'hamiltonien ne commuteront pas entre eux. Ainsi, si certains opérateurs exponentiels simples comme correspondent à des circuits simples, ce n'est pas le cas des hamiltoniens généraux. Et comme ils contiennent des termes non commutatifs, nous ne pouvons pas simplement décomposer l'exponentielle en un produit de termes simples, comme nous pouvons le faire avec les nombres.
Ce n'est donc pas trivial, mais c'est un processus bien étudié dans l'informatique quantique. Nous réalisons l'évolution temporelle sur les ordinateurs quantiques à l'aide d'un processus appelé trotterisation, qui est en soi un sujet très riche [10]. Mais à un niveau très élevé, en divisant l'évolution temporelle en très petites étapes, par exemple étapes de taille , nous limitons les effets de la non-commutativité des termes.
où .
Appelons "sous-espace de Krylov d'ordre r" un sous-espace de Krylov que nous avons généré dans le contexte classique en utilisant directement des puissances de H.
Nous générons maintenant un espace similaire en utilisant l'opérateur d'évolution temporelle unitaire ; nous l'appellerons "espace de Krylov unitaire" . Le sous-espace de Krylov puissance que nous utilisons classiquement ne peut pas être généré directement sur un ordinateur quantique car n'est pas un opérateur unitaire. On peut montrer que l'utilisation du sous-espace de Krylov unitaire donne des garanties de convergence similaires à celles du sous-espace de Krylov puissance, à savoir que l'erreur de l'état fondamental converge efficacement tant que l'état initial a un chevauchement avec l'état fondamental réel qui ne s'évanouit pas exponentiellement, et tant qu'il y a un écart suffisant entre les valeurs propres. Voir la référence [1] pour une discussion plus précise sur la convergence.
Ici, les puissances de deviennent des pas de temps différents (la puissance de avance d'un temps ). Nous pouvons appeler l'élément du sous-espace qui évolue dans le temps pour la durée totale .
Nous pouvons projeter notre hamiltonien H sur le sous-espace de Krylov unitaire, . En d'autres termes, nous calculons chaque élément de la matrice de dans la base . Nous appellerons cette matrice projetée .
3.2 Comment implémenter sur un ordinateur quantique
Les éléments de la matrice de sont donnés par les valeurs d'espérance , qui peuvent être estimées à l'aide de l'ordinateur quantique. N'oubliez pas que peut être écrit comme une somme d'opérateurs de Pauli sur différents qubits, et que tous les opérateurs de Pauli ne peuvent pas être mesurés simultanément. Nous pouvons trier les termes de Pauli en groupes de termes commutatifs et les mesurer tous en même temps. Mais nous pourrions avoir besoin de plusieurs groupes de ce type pour couvrir tous les termes. Le nombre de groupes distincts de navetteurs dans lesquels les termes peuvent être répartis ( ) devient donc important.
Ici, désigne une chaîne de Pauli de la forme ou un ensemble de telles chaînes de Pauli qui commutent entre elles. Étant donné que l'on peut écrire sous la forme d'une somme d'opérateurs mesurables, les expressions suivantes pour les éléments de matrice de peuvent être mises en œuvre à l'aide de l'estimateur primitif IBM Quantum.
Où sont les vecteurs de l'espace de Krylov unitaire et sont les multiples du pas de temps choisi. Sur un ordinateur quantique, le calcul de chaque élément de matrice peut être effectué à l'aide de n'importe quel algorithme permettant d'obtenir un chevauchement entre les états quantiques. Dans cette leçon, nous allons nous intéresser au test de Hadamard. Étant donné que l’ e a pour dimension , l’hamiltonien projeté dans le sous-espace aura pour dimension . Si est suffisamment petit (en général, suffit pour garantir la convergence des estimations des valeurs propres), on peut alors facilement diagonaliser l’hamiltonien projeté , de manière classique. Cependant, nous ne pouvons pas diagonaliser directement en raison de la non-orthogonalité des vecteurs de l'espace de Krylov. Il va falloir mesurer leurs chevauchements et construire une matrice
Cela nous permet de résoudre le problème des valeurs propres dans un espace non orthogonal (également appelé problème généralisé des valeurs propres)
On peut alors obtenir des estimations des valeurs propres et des états propres de en examinant les solutions de ce problème généralisé des valeurs propres. Par exemple, l'estimation de l'énergie de l'état fondamental est obtenue en prenant la plus petite valeur propre et l'état fondamental à partir du vecteur propre correspondant . Les coefficients dans déterminent la contribution des différents vecteurs qui couvrent .
Problème général des valeurs propres
Pourquoi ne pouvons-nous pas simplement diagonaliser ? Étant donné que contient des informations sur la géométrie de la base de Krylov (qui n'est pas orthogonale sauf dans des cas très particuliers), ne décrit pas à lui seul une projection de l'hamiltonien complet, de sorte que ses valeurs propres n'ont aucune relation particulière avec celles de l'hamiltonien complet - elles pourraient être n'importe quelles valeurs aléatoires. La résolution du problème des valeurs propres généralisées est nécessaire pour obtenir les valeurs propres et les vecteurs propres approximatifs correspondant à la projection de l'hamiltonien complet dans l'espace de Krylov...
La figure montre une représentation du circuit du test de Hadamard modifié, une méthode utilisée pour calculer le chevauchement entre différents états quantiques. Pour chaque élément de la matrice , un test de Hadamard entre les états , est effectué. Ceci est mis en évidence dans la figure par le schéma de couleurs pour les éléments de la matrice et les opérations , correspondantes. Ainsi, un ensemble de tests de Hadamard pour toutes les combinaisons possibles de vecteurs de l'espace de Krylov est nécessaire pour calculer tous les éléments de la matrice de l'hamiltonien projeté . Le fil supérieur du circuit de test de Hadamard est un qubit d'ancilla qui est mesuré soit dans la base X, soit dans la base Y. Sa valeur d'espérance détermine la valeur du chevauchement entre les états. Le fil du bas représente tous les qubits de l'hamiltonien du système. L'opération prépare le qubit du système dans l'état contrôlé par l'état du qubit ancillaire (de même pour ) et l'opération représente la décomposition de Pauli de l'hamiltonien du système . La mise en œuvre de cette opération sur un ordinateur quantique est présentée plus en détail ci-dessous.
4. Diagonalisation quantique de Krylov sur un ordinateur quantique
Nous allons maintenant mettre en œuvre la diagonalisation quantique de Krylov sur un véritable ordinateur quantique. Commençons par importer quelques paquets utiles.
import numpy as np
import scipy as sp
import matplotlib.pylab as plt
from typing import Union, List
import warnings
from qiskit.quantum_info import SparsePauliOp, Pauli
from qiskit.circuit import Parameter
from qiskit import QuantumCircuit, QuantumRegister
from qiskit.circuit.library import PauliEvolutionGate
from qiskit.synthesis import LieTrotter
# from qiskit.providers.fake_provider import Fake20QV1
from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2 as Estimator, Batch
import itertools as it
warnings.filterwarnings("ignore")Nous définissons la fonction ci-dessous pour résoudre le problème des valeurs propres généralisées que nous venons d'expliquer.
def solve_regularized_gen_eig(
h: np.ndarray,
s: np.ndarray,
threshold: float,
k: int = 1,
return_dimn: bool = False,
) -> Union[float, List[float]]:
"""
Method for solving the generalized eigenvalue problem with regularization
Args:
h (numpy.ndarray):
The effective representation of the matrix in our Krylov subspace
s (numpy.ndarray):
The matrix of overlaps between vectors of our Krylov subspace
threshold (float):
Cut-off value for the eigenvalue of s
k (int):
Number of eigenvalues to return
return_dimn (bool):
Whether to return the size of the regularized subspace
Returns:
lowest k-eigenvalue(s) that are the solution of the regularized generalized eigenvalue problem
"""
s_vals, s_vecs = sp.linalg.eigh(s)
s_vecs = s_vecs.T
good_vecs = np.array([vec for val, vec in zip(s_vals, s_vecs) if val > threshold])
h_reg = good_vecs.conj() @ h @ good_vecs.T
s_reg = good_vecs.conj() @ s @ good_vecs.T
if k == 1:
if return_dimn:
return sp.linalg.eigh(h_reg, s_reg)[0][0], len(good_vecs)
else:
return sp.linalg.eigh(h_reg, s_reg)[0][0]
else:
if return_dimn:
return sp.linalg.eigh(h_reg, s_reg)[0][:k], len(good_vecs)
else:
return sp.linalg.eigh(h_reg, s_reg)[0][:k]Au moins dans le cadre d'une analyse comparative initiale, il est utile de connaître une solution classique exacte pour vérifier le comportement de la convergence. La fonction ci-dessous calcule l'énergie de l'état fondamental d'un hamiltonien, en utilisant le hamiltonien et le nombre de qubits comme arguments.
def single_particle_gs(H_op, n_qubits):
"""
Find the ground state of the single particle(excitation) sector
"""
H_x = []
for p, coeff in H_op.to_list():
H_x.append(set([i for i, v in enumerate(Pauli(p).x) if v]))
H_z = []
for p, coeff in H_op.to_list():
H_z.append(set([i for i, v in enumerate(Pauli(p).z) if v]))
H_c = H_op.coeffs
print("n_sys_qubits", n_qubits)
n_exc = 1
sub_dimn = int(sp.special.comb(n_qubits + 1, n_exc))
print("n_exc", n_exc, ", subspace dimension", sub_dimn)
few_particle_H = np.zeros((sub_dimn, sub_dimn), dtype=complex)
sparse_vecs = [
set(vec) for vec in it.combinations(range(n_qubits + 1), r=n_exc)
] # list all of the possible sets of n_exc indices of 1s in n_exc-particle states
m = 0
for i, i_set in enumerate(sparse_vecs):
for j, j_set in enumerate(sparse_vecs):
m += 1
if len(i_set.symmetric_difference(j_set)) <= 2:
for p_x, p_z, coeff in zip(H_x, H_z, H_c):
if i_set.symmetric_difference(j_set) == p_x:
sgn = ((-1j) ** len(p_x.intersection(p_z))) * (
(-1) ** len(i_set.intersection(p_z))
)
else:
sgn = 0
few_particle_H[i, j] += sgn * coeff
gs_en = min(np.linalg.eigvalsh(few_particle_H))
print("single particle ground state energy: ", gs_en)
return gs_en4.1 Étape 1 : Mettre en correspondance le problème avec les circuits quantiques et les opérateurs
Nous allons maintenant définir un hamiltonien. Cette fonction se distingue de la fonction ci-dessus en ce sens qu'elle prend un hamiltonien comme argument et ne renvoie que l'état fondamental, et ce de manière classique. L'hamiltonien que nous définissons ici détermine les niveaux d'énergie de tous les états propres énergétiques, et cet hamiltonien peut être construit à l'aide d'opérateurs de Pauli et mis en œuvre sur un ordinateur quantique.
Nous choisissons un hamiltonien correspondant à une chaîne de spins qui peut avoir n'importe quelle orientation dans l'espace, appelée "chaîne de Heisenberg". Nous supposons que le spin peut être influencé par ses voisins les plus proches (les spins et ) mais pas par des voisins plus éloignés. Nous tenons également compte de la possibilité que l'interaction entre les spins soit différente lorsque les spins pointent le long d'axes différents. Cette asymétrie est parfois due, par exemple, à la structure du réseau cristallin dans lequel les spins sont intégrés.
# Define problem Hamiltonian.
n_qubits = 10
# coupling strength for XX, YY, and ZZ interactions
JX = 1
JY = 3
JZ = 2
# Define the Hamiltonian:
H_int = [["I"] * n_qubits for _ in range(3 * (n_qubits - 1))]
for i in range(n_qubits - 1):
H_int[i][i] = "Z"
H_int[i][i + 1] = "Z"
for i in range(n_qubits - 1):
H_int[n_qubits - 1 + i][i] = "X"
H_int[n_qubits - 1 + i][i + 1] = "X"
for i in range(n_qubits - 1):
H_int[2 * (n_qubits - 1) + i][i] = "Y"
H_int[2 * (n_qubits - 1) + i][i + 1] = "Y"
H_int = ["".join(term) for term in H_int]
H_tot = [
(term, JZ)
if term.count("Z") == 2
else (term, JY)
if term.count("Y") == 2
else (term, JX)
for term in H_int
]
# Get operator
H_op = SparsePauliOp.from_list(H_tot)
print(H_tot)Output:
[('ZZIIIIIIII', 2), ('IZZIIIIIII', 2), ('IIZZIIIIII', 2), ('IIIZZIIIII', 2), ('IIIIZZIIII', 2), ('IIIIIZZIII', 2), ('IIIIIIZZII', 2), ('IIIIIIIZZI', 2), ('IIIIIIIIZZ', 2), ('XXIIIIIIII', 1), ('IXXIIIIIII', 1), ('IIXXIIIIII', 1), ('IIIXXIIIII', 1), ('IIIIXXIIII', 1), ('IIIIIXXIII', 1), ('IIIIIIXXII', 1), ('IIIIIIIXXI', 1), ('IIIIIIIIXX', 1), ('YYIIIIIIII', 3), ('IYYIIIIIII', 3), ('IIYYIIIIII', 3), ('IIIYYIIIII', 3), ('IIIIYYIIII', 3), ('IIIIIYYIII', 3), ('IIIIIIYYII', 3), ('IIIIIIIYYI', 3), ('IIIIIIIIYY', 3)]
Le code ci-dessous restreint l'hamiltonien à des états à une seule particule et utilise la norme spectrale pour définir une bonne taille pour notre pas de temps . Nous choisissons de manière heuristique une valeur pour le pas de temps dt (basée sur les limites supérieures de la norme de l'hamiltonien). Ref [9] a montré qu'un pas de temps suffisamment petit est , et qu'il est préférable jusqu'à un certain point de sous-estimer cette valeur plutôt que de la surestimer, car une surestimation peut permettre aux contributions des états à haute énergie de corrompre même l'état optimal dans l'espace de Krylov. D'autre part, le choix d'une valeur trop faible pour entraîne un mauvais conditionnement du sous-espace de Krylov, puisque les vecteurs de base de Krylov diffèrent moins d'un pas de temps à l'autre.
# Get Hamiltonian restricted to single-particle states
single_particle_H = np.zeros((n_qubits, n_qubits))
for i in range(n_qubits):
for j in range(i + 1):
for p, coeff in H_op.to_list():
p_x = Pauli(p).x
p_z = Pauli(p).z
if all(p_x[k] == ((i == k) + (j == k)) % 2 for k in range(n_qubits)):
sgn = ((-1j) ** sum(p_z[k] and p_x[k] for k in range(n_qubits))) * (
(-1) ** p_z[i]
)
else:
sgn = 0
single_particle_H[i, j] += sgn * coeff
for i in range(n_qubits):
for j in range(i + 1, n_qubits):
single_particle_H[i, j] = np.conj(single_particle_H[j, i])
# Set dt according to spectral norm
dt = np.pi / np.linalg.norm(single_particle_H, ord=2)
dtOutput:
np.float64(0.17453292519943295)
Nous spécifions le nombre d'étapes de Trotter à utiliser dans l'évolution temporelle. Nous spécifions également une dimension maximale de Krylov de 4. Cette dimension de Krylov n'est pas assez grande pour des applications réalistes. Mais cela suffit pour cet exemple. De plus, nous vérifierons la convergence à des dimensions encore plus petites. Nous explorerons dans les leçons suivantes des méthodes qui nous permettront de mettre à l'échelle et de projeter nos hamiltoniens sur des sous-espaces plus grands.
# Set parameters for quantum Krylov algorithm
krylov_dim = 4 # size of krylov subspace
num_trotter_steps = 4
dt_circ = dt / num_trotter_stepsPréparation de l'État
Choisissez un état de référence qui présente un certain chevauchement avec l'état fondamental. Pour cet hamiltonien, nous utilisons l'état a avec une excitation dans le qubit du milieu comme état de référence.
qc_state_prep = QuantumCircuit(n_qubits)
qc_state_prep.x(int(n_qubits / 2) + 1)
qc_state_prep.draw("mpl", scale=0.5)Output:
Évolution dans le temps
Nous pouvons réaliser l'opérateur d'évolution temporelle généré par un hamiltonien donné : via l' approximation de Lie-Trotter. Par souci de simplicité, nous utilisons le site PauliEvolutionGate intégré dans le circuit d'évolution temporelle. La syntaxe générale est la suivante.
t = Parameter("t")
## Create the time-evo op circuit
evol_gate = PauliEvolutionGate(
H_op, time=t, synthesis=LieTrotter(reps=num_trotter_steps)
)
qr = QuantumRegister(n_qubits)
qc_evol = QuantumCircuit(qr)
qc_evol.append(evol_gate, qargs=qr)Output:
<qiskit.circuit.instructionset.InstructionSet at 0x7ccaa4664250>
Nous utiliserons une version de cette méthode ci-dessous dans le test de Hadamard, mais en avançant pour les temps .
test de Hadamard
Rappelons que nous souhaitons calculer les éléments de matrice de la matrice d' ainsi que de la matrice de Gram à l'aide du test de Hadamard. Voyons comment cela fonctionne dans ce contexte, en nous concentrant d'abord sur la construction de . Le processus global est illustré ci-dessous. Les couches de blocs de préparation d'états colorés rappellent que ce processus est effectué pour toutes les combinaisons de et dans notre sous-espace.
Les états du système aux étapes indiquées sont les suivants :
Ici, est un terme de Pauli dans la décomposition de l'hamiltonien (notez qu'il ne peut pas s'agir d'une combinaison linéaire de plusieurs termes de Pauli commutés, car cela ne serait pas unitaire - le regroupement est possible en utilisant une construction différente que nous montrerons plus tard). , sont des opérations contrôlées qui préparent , des vecteurs de l'espace de Krylov unitaire, avec . L'application des mesures de et à ce circuit permet de calculer les parties réelles et imaginaires, respectivement, des éléments de la matrice dont nous avons besoin.
En partant de l'étape 4 ci-dessus, appliquer la porte de Hadamard au qubit zeroth.
Mesurez ensuite ou .
D'après l'identité . De même, en mesurant , on obtient
En ajoutant ces étapes à l'évolution temporelle que nous avons établie précédemment, nous écrivons ce qui suit.
## Create the time-evo op circuit
evol_gate = PauliEvolutionGate(
H_op, time=dt, synthesis=LieTrotter(reps=num_trotter_steps)
)
## Create the time-evo op dagger circuit
evol_gate_d = PauliEvolutionGate(
H_op, time=dt, synthesis=LieTrotter(reps=num_trotter_steps)
)
evol_gate_d = evol_gate_d.inverse()
# Put pieces together
qc_reg = QuantumRegister(n_qubits)
qc_temp = QuantumCircuit(qc_reg)
qc_temp.compose(qc_state_prep, inplace=True)
for _ in range(num_trotter_steps):
qc_temp.append(evol_gate, qargs=qc_reg)
for _ in range(num_trotter_steps):
qc_temp.append(evol_gate_d, qargs=qc_reg)
qc_temp.compose(qc_state_prep.inverse(), inplace=True)
# Create controlled version of the circuit
controlled_U = qc_temp.to_gate().control(1)
# Create hadamard test circuit for real part
qr = QuantumRegister(n_qubits + 1)
qc_real = QuantumCircuit(qr)
qc_real.h(0)
qc_real.append(controlled_U, list(range(n_qubits + 1)))
qc_real.h(0)
print("Circuit for calculating the real part of the overlap in S via Hadamard test")
qc_real.draw("mpl", fold=-1, scale=0.5)Output:
Circuit for calculating the real part of the overlap in S via Hadamard test
Nous avons déjà mis en garde contre la profondeur des circuits de Trotter. L'exécution du test de Hadamard dans ces conditions peut donner lieu à un circuit encore plus profond, en particulier une fois que nous avons décomposé en portes natives. Ce chiffre augmente encore si l'on tient compte de la topologie de l'appareil. Ainsi, avant d'utiliser le moindre temps sur l'ordinateur quantique, il est bon de vérifier la profondeur de 2 qubits de notre circuit.
print(
"Number of layers of 2Q operations",
qc_real.decompose(reps=2).depth(lambda x: x[0].num_qubits == 2),
)Output:
Number of layers of 2Q operations 14401
Un circuit de cette complexité ne peut pas fournir de résultats exploitables sur les ordinateurs quantiques modernes. Si nous voulons créer les sites et , il nous faut trouver une meilleure solution. C'est ce qui justifie l'efficacité du test de Hadamard présenté ci-dessous.
4. Étape 2. Optimiser les circuits et les opérateurs pour le matériel cible
Test de Hadamard efficace
Nous pouvons optimiser les circuits profonds pour le test de Hadamard que nous avons obtenu en introduisant certaines approximations et en nous appuyant sur certaines hypothèses concernant l'hamiltonien du modèle. Par exemple, considérons le circuit suivant pour le test de Hadamard :
Supposons que nous puissions calculer classiquement , la valeur propre de sous l'hamiltonien . Cette condition est remplie lorsque l'hamiltonien préserve la symétrie U(1). Bien que cette hypothèse puisse sembler forte, il existe de nombreux cas où l'on peut supposer qu'il existe un état de vide (dans ce cas, il s'agit de l'état ) qui n'est pas affecté par l'action de l'hamiltonien. C'est le cas, par exemple, des hamiltoniens de chimie qui décrivent une molécule stable (où le nombre d'électrons est conservé). Étant donné que la porte , prépare l'état de référence souhaité , par exemple, préparer l'état HF pour la chimie serait un produit de NOT à un seul qubit, de sorte que controlled- est simplement un produit de CNOT. Le circuit ci-dessus met alors en œuvre l'état suivant avant la mesure :
où nous avons utilisé le déphasage simulable classique de l'étape 2 à 3. Les valeurs attendues sont donc les suivantes
En utilisant ces hypothèses, nous avons pu écrire les valeurs attendues des opérateurs d'intérêt avec moins d'opérations contrôlées. En fait, nous ne devons mettre en œuvre que la préparation contrôlée de l'état et non les évolutions temporelles contrôlées. En reformulant notre calcul comme indiqué ci-dessus, nous pourrons réduire considérablement la profondeur des circuits résultants.
Notez qu'en prime, puisque l'opérateur de Pauli apparaît maintenant comme une mesure à la fin du circuit plutôt que comme une porte contrôlée au milieu, il peut être mesuré avec d'autres opérateurs de Pauli commutés comme dans la décomposition donnée ci-dessus.
Décomposer l'opérateur d'évolution temporelle avec la décomposition de Trotter
Au lieu de mettre en œuvre l'opérateur d'évolution temporelle de manière exacte, nous pouvons utiliser la décomposition de Trotter pour en réaliser une approximation. En répétant plusieurs fois une certaine décomposition de Trotter, on parvient à réduire davantage l'erreur introduite par l'approximation. Dans ce qui suit, nous construisons directement l'implémentation de Trotter de la manière la plus efficace pour le graphe d'interaction de l'hamiltonien que nous considérons (interactions entre voisins les plus proches uniquement). En pratique, nous insérons des rotations de Pauli , , avec des intensités de couplage , et ainsi qu’un angle paramétré , qui correspondent à la mise en œuvre approximative de . Compte tenu de la différence de définition entre les rotations de Pauli et l’évolution temporelle que nous cherchons à mettre en œuvre, nous devrons utiliser le paramètre pour obtenir une évolution temporelle de . De plus, nous inversons l’ordre des opérations pour les nombres impairs de répétitions des étapes de Trotter, ce qui est fonctionnellement équivalent mais permet de synthétiser des opérations adjacentes en un seul opérateur unitair . On obtient ainsi un circuit beaucoup moins profond que celui obtenu à l'aide de la fonctionnalité PauliEvolutionGate() générique.
t = Parameter("t")
# Create instruction for rotation about XX+YY-ZZ:
Rxyz_circ = QuantumCircuit(2)
Rxyz_circ.rxx(2 * JX * t, 0, 1)
Rxyz_circ.ryy(2 * JY * t, 0, 1)
Rxyz_circ.rzz(2 * JZ * t, 0, 1)
Rxyz_instr = Rxyz_circ.to_instruction(label="R J_x XX + J_y YY + J_z ZZ")
interaction_list = [
[[i, i + 1] for i in range(0, n_qubits - 1, 2)],
[[i, i + 1] for i in range(1, n_qubits - 1, 2)],
] # linear chain
qr = QuantumRegister(n_qubits)
trotter_step_circ = QuantumCircuit(qr)
for i, color in enumerate(interaction_list):
for interaction in color:
trotter_step_circ.append(Rxyz_instr, interaction)
if i < len(interaction_list) - 1:
trotter_step_circ.barrier()
reverse_trotter_step_circ = trotter_step_circ.reverse_ops()
qc_evol = QuantumCircuit(qr)
for step in range(num_trotter_steps):
if step % 2 == 0:
qc_evol = qc_evol.compose(trotter_step_circ)
else:
qc_evol = qc_evol.compose(reverse_trotter_step_circ)
qc_evol.decompose().draw("mpl", fold=-1, scale=0.5)Output:
Nous préparons à nouveau un état initial pour ce test de Hadamard efficace.
control = 0
excitation = int(n_qubits / 2) + 1
controlled_state_prep = QuantumCircuit(n_qubits + 1)
controlled_state_prep.cx(control, excitation)
controlled_state_prep.draw("mpl", fold=-1, scale=0.5)Output:
Circuits modèles pour calculer les éléments matriciels de et via le test de Hadamard
La seule différence entre les circuits utilisés dans le test de Hadamard sera la phase de l'opérateur d'évolution temporelle et les observables mesurés. Nous pouvons donc préparer un circuit modèle qui représente le circuit générique pour le test de Hadamard, avec des espaces réservés pour les portes qui dépendent de l'opérateur d'évolution temporelle.
# Parameters for the template circuits
parameters = []
for idx in range(1, krylov_dim):
parameters.append(dt_circ * (idx))# Create modified hadamard test circuit
qr = QuantumRegister(n_qubits + 1)
qc = QuantumCircuit(qr)
qc.h(0)
qc.compose(controlled_state_prep, list(range(n_qubits + 1)), inplace=True)
qc.barrier()
qc.compose(qc_evol, list(range(1, n_qubits + 1)), inplace=True)
qc.barrier()
qc.x(0)
qc.compose(controlled_state_prep.inverse(), list(range(n_qubits + 1)), inplace=True)
qc.x(0)
qc.decompose().draw("mpl", fold=-1)Output:
print(
"The optimized circuit has 2Q gates depth: ",
qc.decompose().decompose().depth(lambda x: x[0].num_qubits == 2),
)Output:
The optimized circuit has 2Q gates depth: 50
Cette profondeur est considérablement réduite par rapport au test de Hadamard original. Cette profondeur est gérable par les ordinateurs quantiques modernes, bien qu'elle soit encore assez élevée. Nous devrons utiliser des techniques de pointe pour atténuer les erreurs afin d'obtenir des résultats utiles.
Sélectionner un backend sur lequel exécuter notre calcul de Krylov quantique, de sorte que nous puissions transpiler notre circuit pour l'exécuter sur cet ordinateur quantique.
# Use the least-busy backend or specify a quantum computer using the syntax commented out below.
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Or you may choose a specify backend and channel if necessary for your workflow.
# service = QiskitRuntimeService(channel="ibm_quantum_platform")
# backend = service.backend("ibm_fez")Nous transposons maintenant nos circuits et nos opérateurs.
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
target = backend.target
basis_gates = list(target.operation_names)
pm = generate_preset_pass_manager(
optimization_level=3, backend=backend, basis_gates=basis_gates
)
qc_trans = pm.run(qc)print(qc_trans.depth(lambda x: x[0].num_qubits == 2))
print(qc_trans.count_ops())
qc_trans.draw("mpl", fold=-1, idle_wires=False, scale=0.5)Output:
36
OrderedDict([('rz', 410), ('sx', 361), ('cz', 156), ('x', 18), ('barrier', 6)])
Après optimisation, notre profondeur de deux qubits transposée est encore réduite.
4.3 Étape 3. Exécuter à l'aide d'une primitive « IBM Quantum »
Nous créons maintenant des PUBs pour l'exécution avec l'Estimator.
# Define observables to measure for S
observable_S_real = "I" * (n_qubits) + "X"
observable_S_imag = "I" * (n_qubits) + "Y"
observable_op_real = SparsePauliOp(
observable_S_real
) # define a sparse pauli operator for the observable
observable_op_imag = SparsePauliOp(observable_S_imag)
layout = qc_trans.layout # get layout of transpiled circuit
observable_op_real = observable_op_real.apply_layout(
layout
) # apply physical layout to the observable
observable_op_imag = observable_op_imag.apply_layout(layout)
observable_S_real = (
observable_op_real.paulis.to_labels()
) # get the label of the physical observable
observable_S_imag = observable_op_imag.paulis.to_labels()
observables_S = [[observable_S_real], [observable_S_imag]]
# Define observables to measure for H
# Hamiltonian terms to measure
observable_list = []
for pauli, coeff in zip(H_op.paulis, H_op.coeffs):
# print(pauli)
observable_H_real = pauli[::-1].to_label() + "X"
observable_H_imag = pauli[::-1].to_label() + "Y"
observable_list.append([observable_H_real])
observable_list.append([observable_H_imag])
layout = qc_trans.layout
observable_trans_list = []
for observable in observable_list:
observable_op = SparsePauliOp(observable)
observable_op = observable_op.apply_layout(layout)
observable_trans_list.append([observable_op.paulis.to_labels()])
observables_H = observable_trans_list
# Define a sweep over parameter values
params = np.vstack(parameters).T
# Estimate the expectation value for all combinations of
# observables and parameter values, where the pub result will have
# shape (# observables, # parameter values).
pub = (qc_trans, observables_S + observables_H, params)Les circuits pour sont calculables de manière classique. Nous procédons ainsi avant de passer au cas en utilisant un ordinateur quantique.
from qiskit.quantum_info import StabilizerState, Pauli
qc_cliff = qc.assign_parameters({t: 0})
# Get expectation values from experiment
S_expval_real = StabilizerState(qc_cliff).expectation_value(
Pauli("I" * (n_qubits) + "X")
)
S_expval_imag = StabilizerState(qc_cliff).expectation_value(
Pauli("I" * (n_qubits) + "Y")
)
# Get expectation values
S_expval = S_expval_real + 1j * S_expval_imag
H_expval = 0
for obs_idx, (pauli, coeff) in enumerate(zip(H_op.paulis, H_op.coeffs)):
# Get expectation values from experiment
expval_real = StabilizerState(qc_cliff).expectation_value(
Pauli(pauli[::-1].to_label() + "X")
)
expval_imag = StabilizerState(qc_cliff).expectation_value(
Pauli(pauli[::-1].to_label() + "Y")
)
expval = expval_real + 1j * expval_imag
# Fill-in matrix elements
H_expval += coeff * expval
print(H_expval)Output:
(10+0j)
Bien que nous ayons pu réduire la profondeur de notre porte par des ordres de grandeur en utilisant le test de Hadamard efficace, la profondeur est encore suffisante pour nécessiter une atténuation des erreurs à la pointe de la technologie. Nous précisons ci-dessous les attributs de l'atténuation utilisée. Toutes les méthodes utilisées sont importantes, mais il convient d'attirer l'attention sur l' amplification probabiliste des erreurs (APE) en particulier. Cette technique puissante s'accompagne d'une importante surcharge quantique. Le calcul effectué ici peut prendre 20 minutes ou plus à exécuter sur un véritable ordinateur quantique. Vous pouvez jouer avec les paramètres ci-dessous pour augmenter ou diminuer la précision et, par conséquent, les frais généraux. Les paramètres par défaut ci-dessous permettent d'obtenir des résultats très fidèles.
# Experiment options
num_randomizations = 300
num_randomizations_learning = 20
max_batch_circuits = 20
shots_per_randomization = 100
learning_pair_depths = [0, 4, 24]
noise_factors = [1, 1.3, 1.6]
# Base option formatting
options = {
# Builtin resilience settings for ZNE
"resilience": {
"measure_mitigation": True,
"zne_mitigation": True,
"zne": {"noise_factors": noise_factors},
# TREX noise learning configuration
"measure_noise_learning": {
"num_randomizations": num_randomizations_learning,
"shots_per_randomization": shots_per_randomization,
},
# PEA noise model configuration
"layer_noise_learning": {
"max_layers_to_learn": 10,
"layer_pair_depths": learning_pair_depths,
"shots_per_randomization": shots_per_randomization,
"num_randomizations": num_randomizations_learning,
},
},
# Randomization configuration
"twirling": {
"num_randomizations": num_randomizations,
"shots_per_randomization": shots_per_randomization,
"strategy": "all",
},
# Experimental settings for PEA method
"experimental": {
# # Just in case, disable any further qiskit transpilation not related to twirling / DD
# "skip_transpilation": True,
# Execution configuration
"execution": {
"max_pubs_per_batch_job": max_batch_circuits,
"fast_parametric_update": True,
},
# Error Mitigation configuration
"resilience": {
# ZNE Configuration
"zne": {
"amplifier": "pea",
"return_all_extrapolated": True,
"return_unextrapolated": True,
"extrapolated_noise_factors": [0] + noise_factors,
}
},
},
}Enfin, nous exécutons les circuits pour et avec Estimator.
# This job required 17 minutes of QPU time to run on a Heron r2 processor. This is only an estimate.
# Your execution time may vary.
with Batch(backend=backend) as batch:
# Estimator
estimator = Estimator(mode=batch, options=options)
job = estimator.run([pub], precision=1)4.4 Étape 4. Post-traitement et analyse des résultats
Ce que nous avons obtenu de l'ordinateur quantique, ce sont les éléments individuels de la matrice de et les groupes de Pauli commutés qui composent les éléments de la matrice de . Ces termes doivent être combinés pour récupérer nos matrices, afin que nous puissions résoudre le problème généralisé des valeurs propres.
# Store the outputs as 'results'.
results = job.result()[0]Calculer les matrices hamiltoniennes et de chevauchement effectives
Calculer d'abord la phase accumulée par l'état au cours de l'évolution temporelle incontrôlée
prefactors = [
np.exp(-1j * sum([c for p, c in H_op.to_list() if "Z" in p]) * i * dt)
for i in range(1, krylov_dim)
]Une fois que nous avons les résultats de l'exécution des circuits, nous pouvons post-traiter les données pour calculer les éléments de la matrice de
# Assemble S, the overlap matrix of dimension D:
S_first_row = np.zeros(krylov_dim, dtype=complex)
S_first_row[0] = 1 + 0j
# Add in ancilla-only measurements:
for i in range(krylov_dim - 1):
# Get expectation values from experiment
expval_real = results.data.evs[0][0][i] # automatic extrapolated evs if ZNE is used
expval_imag = results.data.evs[1][0][i] # automatic extrapolated evs if ZNE is used
# Get expectation values
expval = expval_real + 1j * expval_imag
S_first_row[i + 1] += prefactors[i] * expval
S_first_row_list = S_first_row.tolist() # for saving purposes
S_circ = np.zeros((krylov_dim, krylov_dim), dtype=complex)
# Distribute entries from first row across matrix:
for i, j in it.product(range(krylov_dim), repeat=2):
if i >= j:
S_circ[j, i] = S_first_row[i - j]
else:
S_circ[j, i] = np.conj(S_first_row[j - i])from sympy import Matrix
Matrix(S_circ)Output:
Et les éléments de la matrice de
import itertools
# Assemble S, the overlap matrix of dimension D:
H_first_row = np.zeros(krylov_dim, dtype=complex)
H_first_row[0] = H_expval
for obs_idx, (pauli, coeff) in enumerate(zip(H_op.paulis, H_op.coeffs)):
# Add in ancilla-only measurements:
for i in range(krylov_dim - 1):
# Get expectation values from experiment
expval_real = results.data.evs[2 + 2 * obs_idx][0][
i
] # automatic extrapolated evs if ZNE is used
expval_imag = results.data.evs[2 + 2 * obs_idx + 1][0][
i
] # automatic extrapolated evs if ZNE is used
# Get expectation values
expval = expval_real + 1j * expval_imag
H_first_row[i + 1] += prefactors[i] * coeff * expval
H_first_row_list = H_first_row.tolist()
H_eff_circ = np.zeros((krylov_dim, krylov_dim), dtype=complex)
# Distribute entries from first row across matrix:
for i, j in itertools.product(range(krylov_dim), repeat=2):
if i >= j:
H_eff_circ[j, i] = H_first_row[i - j]
else:
H_eff_circ[j, i] = np.conj(H_first_row[j - i])from sympy import Matrix
Matrix(H_eff_circ)Output:
Enfin, nous pouvons résoudre le problème des valeurs propres généralisées pour :
et obtenir une estimation de l'énergie de l'état fondamental
gnd_en_circ_est_list = []
for d in range(1, krylov_dim + 1):
# Solve generalized eigenvalue problem
gnd_en_circ_est = solve_regularized_gen_eig(
H_eff_circ[:d, :d], S_circ[:d, :d], threshold=1e-1
)
gnd_en_circ_est_list.append(gnd_en_circ_est)
print("The estimated ground state energy is: ", gnd_en_circ_est)Output:
The estimated ground state energy is: 10.0
The estimated ground state energy is: 5.933953916292923
The estimated ground state energy is: 4.4101773995740645
The estimated ground state energy is: 3.921288588521255
Pour un secteur à une seule particule, nous pouvons calculer efficacement l'état fondamental de ce secteur de l'hamiltonien de manière classique
gs_en = single_particle_gs(H_op, n_qubits)Output:
n_sys_qubits 10
n_exc 1 , subspace dimension 11
single particle ground state energy: 2.391547869638771
len(H_op)Output:
27
plt.plot(
range(1, krylov_dim + 1),
gnd_en_circ_est_list,
color="blue",
linestyle="-.",
label="KQD estimate",
)
plt.plot(
range(1, krylov_dim + 1),
[gs_en] * krylov_dim,
color="red",
linestyle="-",
label="exact",
)
plt.xticks(range(1, krylov_dim + 1), range(1, krylov_dim + 1))
plt.legend()
plt.xlabel("Krylov space dimension")
plt.ylabel("Energy")
plt.title("Estimating Ground state energy with Krylov Quantum Diagonalization")
plt.show()Output:
5. Discussion et prolongement
Pour récapituler, nous partons d'un état de référence, puis nous le faisons évoluer pendant différentes périodes de temps pour générer le sous-espace de Krylov unitaire. Nous projetons notre hamiltonien sur ce sous-espace. Nous estimons également les chevauchements des vecteurs du sous-espace. Enfin, nous résolvons classiquement le problème des valeurs propres généralisées de dimension inférieure.
Comparons ce qui détermine les coûts de calcul de l'utilisation de la technique de Krylov par la méthode classique et par la méthode de la mécanique quantique. Il n'existe pas d'analogie parfaite entre les approches classiques et quantiques pour toutes les étapes. Ce tableau présente une graduation des différentes étapes à prendre en considération.
Rappelons que les hamiltoniens comportent généralement des termes qui ne peuvent pas être mesurés simultanément (car ils ne commutent pas entre eux). Nous classons les termes de l'hamiltonien en groupes d'opérateurs de Pauli commutatifs qui peuvent tous être mesurés simultanément, et il peut s'avérer nécessaire de recourir à de nombreux groupes de ce type pour rendre compte de tous les termes qui ne commutent pas entre eux. Pour générer une « » sur un ordinateur quantique, il faut effectuer des mesures distinctes pour chaque groupe de chaînes de Pauli commutatives dans l'hamiltonien, et chacune de ces mesures nécessite de nombreux essais. Nous devons effectuer cette opération pour éléments de matrice différents, correspondant à combinaisons de facteurs d'évolution temporelle différents. Il existe parfois des moyens de réduire ce temps de calcul, mais dans cette approche approximative, le temps nécessaire évolue selon la loi . Les éléments de doivent être estimés, ce qui évolue selon la loi . Enfin, la résolution du problème des valeurs propres généralisées dans l’espace projeté nécessite, de manière classique, un temps de calcul de l’ordre de .
Nous voyons que la diagonalisation de Krylov quantique peut être utile dans les cas où le nombre de groupes de Pauli commutés dans l'hamiltonien est relativement faible. Ces dépendances d'échelle suggèrent certaines applications pour lesquelles la méthode de Krylov peut être utile, et d'autres pour lesquelles elle ne le sera probablement pas. Certains hamiltoniens sont très complexes lorsqu'ils sont mis en correspondance avec des qubits, car ils impliquent de nombreuses chaînes de Pauli non commutatives qui ne peuvent pas être facilement divisées en quelques groupes commutatifs. C'est souvent le cas pour les problèmes de chimie quantique, par exemple. Cette complexité présente deux défis majeurs pour les ordinateurs quantiques à court terme :
- L'estimation de chaque élément de devient très coûteuse en raison du grand nombre de termes.
- Les circuits de Trotter nécessaires deviennent d'une profondeur prohibitive.
Ces deux points seront moins problématiques lorsque les ordinateurs quantiques atteindront la tolérance aux pannes, mais ils doivent être pris en compte à court terme. Même les systèmes dont les mappings sont plus "simples" que ceux de la chimie quantique peuvent rencontrer les mêmes obstacles si les hamiltoniens comportent trop de termes non commutatifs. La méthode de Krylov est la plus utile lorsque l'hamiltonien peut être divisé en un nombre relativement restreint de groupes de Pauli commutés et lorsque est facile à mettre en œuvre dans les circuits de trotteur. Ces deux conditions sont remplies, par exemple, pour de nombreux modèles de treillis intéressants en physique. La KQD est particulièrement utile lorsque l'on sait très peu de choses sur l'état fondamental. Cela s'explique par les garanties de convergence inhérentes à cette méthode et par son applicabilité dans des scénarios où les autres méthodes ne sont pas viables en raison d'une connaissance insuffisante de l'état du sol.
Bien que la KQD soit un outil puissant, les aspects du protocole qui prennent du temps, en particulier l'estimation de chaque élément de l'hamiltonien projeté et le chevauchement des états de Krylov, représentent des possibilités d'amélioration. Une autre approche consiste à exploiter les méthodes de Krylov en conjonction avec les méthodes basées sur l'échantillonnage, qui font l'objet de la leçon suivante.
6. Annexes
Annexe I : Sous-espace de Krylov issu d'évolutions en temps réel
L'espace de Krylov unitaire est défini comme suit
pour un certain pas de temps que nous déterminerons plus tard. Supposons temporairement que est pair : définissons alors . Remarquez que lorsque nous projetons l'hamiltonien dans l'espace de Krylov ci-dessus, il est indiscernable de l'espace de Krylov
c'est-à-dire lorsque toutes les évolutions temporelles sont décalées vers l'arrière de pas de temps. La raison pour laquelle il n'est pas possible de les distinguer est que les éléments de la matrice
sont invariants en cas de décalage global du temps d'évolution, puisque les évolutions temporelles commutent avec l'hamiltonien. Pour les impairs, nous pouvons utiliser l'analyse pour les .
Nous voulons montrer que quelque part dans cet espace de Krylov, il est garanti qu'il existe un état de basse énergie. Nous le faisons au moyen du résultat suivant, qui est dérivé du théorème 3.1 dans [3] :
Affirmation 1 : il existe une fonction telle que pour les énergies dans le domaine spectral de l'hamiltonien (c'est-à-dire entre l'énergie de l'état fondamental et l'énergie maximale),...
- pour toutes les valeurs de qui se situent à de , c'est-à-dire qu'elle est exponentiellement supprimée
- est une combinaison linéaire de pour
Nous donnons une preuve ci-dessous, mais elle peut être ignorée sans risque, à moins que l'on ne veuille comprendre l'argument complet et rigoureux. Pour l'instant, nous nous concentrons sur les implications de l'affirmation ci-dessus. En vertu de la propriété 3 ci-dessus, nous pouvons voir que l'espace de Krylov décalé ci-dessus contient l'état . Il s'agit de notre état de basse énergie. Pour comprendre pourquoi, il faut écrire dans la base propre de l'énergie :
où est le kème état propre énergétique et est son amplitude dans l'état initial . Exprimé en termes de ceci, est donné par
en utilisant le fait que l'on peut remplacer par lorsqu'il agit sur l'état propre . L'erreur énergétique de cet état est donc
Pour transformer ceci en une borne supérieure plus facile à comprendre, nous séparons d'abord la somme du numérateur en termes avec et en termes avec :
Nous pouvons limiter le premier terme par ,
où la première étape suit parce que pour chaque dans la somme, et la deuxième étape suit parce que la somme dans le numérateur est un sous-ensemble de la somme dans le dénominateur. Pour le second terme, on commence par abaisser le dénominateur par , puisque : en additionnant le tout, on obtient
Pour simplifier ce qui reste, remarquez que pour tous ces , par la définition de nous savons que . De plus, la borne supérieure de et la borne supérieure de donnent
Ceci est valable pour n'importe quel , donc si nous fixons égal à notre erreur cible, alors la limite d'erreur ci-dessus converge vers celle-ci de manière exponentielle avec la dimension de Krylov . Notez également que si , le terme disparaît entièrement dans la limite ci-dessus.
Pour compléter l'argument, nous notons tout d'abord que ce qui précède n'est que l'erreur énergétique de l'état particulier , plutôt que l'erreur énergétique de l'état de plus faible énergie dans l'espace de Krylov. Toutefois, en vertu du principe variationnel (Rayleigh-Ritz), l'erreur énergétique de l'état de plus faible énergie dans l'espace de Krylov est limitée par l'erreur énergétique de tout état dans l'espace de Krylov, de sorte que ce qui précède est également une limite supérieure de l'erreur énergétique de l'état de plus faible énergie, c'est-à-dire la sortie de l'algorithme de diagonalisation quantique de Krylov.
Il est possible d'effectuer une analyse similaire à la précédente en tenant compte du bruit et de la procédure de seuillage décrite dans le manuel. Voir [2] et [4] pour cette analyse.
Annexe II : preuve de la revendication 1
Ce qui suit est en grande partie dérivé de [3], Théorème 3.1: Soit et l'espace des polynômes résiduels (polynômes dont la valeur en 0 est 1) de degré au plus . La solution de
est
et la valeur minimale correspondante est
Nous voulons convertir cette fonction en une fonction qui peut être exprimée naturellement en termes d'exponentielles complexes, car ce sont les évolutions temporelles réelles qui génèrent l'espace de Krylov quantique. Pour ce faire, il est commode d'introduire la transformation suivante des énergies dans le domaine spectral de l'hamiltonien en nombres dans l'intervalle : définir
où est un pas de temps tel que . Remarquez que et croissent au fur et à mesure que s'éloigne de .
En utilisant maintenant le polynôme avec les paramètres a, b, d fixés à , , et d = int( r/2 ), nous définissons la fonction :
où est l'énergie de l'état fondamental. Nous pouvons voir en insérant que est un polynôme trigonométrique de degré , c'est-à-dire une combinaison linéaire de pour . De plus, d'après la définition de ci-dessus, nous avons que et pour tout dans le domaine spectral tel que nous avons
Références :
[1] https://arxiv.org/abs/2407.14431
[2] https://arxiv.org/abs/1811.09025
[3] https://people.math.ethz.ch/~mhg/pub/biksm.pdf
[4] https://academic.oup.com/book/36426
[5] https://en.wikipedia.org/wiki/Krylov_sous-espace
[6] Méthodes du sous-espace de Krylov : Principes et analyse, Jörg Liesen, Zdenek Strakos https://academic.oup.com/book/36426
[7] Iterative Methods for Sparse Linear Systems" par Yousef Saad
[8] "MINRES-QLP : A Krylov Subspace Method for Indefinite or Singular Symmetric Systems" par Sou-Cheng Choi, Christopher Paige, et Michael Saunders ( https://epubs.siam.org/doi/10.1137/100787921 )
[9] Ethan N. Epperly, Lin Lin et Yuji Nakatsukasa. "Une théorie de la diagonalisation du sous-espace quantique". SIAM Journal on Matrix Analysis and Applications 43, 1263-1290 (2022).