Přeskočit na hlavní obsah

Implementace v Qiskitu

V této sekci se podíváme na některé implementace konceptů z této lekce v Qiskitu. Pokud si chceš tyto implementace spustit sám/sama, což vřele doporučujeme, podívej se na stránku Instalace Qiskitu v dokumentaci IBM Quantum, kde najdeš podrobnosti o nastavení Qiskitu.

Je důležité mít na paměti, že Qiskit je neustále vyvíjen a je primárně zaměřen na maximalizaci výkonu kvantových počítačů, které pomocí něj ovládáš a které se samy stále vyvíjejí. V důsledku toho se Qiskit mění, což může občas vést k zastarávání kódu. S ohledem na to vždy před ukázkami kódu v Qiskitu v tomto kurzu spustíme následující příkazy, aby bylo jasné, která verze Qiskitu byla použita. Od verze Qiskit v1.0 je to jednoduchý způsob, jak zjistit aktuálně nainstalovanou verzi Qiskitu.

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

print(__version__)
2.2.3

Pokud to spouštíš v cloudovém prostředí Pythonu, můžeš potřebovat nainstalovat některé z následujících balíčků:

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

Vektory a matice v Pythonu​

Qiskit používá programovací jazyk Python, takže než budeme mluvit konkrétně o Qiskitu, může být užitečné si velmi stručně projít výpočty s maticemi a vektory v Pythonu.

V Pythonu lze výpočty s maticemi a vektory provádět pomocí třídy array z knihovny NumPy, která poskytuje funkcionalitu pro mnoho numerických a vědeckých výpočtů. Následující kód načte tuto knihovnu, definuje dva sloupcové vektory, ket0 a ket1, odpovídající stavovým vektorům qubitu ∣0⟩\vert 0\rangle a ∣1⟩\vert 1\rangle, a poté vypíše jejich průměr.

import numpy as np

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

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

Pomocí array můžeme také vytvářet matice reprezentující operace.

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

Vezmi prosím na vědomí, že veškerý kód v dané lekci tohoto kurzu se očekává, že bude spouštěn postupně. Proto zde nemusíme znovu importovat NumPy, protože už byl naimportován.

Násobení matic, včetně násobení matice vektorem jako speciálního případu, lze provádět pomocí operátoru @.

print(M1 @ ket1)
print(M1 @ M2)
print(M @ M)
[[1]
[0]]
[[1 1]
[0 0]]
[[1. 0.75]
[0. 0.25]]

Toto formátování výstupu není vizuálně úplně ideální. Jedním řešením pro situace, kdy potřebuješ něco hezčího, je použití funkce array_to_latex v Qiskitu z modulu qiskit.visualization. Všimni si, že v následujícím kódu používáme obecnou funkci Pythonu display (zejména v noteboocích), protože umí vykreslovat objekty pomocí bohatších reprezentací, zatímco print() jednoduše používá řetězcovou reprezentaci objektu. To je podobné tomu, jak pole NumPy poskytují svůj vlastní specializovaný formát, zatímco seznamy Pythonu používají výchozí reprezentaci seznamu.

from qiskit.visualization import array_to_latex

display(array_to_latex(M1 @ ket1))
display(array_to_latex(M1 @ M2))
display(array_to_latex(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}

Stavy, měření a operace​

Qiskit obsahuje několik tříd, které umožňují vytvářet a manipulovat se stavy, měřeními a operacemi -- takže není třeba programovat vše potřebné pro simulaci kvantových stavů, měření a operací v Pythonu od nuly. Níže jsou uvedeny některé příklady, které ti pomohou začít.

Definice a zobrazení stavových vektorů​

Třída Statevector v Qiskitu poskytuje funkcionalitu pro definování a manipulaci s kvantovými stavovými vektory. V následujícím kódu je naimportována třída Statevector a definováno několik vektorů. (Také importujeme funkci sqrt z knihovny NumPy pro výpočet odmocniny. Tuto funkci lze alternativně volat jako np.sqrt za předpokladu, že NumPy již byl naimportován, jak tomu bylo výše; jde pouze o jiný způsob importu a použití této konkrétní funkce.)

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

Třída Statevector obsahuje metodu draw pro zobrazení stavových vektorů různými způsoby, včetně text pro čistý text, latex pro vykreslený LaTeX a latex_source pro zdrojový kód LaTeXu, což se může hodit pro kopírování do dokumentu. (Pro zobrazení LaTeX kódu použij print místo display pro nejlepší výsledky.)

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

22∣0⟩+22∣1⟩\frac{\sqrt{2}}{2} |0\rangle+\frac{\sqrt{2}}{2} |1\rangle

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

Třída Statevector také obsahuje metodu is_valid, která ověřuje, zda je daný vektor platným kvantovým stavovým vektorem (jinými slovy, že má euklidovskou normu rovnou 1):

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

Simulace měření pomocí Statevector​

Dále se podíváme na jeden způsob, jak lze v Qiskitu simulovat měření kvantových stavů, a to pomocí metody measure z třídy Statevector. Použijme stejný stavový vektor qubitu v, který jsme definovali dříve.

display(v.draw("latex"))

(13+2i3)∣0⟩−23∣1⟩(\frac{1}{3} + \frac{2 i}{3}) |0\rangle- \frac{2}{3} |1\rangle

Spuštění metody measure simuluje měření ve standardní bázi. Vrátí výsledek tohoto měření a nový kvantový stavový vektor systému po měření. (Zde používáme Pythonovou funkci print s prefixem f pro formátovaný výstup s vloženými výrazy.)

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

−∣1⟩- |1\rangle

Výsledky měření jsou pravděpodobnostní, takže tato metoda může při opakovaném spuštění vracet různé výsledky. Pro konkrétní příklad vektoru v definovaného výše metoda measure definuje kvantový stavový vektor po měření jako

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

(místo ∣0⟩\vert 0\rangle) nebo

−∣1⟩- \vert 1\rangle

(místo ∣1⟩\vert 1\rangle), v závislosti na výsledku měření. V obou případech jsou alternativy k ∣0⟩\vert 0\rangle a ∣1⟩\vert 1\rangle ve skutečnosti ekvivalentní těmto stavovým vektorům; říká se, že jsou ekvivalentní až na globální fázi, protože jeden se rovná druhému vynásobenému komplexním číslem na jednotkové kružnici. Tato problematika je podrobněji probrána v lekci Kvantové obvody a prozatím ji můžeš bezpečně ignorovat.

Statevector vyhodí chybu, pokud je metoda measure aplikována na neplatný kvantový stavový vektor.

Statevector také disponuje metodou sample_counts, která umožňuje simulovat libovolný počet měření na systému, přičemž pokaždé začíná s novou kopií stavu. Například následující kód ukazuje výsledek měření vektoru v 10001000 krát, což (s vysokou pravděpodobností) vede k výsledku 00 přibližně 55 z každých 99 případů (neboli asi 556556 z 10001000 pokusů) a k výsledku 11 přibližně 44 z každých 99 případů (neboli asi 444444 z 10001000 pokusů). Následující kód také demonstruje funkci plot_histogram z modulu qiskit.visualization pro vizualizaci výsledků.

from qiskit.visualization import plot_histogram

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

Output of the previous code cell

Spuštění tohoto kódu vícekrát s různými počty vzorků místo 10001000, ti může pomoci vybudovat intuici pro to, jak počet pokusů ovlivňuje počet výskytů jednotlivých výsledků. S více a více vzorky se podíl vzorků pro každou možnost pravděpodobně přibližuje odpovídající pravděpodobnosti. Tento jev je obecněji znám jako zákon velkých čísel v teorii pravděpodobnosti.

Provádění operací pomocí Operator a Statevector​

Unitární operace lze v Qiskitu definovat pomocí třídy Operator, jak ukazuje následující příklad. Tato třída obsahuje metodu draw s podobnými argumenty jako Statevector. Vezmi na vědomí, že volba latex produkuje výsledky ekvivalentní array_to_latex.

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}

Unitární operaci můžeme na stavový vektor aplikovat pomocí metody evolve.

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.1464466094−0.3535533906i)∣0⟩+(−0.3535533906+0.8535533906i)∣1⟩(0.1464466094 - 0.3535533906 i) |0\rangle+(-0.3535533906 + 0.8535533906 i) |1\rangle

Náhled na kvantové obvody​

Kvantové obvody budou formálně představeny až v lekci Kvantové obvody, což je třetí lekce v tomto kurzu, ale už teď si můžeme vyzkoušet skládání unitárních operací na qubitu pomocí třídy QuantumCircuit v Qiskitu. Konkrétně můžeme definovat kvantový obvod (který v tomto případě bude jednoduše sekvencí unitárních operací provedených na jednom qubitu) následovně.

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

Output of the previous code cell

Zde používáme metodu draw z třídy QuantumCircuit s rendererem mpl (zkratka pro Matplotlib, vizualizační knihovnu Pythonu). Toto je jediný renderer, který budeme pro kvantové obvody v tomto kurzu používat, ale existují i další možnosti, včetně textového a LaTeX rendereru.

Operace se aplikují sekvenčně, od levé strany diagramu směrem doprava. Šikovný způsob, jak získat unitární matici odpovídající tomuto obvodu, je použít metodu from_circuit z třídy Operator.

display(Operator.from_circuit(circuit).draw("latex"))
[0.1464466094−0.3535533906i0.8535533906+0.3535533906i−0.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}

Můžeme také inicializovat počáteční kvantový stavový vektor a pak ho nechat vyvíjet podle sekvence operací popsané obvodem.

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

(0.1464466094−0.3535533906i)∣0⟩+(−0.3535533906+0.8535533906i)∣1⟩(0.1464466094 - 0.3535533906 i) |0\rangle+(-0.3535533906 + 0.8535533906 i) |1\rangle

Následující kód simuluje experiment, ve kterém je stav získaný z výše uvedeného obvodu měřen standardním bázovým měřením 4000 krát (pokaždé s novou kopií stavu).

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

Output of the previous code cell