Skip to main content
IBM Quantum Platform

Krylov-Quanten-Diagonalisierung

In dieser Lektion über die Krylov-Quantendiagonalisierung (KQD) werden wir folgende Fragen beantworten:

  • Was ist die Krylov-Methode im Allgemeinen?
  • Warum funktioniert die Krylov-Methode und unter welchen Bedingungen?
  • Welche Rolle spielt das Quantencomputing?

Der Quantenteil der Berechnungen stützt sich weitgehend auf die Arbeiten in [1].

Das folgende Video gibt einen Überblick über die Krylov-Methoden in der klassischen Datenverarbeitung, begründet ihre Verwendung und erklärt, wie die Quanteninformatik in diesem Arbeitsbereich eine Rolle spielen kann. Der folgende Text bietet mehr Details und implementiert eine Krylov-Methode sowohl klassisch als auch unter Verwendung eines Quantencomputers.


1. Einführung in die Krylov-Methoden

Eine Krylov-Unterraum-Methode kann sich auf eine von mehreren Methoden beziehen, die auf dem so genannten Krylov-Unterraum aufbauen. Eine vollständige Übersicht über diese Themen würde den Rahmen dieser Lektion sprengen, aber die Referenzen [2-4] können wesentlich mehr Hintergrundinformationen liefern. Hier werden wir uns darauf konzentrieren, was ein Krylov-Unterraum ist, wie und warum er bei der Lösung von Eigenwertproblemen nützlich ist, und schließlich, wie er auf einem Quantencomputer implementiert werden kann.

Definition: Bei einer symmetrischen, positiv semidefiniten N×NN\times N Matrix AA ist der Krylov-Raum Kr\mathcal{K}^r der Ordnung rr der Raum, der von Vektoren aufgespannt wird, die durch Multiplikation höherer Potenzen einer Matrix AA bis zu r−1≤Nr-1\leq N mit einem Referenzvektor ∣v⟩\vert v \rangle erhalten werden.

Kr=span{∣v⟩,A∣v⟩,A2∣v⟩,...,Ar−1∣v⟩}\mathcal{K}^r = \text{span}\left\{ \vert v \rangle, A \vert v \rangle, A^2 \vert v \rangle, ..., A^{r-1} \vert v \rangle \right\}

Obwohl die obigen Vektoren einen so genannten Krylov-Unterraum aufspannen, gibt es keinen Grund zu der Annahme, dass sie orthogonal sein werden. Häufig wird ein iteratives Orthonormalisierungsverfahren ähnlich der Gram-Schmidt-Orthogonalisierung verwendet. Hier ist der Prozess etwas anders, da jeder neue Vektor bei seiner Erzeugung orthogonal zu den anderen gemacht wird. In diesem Zusammenhang wird dies als Arnoldi-Iteration bezeichnet. Ausgehend von dem Anfangsvektor ∣v⟩|v\rangle erzeugt man den nächsten Vektor A∣v⟩A|v\rangle und stellt dann sicher, dass dieser zweite Vektor orthogonal zum ersten ist, indem man seine Projektion auf ∣v⟩|v\rangle abzieht. Das bedeutet

∣v0⟩=∣v⟩∣∣∣v⟩∣∣∣v1⟩=A∣v⟩−⟨v0∣A∣v⟩∣v0⟩∣∣A∣v⟩−⟨v0∣A∣v⟩∣v0⟩∣∣\begin{aligned} |v_0\rangle &=\frac{|v\rangle}{\left|\left| |v\rangle \right|\right|}\\ |v_1\rangle &=\frac{A|v\rangle-\langle v_0|A|v\rangle |v_0\rangle}{\left|\left|A|v\rangle-\langle v_0|A|v\rangle |v_0\rangle \right|\right|} \end{aligned}

Es ist nun leicht zu erkennen, dass ∣v0⟩⊥∣v1⟩|v_0\rangle \perp |v_1\rangle gilt, da

⟨v0∣v1⟩=⟨v0∣A∣v⟩−⟨v0∣A∣v⟩⟨v0∣v0⟩∣∣A∣v⟩−⟨A∣v⟩∣v0⟩∣v0⟩∣∣=0\langle v_0 | v_1\rangle=\frac{\langle v_0 | A|v\rangle-\langle v_0 |A|v\rangle\langle v_0|v_0\rangle}{\left|\left| A|v\rangle-\langle A|v\rangle|v_0\rangle |v_0\rangle \right|\right|}=0

Das Gleiche gilt für den nächsten Vektor, wobei sichergestellt wird, dass er orthogonal zu den beiden vorherigen Vektoren ist:

∣v2⟩=A∣v1⟩−⟨v0∣A∣v1⟩∣v0⟩−⟨v1∣A∣v1⟩∣v1⟩∣∣A∣v1⟩−⟨v0∣A∣v1⟩∣v0⟩−⟨v1∣A∣v1⟩∣v1⟩∣∣|v_2\rangle=\frac{A |v_1\rangle-\langle v_0|A |v_1\rangle |v_0\rangle-\langle v_1|A |v_1\rangle |v_1\rangle}{\left|\left| A |v_1\rangle-\langle v_0|A |v_1\rangle |v_0\rangle-\langle v_1|A |v_1\rangle |v_1\rangle\right|\right|}

Wenn wir diesen Vorgang für alle rr Vektoren wiederholen, haben wir eine vollständige orthonormale Basis für einen Krylov-Raum. Man beachte, dass der Orthogonalisierungsprozess hier einmal r>mr>m zu Null führt, da mm orthogonale Vektoren notwendigerweise den gesamten Raum umfassen. Der Prozess wird auch Null ergeben, wenn ein beliebiger Vektor ein Eigenvektor von AA ist, da alle nachfolgenden Vektoren Vielfache dieses Vektors sind.

1.1 Ein einfaches Beispiel: Krylov von Hand

Gehen wir schrittweise durch die Generierung eines Krylov-Unterraums auf einer trivial kleinen Matrix, damit wir den Prozess sehen können. Wir beginnen mit einer Ausgangsmatrix AA, die für uns von Interesse ist:

A=(4−10−14−10−14)A=\begin{pmatrix}4&-1&0\\-1&4&-1\\0&-1&4\end{pmatrix}

Für dieses kleine Beispiel können wir die Eigenvektoren und Eigenwerte sogar leicht von Hand bestimmen. Wir zeigen hier die numerische Lösung.

# 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]]

Wir halten sie hier für einen späteren Vergleich fest:

a0=2.59,∣0⟩=(1/2−2/21/2)a1=4,∣1⟩=(2/20−2/2)a2=5.41,∣2⟩=(1/22/21/2)\begin{aligned} a_0&=2.59,&|0\rangle&=&\begin{pmatrix}1/2\\-\sqrt{2}/2\\1/2\end{pmatrix}\\ \\ a_1&=4,&|1\rangle&=&\begin{pmatrix}\sqrt{2}/2\\0\\-\sqrt{2}/2\end{pmatrix}\\ \\ a_2&=5.41,&|2\rangle&=&\begin{pmatrix}1/2\\\sqrt{2}/2\\1/2\end{pmatrix} \end{aligned}

Wir möchten untersuchen, wie dieser Prozess funktioniert (oder scheitert), wenn wir die Dimension unseres Krylov-Unterraums rr erhöhen. Zu diesem Zweck werden wir dieses Verfahren anwenden:

  • Erzeugen Sie einen Unterraum des vollständigen Vektorraums, beginnend mit einem zufällig gewählten Vektor ∣v⟩|v\rangle (nennen Sie ihn ∣v0⟩|v_0\rangle, wenn er bereits normalisiert ist, wie oben).
  • Projizieren Sie die vollständige Matrix AA auf diesen Unterraum, und finden Sie die Eigenwerte dieser projizierten Matrix A~\tilde{A}.
  • Vergrößern Sie den Unterraum, indem Sie weitere Vektoren erzeugen und sicherstellen, dass sie orthonormal sind, indem Sie ein Verfahren ähnlich der Gram-Schmidt-Orthogonalisierung anwenden.
  • Projizieren Sie AA auf den größeren Unterraum und finden Sie die Eigenwerte der resultierenden Matrix A~\tilde{A}.
  • Wiederholen Sie dies so lange, bis die Eigenwerte konvergieren (oder in diesem Spielzeugfall, bis Sie Vektoren erzeugt haben, die den gesamten Vektorraum der ursprünglichen Matrix abdecken AA ).

Bei einer normalen Implementierung der Krylov-Methode müsste das Eigenwertproblem für die auf jeden Krylov-Unterraum projizierte Matrix nicht gelöst werden, während sie aufgebaut wird. Man könnte den Unterraum der gewünschten Dimension konstruieren, die Matrix auf diesen Unterraum projizieren und die projizierte Matrix diagonalisieren. Die Projektion und Diagonalisierung in jeder Unterraumdimension wird nur zur Überprüfung der Konvergenz durchgeführt.

r=1r=1 der Dimension:

Wir wählen einen Zufallsvektor, sagen wir

∣v0⟩=(100)|v_0\rangle=\begin{pmatrix}1\\0\\0\end{pmatrix}

Wenn er nicht bereits normalisiert ist, normalisieren Sie ihn.

Wir projizieren nun unsere Matrix AA auf den Unterraum dieses einen Vektors:

A~0=⟨v0∣A∣v0⟩=(100)(4−10−14−10−14)(100)=(4)\tilde{A}_0=\langle v_0| A|v_0\rangle=\begin{pmatrix}1&0&0\end{pmatrix}\begin{pmatrix}4&-1&0\\-1&4&-1\\0&-1&4\end{pmatrix}\begin{pmatrix}1\\0\\0\end{pmatrix}=(4)

Dies ist unsere Projektion der Matrix auf unseren Krylov-Unterraum, wenn dieser nur einen einzigen Vektor enthält, ∣v0⟩|v_0\rangle. Der Eigenwert dieser Matrix ist trivialerweise 4. Wir können uns dies als unsere Schätzung der Eigenwerte (in diesem Fall nur einer) von AA vorstellen. Obwohl es eine schlechte Schätzung ist, ist es die richtige Größenordnung.

r=2r=2 der Dimension:

Wir erzeugen nun den nächsten Vektor in unserem Unterraum durch eine Operation mit AA auf dem vorherigen Vektor:

A∣v0⟩=(4−10−14−10−14)(100)=(4−10)A|v_0\rangle=\begin{pmatrix}4&-1&0\\-1&4&-1\\0&-1&4\end{pmatrix}\begin{pmatrix}1\\0\\0\end{pmatrix}=\begin{pmatrix}4\\-1\\0\end{pmatrix}

Nun subtrahieren wir die Projektion dieses Vektors auf unseren vorherigen Vektor, um die Orthogonalität zu gewährleisten.

∣v1⟩=A∣v0⟩−⟨v0∣A∣v0⟩∣v0⟩|v_1\rangle=A|v_0\rangle-\langle v_0 |A|v_0\rangle|v_0\rangle ∣v1⟩=(4−10)−(100)(4−10)(100)=(0−10)|v_1\rangle=\begin{pmatrix}4\\-1\\0\end{pmatrix}-\begin{pmatrix}1& 0& 0\end{pmatrix}\begin{pmatrix}4\\-1\\0\end{pmatrix}\begin{pmatrix}1\\0\\0\end{pmatrix}=\begin{pmatrix}0\\-1\\0\end{pmatrix}

Wenn er nicht bereits normalisiert ist, normalisieren Sie ihn. In diesem Fall wurde der Vektor bereits normalisiert, so dass

∣v1⟩=(0−10)|v_1\rangle=\begin{pmatrix}0\\-1\\0\end{pmatrix}

Wir projizieren nun unsere Matrix A auf den Unterraum dieser beiden Vektoren:

A~1=(1000−10)(4−10−14−10−14)(100−100)=(1000−10)(41−1−401)=(4114)\tilde{A}_1= \begin{pmatrix} 1&0&0\\0&-1&0 \end{pmatrix} \begin{pmatrix}4&-1&0\\-1&4&-1\\0&-1&4\end{pmatrix}\begin{pmatrix}1&0\\0&-1\\0&0\end{pmatrix}=\begin{pmatrix}1&0&0\\0&-1&0\end{pmatrix}\begin{pmatrix}4&1\\-1&-4\\0&1\end{pmatrix}=\begin{pmatrix}4&1\\1&4\end{pmatrix}

Bleibt noch das Problem, die Eigenwerte dieser Matrix zu bestimmen. Diese Matrix ist jedoch etwas kleiner als die Vollmatrix. Bei Problemen mit sehr großen Matrizen kann die Arbeit mit diesem kleineren Unterraum von großem Vorteil sein.

det⁡(A1~−λI)=0\det(\tilde{A_1}-\lambda I)=0 ∣4−λ114−λ∣=(4−λ)2−1=0\begin{vmatrix} 4-\lambda&1\\1&4-\lambda\end{vmatrix} =(4-\lambda)^2-1=0 4−λ=±1→λ=3,54-\lambda=±1→\lambda=3,5

Dies ist zwar immer noch keine gute Schätzung, aber besser als die Schätzung nullter Ordnung. Wir werden dies in einer weiteren Iteration durchführen, um sicherzustellen, dass der Prozess klar ist. Dies untergräbt jedoch den Sinn der Methode, da wir in der nächsten Iteration eine 3x3 -Matrix diagonalisieren werden, was bedeutet, dass wir weder Zeit noch Rechenleistung gespart haben.

r=3r=3 der Dimension:

Wir erzeugen nun den nächsten Vektor in unserem Unterraum durch eine Operation mit A auf dem vorherigen Vektor:

A∣v1⟩=(4−10−14−10−14)(0−10)=(1−41)A|v_1\rangle=\begin{pmatrix}4&-1&0\\-1&4&-1\\0&-1&4\end{pmatrix}\begin{pmatrix}0\\-1\\0\end{pmatrix}=\begin{pmatrix}1\\-4\\1\end{pmatrix}

Nun subtrahieren wir die Projektion dieses Vektors auf die beiden vorherigen Vektoren, um die Orthogonalität sicherzustellen.

∣v2⟩=A∣v1⟩−⟨v0∣A∣v1⟩∣v0⟩−⟨v1∣A∣v1⟩∣v1⟩∣v2⟩=(1−41)−(100)(1−41)(100)−(0−10)(1−41)(0−10)=(001)\begin{aligned} |v_2\rangle&=A|v_1\rangle-\langle v_0 |A|v_1\rangle|v_0\rangle-\langle v_1 |A|v_1\rangle|v_1\rangle\\ |v_2\rangle&=\begin{pmatrix}1\\-4\\1\end{pmatrix}-\begin{pmatrix}1& 0& 0 \end{pmatrix}\begin{pmatrix}1\\-4\\1\end{pmatrix}\begin{pmatrix}1\\0\\0\end{pmatrix}-\begin{pmatrix}0&-1& 0\end{pmatrix}\begin{pmatrix}1\\-4\\1\end{pmatrix}\begin{pmatrix}0\\-1\\0\end{pmatrix}=\begin{pmatrix}0\\0\\1\end{pmatrix} \end{aligned}

Wenn er nicht bereits normalisiert ist, normalisieren Sie ihn. In diesem Fall wurde der Vektor bereits normalisiert, so dass

∣v2⟩=(001)|v_2 \rangle=\begin{pmatrix}0\\0\\1\end{pmatrix}

Wir projizieren nun unsere Matrix AA auf den Unterraum dieser Vektoren:

A~2=(1000−10001)(4−10−14−10−14)(1000−10001)=(4−101−410−14)(1000−10001)=(410141014)\tilde{A}_2=\begin{pmatrix}1&0&0\\0&-1&0\\0&0&1\end{pmatrix}\begin{pmatrix}4&-1&0\\-1&4&-1\\0&-1&4\end{pmatrix}\begin{pmatrix}1&0&0\\0&-1&0\\0&0&1\end{pmatrix}=\begin{pmatrix}4&-1&0\\1&-4&1\\0&-1&4\end{pmatrix}\begin{pmatrix}1&0&0\\0&-1&0\\0&0&1\end{pmatrix}=\begin{pmatrix}4&1&0\\1&4&1\\0&1&4\end{pmatrix}

Wir bestimmen nun die Eigenwerte:

det⁡(A~2−λI)=0\det(\tilde{A}_2-\lambda I)=0 ∣4−λ1014−λ1014−λ∣=(4−λ)((4−λ)2−1)−(4−λ)=0\begin{vmatrix}4-\lambda&1&0\\1&4-\lambda&1\\0&1&4-\lambda\end{vmatrix} = (4-\lambda)((4-\lambda)^2-1)-(4-\lambda)=0\\ 4−λ=0,4−λ=±21/2→λ=4−21/2,4,4+21/2≈2.59,4,5.414-\lambda=0,4-\lambda=±2^{1/2}→\lambda=4-2^{1/2},4,4+2^{1/2}≈2.59,4,5.41

Diese Eigenwerte sind genau die Eigenwerte der Originalmatrix AA. Dies muss der Fall sein, da wir unseren Krylov-Unterraum so erweitert haben, dass er den gesamten Vektorraum der Originalmatrix AA umfasst.

In diesem Beispiel mag die Krylov-Methode nicht besonders einfacher erscheinen als die direkte Diagonalisierung. Wie wir in späteren Abschnitten sehen werden, ist die Krylov-Methode erst ab einer bestimmten Matrixdimension vorteilhaft; dies soll uns helfen, Eigenwert-/Eigenvektor-Probleme von extrem großen Matrizen zu lösen.

Ein Bild, das zeigt, wie eine sehr große Matrix auf einen Krylov-Unterraum projiziert wird, d. h. Zeilen von Krylov-Vektoren bilden eine Matrix auf der linken Seite, einen Hamiltonian, dann Spalten von Krylov-Vektoren auf der rechten Seite.

Dies ist das einzige Beispiel, das wir zeigen werden, das "von Hand" bearbeitet wurde, aber Abschnitt 2 unten zeigt rechnerische Beispiele.

Begriffsklärung

Ein häufiges Missverständnis ist, dass es nur einen einzigen Krylov-Unterraum für ein bestimmtes Problem gibt. Da es aber viele Ausgangsvektoren gibt, auf die unsere Matrix angewendet werden könnte, gibt es natürlich auch viele mögliche Krylov-Unterräume. Wir werden den Ausdruck "die Krylov-Unterraum" nur verwenden, um auf einen bestimmten Krylov-Unterraum zu verweisen, der bereits für ein bestimmtes Beispiel definiert wurde. Für allgemeine Problemlösungsansätze sprechen wir von einem " Krylov-Unterraum". Eine letzte Klarstellung ist, dass die Bezeichnung " Krylov-Raum " zulässig ist. Er wird oft als " Krylov-Unterraum " bezeichnet, weil er im Zusammenhang mit der Projektion von Matrizen aus einem Ausgangsraum in einen Unterraum verwendet wird. In diesem Zusammenhang werden wir ihn hier meist als Unterraum bezeichnen.

Überprüfen Sie Ihr Verständnis

Erklären Sie, warum es (a) nicht sinnvoll und (b) nicht möglich ist, die Dimension des Krylov-Unterraums rr über die Dimension NN der interessierenden Matrix hinaus zu erweitern.

  • (a) Da wir die Vektoren bei ihrer Erzeugung orthonormalisieren, bildet eine Menge von NN solchen Vektoren eine vollständige Basis, was bedeutet, dass sich jeder Vektor im Raum als Linearkombination dieser Vektoren darstellen lässt.

    (b) Der Orthogonalisierungsprozess besteht darin, die Projektion eines neuen Vektors auf alle vorherigen Vektoren zu subtrahieren. Wenn alle vorherigen Vektoren den gesamten Vektorraum aufspannen, erhalten wir durch Subtraktion der Projektionen auf den vollständigen Unterraum immer einen Nullvektor.

Angenommen, ein Kollege demonstriert die Krylov-Methode anhand einer kleinen Beispielmatrix. Stimmt etwas nicht mit ihrer Wahl der Matrix AA und des Startvektors ∣ψ⟩|\psi\rangle?

A=(213123335)A=\begin{pmatrix}2&1&3\\1&2&3\\3&3&5\end{pmatrix}

und

∣ψ⟩=12(1−10).|\psi\rangle=\frac{1}{\sqrt{2}}\begin{pmatrix}1\\-1\\0\end{pmatrix}.
  • Ihr Kollege hat versehentlich einen Eigenvektor für seinen Ausgangsvektor gewählt. Wenn man mit der Matrix auf den Ausgangsvektor einwirkt, erhält man einfach denselben Vektor zurück, skaliert um den Eigenwert. Dadurch wird kein Unterraum mit zunehmender Dimension erzeugt. Raten Sie Ihrem Kollegen, einen anderen Ausgangsvektor zu wählen und darauf zu achten, dass es sich nicht um einen Eigenvektor handelt.

Wende die Krylov-Methode auf die angegebene Matrix an und wähle dabei einen geeigneten neuen Startvektor aus. Schreiben Sie die Schätzungen des minimalen Eigenwerts für die 0. und 1. Ordnung Ihres Krylov-Unterraums auf.

A=(110111011)A=\begin{pmatrix}1&1&0\\1&1&1\\0&1&1\end{pmatrix}
  • Je nach Wahl des Ausgangsvektors gibt es viele mögliche Antworten. Wir werden wählen:

    ∣v0⟩=13(111).|v_0\rangle=\frac{1}{\sqrt{3}}\begin{pmatrix}1\\1\\1\end{pmatrix}.

    Um „ ∣v1⟩|v_1\rangle “ zu erhalten, wenden wir „ AA “ einmal auf „ ∣v0⟩|v_0\rangle “ an und machen dann „ ∣v1⟩|v_1\rangle “ orthogonal zu „ ∣v0⟩|v_0\rangle “.

    A∣v0⟩=(110111011)13(111)=13(232)A|v_0\rangle=\begin{pmatrix}1&1&0\\1&1&1\\0&1&1\end{pmatrix}\frac{1}{\sqrt{3}}\begin{pmatrix}1\\1\\1\end{pmatrix} = \frac{1}{\sqrt{3}}\begin{pmatrix}2\\3\\2\end{pmatrix}A∣v0⟩−⟨v0∣A∣v0⟩∣v0⟩=13(232)−13(111)13(232)13(111)=13(232)−7313(111)=32(−1/32/3−1/3)A|v_0\rangle - \langle v_0|A|v_0\rangle |v_0\rangle=\frac{1}{\sqrt{3}}\begin{pmatrix}2\\3\\2\end{pmatrix} - \frac{1}{\sqrt{3}}\begin{pmatrix}1&1&1\end{pmatrix}\frac{1}{\sqrt{3}}\begin{pmatrix}2\\3\\2\end{pmatrix}\frac{1}{\sqrt{3}}\begin{pmatrix}1\\1\\1\end{pmatrix} = \frac{1}{\sqrt{3}}\begin{pmatrix}2\\3\\2\end{pmatrix}-\frac{7}{3}\frac{1}{\sqrt{3}}\begin{pmatrix}1\\1\\1\end{pmatrix}=\sqrt{\frac{3}{2}}\begin{pmatrix}-1/3\\2/3\\-1/3\end{pmatrix}

    Bei 0-ter Ordnung ist die Projektion auf unseren Krylov-Unterraum

    ⟨v0∣A∣v0⟩=13(111)(110111011)13(111)=73\langle v_0|A|v_0\rangle=\frac{1}{\sqrt{3}}\begin{pmatrix}1&1&1\end{pmatrix} \begin{pmatrix}1&1&0\\1&1&1\\0&1&1\end{pmatrix} \frac{1}{\sqrt{3}}\begin{pmatrix}1\\1\\1\end{pmatrix} = \frac{7}{3}

    Bei 1. Ordnung ist die Projektion auf diesen Krylov-Unterraum

    ⟨V1∣A∣V1⟩=(131313−1623−16)(110111011)(13−16132313−16)\langle V^1|A|V^1\rangle=\begin{pmatrix}\frac{1}{\sqrt{3}}&\frac{1}{\sqrt{3}}&\frac{1}{\sqrt{3}}\\-\sqrt{\frac{1}{6}}&\sqrt{\frac{2}{3}}&-\sqrt{\frac{1}{6}}\end{pmatrix} \begin{pmatrix}1&1&0\\1&1&1\\0&1&1\end{pmatrix} \begin{pmatrix}\frac{1}{\sqrt{3}}&-\sqrt{\frac{1}{6}}\\\frac{1}{\sqrt{3}}& \sqrt{\frac{2}{3}} \\ \frac{1}{\sqrt{3}}&-\sqrt{\frac{1}{6}}\end{pmatrix}

    Dies kann von Hand geschehen, ist aber am einfachsten mit Numpy zu bewerkstelligen:

    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]]

    Die minimale Eigenwertschätzung lautet -0.414.

1.2 Arten von Krylov-Methoden

"Krylov-Unterraum-Methoden" können sich auf verschiedene iterative Techniken beziehen, die zur Lösung großer linearer Systeme und Eigenwertprobleme verwendet werden. Sie alle haben gemeinsam, dass sie eine Näherungslösung aus einem Krylov-Unterraum konstruieren

Kr(A,∣v⟩)=span{∣v⟩,A∣v⟩,A2∣v⟩,...,Ar−1∣v⟩},\mathcal{K}^r(A,|v\rangle ) = \text{span}\{|v\rangle, A|v\rangle, A^2|v\rangle, ..., A^{r-1}|v\rangle\},

wobei ∣v⟩|v\rangle die anfängliche Schätzung ist (siehe Ref [5] ). Sie unterscheiden sich darin, wie sie die beste Annäherung aus diesem Unterraum auswählen und dabei Faktoren wie Konvergenzrate, Speicherverbrauch und Gesamtberechnungskosten ausgleichen. Der Schwerpunkt dieser Lektion liegt auf der Nutzung von Quantencomputern im Zusammenhang mit Krylov-Unterraummethoden; eine ausführliche Diskussion dieser Methoden würde den Rahmen sprengen. Die nachstehenden kurzen Definitionen dienen nur der Veranschaulichung und enthalten einige Verweise für die weitere Untersuchung dieser Methoden.

Die Methode des konjugierten Gradienten (CG) : Diese Methode wird zur Lösung symmetrischer, positiv definitiver linearer Systeme verwendet [6]. Es minimiert die A-Norm des Fehlers bei jeder Iteration, was es besonders effektiv für Systeme macht, die aus diskretisierten elliptischen PDEs entstehen [7]. Wir werden diesen Ansatz im nächsten Abschnitt verwenden, um zu begründen, warum ein Krylov-Unterraum ein effektiver Unterraum ist, in dem man nach besseren Lösungen für lineare Systeme suchen kann.

Die verallgemeinerte Methode der minimalen Residuen (GMRES) : Diese Methode ist für die Lösung allgemeiner, nicht symmetrischer linearer Systeme konzipiert. Es minimiert die Restnorm über einen Krylov-Raum bei jeder Iteration, was es robust, aber potenziell speicherintensiv für große Systeme macht [7].

Die Methode der minimalen Residuen (MINRES) : Diese Methode wird für die Lösung symmetrischer unbestimmter linearer Systeme verwendet. Es ist ähnlich wie GMRES, nutzt aber die Matrixsymmetrie, um die Rechenkosten zu senken [8].

Weitere erwähnenswerte Ansätze sind die Methode der vollständigen Orthogonalisierung (FOM ), die eng mit der Arnoldi-Methode für Eigenwertprobleme verwandt ist, die Methode des bi-konjugierten Gradienten ( BiCG ) und die Methode der induzierten Dimensionsreduktion (IDR).

1.3 Warum die Krylov-Unterraummethode funktioniert

Hier werden wir motivieren, dass die Krylov-Unterraum-Methode ein effizienter Weg sein sollte, Matrixeigenwerte durch iterative Verfeinerung von Eigenvektor-Näherungen durch die Linse des steilsten Abstiegs zu approximieren. Wir werden argumentieren, dass bei einer anfänglichen Schätzung eines Grundzustands der Raum der aufeinanderfolgenden Korrekturen dieser anfänglichen Schätzung, der die schnellste Konvergenz ergibt, ein Krylov-Unterraum ist. Wir verzichten auf einen rigorosen Nachweis des Konvergenzverhaltens.

Nehmen wir an, unsere interessierende Matrix AA ist symmetrisch und positiv definit. Dies macht unsere Argumentation für die obige CG-Methode besonders relevant. Wir machen hier keine Annahmen über die Sparsamkeit; wir behaupten auch nicht, dass AA hermitesch sein muss (was es sein muss, wenn es ein Hamiltonian ist).

Normalerweise wollen wir ein Problem der folgenden Form lösen

A∣x⟩=∣b⟩.A|x\rangle=|b\rangle.

Man könnte sich vorstellen, dass ∣b⟩=c∣x⟩|b\rangle=c|x\rangle, wobei cc eine Konstante ist, wie bei einem Eigenwertproblem. Aber unsere Problemstellung bleibt vorerst allgemeiner.

Wir beginnen mit einem Vektor ∣x0⟩|x_0\rangle, der eine Näherungslösung darstellt. Obwohl es Parallelen zwischen dieser Vermutung ∣x0⟩|x_0\rangle und ∣v0⟩|v_0\rangle im Abschnitt 1.1 gibt, ziehen wir hier keinen Nutzen daraus. Unser Vermutungs ∣x0⟩|x_0\rangle enthält einen Fehler, den wir als „ ∣e0⟩|e_0\rangle “ bezeichnen:

∣e0⟩:=∣x⟩−∣x0⟩.|e_0\rangle:=|x\rangle−|x_0\rangle.

Außerdem definieren wir die Rest R0R_0 wie folgt:

∣R0⟩=∣b⟩−A∣x0⟩.|R_0\rangle=|b\rangle−A|x_0\rangle.

Hier verwenden wir die Großbuchstaben RR, um das Residuum von der Dimension unseres Krylov-Unterraums rr zu unterscheiden.

Ein echter Eigenvektor mit der Bezeichnung x, ein Schätzwert mit der Bezeichnung x₀ und eine grafische Darstellung des Fehlers zwischen diesen beiden.

Wir wollen nun einen Korrekturschritt der Form

∣x1⟩=∣x0⟩+∣p0⟩,|x_1\rangle=|x_0\rangle+|p_0\rangle,

was hoffentlich unsere Annäherung verbessert. Hier ist ∣p0⟩|p_0\rangle ein noch zu bestimmender Vektor. ∣e1⟩|e_1\rangle sei der Fehler nach der Korrektur. Dann

∣e1⟩=∣x⟩−∣x1⟩=∣x⟩−(∣x0⟩+∣p0⟩)=∣e0⟩−∣p0⟩.|e_1\rangle=|x\rangle−|x_1\rangle=|x\rangle−(|x_0\rangle+|p_0\rangle)=|e_0\rangle−|p_0\rangle. Ein echter Eigenvektor und eine Aktualisierung der Anfangsschätzung. Die aktualisierte Schätzung liegt näher am tatsächlichen Eigenvektor.

Wir sind daran interessiert, wie sich unser Fehler verhält, wenn er durch unsere Matrix transformiert wird. Berechnen wir also die AA -norm des Fehlers. Das heißt

∥∣e0⟩−∣p0⟩∥A2=(⟨e0∣A−⟨p0∣A)(∣e0⟩−∣p0⟩)=⟨e0∣A∣e0⟩−⟨e0∣A∣p0⟩−⟨p0∣A∣e0⟩+⟨p0∣A∣p0⟩=⟨e0∣A∣e0⟩−2⟨e0∣A∣p0⟩+⟨p0∣A∣p0⟩=d−2⟨R0∣p0⟩+⟨p0∣A∣p0⟩,\begin{aligned} ∥|e_0\rangle−|p_0\rangle∥_A^2&=\left(\langle e_0|A−\langle p_0|A\right)\left(|e_0\rangle−|p_0\rangle\right)\\ & = \langle e_0|A|e_0 \rangle − \langle e_0|A|p_0\rangle − \langle p_0|A|e_0\rangle+\langle p_0|A|p_0\rangle\\ & = \langle e_0|A|e_0\rangle−2\langle e_0|A|p_0\rangle+\langle p_0|A|p_0\rangle\\ & = d−2\langle R_0|p_0\rangle +\langle p_0|A|p_0\rangle, \end{aligned}

Dabei haben wir die Symmetrie von AA sowie die Tatsache, dass A∣e0⟩=∣R0⟩A |e_0\rangle = |R_0\rangle, genutzt. Dabei ist dd eine von ∣p0⟩|p_0\rangle unabhängige Konstante. Wie in Abschnitt 1.2 erwähnt, ist die AA -Norm des Fehlers nicht die einzige Größe, die wir zur Minimierung wählen könnten, aber sie ist eine gute Wahl. Wir möchten untersuchen, wie sich diese Größe in Abhängigkeit von der Wahl unserer Korrekturvektoren ∣p0⟩|p_0\rangle verändert. Dazu definieren wir die Funktion ff durch Setzen von

f(∣p0⟩)=⟨p0∣A∣p0⟩−2⟨R0∣p0⟩+d.f(|p_0\rangle)=\langle p_0|A|p_0\rangle−2\langle R_0|p_0\rangle+d.

ff ist einfach der Fehler ∣e1⟩|e_1\rangle in Abhängigkeit von der Korrektur ∣p0⟩|p_0\rangle gemessen in der AA -Norm. Daher wollen wir ∣p0⟩|p_0\rangle so wählen, dass f(∣p0⟩)f(|p_0\rangle) so klein wie möglich ist. Zu diesem Zweck berechnen wir den Gradienten von ff. Unter Verwendung der Symmetrie von AA ergibt sich

∇f(∣p0⟩)=2(A∣p0⟩−∣R0⟩).\nabla f(|p_0\rangle) = 2(A|p_0\rangle−|R_0\rangle).

Die Steigung zeigt in die Richtung des steilsten Anstiegs, was bedeutet, dass ihre entgegengesetzte Richtung die Richtung angibt, in der die Funktion am stärksten abfällt: die Richtung des steilsten Abfalls. Bei unserer anfänglichen Vermutung ∣x0⟩|x_0\rangle, wobei ∣p0⟩=0|p_0\rangle=0 gilt, ergibt sich ∇f(0)=−2∣R0⟩\nabla f(0) = -2|R_0\rangle. Somit nimmt die Funktion ff in Richtung des Residuals ∣R0⟩|R_0\rangle am stärksten ab. Daher würde unsere anfängliche Wahl am meisten von der Addition des Vektors ∣p0⟩=α0∣R0⟩|p_0\rangle=\alpha_0 |R_0\rangle mit einem bestimmten Skalar α0\alpha_0 profitieren.

Im nächsten Schritt wählen wir wieder einen Vektor ∣p1⟩|p_1\rangle und addieren seinen Wert zur aktuellen Approximation. Mit demselben Argument wie zuvor wählen wir ∣p1⟩=α1∣R1⟩|p_1\rangle = \alpha_1 |R_1\rangle für einen Skalar α1\alpha_1. Wir fahren auf diese Weise fort, so dass die kthk^\text{th} Iteration unseres Vektors ist

∣xk+1⟩=∣x0⟩+α0∣R0⟩+α1∣R1⟩+⋯+αk∣Rk⟩.|x_{k+1}\rangle=|x_0\rangle+\alpha_0 |R_0\rangle+\alpha_1 |R_1\rangle+⋯+\alpha_k |R_k\rangle.

Entsprechend wollen wir den Raum, aus dem wir unsere verbesserten Schätzungen auswählen, durch Hinzufügen von ∣R0⟩|R_0\rangle, ∣R1⟩|R_1\rangle, usw. in der Reihenfolge aufbauen. Der geschätzte Vektor kthk^\text{th} liegt in

∣xk+1⟩∈∣x0⟩+span{∣R0⟩,∣R1⟩,…,∣Rk⟩}.|x_{k+1}\rangle\in |x_0\rangle+\text{span}\{|R_0\rangle,|R_1\rangle,…,|R_k\rangle \}.

Unter Verwendung der Beziehung, dass

∣Rk+1⟩=∣b⟩−A∣xk+1⟩=∣b⟩−A(∣xk⟩+αk∣Rk⟩)=∣Rk⟩−αkA∣Rk⟩,|R_{k+1}\rangle=|b\rangle−A |x_{k+1}\rangle=|b\rangle−A(|x_k\rangle+\alpha_k |R_k\rangle)=|R_k\rangle−\alpha_k A |R_k\rangle,

wir sehen, dass

span{∣R0⟩,∣R1⟩,…,∣Rk⟩}=span{∣R0⟩,A∣R0⟩,…,Ak∣R0⟩}.\text{span} \{|R_0\rangle,|R_1\rangle,…,|R_k\rangle \}=\text{span} \{|R_0\rangle,A|R_0\rangle,…,A^{k}|R_0\rangle \}.

Das heißt, der Raum, den wir aufbauen und der die richtige Lösung ∣x⟩|x\rangle am effizientesten approximiert, ist genau der Raum, der durch die aufeinanderfolgende Anwendung der Matrix AA auf ∣R0⟩|R_0\rangle entsteht. Ein Krylov-Unterraum ist der Raum, der durch die Vektoren der aufeinanderfolgenden Richtungen des steilsten Abstiegs aufgespannt wird.

Abschließend möchten wir noch einmal darauf hinweisen, dass wir keine numerischen Aussagen über die Skalierung dieses Ansatzes gemacht haben, noch haben wir den vergleichbaren Nutzen für dünn besetzte Matrizen diskutiert. Dies soll nur dazu dienen, die Verwendung von Krylov-Unterraum-Methoden zu motivieren und ihnen einen gewissen intuitiven Sinn zu verleihen. Wir werden nun das Verhalten dieser Methoden numerisch untersuchen.

Überprüfen Sie Ihr Verständnis

In dem obigen Arbeitsablauf haben wir vorgeschlagen, die AA -Norm des Fehlers zu minimieren. Welche anderen Größen könnte man bei der Suche nach dem Grundzustand und seinem Eigenwert minimieren?

  • Man könnte sich vorstellen, den Residualvektor anstelle der AA -Norm des Fehlers zu verwenden. Es kann Fälle geben, in denen die Betrachtung des Fehlervektors selbst sinnvoll ist.


2. Krylov-Methoden in der klassischen Berechnung

In diesem Abschnitt implementieren wir Arnoldi-Iterationen rechnerisch, so dass wir einen Krylov-Unterraum bei der Lösung von Eigenwertproblemen nutzen können. Wir werden dies zunächst auf ein kleines Beispiel anwenden und dann untersuchen, wie die Berechnungszeit mit zunehmender Größe der interessierenden Matrix steigt. Ein Schlüsselgedanke dabei ist, dass die Erzeugung der Vektoren, die den Krylov-Raum aufspannen, einen großen Anteil an der benötigten Gesamtrechenzeit hat. Der benötigte Speicherplatz variiert zwischen den einzelnen Krylov-Methoden. Allerdings können Speicherbeschränkungen die Verwendung traditioneller Krylov-Methoden einschränken.

2.1 Einfaches Beispiel im kleinen Maßstab

Bei der Erstellung eines Krylov-Unterraums müssen wir die Vektoren in unserem Unterraum orthonormalisieren. Definieren wir eine Funktion, die einen etablierten Vektor aus unserem Unterraum vknown (der nicht normalisiert sein soll) und einen Kandidatenvektor nimmt, den wir zu unserem Unterraum vnext hinzufügen und vnext orthogonal zu vknown und normalisiert machen. Definieren wir ferner eine Funktion, die diesen Prozess für alle etablierten Vektoren in unserem Krylov-Unterraum durchläuft, um eine vollständig orthonormale Menge zu gewährleisten.

# 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 v

Definieren wir nun eine Funktion, die iterativ einen immer größeren Krylov-Unterraum aufbaut, bis der Raum der Krylov-Vektoren den gesamten Raum der ursprünglichen Matrix umfasst. So können wir sehen, wie gut die mit unserer Krylov-Unterraum-Methode erhaltenen Eigenwerte mit den exakten Werten übereinstimmen, und zwar in Abhängigkeit von der Krylov-Unterraum-Dimension. Wichtig ist, dass unsere Funktion krylov_full_build die Krylov-Vektoren, die projizierten Hamiltonianer, die Eigenwerte und die benötigte Zeit liefert.

# 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)

Wir werden dies an einer Matrix testen, die zwar immer noch recht klein ist, aber größer, als wir sie vielleicht von Hand erstellen wollen.

# 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
)

Wir können unsere Funktionen überprüfen, indem wir sicherstellen, dass im letzten Schritt (wenn der Krylov-Raum der vollständige Vektorraum der ursprünglichen Matrix ist) die Eigenwerte der Krylov-Methode genau mit denen der exakten numerischen Diagonalisierung übereinstimmen:

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]

Das war erfolgreich. Entscheidend ist natürlich, wie gut unsere Annäherung in Abhängigkeit von der Dimension unseres Krylov-Unterraums ist. Da wir uns häufig mit der Suche nach Grundzuständen und anderen minimalen Eigenwerten beschäftigen (und aus anderen, eher algebraischen Gründen, die weiter unten erläutert werden), betrachten wir unsere Schätzung des niedrigsten Eigenwerts als Funktion der Dimension des Krylov-Unterraums. Das heißt

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 err
import 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:

Output of the previous code cell

Wir sehen, dass der minimale Eigenwert ziemlich genau erreicht wird, sobald der Krylov-Unterraum auf K2\mathcal{K}^2 angewachsen ist, und gemäß K3\mathcal{K}^3 perfekt ist.

2.2 Zeitskalierung mit Matrixdimension

Wir wollen uns davon überzeugen, dass die Krylov-Methode gegenüber exakten numerischen Eigenlösungen auf folgende Weise vorteilhaft sein kann:

  • Konstruktion von Zufallsmatrizen (nicht spärlich, nicht die ideale Anwendung für KQD)
  • Bestimmen Sie Eigenwerte mit zwei Methoden: direkt mit NumPy und mit einem Krylov-Unterraum.
  • Wir wählen einen Grenzwert dafür, wie genau unsere Eigenwerte sein müssen, bevor wir die Krylov-Schätzungen akzeptieren.
  • Vergleichen Sie die Wandzeit, die für diese beiden Lösungswege benötigt wird.

Vorsichtsmaßnahmen: Wie wir weiter unten ausführlich erörtern werden, lässt sich die Krylov-Quantendiagonalisierung am besten auf Operatoren anwenden, deren Matrixdarstellungen spärlich sind und/oder mit einer kleinen Anzahl von Gruppen von kommutierenden Pauli-Operatoren geschrieben werden können. Die Zufallsmatrizen, die wir hier verwenden, entsprechen nicht dieser Beschreibung. Diese sind nur nützlich, um die Skala auszuloten, bei der klassische Krylov-Methoden nützlich sein könnten. Zweitens werden bei der Anwendung der Krylov-Methode die Eigenwerte unter Verwendung vieler unterschiedlich großer Krylov-Unterräume berechnet. Wir werden die Zeit angeben, die für den minimal dimensionierten Krylov-Unterraum benötigt wird, der die von uns geforderte Genauigkeit für den Grundzustandseigenwert erreicht. Auch dies ist etwas anderes als die Lösung eines Problems, das für exakte Eigenlöser unlösbar ist, da wir die exakte Lösung verwenden, um die benötigte Dimension zu bestimmen.

Wir beginnen mit der Erstellung einer Reihe von Zufallsmatrizen.

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))

Wir diagonalisieren nun jede Matrix direkt mit Numpy. Zum späteren Vergleich berechnen wir die für die Diagonalisierung benötigte Zeit.

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:

Output of the previous code cell

Man beachte, dass in der obigen Abbildung die anomal hohe Zeit um eine Dimension von 125 herum auf die zufällige Natur der Matrizen oder auf die Implementierung auf dem verwendeten klassischen Prozessor zurückzuführen sein könnte, aber nicht reproduzierbar ist. Wenn Sie den Code erneut ausführen, erhalten Sie ein anderes Profil mit anderen anomalen Spitzenwerten.

Nun werden wir für jede Matrix einen Krylov-Unterraum aufbauen und schrittweise Eigenwerte berechnen. Bei jedem Schritt wird geprüft, ob der niedrigste Eigenwert innerhalb des von uns angegebenen absoluten Fehlers erzielt wurde. Der Unterraum, der als erster Eigenwerte innerhalb des angegebenen Fehlers liefert, ist der Unterraum, für den wir die Berechnungszeiten aufzeichnen werden. Die Ausführung dieser Zelle kann je nach Prozessorgeschwindigkeit mehrere Minuten dauern. Es steht Ihnen frei, die Auswertung zu überspringen oder die maximale Dimension der diagonalisierten Matrizen zu verringern. Es genügt, sich die vorberechneten Ergebnisse anzusehen.

# 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 = 1

Wir wollen die Zeiten, die wir für diese beiden Methoden erhalten haben, zum Vergleich aufzeichnen:

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:

Output of the previous code cell

Dies sind die tatsächlich benötigten Zeiten, aber für die Zwecke der Diskussion glätten wir diese Kurven, indem wir den Durchschnitt über einige benachbarte Punkte / Matrixdimensionen bilden. Dies geschieht im Folgenden:

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:

Output of the previous code cell

Beachten Sie, dass die Zeit, die für die Erstellung eines Krylov-Unterraums benötigt wird, zunächst die Zeit übersteigt, die für die vollständige Diagonalisierung in Numpy benötigt wird. Mit zunehmender Größe der Matrix wird die Krylov-Methode jedoch vorteilhaft. Dies gilt auch dann, wenn wir unseren akzeptablen Fehler verringern, aber der Vorteil setzt bei einer größeren Matrixgröße ein. Es lohnt sich, dies auseinander zu nehmen.

Die Zeitkomplexität der numerischen Diagonalisierung ist O(n3)O(n^3) (mit einigen Abweichungen zwischen den Algorithmen). Die Zeitkomplexität der Erzeugung einer orthonormalen Basis von nn Vektoren ist ebenfalls O(n3)O(n^3). Der Vorteil der Krylov-Methode liegt also nicht in der Verwendung der orthonormalen Basis some\it{some}, sondern in der Verwendung einer bestimmten orthonormalen Basis, die effektiv die Eigenwerte von Interesse herausgreift. Wir haben dies bereits in der Skizze eines Beweises im ersten Abschnitt dieser Lektion gesehen, und dies ist entscheidend für die Konvergenzgarantien in Krylov-Verfahren.

Lassen Sie uns einen Blick auf unsere bisherigen Fortschritte werfen:

  • Bei sehr großen Matrizen kann die Krylov-Unterraum-Methode schneller als herkömmliche Diagonalisierungsalgorithmen approximative Eigenwerte innerhalb der erforderlichen Toleranzen liefern.
  • Bei solch großen Matrizen ist die Erzeugung eines Krylov-Unterraums der zeitaufwändigste Teil der Krylov-Unterraum-Methode.
  • Daher wäre ein effizientes Verfahren zur Erzeugung eines Krylov-Unterraums von großem Nutzen. Hier kommt endlich der Quantencomputer ins Spiel.

Überprüfen Sie Ihr Verständnis

Siehe die obenstehende geglättete Darstellung der Diagonalisierungszeiten in Abhängigkeit von der Matrixdimension.

(a) Ab welcher Matrixdimension wurde die Krylov-Methode laut dieser Grafik schneller?

(b) Welche Aspekte der Berechnung könnten den Bereich beeinflussen, ab dem die Krylov-Methode schneller wird?

  • (a) Die Ergebnisse können variieren, wenn man die Berechnung erneut durchführt, doch die Krylov-Methode wird ab einer Dimension von etwa 80–85 schneller.

    (b) Es gibt viele mögliche Antworten. Wichtige Faktoren sind die von uns geforderte Genauigkeit und die Sparsität der zu diagonalizierenden Matrizen.


3. Krylov über Zeitentwicklung

Alles, was wir bisher beschrieben haben, kann klassisch durchgeführt werden. Wie und wann würden wir also einen Quantencomputer verwenden? Bei sehr großen Matrizen kann die Krylov-Methode lange Rechenzeiten und große Mengen an Speicherplatz erfordern. Die für die Matrixoperation von HH auf ∣v⟩|v\rangle benötigte Zeit skaliert im schlimmsten Fall wie O(N2)O(N^2). Selbst die Multiplikation dünn besetzter Matrizen auf einem Vektor (der typische Fall für klassische Krylov-Löser) hat eine Zeitkomplexität, die wie O(N)O(N) skaliert. Dies wird für jeden gewünschten Vektor in unserem Unterraum durchgeführt. Die Unterraumdimension rr ist normalerweise kein signifikanter Bruchteil von NN und skaliert oft wie log⁡(N)\log(N). Die Erzeugung aller Vektoren skaliert also im schlimmsten Fall wie O(N2log⁡(N))O(N^2 \log(N)). Obwohl es noch andere Schritte gibt, wie z. B. die Orthogonalisierung, ist dies die wichtigste Skalierung, die man im Auge behalten sollte.

Das Quantencomputing ermöglicht es uns, die Eigenschaften des Problems zu ändern, die die Skalierung der benötigten Zeit und Ressourcen bestimmen. Anstelle der generellen Abhängigkeit von der Matrixgröße NN werden wir Dinge wie die Anzahl der Schüsse und die Anzahl der nicht-kommutierenden Pauli-Terme sehen, aus denen sich der Hamiltonian zusammensetzt. Sehen wir uns an, wie das funktioniert.

3.1 Zeitliche Entwicklung

Der Operator, der einen Quantenzustand zeitlich entwickelt, ist e−iHt/ℏe^{-iHt/\hbar} (und es ist sehr üblich, insbesondere in der Quanteninformatik, ℏ\hbar aus der Notation zu streichen). Eine Möglichkeit, eine solche Exponentialfunktion eines Operators zu verstehen und sogar zu realisieren, ist die Betrachtung ihrer Taylorreihenentwicklung. Man beachte, dass diese Operation, die auf einen Anfangsvektor ∣v⟩|v\rangle wirkt, eine Summe von Termen mit zunehmenden Potenzen von HH ergibt, die auf den Anfangszustand angewendet werden. Es sieht so aus, als könnten wir unseren Krylov-Unterraum einfach durch Zeitentwicklung unseres anfänglichen Schätzzustandes erstellen!

e−iHt/ℏ→e−iHt≈1−iHt−(H2t2)2+⋯e−iHt∣v⟩≈∣v⟩−iHt∣v⟩−(H2t2)2∣v⟩+⋯\begin{aligned} e^{-iHt/\hbar}→e^{-iHt}&≈1-iHt-\frac{(H^2 t^2)}{2}+⋯\\ e^{-iHt} |v\rangle &≈ |v\rangle-iHt|v\rangle-\frac{(H^2 t^2)}{2}|v\rangle+⋯ \end{aligned}

Die Schwierigkeit besteht darin, die Zeitentwicklung auf einem echten Quantencomputer zu realisieren. Viele der Terme in der Hamiltonformel sind nicht miteinander vertauschbar. Während also einige einfache exponentielle Operatoren wie e−iZe^{-iZ} einfachen Schaltkreisen entsprechen, tun dies allgemeine Hamiltonianer nicht. Und da sie nicht-kommutierende Terme enthalten, können wir den Exponentialwert nicht einfach in ein Produkt einfacher Terme zerlegen, wie wir es bei Zahlen können.

e−iHt=e−i(H1+H2+⋯+Hn)t≠e−iH1te−iH2t...e−iHnte^{-iHt}=e^{-i(H_1+H_2+⋯+H_n)t}\neq e^{-iH_1 t} e^{-iH_2 t}... e^{-iH_n t}

Dies ist also nicht trivial, aber ein gut untersuchter Prozess in der Quanteninformatik. Wir führen die Zeitevolution auf Quantencomputern mit Hilfe eines Prozesses durch, der Trotterisierung genannt wird, was an sich schon ein umfangreiches Thema ist [10]. Aber auf einer sehr hohen Ebene begrenzen wir die Auswirkungen der Nicht-Kommutativität von Termen, indem wir die zeitliche Entwicklung in sehr kleine Schritte unterteilen, z. B. mm mit der Größe dtdt.

e−iHt=e−i(H1+H2+⋯+Hn)t=(e−i(H1+H2+⋯+Hn)t/m)m≈(e−iH1dte−iH2dt…e−iHndt)me^{-iHt}=e^{-i(H_1+H_2+⋯+H_n )t} = (e^{-i(H_1+H_2+⋯+H_n )t/m} )^m ≈(e^{-iH_1 dt} e^{-iH_2 dt} …e^{-iH_n dt} )^m

wobei dt=t/mdt = t/m.

Wir nennen einen Krylov-Unterraum der Ordnung r, den wir im klassischen Kontext mit Potenzen von H direkt erzeugt haben, einen "Potenz-Krylov-Unterraum".

KPr(H,∣v⟩)=span{∣v⟩,H∣v⟩,H2∣v⟩…Hr−1∣v⟩}\mathcal{K}_P^r (H,|v\rangle)=\text{span}\{|v\rangle,H|v\rangle,H^2 |v\rangle… H^{r-1} |v\rangle\}

Jetzt erzeugen wir einen ähnlichen Raum mit dem unitären Zeit-Evolutions-Operator U≡e−iHtU \equiv e^{-iHt}; wir nennen ihn den "unitären Krylov-Raum" KUr\mathcal{K}_U^r. Der Power-Krylov-Unterraum KPr\mathcal{K}_P^r, den wir klassischerweise verwenden, kann nicht direkt auf einem Quantencomputer erzeugt werden, da HH kein unitärer Operator ist. Es kann gezeigt werden, dass die Verwendung des unitären Krylov-Unterraums ähnliche Konvergenzgarantien bietet wie der Potenz-Krylov-Unterraum, nämlich dass der Grundzustandsfehler effizient konvergiert, solange der Anfangszustand ∣v⟩|v\rangle eine Überlappung mit dem wahren Grundzustand hat, die nicht exponentiell verschwindet, und solange eine ausreichende Lücke zwischen den Eigenwerten besteht. Für eine genauere Diskussion der Konvergenz siehe [1].

Hier werden die Potenzen von UU zu verschiedenen Zeitschritten (die Potenz kthk^\text{th} von UU schreitet um eine Zeit k×dtk \times dt voran). Wir können das Element des Unterraums, das sich für die Gesamtzeit kdtk dt zeitlich entwickelt, als ∣ψk⟩|\psi_k\rangle bezeichnen.

U=e−iHdtUk=e−iH(kdt)KUr=span{∣ψ⟩,U∣ψ⟩,U2∣ψ⟩…Ur−1∣ψ⟩}\begin{aligned} U&=e^{-iHdt}\\ U^k&=e^{-iH(kdt)}\\ \mathcal{K}_U^r&=\text{span}\{|\psi\rangle,U|\psi\rangle,U^2 |\psi\rangle… U^{r-1} |\psi\rangle\} \end{aligned}

Wir können unseren Hamiltonian H auf den unitären Krylov-Unterraum KUr\mathcal{K}_U^r projizieren. Mit anderen Worten: Wir berechnen jedes Matrixelement von HH in der Basis KUr\mathcal{K}_U^r. Wir bezeichnen diese projizierte Matrix als H~\tilde{H}.

3.2 Wie man es auf einem Quantencomputer implementiert

Die Matrixelemente von H~\tilde{H} sind durch die Erwartungswerte ⟨ψm∣H∣ψn⟩\langle \psi_m |H| \psi_n\rangle gegeben, die mit dem Quantencomputer geschätzt werden können. Beachten Sie, dass HH als eine Summe von Pauli-Operatoren auf verschiedenen Qubits geschrieben werden kann und dass nicht alle Pauli-Operatoren gleichzeitig gemessen werden können. Wir können die Pauli-Terme in Gruppen von kommutierenden Termen sortieren und alle diese Terme auf einmal messen. Aber wir brauchen vielleicht viele solcher Gruppen, um alle Begriffe abzudecken. Daher wird die Anzahl der verschiedenen Pendelgruppen, in die die Begriffe unterteilt werden können, NGCPN_\text{GCP}, wichtig.

H=∑α=1NGCPcαPαH=\sum_{\alpha=1}^{N_\text{GCP}} c_\alpha P_\alpha

Dabei ist PαP_\alpha ein Pauli-String der Form Pα∼IZIXII...YZXIXP_\alpha \sim IZIXII...YZXIX oder eine Menge solcher Pauli-Strings, die miteinander kommutieren. Da wir „ HH “ als eine solche Summe messbarer Operatoren darstellen können, lassen sich die folgenden Ausdrücke für Matrixelemente von „ H~\tilde{H} “ mithilfe des primitiven Schätzers „ IBM Quantum “ realisieren.

H~mn=⟨ψm∣H∣ψn⟩=⟨ψeiHtm∣H∣ψe−iHtn⟩=⟨ψeiHmdt∣H∣ψe−iHndt⟩\begin{aligned} \tilde{H}_{mn}&=\langle \psi_m |H| \psi_n\rangle\\ &=\langle \psi e^{iHt_m} |H| \psi e^{-iHt_n}\rangle\\ &=\langle \psi e^{iHmdt} |H|\psi e^{-iHndt}\rangle \end{aligned}

Dabei sind ∣ψn⟩=e−iHtn∣ψ⟩\vert \psi_n \rangle = e^{-i H t_n} \vert \psi \rangle die Vektoren des unitären Krylov-Raums und tn=ndtt_n = n dt die Vielfachen des gewählten Zeitschritts dtdt. Auf einem Quantencomputer kann die Berechnung der einzelnen Matrixelemente mit jedem beliebigen Algorithmus erfolgen, der es ermöglicht, eine Überlappung zwischen Quantenzuständen zu erzielen. In dieser Lektion werden wir uns mit dem Hadamard-Test beschäftigen. Da der Hamilton-Operator „ KU\mathcal{K}_U “ die Dimension „ rr “ hat, hat der auf den Unterraum projizierte Hamilton-Operator die Dimension „ r×rr \times r “. Ist „ rr “ klein genug (im Allgemeinen reicht „ r<<100r<<100 “ aus, um die Konvergenz der Schätzungen der Eigenwerte zu gewährleisten), können wir den projizierten Hamilton-Operator „ H~\tilde{H} “ klassisch problemlos diagonalisieren. Allerdings können wir H~\tilde{H} nicht direkt diagonalisieren, da die Vektoren des Krylov-Raums nicht orthogonal sind. Wir müssen ihre Überschneidungen messen und eine Matrix erstellen: S~\tilde{S}

S~mn=⟨ψm∣ψn⟩\tilde{S}_{mn} = \langle \psi_m \vert \psi_n \rangle

Dies ermöglicht es uns, das Eigenwertproblem in einem nicht-orthogonalen Raum zu lösen (auch verallgemeinertes Eigenwertproblem genannt)

H~ c⃗=E S~ c⃗\tilde{H} \ \vec{c} = E \ \tilde{S} \ \vec{c}

Anhand der Lösungen dieses verallgemeinerten Eigenwertproblems können dann die Eigenwerte und Eigenzustände von HH geschätzt werden. Die Schätzung der Energie des Grundzustands wird beispielsweise durch die Ermittlung des kleinsten Eigenwerts EE und des Grundzustands aus dem entsprechenden Eigenvektor c⃗\vec{c} gewonnen. Die Koeffizienten in c⃗\vec{c} bestimmen den Beitrag der verschiedenen Vektoren, die KU\mathcal{K}_U aufspannen.

Verallgemeinertes Eigenwertproblem

Warum können wir nicht einfach H~\tilde{H} diagonalisieren? Da S~\tilde{S} die Information über die Geometrie der Krylov-Basis enthält (die in allen außer in sehr speziellen Fällen nicht orthogonal ist), beschreibt H~\tilde{H} für sich genommen keine Projektion des vollständigen Hamiltonianers, so dass seine Eigenwerte keine besondere Beziehung zu denen des vollständigen Hamiltonianers haben - sie könnten beliebige Werte sein. Die Lösung des verallgemeinerten Eigenwertproblems ist erforderlich, um die approximativen Eigenwerte und Eigenvektoren zu erhalten, die der Projektion des vollständigen Hamiltonianers in den Krylov-Raum entsprechen.

Ein Schaltplan mit vielen Ebenen, der anzeigt, dass die Schaltung viele Male mit verschiedenen Zuständen verwendet werden muss, um den modifizierten Hadamard-Test durchzuführen.

Die Abbildung zeigt eine Schaltungsdarstellung des modifizierten Hadamard-Tests, einer Methode, die zur Berechnung der Überlappung zwischen verschiedenen Quantenzuständen verwendet wird. Für jedes Matrixelement H~i,j\tilde{H}_{i,j} wird ein Hadamard-Test zwischen den Zuständen ∣ψi⟩\vert \psi_i \rangle, ∣ψj⟩\vert \psi_j \rangle durchgeführt. Dies wird in der Abbildung durch das Farbschema für die Matrixelemente und die entsprechenden Operationen Prep  ψi\text{Prep} \; \psi_i, Prep  ψj\text{Prep} \; \psi_j hervorgehoben. Daher ist eine Reihe von Hadamard-Tests für alle möglichen Kombinationen von Krylov-Raum-Vektoren erforderlich, um alle Matrixelemente des projizierten Hamiltonian H~\tilde{H} zu berechnen. Der oberste Draht in der Hadamard-Testschaltung ist ein Ancilla-Qubit, das entweder in der X- oder Y-Basis gemessen wird; sein Erwartungswert bestimmt den Wert der Überlappung zwischen den Zuständen. Der untere Draht stellt alle Qubits des System-Hamiltonianers dar. Die Operation Prep  ψi\text{Prep} \; \psi_i bereitet das System-Qubit auf den Zustand ∣ψi⟩\vert \psi_i \rangle vor, der durch den Zustand des Ancilla-Qubits gesteuert wird (ähnlich für Prep  ψj\text{Prep} \; \psi_j ), und die Operation PP stellt die Pauli-Zerlegung des System-Hamiltonschen H=∑iPiH = \sum_i P_i dar. Die Umsetzung dieser Operation auf einem Quantencomputer wird im Folgenden näher erläutert.


4. Krylov-Quanten-Diagonalisierung auf einem Quantencomputer

Wir werden nun die Krylov-Quanten-Diagonalisierung auf einem echten Quantencomputer implementieren. Beginnen wir damit, einige nützliche Pakete zu importieren.

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")

Wir definieren die folgende Funktion, um das oben erläuterte verallgemeinerte Eigenwertproblem zu lösen.

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]

Zumindest für ein erstes Benchmarking ist es nützlich, eine exakte klassische Lösung zu kennen, um das Konvergenzverhalten zu überprüfen. Die folgende Funktion berechnet die Grundzustandsenergie eines Hamiltonianers, indem sie den Hamiltonianer und die Anzahl der Qubits als Argumente verwendet.

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_en

4.1 Schritt 1: Problem auf Quantenschaltungen und Operatoren abbilden

Jetzt werden wir einen Hamiltonianer definieren. Sie unterscheidet sich von der obigen Funktion dadurch, dass die obige Funktion einen Hamiltonian als Argument nimmt und nur den Grundzustand zurückgibt, und zwar auf klassische Weise. Dieser Hamiltonian, den wir hier definieren, bestimmt die Energieniveaus aller Energieeigenzustände, und dieser Hamiltonian kann mit Pauli-Operatoren konstruiert und auf einem Quantencomputer implementiert werden.

Wir wählen einen Hamiltonian, der einer Kette von Spins entspricht, die eine beliebige Orientierung im Raum haben können, eine sogenannte "Heisenberg-Kette". Wir gehen davon aus, dass der Spin ithi^\text{th} von seinen nächsten Nachbarn (den Spins (i−1)th(i-1)^\text{th} und (i+1)th(i+1)^\text{th} ) beeinflusst werden kann, nicht aber von weiter entfernten Nachbarspins. Wir berücksichtigen auch die Möglichkeit, dass die Wechselwirkung zwischen Spins unterschiedlich ist, wenn die Spins entlang verschiedener Achsen zeigen. Diese Asymmetrie kann beispielsweise durch die Struktur des Kristallgitters entstehen, in das die Spins eingebettet sind.

# 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)]

Der folgende Code beschränkt den Hamiltonian auf Einzelteilchenzustände und verwendet die Spektralnorm, um eine gute Größe für unseren Zeitschritt dtdt festzulegen. Wir wählen heuristisch einen Wert für den Zeitschritt dt (basierend auf Obergrenzen für die Hamilton-Norm). In [9] wurde gezeigt, dass ein hinreichend kleiner Zeitschritt π/∣∣H∣∣\pi/\vert \vert H \vert \vert ist und dass es bis zu einem gewissen Punkt vorzuziehen ist, diesen Wert zu unterschätzen, anstatt ihn zu überschätzen, da eine Überschätzung dazu führen kann, dass Beiträge von hochenergetischen Zuständen sogar den optimalen Zustand im Krylov-Raum verfälschen. Andererseits führt eine zu kleine Wahl von dtdt zu einer schlechteren Konditionierung des Krylov-Unterraums, da sich die Krylov-Basisvektoren von Zeitschritt zu Zeitschritt weniger unterscheiden.

# 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)
dt

Output:

np.float64(0.17453292519943295)

Wir legen die Anzahl der Trotter-Schritte fest, die in der Zeitentwicklung verwendet werden sollen. Wir legen außerdem eine maximale Krylov-Dimension von 4 fest. Diese Krylov-Dimension ist für realistische Anwendungen nicht groß genug. Aber für dieses Beispiel reicht es aus. Darüber hinaus werden wir die Konvergenz bei noch kleineren Dimensionen überprüfen. In späteren Lektionen werden wir Methoden untersuchen, mit denen wir unsere Hamilton-Operatoren auf größere Unterräume skalieren und projizieren können.

# Set parameters for quantum Krylov algorithm
krylov_dim = 4  # size of krylov subspace
num_trotter_steps = 4
dt_circ = dt / num_trotter_steps

Staatliche Vorbereitung

Wählen Sie einen Referenzzustand ∣ψ⟩\vert \psi \rangle, der sich mit dem Grundzustand überschneidet. Für diesen Hamiltonian verwenden wir den a-Zustand mit einer Anregung im mittleren Qubit ∣00..010...00⟩\vert 00..010...00 \rangle als unseren Referenzzustand.

qc_state_prep = QuantumCircuit(n_qubits)
qc_state_prep.x(int(n_qubits / 2) + 1)
qc_state_prep.draw("mpl", scale=0.5)

Output:

Output of the previous code cell

Zeitliche Entwicklung

Wir können den Zeitentwicklungsoperator, der durch einen gegebenen Hamiltonian erzeugt wird, über die Lie-Trotter-Approximation realisieren: U=e−iHtU=e^{-iHt}. Der Einfachheit halber verwenden wir die integrierte PauliEvolutionGate in der Zeitentwicklungsschaltung. Die allgemeine Syntax hierfür lautet wie folgt.

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>

Wir werden eine Version davon unten im Hadamard-Test verwenden, aber für die Zeiten dtdt vorwärts schreiten.

Hadamard-Test

Zur Erinnerung: Wir möchten die Matrixelemente sowohl von H~\tilde{H} als auch von der Gram-Matrix S~\tilde{S} mithilfe des Hadamard-Tests berechnen. Schauen wir uns einmal an, wie das in diesem Zusammenhang funktioniert, und konzentrieren wir uns dabei zunächst auf den Aufbau von „ H~\tilde{H} “. Der gesamte Prozess ist im folgenden Diagramm dargestellt. Die Schichten aus farbigen Zustandsvorbereitungsblöcken Prep∣ψi⟩\text{Prep}|\psi_i\rangle dienen als Hinweis darauf, dass dieser Vorgang für alle Kombinationen von ∣ψi⟩|\psi_i\rangle und ∣ψj⟩|\psi_j\rangle in unserem Unterraum durchgeführt wird.

Das Bild eines Quantenschaltkreises mit vielen Schichten zeigt, dass der Schaltkreis für viele verschiedene Zustände ausgewertet werden muss, um den Hadamard-Test durchzuführen.

Die Zustände des Systems bei den angegebenen Schritten sind:

Step 0:∣Ψ⟩=∣0⟩∣0⟩NStep 1:∣Ψ⟩=12(∣0⟩+∣1⟩)∣0⟩NStep 2:∣Ψ⟩=12(∣0⟩∣0⟩N+∣1⟩∣ψi⟩)Step 3:∣Ψ⟩=12(∣0⟩∣0⟩N+∣1⟩P∣ψi⟩)Step 4:∣Ψ⟩=12(∣0⟩∣ψj⟩+∣1⟩P∣ψi⟩)\begin{aligned} \text{Step 0:}\qquad|\Psi\rangle & = |0\rangle|0\rangle^N \\ \text{Step 1:}\qquad|\Psi\rangle & = \frac{1}{\sqrt{2}}\Big(|0\rangle + |1\rangle \Big)|0\rangle^N \\ \text{Step 2:}\qquad|\Psi\rangle & = \frac{1}{\sqrt{2}}\Big(|0\rangle|0\rangle^N+|1\rangle |\psi_i\rangle\Big)\\ \text{Step 3:}\qquad|\Psi\rangle & = \frac{1}{\sqrt{2}}\Big(|0\rangle |0\rangle^N+|1\rangle P |\psi_i\rangle\Big) \\ \text{Step 4:}\qquad|\Psi\rangle & = \frac{1}{\sqrt{2}}\Big(|0\rangle |\psi_j\rangle+|1\rangle P|\psi_i\rangle\Big) \end{aligned}

Hier ist PP ein Pauli-Term in der Zerlegung des Hamiltonianers (beachten Sie, dass es sich nicht um eine Linearkombination von mehreren kommutierenden Pauli-Termen handeln kann, da dies nicht unitär wäre - die Gruppierung ist mit einer anderen Konstruktion möglich, die wir später zeigen werden) Prep  ψi\text{Prep} \; \psi_i, Prep  ψj\text{Prep} \; \psi_j sind kontrollierte Operationen, die ∣ψi⟩|\psi_i\rangle, ∣ψj⟩|\psi_j\rangle Vektoren des unitären Krylov-Raums mit ∣ψk⟩=e−iHkdt∣ψ⟩=e−iHkdtUψ∣0⟩N|\psi_k\rangle = e^{-i H k dt } \vert \psi \rangle = e^{-i H k dt } U_{\psi} \vert 0 \rangle^N vorbereiten. Die Anwendung von Messungen von XX und YY auf diese Schaltung berechnet den Real- bzw. Imaginärteil der benötigten Matrixelemente.

Beginnend mit Schritt 4 oben, wenden Sie das Hadamard-Tor HH auf das nullte Qubit an.

∣Ψ⟩⟶12∣0⟩(∣ψj⟩+P∣ψi⟩)+12∣1⟩(∣ψj⟩−P∣ψi⟩)\begin{equation*} |\Psi\rangle \longrightarrow\quad\frac{1}{2}|0\rangle\Big( |\psi_j\rangle + P|\psi_i\rangle\Big) + \frac{1}{2}|1\rangle\Big(|\psi_j\rangle - P|\psi_i\rangle\Big) \end{equation*}

Messen Sie dann entweder XX oder YY.

⇒⟨X⟩=14(∥∣ψj⟩+P∣ψi⟩∥2−∥∣ψj⟩−P∣ψi⟩∥2)=Re[⟨ψj∣P∣ψi⟩].\begin{equation*} \begin{split} \Rightarrow\quad\langle X\rangle &= \frac{1}{4}\Bigg(\Big\|| \psi_j\rangle + P|\psi_i\rangle \Big\|^2-\Big\||\psi_j\rangle - P|\psi_i\rangle\Big\|^2\Bigg) \\ &= \text{Re}\Big[\langle\psi_j| P|\psi_i\rangle\Big]. \end{split} \end{equation*}

Aus der Identität ∣a+b∥2=⟨a+b∣a+b⟩=∥a∥2+∥b∥2+2Re⟨a∣b⟩|a + b\|^2 = \langle a + b | a + b \rangle = \|a\|^2 + \|b\|^2 + 2\text{Re}\langle a | b \rangle. In ähnlicher Weise ergibt die Messung von YY

⟨Y⟩=Im[⟨ψj∣P∣ψi⟩].\begin{equation*} \langle Y\rangle = \text{Im}\Big[\langle\psi_j| P|\psi_i\rangle\Big]. \end{equation*}

Wenn wir diese Schritte zu der Zeitentwicklung hinzufügen, die wir zuvor festgelegt haben, ergibt sich folgendes.

## 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
Output of the previous code cell

Wir haben bereits vor der Tiefe der Trotter-Kreise gewarnt. Die Durchführung des Hadamard-Tests unter diesen Bedingungen kann zu einer noch tieferen Schaltung führen, vor allem, wenn wir in native Gatter zerlegen. Dieser Wert erhöht sich noch weiter, wenn man die Topologie des Geräts berücksichtigt. Bevor wir also Zeit auf dem Quantencomputer verbringen, sollten wir die 2-Qubit-Tiefe unserer Schaltung überprüfen.

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

Ein Schaltkreis dieser Komplexität kann auf modernen Quantencomputern keine verwertbaren Ergebnisse liefern. Wenn wir H~\tilde{H} und S~\tilde{S} erstellen wollen, brauchen wir einen besseren Ansatz. Dies ist der Grund für den im Folgenden vorgestellten effizienten Hadamard-Test.

4. 2 Schritt 2. Optimieren Sie Schaltungen und Operatoren für die Zielhardware

Effizienter Hadamard-Test

Wir können die tiefen Schaltkreise für den Hadamard-Test, die wir erhalten haben, optimieren, indem wir einige Näherungen einführen und uns auf einige Annahmen über den Modell-Hamiltonian stützen. Betrachten Sie zum Beispiel die folgende Schaltung für den Hadamard-Test:

Das Bild eines Quantenschaltkreises mit vielen Schichten zeigt, dass der Schaltkreis für viele verschiedene unitäre Operatoren ausgewertet werden muss, um den modifizierten, effizienten Hadamard-Test durchzuführen.

Angenommen, wir können E0E_0, den Eigenwert von ∣0⟩N|0\rangle^N unter dem Hamiltonian HH klassisch berechnen. Dies ist erfüllt, wenn der Hamiltonian die U(1)-Symmetrie bewahrt. Obwohl dies eine starke Annahme zu sein scheint, gibt es viele Fälle, in denen man davon ausgehen kann, dass es einen Vakuumzustand gibt (in diesem Fall entspricht er dem Zustand ∣0⟩N|0\rangle^N ), der von der Wirkung des Hamiltonianers unbeeinflusst ist. Dies gilt zum Beispiel für chemische Hamiltonianer, die stabile Moleküle beschreiben (bei denen die Anzahl der Elektronen erhalten bleibt). Da das Tor Prep  ψ0\text{Prep} \; \psi_0 den gewünschten Referenzzustand ∣ψ0⟩=Prep  ψ0∣0⟩=e−iH0dtUψ0∣0⟩\ket{\psi_0} = \text{Prep} \; \psi_0 \ket{0} = e^{-i H 0 dt} U_{\psi_0} \ket{0} herstellt, wäre beispielsweise die Herstellung des HF-Zustands für die Chemie Prep  ψ0\text{Prep} \; \psi_0 ein Produkt von Ein-Qubit-NOTs, so dass kontrolliert- Prep  ψ0\text{Prep} \; \psi_0 nur ein Produkt von CNOTs ist. Die obige Schaltung implementiert dann den folgenden Zustand vor der Messung:

Step 0:∣Ψ⟩=∣0⟩∣0⟩NStep 1:∣Ψ⟩=12(∣0⟩∣0⟩N+∣1⟩∣0⟩N)Step 2:∣Ψ⟩=12(∣0⟩∣0⟩N+∣1⟩∣ψ0⟩)Step 3:∣Ψ⟩=12(eiϕ∣0⟩∣0⟩N+∣1⟩U∣ψ0⟩)Step 4:∣Ψ⟩=12(eiϕ∣0⟩∣ψ0⟩+∣1⟩U∣ψ0⟩)=12(∣+⟩(eiϕ∣ψ0⟩+U∣ψ0⟩)+∣−⟩(eiϕ∣ψ0⟩−U∣ψ0⟩))=12(∣+i⟩(eiϕ∣ψ0⟩−iU∣ψ0⟩)+∣−i⟩(eiϕ∣ψ0⟩+iU∣ψ0⟩))\begin{aligned} \text{Step 0:}\qquad|\Psi\rangle & = \ket{0} \ket{0}^{N}\\ \text{Step 1:}\qquad|\Psi\rangle & = \frac{1}{\sqrt{2}}\left(\ket{0}\ket{0}^N+ \ket{1} \ket{0}^N\right)\\ \text{Step 2:}\qquad|\Psi\rangle & = \frac{1}{\sqrt{2}}\left(|0\rangle|0\rangle^N+|1\rangle|\psi_0\rangle\right)\\ \text{Step 3:}\qquad|\Psi\rangle & = \frac{1}{\sqrt{2}}\left(e^{i\phi}\ket{0}\ket{0}^N+\ket{1} U\ket{\psi_0}\right)\\ \text{Step 4:}\qquad|\Psi\rangle & = \frac{1}{\sqrt{2}}\left(e^{i\phi}\ket{0} \ket{\psi_0}+\ket{1} U\ket{\psi_0}\right)\\ & = \frac{1}{2}\left(\ket{+}\left(e^{i\phi}\ket{\psi_0}+U\ket{\psi_0}\right)+\ket{-}\left(e^{i\phi}\ket{\psi_0}-U\ket{\psi_0}\right)\right)\\ & = \frac{1}{2}\left(\ket{+i}\left(e^{i\phi}\ket{\psi_0}-iU\ket{\psi_0}\right)+\ket{-i}\left(e^{i\phi}\ket{\psi_0}+iU\ket{\psi_0}\right)\right) \end{aligned}

wobei wir die klassische simulierbare Phasenverschiebung U∣0⟩N=eiϕ∣0⟩N U\ket{0}^N = e^{i\phi}\ket{0}^N von Schritt 2 bis 3 verwendet haben. Daher sind die Erwartungswerte

⟨X⊗P⟩=14((e−iϕ⟨ψ0∣+⟨ψ0∣U†)P(eiϕ∣ψ0⟩+U∣ψ0⟩)−(e−iϕ⟨ψ0∣−⟨ψ0∣U†)P(eiϕ∣ψ0⟩−U∣ψ0⟩))=Re[e−iϕ⟨ψ0∣PU∣ψ0⟩],\begin{aligned} \langle X\otimes P\rangle&=\frac{1}{4} \Big( \left(e^{-i\phi}\bra{\psi_0}+\bra{\psi_0}U^\dagger\right)P\left(e^{i\phi}\ket{\psi_0}+U\ket{\psi_0}\right) \\ &\qquad-\left(e^{-i\phi}\bra{\psi_0}-\bra{\psi_0}U^\dagger\right)P\left(e^{i\phi}\ket{\psi_0}-U\ket{\psi_0}\right) \Big)\\ &=\text{Re}\left[e^{-i\phi}\bra{\psi_0}PU\ket{\psi_0}\right], \end{aligned} ⟨Y⊗P⟩=14((e−iϕ⟨ψ0∣+i⟨ψ0∣U†)P(eiϕ0∣ψ0⟩−iU∣ψ0⟩)−(e−iϕ⟨ψ0∣−i⟨ψ0∣U†)P(eiϕ∣ψ0⟩+iU∣ψ0⟩))=Im[e−iϕ⟨ψ0∣PU∣ψ0⟩]. \begin{aligned} \langle Y\otimes P\rangle&=\frac{1}{4} \Big( \left(e^{-i\phi}\bra{\psi_0}+i\bra{\psi_0}U^\dagger\right)P\left(e^{i\phi_0}\ket{\psi_0}-iU\ket{\psi_0}\right) \\ &\qquad-\left(e^{-i\phi}\bra{\psi_0}-i\bra{\psi_0}U^\dagger\right)P\left(e^{i\phi}\ket{\psi_0}+iU\ket{\psi_0}\right) \Big)\\ &=\text{Im}\left[e^{-i\phi}\bra{\psi_0}PU\ket{\psi_0}\right]. \end{aligned}

Mit diesen Annahmen konnten wir die Erwartungswerte der interessierenden Operatoren mit weniger kontrollierten Operationen schreiben. In der Tat müssen wir nur die kontrollierte Zustandsvorbereitung Prep  ψ0\text{Prep} \; \psi_0 und nicht die kontrollierte Zeitentwicklung implementieren. Wenn wir unsere Berechnung wie oben beschrieben umgestalten, können wir die Tiefe der resultierenden Schaltkreise erheblich reduzieren.

Da der Pauli-Operator nun als Messwert am Ende der Schaltung erscheint und nicht mehr als kontrolliertes Tor in der Mitte, kann er als Bonus zusammen mit anderen pendelnden Pauli-Operatoren gemessen werden, wie in der oben angegebenen Zerlegung H=∑α=1NGCPcαPαH=\sum_{\alpha = 1}^{N_\text{GCP}}c_\alpha P_\alpha .

Zeitentwicklungsoperator mit Trotter-Zerlegung zerlegen

Anstatt den Zeitentwicklungsoperator exakt zu implementieren, können wir die Trotter-Zerlegung verwenden, um eine Näherung davon zu implementieren. Durch mehrmaliges Wiederholen einer bestimmten Reihenfolge der Trotter-Zerlegung lässt sich der durch die Näherung verursachte Fehler weiter reduzieren. Im Folgenden erstellen wir direkt die Trotter-Implementierung auf die effizienteste Weise für den Wechselwirkungsgraphen des von uns betrachteten Hamilton-Operators (ausschließlich Wechselwirkungen zwischen nächsten Nachbarn). In der Praxis fügen wir Pauli-Rotationen ein: RxxR_{xx}, RyyR_{yy}, RzzR_{zz} mit Kopplungsstärken JxJ_x, JyJ_y und JzJ_z sowie einem parametrisierten Winkel tt, die der näherungsweisen Implementierung von e−i(JxXX+JyYY+JzZZ)te^{-i (J_x XX + J_y YY + J_z ZZ) t} entsprechen. Angesichts der unterschiedlichen Definition der Pauli-Rotationen und der Zeitentwicklung, die wir implementieren möchten, müssen wir den Parameter 2∗dt2*dt verwenden, um eine Zeitentwicklung gemäß dtdt zu erzielen. Darüber hinaus kehren wir bei einer ungeraden Anzahl von Wiederholungen der Trotter-Schritte die Reihenfolge der Operationen um, was funktional äquivalent ist, aber die Synthese benachbarter Operationen in einem einzigen unitaren „ SU(2)SU(2) “ ermöglicht. Dadurch ergibt sich eine wesentlich flachere Schaltung als bei Verwendung der generischen Funktionalität PauliEvolutionGate() .

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:

Output of the previous code cell

Für diesen effizienten Hadamard-Test bereiten wir erneut einen Ausgangszustand vor.

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:

Output of the previous code cell

Vorlagen-Schaltungen zur Berechnung von Matrixelementen von S~\tilde{S} und H~\tilde{H} über den Hadamard-Test

Der einzige Unterschied zwischen den beim Hadamard-Test verwendeten Schaltungen besteht in der Phase des Zeitentwicklungsoperators und den gemessenen Messgrößen. Daher können wir eine Schablonenschaltung erstellen, die die allgemeine Schaltung für den Hadamard-Test darstellt, mit Platzhaltern für die Gatter, die vom Zeitentwicklungsoperator abhängen.

# 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:

Output of the previous code cell
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

Diese Tiefe ist im Vergleich zum ursprünglichen Hadamard-Test wesentlich geringer. Diese Tiefe ist auf modernen Quantencomputern überschaubar, wenngleich sie immer noch recht hoch ist. Um brauchbare Ergebnisse zu erhalten, müssen wir den neuesten Stand der Technik bei der Fehlerminderung anwenden.

Wählen Sie ein Backend aus, auf dem unsere Quanten-Krylov-Berechnung ausgeführt werden soll, damit wir unsere Schaltung für die Ausführung auf diesem Quantencomputer übersetzen können.

# 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")

Wir setzen nun unsere Schaltungen und Operatoren um.

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)])
Output of the previous code cell

Nach der Optimierung wird unsere transpilierte Zwei-Qubit-Tiefe weiter reduziert.

4.3 Schritt 3. Mit der Primitive „ IBM Quantum “ ausführen

Wir erstellen nun PUBs zur Ausführung mit 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)

Die Schaltungen für t=0t=0 sind klassisch berechenbar. Wir führen dies durch, bevor wir zum Fall t≠0t\neq 0 mit einem Quantencomputer übergehen.

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)

Obwohl wir unsere Gattertiefe mit dem effizienten Hadamard-Test um Größenordnungen reduzieren konnten, ist die Tiefe immer noch ausreichend, um eine moderne Fehlerbegrenzung zu erfordern. Im Folgenden werden die Attribute der verwendeten Abschwächung aufgeführt. Alle verwendeten Methoden sind wichtig, aber es lohnt sich, speziell die probabilistische Fehlerverstärkung (PEA) zu erwähnen. Diese leistungsstarke Technik ist mit einem großen Quanten-Overhead verbunden. Die hier durchgeführte Berechnung kann auf einem echten Quantencomputer 20 Minuten oder mehr dauern. Sie können mit den nachstehenden Parametern spielen, um die Genauigkeit und den damit verbundenen Aufwand zu erhöhen oder zu verringern. Die folgenden Standardeinstellungen führen zu sehr realitätsnahen Ergebnissen.

# 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,
            }
        },
    },
}

Schließlich führen wir die Schaltungen für S~\tilde{S} und H~\tilde{H} mit Estimator aus.

# 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 Schritt 4. Nachbearbeitung und Analyse der Ergebnisse

Was wir vom Quantencomputer erhalten haben, sind die einzelnen Matrixelemente von S~\tilde{S} und die kommutierenden Pauli-Gruppen, aus denen sich die Matrixelemente von H~\tilde{H} zusammensetzen.

# Store the outputs as 'results'.
results = job.result()[0]

Berechnen Sie den effektiven Hamilton-Operator und die Überlappungsmatrizen

Berechnen Sie zunächst die Phase, die durch den Zustand ∣0⟩\vert 0 \rangle während der unkontrollierten Zeitentwicklung akkumuliert wird

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)
]

Sobald wir die Ergebnisse der Schaltkreisausführungen haben, können wir die Daten nachbearbeiten, um die Matrixelemente von SS

# 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:

[1.00.149322296177984−0.283023058106896i0.185815978760175−0.0910521940394691i0.0940509850777074−0.094154537369141i0.149322296177984+0.283023058106896i1.00.149322296177984−0.283023058106896i0.185815978760175−0.0910521940394691i0.185815978760175+0.0910521940394691i0.149322296177984+0.283023058106896i1.00.149322296177984−0.283023058106896i0.0940509850777074+0.094154537369141i0.185815978760175+0.0910521940394691i0.149322296177984+0.283023058106896i1.0]\displaystyle \left[\begin{matrix}1.0 & 0.149322296177984 - 0.283023058106896 i & 0.185815978760175 - 0.0910521940394691 i & 0.0940509850777074 - 0.094154537369141 i\\0.149322296177984 + 0.283023058106896 i & 1.0 & 0.149322296177984 - 0.283023058106896 i & 0.185815978760175 - 0.0910521940394691 i\\0.185815978760175 + 0.0910521940394691 i & 0.149322296177984 + 0.283023058106896 i & 1.0 & 0.149322296177984 - 0.283023058106896 i\\0.0940509850777074 + 0.094154537369141 i & 0.185815978760175 + 0.0910521940394691 i & 0.149322296177984 + 0.283023058106896 i & 1.0\end{matrix}\right]

Und die Matrixelemente von H~\tilde{H}

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:

[10.0−3.02044405310714−2.80721615865252i0.496487054782717+0.188101957039621i1.0770511571923+0.104340737159455i−3.02044405310714+2.80721615865252i10.0−3.02044405310714−2.80721615865252i0.496487054782717+0.188101957039621i0.496487054782717−0.188101957039621i−3.02044405310714+2.80721615865252i10.0−3.02044405310714−2.80721615865252i1.0770511571923−0.104340737159455i0.496487054782717−0.188101957039621i−3.02044405310714+2.80721615865252i10.0]\displaystyle \left[\begin{matrix}10.0 & -3.02044405310714 - 2.80721615865252 i & 0.496487054782717 + 0.188101957039621 i & 1.0770511571923 + 0.104340737159455 i\\-3.02044405310714 + 2.80721615865252 i & 10.0 & -3.02044405310714 - 2.80721615865252 i & 0.496487054782717 + 0.188101957039621 i\\0.496487054782717 - 0.188101957039621 i & -3.02044405310714 + 2.80721615865252 i & 10.0 & -3.02044405310714 - 2.80721615865252 i\\1.0770511571923 - 0.104340737159455 i & 0.496487054782717 - 0.188101957039621 i & -3.02044405310714 + 2.80721615865252 i & 10.0\end{matrix}\right]

Schließlich können wir das verallgemeinerte Eigenwertproblem für H~\tilde{H} lösen:

H~c⃗=cSc⃗\tilde{H} \vec{c} = c S \vec{c}

und erhalten eine Schätzung der Grundzustandsenergie cminc_{min}

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

Für einen Ein-Teilchen-Sektor können wir den Grundzustand dieses Sektors des Hamiltonianers effizient und klassisch berechnen

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:

Output of the previous code cell

5. Diskussion und Erweiterung

Zusammenfassend lässt sich sagen, dass wir mit einem Referenzzustand beginnen und diesen dann über verschiedene Zeiträume hinweg entwickeln, um den unitären Krylov-Unterraum zu erzeugen. Wir projizieren unseren Hamiltonianer auf diesen Unterraum. Wir schätzen auch die Überschneidungen der Unterraum-Vektoren. Schließlich lösen wir das niederdimensionale, verallgemeinerte Eigenwertproblem auf klassische Weise.

Ein Überblick über das Flussdiagramm von QKD: Beginnen Sie mit einem Referenzzustand, entwickeln Sie den Zustand zu annähernden Krylov-Vektoren, projizieren Sie in den Krylov-Unterraum, diagonalisieren Sie den projizierten Unterraum klassisch und bestimmen Sie die Grundzustandseigenschaften.

Vergleichen wir, was die Rechenkosten der klassischen und der quantenmechanischen Krylov-Technik bestimmt. Nicht für alle Schritte gibt es perfekte Analogien zwischen klassischen und Quantenansätzen. In dieser Tabelle sind einige Skalierungen der verschiedenen Schritte zusammengestellt.

Eine Tabelle zur Beschreibung der Skalierung verschiedener Prozesse im klassischen und im Quantenansatz der Krylov-Methoden. Manche Quantenschritte haben kein Analogon. Die Skalierungen sind die gleichen wie im Text.

Man beachte, dass Hamilton-Operatoren im Allgemeinen Terme enthalten, die nicht gleichzeitig gemessen werden können (da sie nicht miteinander kommutieren). Wir ordnen die Terme im Hamilton-Operator in Gruppen kommutierender Pauli-Operatoren ein, die alle gleichzeitig gemessen werden können, und benötigen möglicherweise viele solcher Gruppen, um alle Terme zu berücksichtigen, die nicht miteinander kommutieren. Um „ H~\tilde{H} “ auf einem Quantencomputer zu berechnen, sind für jede Gruppe kommutierender Pauli-Strings im Hamilton-Operator separate Messungen erforderlich, und jede dieser Messungen erfordert viele Durchläufe. Wir müssen dies für r2r^2 verschiedene Matrixelemente durchführen, die den r2r^2 Kombinationen verschiedener Zeitentwicklungsfaktoren entsprechen. Es gibt zwar manchmal Möglichkeiten, dies zu reduzieren, doch bei dieser groben Behandlung skaliert der Zeitaufwand wie „ Nshots×NGCP×r2N_\text{shots}\times N_\text{GCP} \times r^2 “. Die Elemente von „ SS “ müssen geschätzt werden, was wie „ O(Nshots×r2)O(N_\text{shots}\times r^2) “ skaliert. Schließlich erfordert die Lösung des verallgemeinerten Eigenwertproblems im projizierten Raum klassischerweise „ O(r3)O(r^3) “.

Wir sehen, dass die Quanten-Krylov-Diagonalisierung in Fällen nützlich sein kann, in denen die Anzahl der kommutierenden Pauli-Gruppen im Hamiltonian relativ klein ist. Diese Skalierungsabhängigkeiten deuten auf einige Anwendungen hin, bei denen die Krylov-Methode nützlich sein kann, und andere, bei denen dies wahrscheinlich nicht der Fall sein wird. Einige Hamiltonianer haben eine hohe Komplexität, wenn sie auf Qubits abgebildet werden, da sie viele nicht-kommutierende Pauli-Strings beinhalten, die nicht einfach in einige wenige kommutierende Gruppen unterteilt werden können. Dies ist zum Beispiel bei Problemen der Quantenchemie häufig der Fall. Diese Komplexität stellt die Quantencomputer der nahen Zukunft vor zwei große Herausforderungen:

  • Die Schätzung der einzelnen Elemente von H~\tilde{H} wird aufgrund der großen Anzahl von Termen rechenintensiv.
  • Die erforderlichen Trotter-Schaltkreise werden unerschwinglich tief.

Beide oben genannten Punkte werden weniger problematisch sein, wenn Quantencomputer Fehlertoleranz erreichen, aber sie müssen in naher Zukunft berücksichtigt werden. Selbst Systeme mit "einfacheren" Abbildungen als in der Quantenchemie können auf die gleichen Hindernisse stoßen, wenn die Hamiltonianer zu viele nichtkommutierende Terme haben. Die Krylov-Methode ist am nützlichsten, wenn der Hamiltonian in relativ wenige kommutierende Pauli-Gruppen unterteilt werden kann und wenn HH leicht in Trotter-Schaltungen zu implementieren ist. Beide Bedingungen sind zum Beispiel für viele Gittermodelle, die in der Physik von Interesse sind, erfüllt. KQD ist besonders nützlich, wenn sehr wenig über den Grundzustand bekannt ist. Dies ist auf die inhärenten Konvergenzgarantien und die Anwendbarkeit in Szenarien zurückzuführen, in denen alternative Methoden aufgrund unzureichender Grundzustandskenntnisse unhaltbar sind.

KQD ist zwar ein leistungsfähiges Werkzeug, aber die zeitaufwändigen Aspekte des Protokolls, insbesondere die Schätzung der einzelnen Elemente des projizierten Hamiltonian und die Überlappung der Krylov-Zustände, bieten Möglichkeiten zur Verbesserung. Ein alternativer Ansatz ist die Nutzung von Krylov-Methoden in Verbindung mit stichprobenbasierten Methoden, die Gegenstand der nächsten Lektion sind.


6. Anhänge

Anhang I: Krylov-Unterraum aus Echtzeit-Entwicklungen

Der unitäre Krylov-Raum ist definiert als

KU(H,∣ψ⟩)=span{∣ψ⟩,e−iH dt∣ψ⟩,…,e−irH dt∣ψ⟩}\mathcal{K}_U(H, |\psi\rangle) = \text{span}\left\{ |\psi\rangle, e^{-iH\,dt} |\psi\rangle, \dots, e^{-irH\,dt} |\psi\rangle \right\}

für einen bestimmten Zeitschritt dtdt, den wir später festlegen werden. Nehmen wir vorläufig an, dass rr gerade ist: Definieren Sie dann d=r/2d=r/2. Beachten Sie, dass der Hamiltonian, wenn wir ihn in den obigen Krylov-Raum projizieren, nicht vom Krylov-Raum zu unterscheiden ist

KU(H,∣ψ⟩)=span{ei d H dt∣ψ⟩,ei(d−1)H dt∣ψ⟩,…,e−i(d−1)H dt∣ψ⟩,e−i d H dt∣ψ⟩},\mathcal{K}_U(H, |\psi\rangle) = \text{span}\left\{ e^{i\,d\,H\,dt}|\psi\rangle, e^{i(d-1)H\,dt} |\psi\rangle, \dots, e^{-i(d-1)H\,dt} |\psi\rangle, e^{-i\,d\,H\,dt} |\psi\rangle \right\},

d. h., alle Zeitverläufe sind um dd Zeitschritte nach hinten verschoben. Der Grund für die Ununterscheidbarkeit ist, dass die Matrixelemente

H~j,k=⟨ψ∣ei j H dtHe−i k H dt∣ψ⟩=⟨ψ∣Hei(j−k)H dt∣ψ⟩\tilde{H}_{j,k} = \langle\psi|e^{i\,j\,H\,dt}He^{-i\,k\,H\,dt}|\psi\rangle=\langle\psi|He^{i(j-k)H\,dt}|\psi\rangle

sind invariant bei allgemeinen Verschiebungen der Evolutionszeit, da die Zeitentwicklungen mit dem Hamiltonian pendeln. Für ungerade rr können wir die Analyse für r−1r-1 verwenden.

Wir wollen zeigen, dass es irgendwo in diesem Krylov-Raum garantiert einen energiearmen Zustand gibt. Wir tun dies mit Hilfe des folgenden Ergebnisses, das aus Theorem 3.1 in [3] abgeleitet ist:

Behauptung 1: Es gibt eine Funktion ff, so dass für Energien EE im Spektralbereich des Hamiltonian (d.h. zwischen der Grundzustandsenergie und der maximalen Energie)...

  1. f(E0)=1f(E_0)=1
  2. ∣f(E)∣≤2(1+δ)−d|f(E)|\le2\left(1 + \delta\right)^{-d} für alle Werte von EE, die ≥δ\ge\delta von E0E_0 entfernt liegen, d. h. sie wird exponentiell unterdrückt
  3. f(E)f(E) ist eine lineare Kombination von eijE dte^{ijE\,dt} für j=−d,−d+1,...,d−1,dj=-d,-d+1,...,d-1,d

Im Folgenden geben wir einen Beweis an, den man aber getrost überspringen kann, es sei denn, man möchte das vollständige, strenge Argument verstehen. Im Moment konzentrieren wir uns auf die Auswirkungen der obigen Behauptung. Aus der obigen Eigenschaft 3 geht hervor, dass der obige verschobene Krylov-Raum den Zustand f(H)∣ψ⟩f(H)|\psi\rangle enthält. Dies ist unser energiearmer Zustand. Um zu sehen, warum, schreiben Sie ∣ψ⟩|\psi\rangle in die Energie-Eigenbasis:

∣ψ⟩=∑k=0Nγk∣Ek⟩,|\psi\rangle = \sum_{k=0}^{N}\gamma_k|E_k\rangle,

wobei ∣Ek⟩|E_k\rangle der k-te Energieeigenzustand und γk\gamma_k seine Amplitude im Ausgangszustand ∣ψ⟩|\psi\rangle ist. Ausgedrückt in diesem Sinne ist f(H)∣ψ⟩f(H)|\psi\rangle gegeben durch

f(H)∣ψ⟩=∑k=0Nγkf(Ek)∣Ek⟩,f(H)|\psi\rangle = \sum_{k=0}^{N}\gamma_kf(E_k)|E_k\rangle,

unter Ausnutzung der Tatsache, dass wir HH durch EkE_k ersetzen können, wenn sie auf den Eigenzustand ∣Ek⟩|E_k\rangle wirkt. Der Energiefehler dieses Zustands ist daher

energy error=⟨ψ∣f(H)(H−E0)f(H)∣ψ⟩⟨ψ∣f(H)2∣ψ⟩\text{energy error} = \frac{\langle\psi|f(H)(H-E_0)f(H)|\psi\rangle}{\langle\psi|f(H)^2|\psi\rangle} =∑k=0N∣γk∣2f(Ek)2(Ek−E0)∑k=0N∣γk∣2f(Ek)2.= \frac{\sum_{k=0}^{N}|\gamma_k|^2f(E_k)^2(E_k-E_0)}{\sum_{k=0}^{N}|\gamma_k|^2f(E_k)^2}.

Um dies in eine obere Schranke umzuwandeln, die leichter zu verstehen ist, trennen wir zunächst die Summe im Zähler in Terme mit Ek−E0≤δE_k-E_0\le\delta und Terme mit Ek−E0>δE_k-E_0>\delta :

energy error=∑Ek≤E0+δ∣γk∣2f(Ek)2(Ek−E0)∑k=0N∣γk∣2f(Ek)2+∑Ek>E0+δ∣γk∣2f(Ek)2(Ek−E0)∑k=0N∣γk∣2f(Ek)2.\text{energy error} = \frac{\sum_{E_k\le E_0+\delta}|\gamma_k|^2f(E_k)^2(E_k-E_0)}{\sum_{k=0}^{N}|\gamma_k|^2f(E_k)^2} + \frac{\sum_{E_k> E_0+\delta}|\gamma_k|^2f(E_k)^2(E_k-E_0)}{\sum_{k=0}^{N}|\gamma_k|^2f(E_k)^2}.

Wir können den ersten Term durch δ\delta nach oben begrenzen,

∑Ek≤E0+δ∣γk∣2f(Ek)2(Ek−E0)∑k=0N∣γk∣2f(Ek)2<δ∑Ek≤E0+δ∣γk∣2f(Ek)2∑k=0N∣γk∣2f(Ek)2≤δ,\frac{\sum_{E_k\le E_0+\delta}|\gamma_k|^2f(E_k)^2(E_k-E_0)}{\sum_{k=0}^{N}|\gamma_k|^2f(E_k)^2} < \frac{\delta\sum_{E_k\le E_0+\delta}|\gamma_k|^2f(E_k)^2}{\sum_{k=0}^{N}|\gamma_k|^2f(E_k)^2} \le \delta,

wobei der erste Schritt folgt, weil Ek−E0≤δE_k-E_0\le\delta für jede EkE_k in der Summe gilt, und der zweite Schritt folgt, weil die Summe im Zähler eine Teilmenge der Summe im Nenner ist. Für den zweiten Term schränken wir zunächst den Nenner durch ∣γ0∣2|\gamma_0|^2 ein, da f(E0)2=1f(E_0)^2=1 : Addiert man alles wieder zusammen, so ergibt sich

energy error≤δ+1∣γ0∣2∑Ek>E0+δ∣γk∣2f(Ek)2(Ek−E0).\text{energy error} \le \delta + \frac{1}{|\gamma_0|^2}\sum_{E_k>E_0+\delta}|\gamma_k|^2f(E_k)^2(E_k-E_0).

Um den Rest zu vereinfachen, sei darauf hingewiesen, dass wir für alle diese EkE_k durch die Definition von ff wissen, dass f(Ek)2≤4(1+δ)−2df(E_k)^2 \le 4\left(1 + \delta\right)^{-2d}. Zusätzlich ergibt die obere Begrenzung von Ek−E0<2∥H∥E_k-E_0<2\|H\| und die obere Begrenzung von ∑Ek>E0+δ∣γk∣2<1\sum_{E_k>E_0+\delta}|\gamma_k|^2<1

energy error≤δ+8∣γ0∣2∥H∥(1+δ)−2d.\text{energy error} \le \delta + \frac{8}{|\gamma_0|^2}\|H\|\left(1 + \delta\right)^{-2d}.

Dies gilt für jede δ>0\delta>0. Wenn wir also δ\delta gleich unserem Zielfehler setzen, dann konvergiert die obige Fehlergrenze exponentiell mit der Krylov-Dimension 2d=r2d=r gegen diese. Beachten Sie auch, dass bei δ<E1−E0\delta<E_1-E_0 der Term δ\delta in der obigen Schranke vollständig verschwindet.

Um das Argument zu vervollständigen, stellen wir zunächst fest, dass es sich bei der obigen Angabe nur um den Energiefehler des jeweiligen Zustands f(H)∣ψ⟩f(H)|\psi\rangle handelt und nicht um den Energiefehler des Zustands mit der niedrigsten Energie im Krylov-Raum. Nach dem (Rayleigh-Ritz-)Variationsprinzip ist der Energiefehler des Zustands mit der niedrigsten Energie im Krylov-Raum jedoch durch den Energiefehler eines beliebigen Zustands im Krylov-Raum nach oben hin begrenzt, so dass die obige Aussage auch eine Obergrenze für den Energiefehler des Zustands mit der niedrigsten Energie, d. h. des Ergebnisses des Krylov-Quantendiagonalisierungsalgorithmus, darstellt.

Es kann eine ähnliche Analyse wie die obige durchgeführt werden, die zusätzlich das Rauschen und das im Notizbuch besprochene Schwellenwertverfahren berücksichtigt. Siehe [2] und [4] für diese Analyse.

Anhang II: Nachweis für Anspruch 1

Das Folgende ist größtenteils aus [3], Theorem 3.1: abgeleitet. Sei 0<a<b0 < a < b und sei Πd∗\Pi^*_d der Raum der Restpolynome (Polynome, deren Wert bei 0 gleich 1 ist) vom Grad höchstens dd. Die Lösung von

β(a,b,d)=min⁡p∈Πd∗max⁡x∈[a,b]∣p(x)∣\beta(a, b, d) = \min_{p \in \Pi^*_d} \max_{x \in [a, b]} |p(x)| \quad

ist

p∗(x)=Td(b+a−2xb−a)Td(b+ab−a),p^*(x) = \frac{T_d\left(\frac{b + a - 2x}{b - a}\right)}{T_d\left(\frac{b + a}{b - a}\right)}, \quad

und der entsprechende Minimalwert ist

β(a,b,d)=Td−1(b+ab−a).\beta(a, b, d) = T_d^{-1}\left(\frac{b + a}{b - a}\right).

Wir wollen dies in eine Funktion umwandeln, die natürlich in Form von komplexen Exponentialen ausgedrückt werden kann, denn das sind die realen Zeitentwicklungen, die den Quanten-Krylov-Raum erzeugen. Zu diesem Zweck ist es zweckmäßig, die folgende Transformation der Energien im Spektralbereich des Hamiltonian in Zahlen im Bereich [0,1][0,1] einzuführen: definieren

g(E)=1−cos⁡((E−E0)dt)2,g(E) = \frac{1-\cos\big((E-E_0)dt\big)}{2},

wobei dtdt ein solcher Zeitschritt ist, dass −π<E0dt<Emaxdt<π-\pi < E_0dt < E_\text{max}dt < \pi. Beachten Sie, dass g(E0)=0g(E_0)=0 und g(E)g(E) wachsen, wenn EE sich von E0E_0 entfernt.

Mit dem Polynom p∗(x)p^*(x) und den Parametern a, b, d, die auf a=g(E0+δ)a = g(E_0 + \delta), b=1b = 1 und d = int( r/2 ) gesetzt sind, definieren wir die Funktion:

f(E)=p∗(g(E))=Td(1+2cos⁡((E−E0)dt)−cos⁡(δ dt)1+cos⁡(δ dt))Td(1+21−cos⁡(δ dt)1+cos⁡(δ dt))f(E) = p^* \left( g(E) \right) = \frac{T_d\left(1 + 2\frac{\cos\big((E-E_0)dt\big) - \cos\big(\delta\,dt\big)}{1 +\cos\big(\delta\,dt\big)}\right)}{T_d\left(1 + 2\frac{1-\cos\big(\delta\,dt\big)}{1 + \cos\big(\delta\,dt\big)}\right)}

wobei E0E_0 die Grundzustandsenergie ist. Durch Einsetzen von cos⁡(x)=eix+e−ix2\cos(x)=\frac{e^{ix}+e^{-ix}}{2} können wir sehen, dass f(E)f(E) ein trigonometrisches Polynom vom Grad dd ist, d. h. eine Linearkombination von eijE dte^{ijE\,dt} für j=−d,−d+1,...,d−1,dj=-d,-d+1,...,d-1,d. Außerdem ergibt sich aus der obigen Definition von p∗(x)p^*(x), dass f(E0)=p(0)=1f(E_0)=p(0)=1 und für jedes EE im Spektralbereich, das ∣E−E0∣>δ\vert E-E_0 \vert > \delta ist, gilt

∣f(E)∣≤β(a,b,d)=Td−1(1+21−cos⁡(δ dt)1+cos⁡(δ dt))|f(E)| \le \beta(a, b, d) = T_d^{-1}\left(1 + 2\frac{1-\cos\big(\delta\,dt\big)}{1 + \cos\big(\delta\,dt\big)}\right) ≤2(1+δ)−d=2(1+δ)−⌊k/2⌋.\leq 2\left(1 + \delta\right)^{-d} = 2\left(1 + \delta\right)^{-\lfloor k/2\rfloor}.

Quellenangaben:

[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_Unterraum

[6] Krylov-Unterraum-Methoden: Grundlagen und Analyse, Jörg Liesen, Zdenek Strakos https://academic.oup.com/book/36426

[7] Iterative Methoden für spärliche lineare Systeme" von Yousef Saad

[8] "MINRES-QLP: A Krylov Subspace Method for Indefinite or Singular Symmetric Systems" von Sou-Cheng Choi, Christopher Paige, und Michael Saunders ( https://epubs.siam.org/doi/10.1137/100787921 )

[9] Ethan N. Epperly, Lin Lin, und Yuji Nakatsukasa. "Eine Theorie der Quantenunterraumdiagonalisierung". SIAM Journal on Matrix Analysis and Applications 43, 1263-1290 (2022).

[10] https://link.aps.org/doi/10.1103/PRXQuantum.4.030319

War diese Seite hilfreich?
Melden Sie auf GitHub einen Fehler, einen Tippfehler oder fordern Sie Inhalte an.