Implementace v Qiskitu
V této lekci implementujeme některé myšlenky z lekce o provázanosti v akci pomocí Qiskitu.
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-aer
from qiskit import __version__
print(__version__)
2.1.1
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit_aer import AerSimulator
from qiskit.visualization import plot_histogram, array_to_latex
from qiskit.result import marginal_distribution
from qiskit.circuit.library import UGate
from math import pi
import random
Tady je implementace teleportačního protokolu jako kvantový Circuit.
qubit = QuantumRegister(1, "Q")
ebit0 = QuantumRegister(1, "A")
ebit1 = QuantumRegister(1, "B")
a = ClassicalRegister(1, "a")
b = ClassicalRegister(1, "b")
protocol = QuantumCircuit(qubit, ebit0, ebit1, a, b)
# Prepare ebit used for teleportation
protocol.h(ebit0)
protocol.cx(ebit0, ebit1)
protocol.barrier()
# Alice's operations
protocol.cx(qubit, ebit0)
protocol.h(qubit)
protocol.barrier()
# Alice measures and sends classical bits to Bob
protocol.measure(ebit0, a)
protocol.measure(qubit, b)
protocol.barrier()
# Bob uses the classical bits to conditionally apply gates
with protocol.if_test((a, 1)):
protocol.x(ebit1)
with protocol.if_test((b, 1)):
protocol.z(ebit1)
display(protocol.draw(output="mpl"))
Circuit využívá několik funkcí Qiskitu, které jsme v předchozích lekcích ještě neviděli, včetně funkcí barrier a if_test.
Funkce barrier vytváří vizuální oddělení, díky kterému je diagram Circuit čitelnější, a zároveň brání Qiskitu provádět různá zjednodušení a optimalizace přes bariéru během kompilace, když se Circuit spouštějí na reálném hardwaru.
Funkce if_test aplikuje operaci podmíněně v závislosti na klasickém bitu nebo registru.
Circuit nejprve inicializuje do stavu (což není součástí samotného protokolu), následují Aliciny operace, pak její měření a nakonec Bobovy operace. Abychom ověřili, že protokol funguje správně, aplikujeme náhodně vygenerovanou jednoqubitovou Gate na inicializovaný stav qubit a získáme náhodný kvantový stavový vektor k teleportaci. Aplikací inverze (tedy komplexně sdruženého transpozice) této hradlo na po proběhnutí protokolu můžeme ověřit, že stav byl teleportován, tím, že měřením uvidíme, že se vrátil do stavu .
Nejprve náhodně zvolíme unitární qubitovou Gate.
random_gate = UGate(
theta=random.random() * 2 * pi,
phi=random.random() * 2 * pi,
lam=random.random() * 2 * pi,
)
display(array_to_latex(random_gate.to_matrix()))
Teď vytvoříme nový testovací Circuit, který nejprve aplikuje naši náhodnou Gate na , pak spustí teleportační Circuit a nakonec aplikuje inverzi naší náhodné Gate na qubit a provede měření. Výsledek by měl být s jistotou.
# Create a new circuit including the same bits and qubits used in the
# teleportation protocol.
test = QuantumCircuit(qubit, ebit0, ebit1, a, b)
# Start with the randomly selected gate on Q
test.append(random_gate, qubit)
test.barrier()
# Append the entire teleportation protocol from above.
test = test.compose(protocol)
test.barrier()
# Finally, apply the inverse of the random unitary to B and measure.
test.append(random_gate.inverse(), ebit1)
result = ClassicalRegister(1, "Result")
test.add_register(result)
test.measure(ebit1, result)
display(test.draw(output="mpl"))
Nakonec spustíme Aer simulátor na tomto Circuit a vykreslíme histogram výstupů. Uvidíme statistiky pro všechny tři klasické bity: spodní/nejlevější bit by měl být vždy , což indikuje, že qubit byl úspěšně teleportován do , zatímco ostatní dva bity by měly být přibližně rovnoměrně rozdělené.
result = AerSimulator().run(test).result()
statistics = result.get_counts()
display(plot_histogram(statistics))
Můžeme také vyfiltrovat statistiky tak, aby se zaměřily pouze na výsledkový qubit testu, pokud chceme, a to takto:
filtered_statistics = marginal_distribution(statistics, [2])
display(plot_histogram(filtered_statistics))
Superdense kódování
Superdense kódování je protokol, který v jistém smyslu dosahuje komplementárního cíle k teleportaci. Místo toho, aby umožňoval přenos jednoho qubit pomocí dvou klasických bitů komunikace (za cenu jednoho e-bitu provázanosti), umožňuje přenos dvou klasických bitů pomocí jednoho qubit kvantové komunikace (opět za cenu jednoho e-bitu provázanosti).
Podrobněji: máme odesílatele (Alice) a příjemce (Boba), kteří sdílejí jeden e-bit provázanosti. Podle konvencí zavedených v lekci to znamená, že Alice drží qubit , Bob drží qubit , a společně je pár ve stavu . Alice chce Bobovi přenést dva klasické bity, které budeme značit a , a dosáhne toho odesláním jednoho qubit.
Je rozumné považovat tento výkon za méně zajímavý než to, čeho dosahuje teleportace. Odesílání qubit bude pravděpodobně v dohledné budoucnosti mnohem obtížnější než odesílání klasických bitů, takže výměna jednoho qubit kvantové komunikace za dva bity klasické komunikace, navíc za cenu e-bitu, se sotva zdá stát za to. To ale neznamená, že superdense kódování není zajímavé, protože zcela jistě je.
V souladu s tématem lekce je jedním z důvodů, proč je superdense kódování zajímavé, to, že demonstruje konkrétní a (v kontextu teorie informace) poměrně pozoruhodné využití provázanosti. Slavná věta v kvantové teorii informace, známá jako Holevova věta, říká, že bez použití sdíleného provázaného stavu je nemožné komunikovat více než jeden bit klasické informace odesláním jednoho qubit. (Holevova věta je obecnější. Její přesné znění je technické a vyžaduje vysvětlení, ale toto je jeden z jejích důsledků.) Prostřednictvím superdense kódování tedy sdílená provázanost efektivně umožňuje zdvojení kapacity klasického přenosu informace při odesílání qubit.
Protokol
Následující diagram kvantového Circuit popisuje protokol superdense kódování:

Slovy, tady je co Alice dělá:
-
Pokud , Alice provede Gate na svém qubit (a pokud , neprovede nic).
-
Pokud , Alice provede Gate na svém qubit (a pokud , neprovede nic).
Alice pak pošle svůj qubit Bobovi.
Když Bob přijme qubit , nejprve provede Gate CNOT, kde je řídicí a je cílový, a pak aplikuje Hadamardovu Gate na . Poté změří a získá a a získá , v obou případech měřením ve standardní bázi.
Analýza
Myšlenka za tímto protokolem je jednoduchá: Alice si vlastně vybere, který Bellův stav chce sdílet s Bobem, pošle Bobovi svůj qubit a Bob měřením zjistí, který Bellův stav Alice zvolila.
To znamená, že na začátku sdílejí a v závislosti na bitech a Alice buď tento stav ponechá beze změny, nebo ho posune do jednoho z ostatních Bellových stavů aplikací nebo na svůj qubit
Bobovy akce mají na čtyři Bellovy stavy následující efekty:
To si můžeš ověřit přímo tak, že spočítáš výsledky Bobových operací na těchto stavech jeden po druhém.
Takže když Bob provede svá měření, dokáže určit, který Bellův stav Alice zvolila. Ověření, že protokol funguje správně, je otázkou kontroly každého případu:
-
Pokud , pak stav v okamžiku, kdy Bob obdrží , je . Tento stav transformuje na a získá .
-
Pokud , pak stav v okamžiku, kdy Bob obdrží , je . Tento stav transformuje na a získá .
-
Pokud , pak stav v okamžiku, kdy Bob obdrží , je . Tento stav transformuje na a získá .
-
Pokud , pak stav v okamžiku, kdy Bob obdrží , je . Tento stav transformuje na a získá . (Záporný fázový faktor zde nemá žádný vliv.)
Implementace superdense coding
Tady je jednoduchá implementace superdense coding, kde specifikujeme Circuit sám v závislosti na bitech, které se mají přenést. Nejdřív zvolíme dva bity k přenosu. (Později je budeme volit náhodně, ale prozatím jednoduše provedeme libovolnou volbu.)
c = "1"
d = "0"
Teď sestavíme Circuit podle toho. Zde necháme Qiskit použít výchozí názvy pro qubity: pro horní qubit a pro spodní.
protocol = QuantumCircuit(2)
# Prepare ebit used for superdense coding
protocol.h(0)
protocol.cx(0, 1)
protocol.barrier()
# Alice's operations
if d == "1":
protocol.z(0)
if c == "1":
protocol.x(0)
protocol.barrier()
# Bob's actions
protocol.cx(0, 1)
protocol.h(0)
protocol.measure_all()
display(protocol.draw(output="mpl"))
Nic moc nového tu není, až na funkci measure_all, která změří všechny qubity a uloží výsledky do jednoho klasického registru (v tomto případě tedy se dvěma bity).
Spuštění simulátoru Aer produkuje očekávaný výstup.
result = AerSimulator().run(protocol).result()
statistics = result.get_counts()
for outcome, frequency in statistics.items():
print(f"Measured {outcome} with frequency {frequency}")
display(plot_histogram(statistics))
Measured 10 with frequency 1024
Teď použijeme další qubit jako generátor náhodných bitů -- v podstatě k hodu spravedlivou mincí. Použijeme ho k náhodnému výběru a a pak spustíme protokol superdense coding.
rbg = QuantumRegister(1, "coin")
ebit0 = QuantumRegister(1, "A")
ebit1 = QuantumRegister(1, "B")
Alice_c = ClassicalRegister(1, "Alice c")
Alice_d = ClassicalRegister(1, "Alice d")
test = QuantumCircuit(rbg, ebit0, ebit1, Alice_d, Alice_c)
# Initialize the ebit
test.h(ebit0)
test.cx(ebit0, ebit1)
test.barrier()
# Use the 'coin' qubit twice to generate Alice's bits c and d.
test.h(rbg)
test.measure(rbg, Alice_c)
test.h(rbg)
test.measure(rbg, Alice_d)
test.barrier()
# Now the protocol runs, starting with Alice's actions, which depend
# on her bits.
with test.if_test((Alice_d, 1), label="Z"):
test.z(ebit0)
with test.if_test((Alice_c, 1), label="X"):
test.x(ebit0)
test.barrier()
# Bob's actions
test.cx(ebit0, ebit1)
test.h(ebit0)
test.barrier()
Bob_c = ClassicalRegister(1, "Bob c")
Bob_d = ClassicalRegister(1, "Bob d")
test.add_register(Bob_d)
test.add_register(Bob_c)
test.measure(ebit0, Bob_d)
test.measure(ebit1, Bob_c)
display(test.draw(output="mpl"))
Spuštění simulátoru Aer ukazuje výsledky: klasické bity Alice a Boba se vždy shodují.
result = AerSimulator().run(test).result()
statistics = result.get_counts()
display(plot_histogram(statistics))
Implementace v Qiskitu
CHSH hru společně s výše definovanou kvantovou strategií můžeme v Qiskitu implementovat následovně.
Nejprve zde je definice samotné hry, která umožňuje připojit libovolnou strategii jako argument.
def chsh_game(strategy):
# This function runs the CHSH game, using the strategy (a function
# from two bits to two bits), returning 1 for a win and 0 for a loss.
# Choose x and y randomly
x, y = random.randint(0, 1), random.randint(0, 1)
# Use the strategy to determine a and b
a, b = strategy(x, y)
# Decide if the strategy wins or loses
if (a != b) == (x & y):
return 1 # Win
return 0 # Lose
Nyní vytvoříme funkci, která vygeneruje Circuit v závislosti na otázkách pro Alice a Boba. Pro jednoduchost ponecháme qubit s jejich výchozími názvy a pro akce Alice a Boba použijeme vestavěný Gate.
def chsh_circuit(x, y):
# This function creates a `QuantumCircuit` implementing the quantum
# strategy described above (including the e-bit preparation).
qc = QuantumCircuit(2, 2)
# Prepare an e-bit
qc.h(0)
qc.cx(0, 1)
qc.barrier()
# Alice's actions
if x == 0:
qc.ry(0, 0)
else:
qc.ry(-pi / 2, 0)
qc.measure(0, 0)
# Bob's actions
if y == 0:
qc.ry(-pi / 4, 1)
else:
qc.ry(pi / 4, 1)
qc.measure(1, 1)
return qc
Zde jsou čtyři možné Circuit v závislosti na tom, jaké otázky jsou položeny.
# Draw the four possible circuits
print("(x,y) = (0,0)")
display(chsh_circuit(0, 0).draw(output="mpl"))
print("(x,y) = (0,1)")
display(chsh_circuit(0, 1).draw(output="mpl"))
print("(x,y) = (1,0)")
display(chsh_circuit(1, 0).draw(output="mpl"))
print("(x,y) = (1,1)")
display(chsh_circuit(1, 1).draw(output="mpl"))
(x,y) = (0,0)
(x,y) = (0,1)
(x,y) = (1,0)
(x,y) = (1,1)
Nyní vytvoříme úlohu pomocí simulátoru Aer, která spustí Circuit jednou pro daný vstupní pár .
def quantum_strategy(x, y):
# This function runs the appropriate quantum circuit defined above
# one time and returns the measurement results
# Setting `shots=1` to run the circuit once
result = AerSimulator().run(chsh_circuit(x, y), shots=1).result()
statistics = result.get_counts()
# Determine the output bits and return them
bits = list(statistics.keys())[0]
a, b = bits[0], bits[1]
return a, b
Nakonec zahrajeme hru 1000krát a vypočítáme podíl výher strategie.
NUM_GAMES = 1000
TOTAL_SCORE = 0
for _ in range(NUM_GAMES):
TOTAL_SCORE += chsh_game(quantum_strategy)
print("Fraction of games won:", TOTAL_SCORE / NUM_GAMES)
Fraction of games won: 0.867
Můžeme také definovat klasickou strategii a podívat se, jak dobře funguje. Toto je jen jedna strategie — další lze otestovat úpravou kódu — ale patří mezi optimální klasické strategie.
def classical_strategy(x, y):
# This function implements just one example of an optimal classical
# strategy for the CHSH game. Other classical strategies can be
# implemented by changing the bit values assigned to a and b.
# Alice's answer
if x == 0:
a = 0
elif x == 1:
a = 1
# Bob's answer
if y == 0:
b = 1
elif y == 1:
b = 0
return a, b
Znovu zahrajeme hru 1000krát, abychom viděli, jak dobře funguje.
NUM_GAMES = 1000
TOTAL_SCORE = 0
for _ in range(NUM_GAMES):
TOTAL_SCORE += chsh_game(classical_strategy)
print("Fraction of games won:", TOTAL_SCORE / NUM_GAMES)
Fraction of games won: 0.747
I když je v tom náhodnost, statistiky se po 1000 bězích velmi pravděpodobně příliš neodchýlí. Kvantová strategie vyhrává přibližně v 85 % případů, zatímco klasická strategie nemůže vyhrát více než přibližně v 75 % případů.