Lewati ke konten utama

Implementasi Qiskit

Di pelajaran ini, kita mengimplementasikan beberapa ide dari pelajaran tentang entanglement in action, menggunakan Qiskit.

# 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

Berikut ini adalah implementasi Circuit kuantum dari protokol teleportasi.

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

Output of the previous code cell

Circuit ini menggunakan beberapa fitur Qiskit yang belum kita lihat di pelajaran sebelumnya, termasuk fungsi barrier dan if_test. Fungsi barrier membuat pemisah visual yang membuat diagram Circuit lebih mudah dibaca, dan juga mencegah Qiskit melakukan berbagai penyederhanaan dan optimasi melintasi barrier saat kompilasi ketika Circuit dijalankan di hardware nyata. Fungsi if_test menerapkan operasi secara kondisional tergantung pada bit atau register klasik.

Circuit ini pertama-tama menginisialisasi (A,B)(\mathsf{A},\mathsf{B}) agar berada dalam keadaan ϕ+\vert \phi^+\rangle (yang bukan bagian dari protokol itu sendiri), diikuti oleh operasi Alice, lalu pengukurannya, dan akhirnya operasi Bob. Untuk menguji bahwa protokol bekerja dengan benar, kita akan menerapkan Gate single-qubit yang dibuat secara acak ke keadaan 0\vert 0\rangle yang diinisialisasi dari Q\mathsf{Q} untuk mendapatkan vektor keadaan kuantum acak yang akan diteleportasi. Dengan menerapkan invers (yakni, conjugate transpose) dari Gate tersebut ke B\mathsf{B} setelah protokol dijalankan, kita bisa memverifikasi bahwa keadaan telah diteleportasi dengan mengukur untuk melihat bahwa ia telah kembali ke keadaan 0\vert 0\rangle.

Pertama kita akan memilih Gate qubit uniter secara acak.

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()))
[0.98972121580.01950801030.141673401i0.0603319186+0.1296609988i0.8319925233+0.5360378028i] \begin{bmatrix} 0.9897212158 & -0.0195080103 - 0.141673401 i \\ 0.0603319186 + 0.1296609988 i & -0.8319925233 + 0.5360378028 i \\ \end{bmatrix}

Sekarang kita akan membuat Circuit pengujian baru yang pertama menerapkan Gate acak kita ke Q\mathsf{Q}, lalu menjalankan Circuit teleportasi, dan akhirnya menerapkan invers dari Gate acak kita ke qubit B\mathsf{B} dan mengukur. Hasilnya seharusnya 00 dengan kepastian.

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

Output of the previous code cell

Terakhir, mari kita jalankan simulator Aer pada Circuit ini dan plot histogram dari outputnya. Kita akan melihat statistik untuk ketiga bit klasik: bit paling bawah/paling kiri seharusnya selalu 00, menunjukkan bahwa qubit Q\mathsf{Q} berhasil diteleportasi ke B\mathsf{B}, sementara dua bit lainnya seharusnya kira-kira seragam.

result = AerSimulator().run(test).result()
statistics = result.get_counts()
display(plot_histogram(statistics))

Output of the previous code cell

Kita juga bisa memfilter statistik untuk fokus hanya pada qubit hasil pengujian jika kita mau, seperti ini:

filtered_statistics = marginal_distribution(statistics, [2])
display(plot_histogram(filtered_statistics))

Output of the previous code cell

Pengodean superdense

Superdense coding adalah protokol yang, dalam arti tertentu, mencapai tujuan yang komplementer dengan teleportasi. Daripada memungkinkan transmisi satu qubit menggunakan dua bit komunikasi klasik (dengan biaya satu e-bit dari entanglement), ia memungkinkan transmisi dua bit klasik menggunakan satu qubit komunikasi kuantum (sekali lagi, dengan biaya satu e-bit dari entanglement).

Lebih rincinya, kita punya pengirim (Alice) dan penerima (Bob) yang berbagi satu e-bit entanglement. Sesuai konvensi yang berlaku untuk pelajaran ini, ini berarti Alice memegang qubit A\mathsf{A}, Bob memegang qubit B\mathsf{B}, dan bersama-sama pasangan (A,B)(\mathsf{A},\mathsf{B}) berada dalam keadaan ϕ+\vert\phi^+\rangle. Alice ingin mengirimkan dua bit klasik ke Bob, yang akan kita sebut cc dan dd, dan dia akan melakukan ini dengan mengirimkan satu qubit kepadanya.

Wajar saja jika pencapaian ini dipandang kurang menarik dibanding yang dicapai teleportasi. Mengirimkan qubit kemungkinan akan jauh lebih sulit daripada mengirimkan bit klasik untuk waktu yang bisa kita perkirakan sehingga menukar satu qubit komunikasi kuantum dengan dua bit komunikasi klasik, dengan biaya satu e-bit pula, hampir tidak tampak sepadan. Namun, ini tidak berarti bahwa superdense coding tidak menarik, karena tentu saja ia sangat menarik.

Sesuai tema pelajaran ini, salah satu alasan mengapa superdense coding menarik adalah bahwa ia mendemonstrasikan penggunaan entanglement yang konkret dan (dalam konteks teori informasi) cukup mencolok. Sebuah teorema terkenal dalam teori informasi kuantum, yang dikenal sebagai Teorema Holevo, menyiratkan bahwa tanpa menggunakan keadaan terbelit bersama, adalah mustahil untuk mengkomunikasikan lebih dari satu bit informasi klasik dengan mengirimkan satu qubit. (Teorema Holevo lebih umum dari ini. Pernyataan pastinya bersifat teknis dan memerlukan penjelasan, tetapi ini adalah salah satu konsekuensinya.) Jadi, melalui superdense coding, entanglement bersama secara efektif memungkinkan penggandaan kapasitas pembawa informasi klasik dari pengiriman qubit.

Protokol

Diagram Circuit kuantum berikut menggambarkan protokol superdense coding:

Superdense coding circuit

Dalam kata-kata, inilah yang Alice lakukan:

  1. Jika d=1d=1, Alice melakukan Gate ZZ pada Qubitnya A\mathsf{A} (dan jika d=0d=0 dia tidak melakukannya).

  2. Jika c=1c=1, Alice melakukan Gate XX pada Qubitnya A\mathsf{A} (dan jika c=0c=0 dia tidak melakukannya).

Alice kemudian mengirimkan Qubitnya A\mathsf{A} kepada Bob.

Yang Bob lakukan ketika menerima qubit A\mathsf{A} adalah pertama-tama melakukan Gate controlled-NOT, dengan A\mathsf{A} sebagai kontrol dan B\mathsf{B} sebagai target, dan kemudian dia menerapkan Gate Hadamard ke A\mathsf{A}. Dia kemudian mengukur B\mathsf{B} untuk mendapatkan cc dan A\mathsf{A} untuk mendapatkan dd, dengan pengukuran basis standar di kedua kasus.

Analisis

Ide di balik protokol ini sederhana: Alice secara efektif memilih keadaan Bell mana yang ingin dia bagikan dengan Bob, dia mengirimkan Qubitnya kepada Bob, dan Bob mengukur untuk menentukan keadaan Bell mana yang Alice pilih.

Artinya, awalnya mereka berbagi ϕ+,\vert\phi^+\rangle, dan tergantung pada bit cc dan d,d, Alice bisa membiarkan keadaan ini apa adanya atau menggesernya ke salah satu keadaan Bell lainnya dengan menerapkan I,\mathbb{I}, X,X, Z,Z, atau XZXZ pada Qubitnya A.\mathsf{A}.

(II)ϕ+=ϕ+(IZ)ϕ+=ϕ(IX)ϕ+=ψ+(IXZ)ϕ+=ψ\begin{aligned} (\mathbb{I} \otimes \mathbb{I}) \vert \phi^+ \rangle & = \vert \phi^+\rangle \\ (\mathbb{I} \otimes Z) \vert \phi^+ \rangle & = \vert \phi^-\rangle \\ (\mathbb{I} \otimes X) \vert \phi^+ \rangle & = \vert \psi^+\rangle \\ (\mathbb{I} \otimes XZ) \vert \phi^+ \rangle & = \vert \psi^-\rangle \end{aligned}

Aksi Bob memiliki efek berikut pada keempat keadaan Bell:

ϕ+00ϕ01ψ+10ψ11\begin{aligned} \vert \phi^+\rangle & \mapsto \vert 00\rangle\\ \vert \phi^-\rangle & \mapsto \vert 01\rangle\\ \vert \psi^+\rangle & \mapsto \vert 10\rangle\\ \vert \psi^-\rangle & \mapsto -\vert 11\rangle\\ \end{aligned}

Ini bisa diperiksa secara langsung, dengan menghitung hasil operasi Bob pada keadaan-keadaan ini satu per satu.

Jadi, ketika Bob melakukan pengukurannya, dia dapat menentukan keadaan Bell mana yang Alice pilih. Untuk memverifikasi bahwa protokol bekerja dengan benar adalah masalah memeriksa setiap kasus:

  • Jika cd=00cd = 00, maka keadaan (B,A)(\mathsf{B},\mathsf{A}) saat Bob menerima A\mathsf{A} adalah ϕ+\vert \phi^+\rangle. Dia mengubah keadaan ini menjadi 00\vert 00\rangle dan memperoleh cd=00cd = 00.

  • Jika cd=01cd = 01, maka keadaan (B,A)(\mathsf{B},\mathsf{A}) saat Bob menerima A\mathsf{A} adalah ϕ\vert \phi^-\rangle. Dia mengubah keadaan ini menjadi 01\vert 01\rangle dan memperoleh cd=01cd = 01.

  • Jika cd=10cd = 10, maka keadaan (B,A)(\mathsf{B},\mathsf{A}) saat Bob menerima A\mathsf{A} adalah ψ+\vert \psi^+\rangle. Dia mengubah keadaan ini menjadi 10\vert 10\rangle dan memperoleh cd=10cd = 10.

  • Jika cd=11cd = 11, maka keadaan (B,A)(\mathsf{B},\mathsf{A}) saat Bob menerima A\mathsf{A} adalah ψ\vert \psi^-\rangle. Dia mengubah keadaan ini menjadi 11-\vert 11\rangle dan memperoleh cd=11cd = 11. (Faktor fase negatif-satu tidak berpengaruh di sini.)

Implementasi superdense coding

Berikut ini adalah implementasi sederhana dari superdense coding di mana kita menentukan Circuit itu sendiri tergantung pada bit yang akan dikirim. Pertama kita akan memilih dua bit yang akan dikirim. (Nanti kita akan memilihnya secara acak, tapi untuk sekarang kita hanya membuat pilihan sembarang.)

c = "1"
d = "0"

Sekarang kita akan membangun Circuit sesuai dengan itu. Di sini kita akan membiarkan Qiskit menggunakan nama default untuk qubit: q0\mathsf{q}_0 untuk qubit atas dan q1\mathsf{q}_1 untuk yang bawah.

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

Output of the previous code cell

Tidak banyak yang baru di sini, kecuali fungsi measure_all, yang mengukur semua qubit dan menempatkan hasilnya ke dalam satu register klasik (dan oleh karena itu memiliki dua bit dalam kasus ini).

Menjalankan simulator Aer menghasilkan output yang diharapkan.

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

Output of the previous code cell

Sekarang mari kita gunakan qubit tambahan sebagai generator bit acak — pada dasarnya untuk melempar koin yang adil. Kita akan menggunakannya untuk memilih cc dan dd secara acak, lalu menjalankan 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"))

Output of the previous code cell

Menjalankan simulator Aer menunjukkan hasilnya: bit klasik Alice dan Bob selalu sama.

result = AerSimulator().run(test).result()
statistics = result.get_counts()
display(plot_histogram(statistics))

Output of the previous code cell

Implementasi Qiskit

Kita bisa mengimplementasikan permainan CHSH, beserta strategi kuantum yang didefinisikan di atas, dalam Qiskit sebagai berikut.

Pertama, berikut adalah definisi permainan itu sendiri, yang memungkinkan strategi sembarang dimasukkan sebagai argumen.

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

Sekarang kita akan membuat fungsi yang menghasilkan Circuit tergantung pada pertanyaan untuk Alice dan Bob. Kita akan membiarkan qubit memiliki nama default mereka untuk kesederhanaan, dan kita akan menggunakan Gate Ry(θ)R_y(\theta) bawaan untuk tindakan Alice dan Bob.

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

Berikut adalah empat kemungkinan Circuit, tergantung pertanyaan mana yang diajukan.

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

Output of the previous code cell

(x,y) = (0,1)

Output of the previous code cell

(x,y) = (1,0)

Output of the previous code cell

(x,y) = (1,1)

Output of the previous code cell

Sekarang kita akan membuat job menggunakan simulator Aer yang menjalankan Circuit satu kali untuk pasangan input yang diberikan (x,y)(x,y).

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

Terakhir, kita akan memainkan permainan 1000 kali dan menghitung fraksi yang dimenangkan oleh strategi ini.

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

Kita juga bisa mendefinisikan strategi klasik dan melihat seberapa baik hasilnya. Ini hanya satu strategi — yang lain bisa diuji dengan mengubah kode — tetapi ini termasuk salah satu strategi klasik yang optimal.

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

Sekali lagi mari kita mainkan permainan 1000 kali untuk melihat seberapa baik hasilnya.

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

Meskipun ada keacakan yang terlibat, statistik sangat kecil kemungkinannya untuk menyimpang terlalu jauh setelah 1000 kali percobaan. Strategi kuantum menang dalam sekitar 85% permainan, sementara strategi klasik tidak bisa menang lebih dari sekitar 75%.