Zum Hauptinhalt springen

Quantenalgorithmen: Phasenschätzung

hinweis

Kento Ueda (15. Mai 2024)

Dieses Notebook vermittelt die grundlegenden Konzepte und die Implementierung der Quanten-Fourier-Transformation (QFT) und der Quantenphasenschätzung (QPE).

PDF der Originalvorlesung herunterladen. Beachte, dass manche Code-Schnipsel veraltet sein können, da es sich um statische Bilder handelt.

Ungefähre QPU-Zeit für dieses Experiment: 7 Sekunden.

1. Einführung

Quanten-Fourier-Transformation (QFT)

Die Quanten-Fourier-Transformation ist das quantenmechanische Pendant zur klassischen diskreten Fourier-Transformation. Es handelt sich um eine lineare Transformation, die auf Quantenzustände angewendet wird und Rechenbasisdarstellungen in ihre Fourier-Basis-Darstellungen überführt. Die QFT spielt eine zentrale Rolle in vielen Quantenalgorithmen und bietet eine effiziente Methode, Periodizitätsinformationen aus Quantenzuständen zu extrahieren. Die QFT kann mit O(N2)O(N^2) Operationen durch Quantengates wie Hadamard-Gates und Control-Phase-Gates für NN Qubits implementiert werden, was einen exponentiellen Speedup gegenüber der klassischen Fourier-Transformation ermöglicht.

  • Anwendungen: Sie ist ein grundlegender Baustein in Quantenalgorithmen wie Shors Algorithmus zur Faktorisierung großer ganzer Zahlen und zum diskreten Logarithmus.

Quantenphasenschätzung (QPE)

Die Quantenphasenschätzung ist ein Quantenalgorithmus, der die Phase eines Eigenvektors eines unitären Operators schätzt. Dieser Algorithmus bildet eine Brücke zwischen den abstrakten mathematischen Eigenschaften von Quantenzuständen und ihren rechnerischen Anwendungen.

  • Anwendungen: Er kann Probleme lösen wie das Finden von Eigenwerten unitärer Matrizen und die Simulation von Quantensystemen.

Zusammen bilden QFT und QPE das wesentliche Rückgrat vieler Quantenalgorithmen, die Probleme lösen, die für klassische Computer nicht handhabbar sind. Am Ende dieses Notebooks wirst du verstehen, wie diese Techniken implementiert werden.

2. Grundlagen der Quanten-Fourier-Transformation (QFT)

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-aer qiskit-ibm-runtime
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit_aer import AerSimulator
from qiskit.visualization import plot_histogram, plot_bloch_multivector
from qiskit.quantum_info import Statevector
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import Sampler

from numpy import pi

In Analogie zur diskreten Fourier-Transformation wirkt die QFT auf einen Quantenzustand X=j=0N1xjj\vert X\rangle = \sum_{j=0}^{N-1} x_j \vert j \rangle für NN Qubits und bildet ihn auf den Quantenzustand Y=k=0N1ykk\vert Y\rangle = \sum_{k=0}^{N-1} y_k \vert k \rangle ab.

QFTN:j1Nk=0N1ωNjkkQFT_{N}: \vert j \rangle \mapsto \frac{1}{\sqrt{N}}\sum_{k=0}^{N-1}\omega_N^{jk} \vert k \rangle

wobei ωNjk=e2πijkN\omega_N^{jk} = e^{2\pi i \frac{jk}{N}}.

Oder als unitäre Matrix dargestellt:

UQFT=1Nj=0N1k=0N1ωNjkkjU_{QFT} = \frac{1}{\sqrt{N}} \sum_{j=0}^{N-1} \sum_{k=0}^{N-1} \omega_N^{jk} \vert k \rangle \langle j \vert

2.1. Intuition

Die Quanten-Fourier-Transformation (QFT) transformiert zwischen zwei Basen: der Rechengrundlage (Z-Basis) und der Fourier-Basis. Was bedeutet die Fourier-Basis in diesem Kontext? Du erinnerst dich sicher, dass die Fourier-Transformation F(ω)F(\omega) einer Funktion f(x)f(x) die Faltung von f(x)f(x) mit einer Sinusfunktion der Frequenz ω\omega beschreibt. Vereinfacht gesagt: Die Fourier-Transformation ist eine Funktion, die beschreibt, wie viel von jeder Frequenz ω\omega nötig wäre, um f(x)f(x) aus Sinusfunktionen (oder Kosinusfunktionen) aufzubauen. Um ein besseres Gefühl dafür zu bekommen, was die QFT in diesem Kontext bedeutet, betrachte die schrittweisen Bilder unten, die eine in Binär kodierte Zahl mit vier Qubits zeigen:

In der Rechenbasis speichern wir Zahlen in Binärform mithilfe der Zustände 0|0\rangle und 1|1\rangle.

Beachte die Frequenz, mit der die verschiedenen Qubits wechseln: Das linkeste Qubit wechselt bei jeder Erhöhung der Zahl, das nächste bei jeder zweiten, das dritte bei jeder vierten Erhöhung usw.

Wenn wir eine Quanten-Fourier-Transformation auf diese Zustände anwenden, bilden wir ab:

Zustand in RechenbasisQFTZustand in Fourier-Basis|\text{Zustand in Rechenbasis}\rangle \quad \xrightarrow[]{\text{QFT}} \quad |\text{Zustand in Fourier-Basis}\rangle QFTx=x~\text{QFT}|x\rangle = |\widetilde{x}\rangle

(Zustände in der Fourier-Basis werden oft mit einer Tilde (~) bezeichnet.)

In der Fourier-Basis speichern wir Zahlen durch verschiedene Rotationen um die Z-Achse:

IFRAME

Die zu speichernde Zahl bestimmt den Winkel, um den jedes Qubit um die Z-Achse gedreht wird. Im Zustand 0~|\widetilde{0}\rangle befinden sich alle Qubits im Zustand +|{+}\rangle. Wie im obigen Beispiel zu sehen, wurde zur Kodierung des Zustands 5~|\widetilde{5}\rangle auf 4 Qubits das linkeste Qubit um 52n=516\tfrac{5}{2^n} = \tfrac{5}{16} volle Umdrehungen (516×2π\tfrac{5}{16}\times 2\pi Radiant) gedreht. Das nächste Qubit wird doppelt so weit gedreht (1016×2π\tfrac{10}{16}\times 2\pi Radiant bzw. 10/1610/16 volle Umdrehungen), dieser Winkel wird für das nächste Qubit wieder verdoppelt usw.

Beachte erneut die Frequenz, mit der jedes Qubit wechselt. Das linkeste Qubit (qubit 0) hat in diesem Fall die niedrigste Frequenz, das rechteste die höchste.

2.2 Beispiel: 1-Qubit-QFT

Betrachten wir den Fall N=21=2N = 2^1 = 2.

Die unitäre Matrix lässt sich schreiben als:

UQFT=12j=01k=01ω2jkkj=12(ω2000+ω2001+ω2010+ω2111)=12(00+01+1011)=H\begin{aligned} U_{QFT} & = \frac{1}{\sqrt{2}} \sum_{j=0}^{1} \sum_{k=0}^{1} \omega_2^{jk} \vert k \rangle \langle j \vert \\ & = \frac{1}{\sqrt{2}} (\omega_2^{0} \vert 0 \rangle \langle 0 \vert + \omega_2^{0} \vert 0 \rangle \langle 1 \vert + \omega_2^{0} \vert 1 \rangle \langle 0 \vert + \omega_2^{1} \vert 1 \rangle \langle 1 \vert) \\ & = \frac{1}{\sqrt{2}} (\vert 0 \rangle \langle 0 \vert + \vert 0 \rangle \langle 1 \vert + \vert 1 \rangle \langle 0 \vert - \vert 1 \rangle \langle 1 \vert) \\ & = H \end{aligned}

Diese Operation entspricht dem Ergebnis der Anwendung des Hadamard-Gates (HH).

2.3 Produktdarstellung der QFT

Verallgemeinern wir die Transformation für N=2nN = 2^n, QFTNQFT_{N} angewendet auf den Zustand x=x1xn\vert x \rangle = \vert x_1\ldots x_n \rangle.

QFTNx=1Ny=0N1ωNxyy=1Ny=0N1e2πixy/2ny daωNxy=e2πixyNundN=2n=1Ny=0N1e2πi(k=1nyk/2k)xy1ynumgeschrieben in gebrochener Bina¨rnotationy=y1yn,y/2n=k=1nyk/2k=1Ny=0N1k=1ne2πixyk/2ky1ynnach Entwicklung des Exponentials einer Summe als Produkt von Exponentialen,=1Nk=1n(0+e2πix/2k1)nach Umordnung der Summen und Produkte sowie Entwicklungy=0N1=y1=01y2=01yn=01=1N(0+e2πi2x1)(0+e2πi22x1)(0+e2πi2n1x1)(0+e2πi2nx1)\begin{aligned} QFT_N\vert x \rangle & = \frac{1}{\sqrt{N}} \sum_{y=0}^{N-1}\omega_N^{xy} \vert y \rangle \\ & = \frac{1}{\sqrt{N}} \sum_{y=0}^{N-1} e^{2 \pi i xy / 2^n} \vert y \rangle ~\text{da}\: \omega_N^{xy} = e^{2\pi i \frac{xy}{N}} \:\text{und}\: N = 2^n \\ & = \frac{1}{\sqrt{N}} \sum_{y=0}^{N-1} e^{2 \pi i \left(\sum_{k=1}^n y_k/2^k\right) x} \vert y_1 \ldots y_n \rangle \:\text{umgeschrieben in gebrochener Binärnotation}\: y = y_1\ldots y_n, y/2^n = \sum_{k=1}^n y_k/2^k \\ & = \frac{1}{\sqrt{N}} \sum_{y=0}^{N-1} \prod_{k=1}^n e^{2 \pi i x y_k/2^k } \vert y_1 \ldots y_n \rangle \:\text{nach Entwicklung des Exponentials einer Summe als Produkt von Exponentialen,} \\ & = \frac{1}{\sqrt{N}} \bigotimes_{k=1}^n \left(\vert0\rangle + e^{2 \pi i x /2^k } \vert1\rangle \right) \:\text{nach Umordnung der Summen und Produkte sowie Entwicklung} \sum_{y=0}^{N-1} = \sum_{y_1=0}^{1}\sum_{y_2=0}^{1}\ldots\sum_{y_n=0}^{1} \\ & = \frac{1}{\sqrt{N}} \left(\vert0\rangle + e^{\frac{2\pi i}{2}x} \vert1\rangle\right) \otimes \left(\vert0\rangle + e^{\frac{2\pi i}{2^2}x} \vert1\rangle\right) \otimes \ldots \otimes \left(\vert0\rangle + e^{\frac{2\pi i}{2^{n-1}}x} \vert1\rangle\right) \otimes \left(\vert0\rangle + e^{\frac{2\pi i}{2^n}x} \vert1\rangle\right) \end{aligned}

2.4 Beispiel: Circuit-Konstruktion der 3-Qubit-QFT

Aus der obigen Beschreibung ist möglicherweise nicht sofort klar, wie ein QFT-Circuit konstruiert werden soll. Beachte zunächst, dass wir bei drei Qubits Phasen erwarten, die sich mit unterschiedlichen „Raten" entwickeln. Wie das genau in einen Circuit übersetzt wird, bleibt als Übung dem Leser überlassen. Es gibt mehrere Diagramme und Beispiele im Vorlesungs-PDF. Weitere Ressourcen findest du in dieser Lektion des Kurses „Grundlagen der Quantenalgorithmen".

Wir demonstrieren die QFT nur mit Simulatoren und verwenden das Qiskit-Patterns-Framework erst, wenn wir zur Quantenphasenschätzung übergehen.

# Prepare for 3 qubits circuit
qr = QuantumRegister(3)
cr = ClassicalRegister(3)
qc = QuantumCircuit(qr, cr)
qc.h(2)
qc.cp(pi / 2, 1, 2) # Controlled-phase gate from qubit 1 to qubit 2
qc.cp(pi / 4, 0, 2) # Controlled-phase gate from qubit 0 to qubit 2
qc.draw(output="mpl")

Output of the previous code cell

qc.h(1)
qc.cp(pi / 2, 0, 1) # Controlled-phase gate from qubit 0 to qubit 1

qc.draw(output="mpl")

Output of the previous code cell

qc.h(0)

qc.draw(output="mpl")

Output of the previous code cell

qc.swap(0, 2)

qc.draw(output="mpl")

Output of the previous code cell

Als Beispiel wenden wir die QFT auf 5|5\rangle an.

Zunächst bestätigen wir die Binärdarstellung der Ganzzahl 5 und erstellen den Circuit, der den Zustand 5 kodiert:

bin(5)
'0b101'
qc = QuantumCircuit(3)

qc.x(0)
qc.x(2)
qc.draw(output="mpl")

Output of the previous code cell

Wir überprüfen die Quantenzustände mit dem Aer-Simulator:

statevector = Statevector(qc)
plot_bloch_multivector(statevector)

Output of the previous code cell

Schließlich fügen wir die QFT hinzu und sehen den Endzustand unserer Qubits:

qc.h(2)
qc.cp(pi / 2, 1, 2)
qc.cp(pi / 4, 0, 2)

qc.h(1)
qc.cp(pi / 2, 0, 1)

qc.h(0)

qc.swap(0, 2)

qc.draw(output="mpl")

Output of the previous code cell

statevector = Statevector(qc)
plot_bloch_multivector(statevector)

Output of the previous code cell

Wir sehen, dass die QFT-Funktion korrekt funktioniert hat. Qubit 0 wurde um 58\tfrac{5}{8} einer vollen Umdrehung gedreht, Qubit 1 um 108\tfrac{10}{8} volle Umdrehungen (entspricht 14\tfrac{1}{4} einer vollen Umdrehung) und Qubit 2 um 208\tfrac{20}{8} volle Umdrehungen (entspricht 12\tfrac{1}{2} einer vollen Umdrehung).

2.5 Übung: QFT

(1) Implementiere die QFT für 4 Qubits.

##your code goes here##

(2) Wende die QFT auf 14|14\rangle an, simuliere und zeichne den Zustandsvektor auf der Bloch-Sphäre.

##your code goes here##

Lösung der Übung: QFT

(1)

qr = QuantumRegister(4)
cr = ClassicalRegister(4)
qc = QuantumCircuit(qr, cr)

qc.h(3)
qc.cp(pi / 2, 2, 3)
qc.cp(pi / 4, 1, 3)
qc.cp(pi / 8, 0, 3)

qc.h(2)
qc.cp(pi / 2, 1, 2)
qc.cp(pi / 4, 0, 2)

qc.h(1)
qc.cp(pi / 2, 0, 1)

qc.h(0)

qc.swap(0, 3)
qc.swap(1, 2)

qc.draw(output="mpl")

Output of the previous code cell

(2)

bin(14)
'0b1110'
qc = QuantumCircuit(4)

qc.x(1)
qc.x(2)
qc.x(3)
qc.draw("mpl")

Output of the previous code cell

qc.h(3)
qc.cp(pi / 2, 2, 3)
qc.cp(pi / 4, 1, 3)
qc.cp(pi / 8, 0, 3)

qc.h(2)
qc.cp(pi / 2, 1, 2)
qc.cp(pi / 4, 0, 2)

qc.h(1)
qc.cp(pi / 2, 0, 1)
qc.h(0)

qc.swap(0, 3)
qc.swap(1, 2)

qc.draw(output="mpl")

Output of the previous code cell

statevector = Statevector(qc)
plot_bloch_multivector(statevector)

Output of the previous code cell

3. Grundlagen der Quantenphasenschätzung (QPE)

Gegeben eine unitäre Operation UU, schätzt die QPE θ\theta in Uψ=e2πiθψU\vert\psi \rangle =e^{\boldsymbol{2\pi i} \theta }|\psi \rangle; da UU unitär ist, haben alle Eigenwerte den Betrag 1.

3.1 Vorgehen

1. Aufbau

ψ\vert\psi\rangle befindet sich in einem Qubit-Register. Ein weiteres Register aus nn Qubits bildet das Zählregister, in dem wir den Wert 2nθ2^n\theta speichern:

ψ0=0nψ|\psi_0\rangle = \lvert 0 \rangle^{\otimes n} \lvert \psi \rangle

2. Superposition

Wende eine nn-Bit-Hadamard-Gate-Operation HnH^{\otimes n} auf das Zählregister an:

ψ1=12n2(0+1)nψ|\psi_1\rangle = {\frac {1}{2^{\frac {n}{2}}}}\left(|0\rangle +|1\rangle \right)^{\otimes n} \lvert \psi \rangle

3. Gesteuerte unitäre Operationen

Wir führen das gesteuerte Unitäre CUCU ein, das den unitären Operator UU auf das Zielregister anwendet, wenn das entsprechende Steuer-Bit 1|1\rangle ist. Da UU ein unitärer Operator mit Eigenvektor ψ|\psi\rangle ist, sodass Uψ=e2πiθψU|\psi \rangle =e^{\boldsymbol{2\pi i} \theta }|\psi \rangle, gilt:

U2jψ=U2j1Uψ=U2j1e2πiθψ==e2πi2jθψU^{2^{j}}|\psi \rangle =U^{2^{j}-1}U|\psi \rangle =U^{2^{j}-1}e^{2\pi i\theta }|\psi \rangle =\cdots =e^{2\pi i2^{j}\theta }|\psi \rangle

3.2 Beispiel: T-Gate-QPE

Verwenden wir das TT-Gate als Beispiel für die QPE und schätzen wir seine Phase θ\theta.

T1=(100eiπ4)(01)=eiπ41T|1\rangle = \begin{pmatrix} 1 & 0\\ 0 & e^\frac{i\pi}{4}\\ \end{pmatrix} \begin{pmatrix} 0\\ 1\\ \end{pmatrix} = e^\frac{i\pi}{4}|1\rangle

Wir erwarten:

θ=18\theta = \frac{1}{8}

wobei

T1=e2iπθ1T|1\rangle = e^{2i\pi\theta}|1\rangle

Wir initialisieren ψ=1\vert\psi\rangle = \vert1\rangle als Eigenvektor des TT-Gates durch Anwendung eines XX-Gates:

qpe = QuantumCircuit(4, 3)
qpe.x(3)
qpe.draw(output="mpl")

Output of the previous code cell

Als nächstes wenden wir Hadamard-Gates auf die Zähl-Qubits an:

for qubit in range(3):
qpe.h(qubit)
qpe.draw(output="mpl")

Output of the previous code cell

Wir führen die gesteuerten unitären Operationen durch:

repetitions = 1
for counting_qubit in range(3):
for i in range(repetitions):
qpe.cp(pi / 4, counting_qubit, 3) # This is C-U
repetitions *= 2
qpe.draw(output="mpl")

Output of the previous code cell

Wir wenden die inverse Quanten-Fourier-Transformation an, um den Zustand des Zählregisters zu konvertieren, und messen dann das Zählregister:

from qiskit.circuit.library import QFT
# Apply inverse QFT
qpe.append(QFT(3, inverse=True), [0, 1, 2])
qpe.draw(output="mpl")

Output of the previous code cell

for n in range(3):
qpe.measure(n, n)
qpe.draw(output="mpl")

Output of the previous code cell

Wir können mit dem Aer-Simulator simulieren:

aer_sim = AerSimulator()
shots = 2048

pm = generate_preset_pass_manager(backend=aer_sim, optimization_level=1)
t_qpe = pm.run(qpe)

sampler = Sampler(mode=aer_sim)
job = sampler.run([t_qpe], shots=shots)
result = job.result()
answer = result[0].data.c.get_counts()

plot_histogram(answer)

Output of the previous code cell

Wir erhalten ein Ergebnis (001) mit Sicherheit, was als Dezimalzahl 1 ergibt. Nun müssen wir unser Ergebnis (1) durch 2n2^n teilen, um θ\theta zu erhalten:

θ=123=18\theta = \frac{1}{2^3} = \frac{1}{8}

Shors Algorithmus ermöglicht es uns, eine Zahl zu faktorisieren, indem wir einen Circuit mit unbekanntem θ\theta konstruieren und θ\theta bestimmen.

3.3 Übung

Schätze θ=1/3\theta = 1/3 mit 3 Qubits zum Zählen und einem Qubit für einen Eigenvektor.

P1=eiλ1P|1\rangle = e^{i\lambda}|1\rangle. Da wir U1=e2πi131U|1\rangle = e^{2\pi i \tfrac{1}{3}}|1\rangle implementieren wollen, müssen wir λ=2π3\lambda = \tfrac{2 \pi}{3} setzen.

##your code goes here##

Lösung der Übung: θ=1/3\theta = 1/3

# Create and set up circuit
qpe = QuantumCircuit(4, 3)

# Apply H-Gates to counting qubits:
for qubit in range(3):
qpe.h(qubit)

# Prepare our eigenstate |psi>:
qpe.x(3)

# Do the controlled-U operations:
angle = 2 * pi / 3
repetitions = 1
for counting_qubit in range(3):
for i in range(repetitions):
qpe.cp(angle, counting_qubit, 3)
repetitions *= 2

# Do the inverse QFT:
qpe.append(QFT(3, inverse=True), [0, 1, 2])

for n in range(3):
qpe.measure(n, n)

qpe.draw(output="mpl")

Output of the previous code cell

aer_sim = AerSimulator()
shots = 4096

pm = generate_preset_pass_manager(backend=aer_sim, optimization_level=1)
t_qpe = pm.run(qpe)

sampler = Sampler(mode=aer_sim)
job = sampler.run([t_qpe], shots=shots)
result = job.result()
answer = result[0].data.c.get_counts()

plot_histogram(answer)

Output of the previous code cell

4. Ausführung mit dem Qiskit Runtime Sampler Primitive

Wir werden QPE auf einem echten Quantengerät durchführen und dabei den 4 Schritten der Qiskit-Patterns folgen.

  1. Problem in ein quantennatives Format überführen
  2. Die Circuits optimieren
  3. Den Ziel-Circuit ausführen
  4. Die Ergebnisse nachbearbeiten
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import Sampler
# Loading your IBM Quantum® account(s)

service = QiskitRuntimeService()

4.1 Schritt 1: Problem auf Quantencircuits und Operatoren abbilden

qpe = QuantumCircuit(4, 3)
qpe.x(3)
for qubit in range(3):
qpe.h(qubit)
repetitions = 1
for counting_qubit in range(3):
for i in range(repetitions):
qpe.cp(pi / 4, counting_qubit, 3) # This is C-U
repetitions *= 2
qpe.append(QFT(3, inverse=True), [0, 1, 2])
for n in range(3):
qpe.measure(n, n)
qpe.draw(output="mpl")

Output of the previous code cell

backend = service.least_busy(simulator=False, operational=True, min_num_qubits=4)

print(backend)
<IBMBackend('ibm_strasbourg')>

4.2 Schritt 2: Für Zielhardware optimieren

# Transpile the circuit into basis gates executable on the hardware
pm = generate_preset_pass_manager(backend=backend, optimization_level=2)
qc_compiled = pm.run(qpe)

qc_compiled.draw("mpl", idle_wires=False)

Output of the previous code cell

4.3 Schritt 3: Auf Zielhardware ausführen

real_sampler = Sampler(mode=backend)
job = real_sampler.run([qc_compiled], shots=1024)
job_id = job.job_id()
print("job id:", job_id)
job id: d13p4zb5z6q00087ag00
job = service.job(job_id)  # Input your job-id between the quotations
job.status()
'DONE'
result_real = job.result()
print(result_real)
PrimitiveResult([SamplerPubResult(data=DataBin(c=BitArray(<shape=(), num_shots=1024, num_bits=3>)), metadata={'circuit_metadata': {}})], metadata={'execution': {'execution_spans': ExecutionSpans([DoubleSliceSpan(<start='2025-06-09 22:39:00', stop='2025-06-09 22:39:00', size=1024>)])}, 'version': 2})

4.4 Schritt 4: Ergebnisse nachbearbeiten

from qiskit.visualization import plot_histogram

plot_histogram(result_real[0].data.c.get_counts())

Output of the previous code cell

# See the version of Qiskit
import qiskit

qiskit.__version__
'2.0.2'