Zum Hauptinhalt springen

Qiskit-Implementierung

In diesem Abschnitt schauen wir uns einige Qiskit-Implementierungen der in dieser Lektion eingeführten Konzepte an. Wenn du diese Implementierungen selbst ausführen möchtest – was sehr empfohlen wird –, sieh dir die Seite Install Qiskit in der IBM Quantum-Dokumentation an, um zu erfahren, wie du Qiskit einrichten kannst.

Es sollte beachtet werden, dass Qiskit kontinuierlich weiterentwickelt wird und hauptsächlich darauf ausgerichtet ist, die Leistung der Quantencomputer zu maximieren, für deren Betrieb es eingesetzt wird, die sich ebenfalls ständig weiterentwickeln. Daher unterliegt Qiskit Änderungen, die gelegentlich zu Code-Deprecation führen können. Mit diesem Gedanken im Hinterkopf werden wir in diesem Kurs immer die folgenden Befehle ausführen, bevor wir Beispiele von Qiskit-Code präsentieren, sodass klar ist, welche Version von Qiskit verwendet wurde. Ab Qiskit v1.0 ist dies eine einfache Möglichkeit zu sehen, welche Version von Qiskit aktuell installiert ist.

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit
from qiskit import __version__

print(__version__)
2.1.1

Wenn du dies in einer cloudbasierten Python-Umgebung ausführst, musst du möglicherweise einige der folgenden Pakete installieren:

#!pip install qiskit
#!pip install jupyter
#!pip install sympy
#!pip install matplotlib
#!pip install pylatexenc

Vektoren und Matrizen in Python

Qiskit verwendet die Programmiersprache Python. Bevor wir speziell auf Qiskit eingehen, kann es daher hilfreich sein, kurz auf Matrix- und Vektorberechnungen in Python einzugehen.

In Python können Matrix- und Vektorberechnungen mit der Klasse array aus der NumPy-Bibliothek durchgeführt werden, die Funktionalität für viele numerische und wissenschaftliche Berechnungen bietet. Der folgende Code lädt diese Bibliothek, definiert zwei Spaltenvektoren, ket0 und ket1, die den Qubit-Zustandsvektoren 0\vert 0\rangle und 1\vert 1\rangle entsprechen, und gibt dann ihren Mittelwert aus.

import numpy as np

ket0 = np.array([[1], [0]])
ket1 = np.array([[0], [1]])

print(ket0 / 2 + ket1 / 2)
[[0.5]
[0.5]]

Wir können array auch zur Erstellung von Matrizen verwenden, die Operationen darstellen können.

M1 = np.array([[1, 1], [0, 0]])
M2 = np.array([[1, 0], [0, 1]])
M = M1 / 2 + M2 / 2
print(M)
[[1.  0.5]
[0. 0.5]]

Bitte beachte, dass aller Code innerhalb einer Lektion in diesem Kurs sequenziell ausgeführt werden sollte. Daher müssen wir NumPy hier nicht erneut importieren, da es bereits importiert wurde.

Matrizenmultiplikation, einschließlich Matrix-Vektor-Multiplikation als Spezialfall, kann mit der Funktion matmul aus NumPy durchgeführt werden.

print(np.matmul(M1, ket1))
print(np.matmul(M1, M2))
print(np.matmul(M, M))
[[1]
[0]]
[[1 1]
[0 0]]
[[1. 0.75]
[0. 0.25]]

Diese Ausgabeformatierung lässt optisch etwas zu wünschen übrig. Eine Lösung für Situationen, die eine ansprechendere Darstellung erfordern, ist die Funktion array_to_latex in Qiskit aus dem Modul qiskit.visualization. Beachte, dass wir im folgenden Code Pythons generische display-Funktion verwenden. Im Gegensatz dazu kann das Verhalten von print je nach ausgegebenem Inhalt variieren, wie es zum Beispiel bei Arrays aus NumPy der Fall ist.

from qiskit.visualization import array_to_latex

display(array_to_latex(np.matmul(M1, ket1)))
display(array_to_latex(np.matmul(M1, M2)))
display(array_to_latex(np.matmul(M, M)))
[10] \begin{bmatrix} 1 \\ 0 \\ \end{bmatrix} [1100] \begin{bmatrix} 1 & 1 \\ 0 & 0 \\ \end{bmatrix} [134014] \begin{bmatrix} 1 & \frac{3}{4} \\ 0 & \frac{1}{4} \\ \end{bmatrix}

Zustände, Messungen und Operationen

Qiskit enthält mehrere Klassen, die es ermöglichen, Zustände, Messungen und Operationen zu erstellen und zu manipulieren – es ist also nicht nötig, alles, was für die Simulation von Quantenzuständen, Messungen und Operationen benötigt wird, in Python selbst zu programmieren. Im Folgenden sind einige Beispiele zum Einstieg aufgeführt.

Zustandsvektoren definieren und anzeigen

Die Klasse Statevector in Qiskit bietet Funktionalität zum Definieren und Manipulieren von Quantenzustandsvektoren. Im folgenden Code wird die Klasse Statevector importiert und einige Vektoren werden definiert. (Wir importieren auch die sqrt-Funktion aus der NumPy-Bibliothek, um eine Quadratwurzel zu berechnen. Diese Funktion könnte alternativ als np.sqrt aufgerufen werden, sofern NumPy bereits importiert wurde, wie es oben der Fall ist; dies ist lediglich eine andere Möglichkeit, diese spezifische Funktion zu importieren und zu nutzen.)

from qiskit.quantum_info import Statevector
from numpy import sqrt

u = Statevector([1 / sqrt(2), 1 / sqrt(2)])
v = Statevector([(1 + 2.0j) / 3, -2 / 3])
w = Statevector([1 / 3, 2 / 3])

Die Klasse Statevector enthält eine draw-Methode zur Darstellung von Zustandsvektoren auf verschiedene Arten, darunter text für Klartext, latex für gerendertes LaTeX und latex_source für LaTeX-Code, was beim Kopieren in Dokumente praktisch sein kann. (Verwende print anstelle von display, um LaTeX-Code optimal darzustellen.)

display(u.draw("text"))
display(u.draw("latex"))
print(u.draw("latex_source"))
[0.70710678+0.j,0.70710678+0.j]

220+221\frac{\sqrt{2}}{2} |0\rangle+\frac{\sqrt{2}}{2} |1\rangle

\frac{\sqrt{2}}{2} |0\rangle+\frac{\sqrt{2}}{2} |1\rangle

Die Klasse Statevector enthält außerdem die Methode is_valid, die überprüft, ob ein gegebener Vektor ein gültiger Quantenzustandsvektor ist (d. h. ob er die euklidische Norm 1 hat):

display(u.is_valid())
display(w.is_valid())
True
False

Messungen mit Statevector simulieren

Als Nächstes sehen wir uns eine Möglichkeit an, wie Messungen von Quantenzuständen in Qiskit simuliert werden können, mithilfe der Methode measure der Klasse Statevector. Verwenden wir denselben Qubit-Zustandsvektor v wie zuvor definiert.

display(v.draw("latex"))

(13+2i3)0231(\frac{1}{3} + \frac{2 i}{3}) |0\rangle- \frac{2}{3} |1\rangle

Die Ausführung der Methode measure simuliert eine Standard-Basismessung. Sie gibt das Ergebnis dieser Messung zurück sowie den neuen Quantenzustandsvektor des Systems nach der Messung. (Hier verwenden wir Pythons print-Funktion mit einem f-Präfix für formatierte Ausgabe mit eingebetteten Ausdrücken.)

outcome, state = v.measure()
print(f"Measured: {outcome}\nPost-measurement state:")
display(state.draw("latex"))
Measured: 1
Post-measurement state:

1- |1\rangle

Messergebnisse sind probabilistisch, daher kann diese Methode bei mehrmaliger Ausführung unterschiedliche Ergebnisse liefern. Für das spezifische Beispiel des oben definierten Vektors v definiert die Methode measure den Quantenzustandsvektor nach der Messung als

(1+2i5)0\biggl(\frac{1 + 2i}{\sqrt{5}}\biggr) \vert 0\rangle

(anstatt 0\vert 0\rangle) oder

1- \vert 1\rangle

(anstatt 1\vert 1\rangle), je nach Messergebnis. In beiden Fällen sind die Alternativen zu 0\vert 0\rangle und 1\vert 1\rangle tatsächlich äquivalent zu diesen Zustandsvektoren; man sagt, sie sind äquivalent bis auf eine globale Phase, da jeder gleich dem anderen multipliziert mit einer komplexen Zahl auf dem Einheitskreis ist. Dieses Thema wird in der Lektion Quantum circuits ausführlicher besprochen und kann vorerst ignoriert werden.

Statevector gibt einen Fehler aus, wenn die Methode measure auf einen ungültigen Quantenzustandsvektor angewendet wird.

Statevector verfügt außerdem über eine Methode sample_counts, die die Simulation einer beliebigen Anzahl von Messungen an dem System ermöglicht, wobei jedes Mal mit einer frischen Kopie des Zustands begonnen wird. Der folgende Code zeigt zum Beispiel das Ergebnis der 10001000-maligen Messung des Vektors v, was (mit hoher Wahrscheinlichkeit) zu dem Ergebnis 00 in etwa 55 von jeweils 99 Messungen führt (oder etwa 556556 der 10001000 Versuche) und zu dem Ergebnis 11 in etwa 44 von jeweils 99 Messungen (oder etwa 444444 der 10001000 Versuche). Der folgende Code demonstriert auch die Funktion plot_histogram aus dem Modul qiskit.visualization zur Visualisierung der Ergebnisse.

from qiskit.visualization import plot_histogram

statistics = v.sample_counts(1000)
plot_histogram(statistics)

Ausgabe der vorherigen Code-Zelle

Das mehrfache Ausführen dieses Codes mit unterschiedlichen Stichprobenanzahlen anstelle von 10001000 kann dabei helfen, ein Gefühl dafür zu entwickeln, wie die Anzahl der Versuche die Häufigkeit der einzelnen Ergebnisse beeinflusst. Mit immer mehr Stichproben nähert sich der Anteil der Stichproben für jede Möglichkeit wahrscheinlich immer mehr der entsprechenden Wahrscheinlichkeit an. Dieses Phänomen ist in der Wahrscheinlichkeitstheorie allgemein als Gesetz der großen Zahlen bekannt.

Operationen mit Operator und Statevector durchführen

Unitäre Operationen können in Qiskit mit der Klasse Operator definiert werden, wie im folgenden Beispiel. Diese Klasse enthält eine draw-Methode mit ähnlichen Argumenten wie Statevector. Beachte, dass die Option latex Ergebnisse erzeugt, die äquivalent zu array_from_latex sind.

from qiskit.quantum_info import Operator

Y = Operator([[0, -1.0j], [1.0j, 0]])
H = Operator([[1 / sqrt(2), 1 / sqrt(2)], [1 / sqrt(2), -1 / sqrt(2)]])
S = Operator([[1, 0], [0, 1.0j]])
T = Operator([[1, 0], [0, (1 + 1.0j) / sqrt(2)]])

display(T.draw("latex"))
[10022+2i2] \begin{bmatrix} 1 & 0 \\ 0 & \frac{\sqrt{2}}{2} + \frac{\sqrt{2} i}{2} \\ \end{bmatrix}

Wir können eine unitäre Operation auf einen Zustandsvektor mit der Methode evolve anwenden.

v = Statevector([1, 0])

v = v.evolve(H)
v = v.evolve(T)
v = v.evolve(H)
v = v.evolve(S)
v = v.evolve(Y)

display(v.draw("latex"))

(0.14644660940.3535533906i)0+(0.3535533906+0.8535533906i)1(0.1464466094 - 0.3535533906 i) |0\rangle+(-0.3535533906 + 0.8535533906 i) |1\rangle

Ein Vorblick auf Quantum Circuits

Quantum Circuits werden erst in der Lektion Quantum circuits formell eingeführt, die die dritte Lektion in diesem Kurs ist, aber wir können dennoch mit der Komposition unitärer Qubit-Operationen mithilfe der Klasse QuantumCircuit in Qiskit experimentieren. Insbesondere können wir einen Quantum Circuit (der in diesem Fall einfach eine Folge unitärer Operationen auf einem einzigen Qubit ist) wie folgt definieren.

from qiskit import QuantumCircuit

circuit = QuantumCircuit(1)

circuit.h(0)
circuit.t(0)
circuit.h(0)
circuit.s(0)
circuit.y(0)

display(circuit.draw(output="mpl"))

Ausgabe der vorherigen Code-Zelle

Hier verwenden wir die Methode draw der Klasse QuantumCircuit mit dem mpl-Renderer (kurz für Matplotlib, eine Python-Visualisierungsbibliothek). Dies ist der einzige Renderer, den wir in diesem Kurs für Quantum Circuits verwenden werden, aber es gibt andere Optionen, darunter einen textbasierten und einen LaTeX-basierten Renderer.

Die Operationen werden sequenziell angewendet, beginnend links und endend rechts im Diagramm. Eine praktische Möglichkeit, die unitäre Matrix zu erhalten, die diesem Circuit entspricht, ist die Methode from_circuit der Klasse Operator.

display(Operator.from_circuit(circuit).draw("latex"))
[0.14644660940.3535533906i0.8535533906+0.3535533906i0.3535533906+0.8535533906i0.3535533906+0.1464466094i] \begin{bmatrix} 0.1464466094 - 0.3535533906 i & 0.8535533906 + 0.3535533906 i \\ -0.3535533906 + 0.8535533906 i & 0.3535533906 + 0.1464466094 i \\ \end{bmatrix}

Wir können auch einen Anfangs-Quantenzustandsvektor initialisieren und diesen Zustand dann gemäß der durch den Circuit beschriebenen Operationsfolge entwickeln.

ket0 = Statevector([1, 0])
v = ket0.evolve(circuit)
display(v.draw("latex"))

(0.14644660940.3535533906i)0+(0.3535533906+0.8535533906i)1(0.1464466094 - 0.3535533906 i) |0\rangle+(-0.3535533906 + 0.8535533906 i) |1\rangle

Der folgende Code simuliert ein Experiment, bei dem der aus dem obigen Circuit erhaltene Zustand 4000-mal mit einer Standard-Basismessung gemessen wird (jedes Mal mit einer frischen Kopie des Zustands).

statistics = v.sample_counts(4000)
display(plot_histogram(statistics))

Ausgabe der vorherigen Code-Zelle