---
# System prepended metadata

title: 'Qracon- E91 with Qiskit '
tags: [Qracon]

---

# Qracon- E91 with Qiskit

這是一篇關於如何使用Qiskit建立一個E91演算法並得出金鑰的筆記
接下來我將會介紹E91協議的執行原理和如何執行，希望本程式能夠在讀者理解此演算法的過程中起到些幫助，或是讓讀者在復刻此程式時能帶給讀者更多的樂趣。

Jupyter notebook版本可以在[這裡](https://www.mediafire.com/file/2h2n9jlp52ztwc4/Qracon-_E91_with_Qiskit.ipynb/file)下載到

## 量子糾纏
讓我們假設AB電子原為一體，現在當他們分裂成A和B電子時他們的旋轉方向會因角動量守恆而相反，當我們觀測A電子的自旋方向時，我們便能得知B電子的自旋方向。

這裡便是1種糾纏態，屬於其中一種Bell states $$\frac{1}{\sqrt{2}}(\lvert11\rangle + \lvert00\rangle)$$
而我們這次用到的 singlet state 可寫成以下，且也是屬於Bell state的一種。
$$
  \frac{1}{\sqrt{2}}(\lvert01\rangle - \lvert10\rangle)$$
  
---
## CHSH不等式
要說明CHSH不等式我們得先說明局域實在論，
1.資訊的傳輸不可超越光速 (局域論 Locality)
2.事物的存在不受觀測引響 (實在論 Realism) 

讓我們假設有一個人Victor將分別發送$\pm1$給Alice(A)與Bob(B)，而A與B將會從X和Y取一個方向量測V發送的訊號。
如此我們便能計算A與B的期望值總和。
$$A_XB_X\ +\ A_XB_Y +\ A_YB_X\ -\ A_YB_Y\ =\ (A_X\ +\ A_Y)B_X\ +\ \ (A_X\ -\ A_Y)B_Y$$
可以看出來$(A_X\ +\ A_Y)$或$(A_X\ -\ A_Y)$其一會是$0$，畢竟結果會是$-1,1$或$1,-1$其中的一個。
所以多次執行後上式的期望值應不大於$2$。$$-2\ \le\ C\ \le\ 2$$

但如果Victor不再發送古典的$\pm1$，而改發送糾纏量子對的話，經過實驗我們發現期望值居然大約等於$2.8$。
$$|CHSH\rangle\ \approx\ 2\sqrt{2}$$

我們可以假設今天Alice可以各自從X和Z方向擇一測量收到的糾纏對，Bob則可以從W或V方向選擇測量，其中 $W = \frac{1}{\sqrt{2}} (X + Z)$, $V = \frac{1}{\sqrt{2}} (-X + Z)$
那我們的測量期望值應為
\begin{eqnarray*}
 \langle X \otimes W \rangle_{\psi_s} &= -\frac{1}{\sqrt{2}}, \quad 
 \langle X \otimes V \rangle_{\psi_s} &= \frac{1}{\sqrt{2}}, \qquad\qquad  \\
 \langle Z \otimes W \rangle_{\psi_s} &= -\frac{1}{\sqrt{2}}, \quad
 \langle Z \otimes V \rangle_{\psi_s} &= -\frac{1}{\sqrt{2}}.
\end{eqnarray*}
$$C =
\langle X\otimes W \rangle - \langle X \otimes V \rangle + \langle Z \otimes W \rangle + \langle Z \otimes V \rangle =
-2 \sqrt{2}. \qquad\qquad $$

這違背了我們之前古典物理中所證明的期望值絕對值應不大於 $2$ 的理論，所以該理論假設的局域論與實在論勢必會有一個不存在於量子力學，這代表了這些量子在觀測前不存在一個確定的值。
當然我們可用此不等式來檢查量子是否糾纏，且這正是我們要做的。

更多內容可以參考[這裡](https://learning.quantum.ibm.com/tutorial/chsh-inequality)
## 金鑰製作
請先確認**qiskit version = 1.1.1**，其餘版本可能導致程式碼失效。
```
from qiskit import __version__
__version__
```
一樣先導入會用到的庫備用
```
import numpy as np
import random
import re
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit_aer import Aer
```
製作一個電路使2個qubits成為上面提到的 singlet 糾纏態，並為其建立4個classicalbits。
```
qr = QuantumRegister(2, name="qr")
cr = ClassicalRegister(4, name="cr")
singlet = QuantumCircuit(qr, cr, name='singlet')
singlet.x(qr[0])
singlet.x(qr[1])
singlet.h(qr[0])
singlet.cx(qr[0],qr[1])

#singlet.draw('mpl') #用以觀察電路
```
製作Alice與Bob各自的三個測量方向 $(X,Z,W,V)$。!\begin{align*}
 b_i = 1: \quad \vec{a}_1 &= (1,0,0) \quad (X \text{ observable}) &
 b_j^{'} = 1: \quad \vec{b}_1 &= \left(\frac{1}{\sqrt{2}},0,\frac{1}{\sqrt{2}}\right) \quad (W \text{ observable})
 \\
 b_i = 2: \quad \vec{a}_2 &= \left(\frac{1}{\sqrt{2}},0,\frac{1}{\sqrt{2}}\right) \quad (W \text{ observable}) &
 b_j^{'} = 2: \quad \vec{b}_2 &= (0,0,1) \quad ( \text{Z observable})
 \\
 b_i = 3: \quad \vec{a}_3 &= (0,0,1) \quad (Z \text{ observable}) &
 b_j^{'} = 3: \quad \vec{b}_3 &= \left(-\frac{1}{\sqrt{2}},0,\frac{1}{\sqrt{2}}\right) \quad (V \text{ observable})
\end{align*}![vectors](https://hackmd.io/_uploads/HkDUVFWOA.png)

```
## Alice的觀測器電路

#A1電路用 X 基底測量qubits
measureA1 = QuantumCircuit(qr, cr, name='measureA1')
measureA1.h(qr[0])
measureA1.measure(qr[0],cr[0])

#A2電路用 W 基底測量qubits
measureA2 = QuantumCircuit(qr, cr, name='measureA2')
measureA2.s(qr[0])
measureA2.h(qr[0])
measureA2.t(qr[0])
measureA2.h(qr[0])
measureA2.measure(qr[0],cr[0])

#A3電路用 Z 基底測量qubits
measureA3 = QuantumCircuit(qr, cr, name='measureA3')
measureA3.measure(qr[0],cr[0])

## Bob的觀測器電路

#B1電路用 W 基底測量qubits
measureB1 = QuantumCircuit(qr, cr, name='measureB1')
measureB1.s(qr[1])
measureB1.h(qr[1])
measureB1.t(qr[1])
measureB1.h(qr[1])
measureB1.measure(qr[1],cr[1])

#B2電路用 Z 基底測量qubits
measureB2 = QuantumCircuit(qr, cr, name='measureB2')
measureB2.measure(qr[1],cr[1])

#B3電路用 V 基底測量qubits
measureB3 = QuantumCircuit(qr, cr, name='measureB3')
measureB3.s(qr[1])
measureB3.h(qr[1])
measureB3.tdg(qr[1])
measureB3.h(qr[1])
measureB3.measure(qr[1],cr[1])

#將以上電路合併成List
aliceMeasurements = [measureA1, measureA2, measureA3]
bobMeasurements = [measureB1, measureB2, measureB3]
```
輸入發送的Singlet量子對數量
並隨機選擇Alice與Bob將使用的測量方向
```
numberOfSinglets = 500

aliceMeasurementChoices = [random.randint(1, 3) for i in range(numberOfSinglets)] 
bobMeasurementChoices = [random.randint(1, 3) for i in range(numberOfSinglets)] 
```
將測量電路根據上翻選擇的測量方向組合至電路中
首先產生一個singlet量子對，並將第一顆給Alice測量，第二顆給Bob測量。
最後再為每個電路命名。
```
circuits = []

for i in range(numberOfSinglets):
    #合併singlet電路兩個觀測電路
    circuitName = singlet.compose(aliceMeasurements[aliceMeasurementChoices[i]-1]).compose(bobMeasurements[bobMeasurementChoices[i]-1])
    
    #命名電路
    circuitName.name=f'{str(j)} ":A"{str(aliceMeasurementChoices[j])}"_B"{str(bobMeasurementChoices[j] + 2)}

    
    #將電路加至List中
    circuits.append(circuitName)
```
確認以上製作的量子電路
```
print(circuits[0].name) #可選擇其他circuits查看
circuits[0].draw('mpl')
```
接下來開始執行量子電路。
先選擇模擬器後執行電路，並印出結果。
這次選用可以批量執行電路的qasm_simulator。
```
simulator = Aer.get_backend('qasm_simulator')
result = simulator.run(circuits,shots=1).result()
```
```
result.get_counts(circuits[0])
```
{'0010': 1}
應該會得到類似0010，Alice和Bob的結果儲存在前兩碼'10'上

接著我們利用python的正規表達式re來分類結果。
```
abPatterns = [
    re.compile('..00$'), #尋找後兩碼為00的結果(Alice和Bob各得到-1)
    re.compile('..01$'), #尋找後兩碼為01的結果
    re.compile('..10$'), #尋找後兩碼為10的結果(Alice得到-1、Bob得到1)
    re.compile('..11$')  # 尋找後兩碼為11的結果
]
```
然後將結果分類進aliceResults和bobResults中
```
aliceResults = [] # Alice的結果(string a)
bobResults = [] # Bob的結果(string a')

for i in range(numberOfSinglets):
    #將結果轉入res List中
    res = list(result.get_counts(circuits[i]).keys())[0]
    
    if abPatterns[0].search(res): #如果後兩碼為-1,-1
        aliceResults.append(-1) #紀錄Alice得到-1
        bobResults.append(-1) #紀錄Bob得到-1
    if abPatterns[1].search(res):
        aliceResults.append(1)
        bobResults.append(-1)
    if abPatterns[2].search(res): ##如果後兩碼為-1,1
        aliceResults.append(-1) #紀錄Alice得到-1
        bobResults.append(1) #紀錄Bob得到1
    if abPatterns[3].search(res): 
        aliceResults.append(1)
        bobResults.append(1)
```
接著將以上結果比對並製成能夠加密的金鑰。
理論上根據singlet量子對，如果使用相同的觀測電路測量，Alice的結果應與Bob異號。
```
aliceKey = [] # Alice的密碼串
bobKey = [] # Bob的密碼串

#比對Alice與Bob的觀測電路選擇
for i in range(numberOfSinglets):
    #如果Alice和Bob用了同一種基底的觀測電路(不是同一種號碼)
    if (aliceMeasurementChoices[i] == 2 and bobMeasurementChoices[i] == 1) or (aliceMeasurementChoices[i] == 3 and bobMeasurementChoices[i] == 2):
        aliceKey.append(aliceResults[i]) #記錄Alice的密鑰
        bobKey.append(- bobResults[i]) #記錄Bob的密鑰，其應為Alice的密鑰的異號
        
keyLength = len(aliceKey) #密鑰的長度
```
接下來我們比對Alice與Bob的密鑰，理論上應為相同，否則加密便會失敗。
```
abKeyMismatches = 0 #紀錄錯誤的位數

for j in range(keyLength):
    if aliceKey[j] != bobKey[j]:
        abKeyMismatches += 1
```

計算CHSH期望值
```
#計算CHSH的程式
def chsh_corr(result):
    
    # lists with the counts of measurement results
    # each element represents the number of (-1,-1), (-1,1), (1,-1) and (1,1) results respectively
    countA1B1 = [0, 0, 0, 0] # XW observable
    countA1B3 = [0, 0, 0, 0] # XV observable
    countA3B1 = [0, 0, 0, 0] # ZW observable
    countA3B3 = [0, 0, 0, 0] # ZV observable

    for i in range(numberOfSinglets):

        res = list(result.get_counts(circuits[i]).keys())[0]

        # if the spins of the qubits of the i-th singlet were projected onto the a_1/b_1 directions
        if (aliceMeasurementChoices[i] == 1 and bobMeasurementChoices[i] == 1):
            for j in range(4):
                if abPatterns[j].search(res):
                    countA1B1[j] += 1

        if (aliceMeasurementChoices[i] == 1 and bobMeasurementChoices[i] == 3):
            for j in range(4):
                if abPatterns[j].search(res):
                    countA1B3[j] += 1

        if (aliceMeasurementChoices[i] == 3 and bobMeasurementChoices[i] == 1):
            for j in range(4):
                if abPatterns[j].search(res):
                    countA3B1[j] += 1
                    
        # if the spins of the qubits of the i-th singlet were projected onto the a_3/b_3 directions
        if (aliceMeasurementChoices[i] == 3 and bobMeasurementChoices[i] == 3):
            for j in range(4):
                if abPatterns[j].search(res):
                    countA3B3[j] += 1
                    
    # number of the results obtained from the measurements in a particular basis
    total11 = sum(countA1B1)
    total13 = sum(countA1B3)
    total31 = sum(countA3B1)
    total33 = sum(countA3B3)      
                    
    # expectation values of XW, XV, ZW and ZV observables (2)
    expect11 = (countA1B1[0] - countA1B1[1] - countA1B1[2] + countA1B1[3])/total11 # -1/sqrt(2)
    expect13 = (countA1B3[0] - countA1B3[1] - countA1B3[2] + countA1B3[3])/total13 # 1/sqrt(2)
    expect31 = (countA3B1[0] - countA3B1[1] - countA3B1[2] + countA3B1[3])/total31 # -1/sqrt(2)
    expect33 = (countA3B3[0] - countA3B3[1] - countA3B3[2] + countA3B3[3])/total33 # -1/sqrt(2) 
    
    corr = expect11 - expect13 + expect31 + expect33 # calculate the CHSC correlation value (3)
    
    return corr
```

接著來看看成果吧。
```
corr = chsh_corr(result) #計算CHSH

#CHSH結果
print('CHSH correlation value: ' + str(round(corr, 3)))

#金鑰
print('Length of the key: ' + str(keyLength))
print('Number of mismatching bits: ' + str(abKeyMismatches) + '\n')
```
CHSH correlation value: -2.607
Length of the key: 106
Number of mismatching bits: 0

可以看到CHSH的期望值大約等於$-2\sqrt{2}\approx-2.828$，這證明了Alice與Bob的量子確實是糾纏在一起的。
並且Alice和Bob的金鑰沒有任何錯誤。

## Eve
讓我們看看如果Eve在Alice與Bob之前提前測量了這些量子會怎樣。

讓我們製作Eve提前觀測的電路
```
#提前用W方向觀測Alice的qubit
measureEA2 = QuantumCircuit(qr, cr, name='measureEA2')
measureEA2.s(qr[0])
measureEA2.h(qr[0])
measureEA2.t(qr[0])
measureEA2.h(qr[0])
measureEA2.measure(qr[0],cr[2])
measureEA2.barrier()

#提前用Z方向觀測Alice的qubit
measureEA3 = QuantumCircuit(qr, cr, name='measureEA3')
measureEA3.measure(qr[0],cr[2])
measureEA3.barrier()

#提前用W方向觀測Bob的qubit
measureEB1 = QuantumCircuit(qr, cr, name='measureEB1')
measureEB1.s(qr[1])
measureEB1.h(qr[1])
measureEB1.t(qr[1])
measureEB1.h(qr[1])
measureEB1.measure(qr[1],cr[3])
measureEB1.barrier()
#提前用Z方向觀測Bob的qubit
measureEB2 = QuantumCircuit(qr, cr, name='measureEB2')
measureEB2.measure(qr[1],cr[3])
measureEB2.barrier()

#合併
eveMeasurements = [measureEA2, measureEA3, measureEB1, measureEB2]
```
接著我們讓Eve隨機選擇使用的觀測方向
```
#第一和第二位分別代表Eve對Alice和Bob的qubit的測量
eveMeasurementChoices = []

for j in range(numberOfSinglets):      
    if random.uniform(0, 1) <= 0.5: #50%會由WW方向測量
        eveMeasurementChoices.append([0, 2])
    else: #50%會由ZZ方向測量
        eveMeasurementChoices.append([1, 3])
```
和剛剛一樣讓我們合併，以上電路
```
circuits = [] #合併的電路

for j in range(numberOfSinglets):
    #合併電路
    circuitName = singlet.compose(eveMeasurements[eveMeasurementChoices[j][0]-1]).compose(eveMeasurements[eveMeasurementChoices[j][1]-1]).compose(aliceMeasurements[aliceMeasurementChoices[j]-1]).compose(bobMeasurements[bobMeasurementChoices[j]-1])
    
    #命名電路
    circuitName.name=f'{str(j)} ":A"{str(aliceMeasurementChoices[j])}"_B"{str(bobMeasurementChoices[j] + 2)}"_E"{str(eveMeasurementChoices[j][0])}{str(eveMeasurementChoices[j][1] - 1)}'
    
    #合併以上電路至List中
    circuits.append(circuitName)
```
用*qasm_simulator*模擬並印出結果
```
simulator = Aer.get_backend('qasm_simulator')
result = simulator.run(circuits,shots=1).result()
print(str(circuits[0].name) + '\t' + str(result.get_counts(circuits[0]))) #可以更改號碼
```
0 ":A"2"_B"5"_E"01	 {'0101': 1}
可以看到後兩位不再是00了而是Eve量測後的結果'01'，而前面兩位依舊是Alice與Bob的結果。

接著我們修正正規表達式的搜尋方法。
```
ePatterns = [
    re.compile('00..$'), #Eve從Alice與Bob的qubits獲得-1,-1
    re.compile('01..$'), #Eve從Alice與Bob的qubits獲得1,-1
    re.compile('10..$'),
    re.compile('11..$')  
]
```
接著我們用前面製作的abPatterns與ePatterns來處理我們的結果。
```
aliceResults = [] 
bobResults = [] 


#第一和第二位分別儲存Alice與Bob的qubit的量測結果
eveResults = [] 

for j in range(numberOfSinglets):
    
    res = list(result.get_counts(circuits[j]).keys())[0] # extract a key from the dict and transform it to str
    
    # Alice and Bob
    if abPatterns[0].search(res): # check if the key is '..00' (if the measurement results are -1,-1)
        aliceResults.append(-1) # Alice got the result -1 
        bobResults.append(-1) # Bob got the result -1
    if abPatterns[1].search(res):
        aliceResults.append(1)
        bobResults.append(-1)
    if abPatterns[2].search(res): # check if the key is '..10' (if the measurement results are -1,1)
        aliceResults.append(-1) # Alice got the result -1 
        bobResults.append(1) # Bob got the result 1
    if abPatterns[3].search(res): 
        aliceResults.append(1)
        bobResults.append(1)

    # Eve
    if ePatterns[0].search(res): # check if the key is '00..'
        eveResults.append([-1, -1]) # results of the measurement of Alice's and Bob's qubits are -1,-1
    if ePatterns[1].search(res):
        eveResults.append([1, -1])
    if ePatterns[2].search(res):
        eveResults.append([-1, 1])
    if ePatterns[3].search(res):
        eveResults.append([1, 1])
```
接著生成金鑰。
```
aliceKey = [] 
bobKey = [] 
eveKeys = [] # Eve 竊取的金鑰

# 將測量方向的選擇與測量結果做比對
for j in range(numberOfSinglets):
    # 如果Alice和Bob用了同一種基底的觀測電路
    if (aliceMeasurementChoices[j] == 2 and bobMeasurementChoices[j] == 1) or (aliceMeasurementChoices[j] == 3 and bobMeasurementChoices[j] == 2):  
        aliceKey.append(aliceResults[j]) # record the i-th result obtained by Alice as the bit of the secret key k
        bobKey.append(-bobResults[j]) # record the multiplied by -1 i-th result obtained Bob as the bit of the secret key k'
        eveKeys.append([eveResults[j][0], -eveResults[j][1]]) # record the i-th bits of the keys of Eve 

keyLength = len(aliceKey) #金鑰位數
```
計算Alice與Bob金鑰之間的錯誤位數。
```
abKeyMismatches = 0 #Alice與Bob的金鑰錯誤位數
eaKeyMismatches = 0 #Eve與Alice的金鑰錯誤位數
ebKeyMismatches = 0 #Eve與Bob的金鑰錯誤位數

for j in range(keyLength):
    if aliceKey[j] != bobKey[j]: 
        abKeyMismatches += 1
    if eveKeys[j][0] != aliceKey[j]:
        eaKeyMismatches += 1
    if eveKeys[j][1] != bobKey[j]:
        ebKeyMismatches += 1
```
我們也可以看看Eve竊取了多少百分率的金鑰。
```
eaKnowledge = (keyLength - eaKeyMismatches)/keyLength # Alice金鑰被竊取的百分率
ebKnowledge = (keyLength - ebKeyMismatches)/keyLength #Bob金鑰被竊取的百分率
```
我們也計算CHSH值，用來確認Alice與Bob的金鑰是否還糾纏在一起。
```
corr = chsh_corr(result) 
```
結果

```
# CHSH不等式測試
print('CHSH correlation value: ' + str(round(corr, 3)) + '\n')

# 金鑰
print('Length of the key: ' + str(keyLength))
print('Number of mismatching bits: ' + str(abKeyMismatches) + '\n')

print('Eve\'s knowledge of Alice\'s key: ' + str(round(eaKnowledge * 100, 2)) + ' %')
print('Eve\'s knowledge of Bob\'s key: ' + str(round(ebKnowledge * 100, 2)) + ' %')
```
CHSH correlation value: -1.461

Length of the key: 115
Number of mismatching bits: 10

Eve's knowledge of Alice's key: 92.17 %
Eve's knowledge of Bob's key: 93.91 %

可以看到雖然成功Eve獲得了大部分的金鑰，但這卻影響了CHSH不等式的值，使其大幅偏離$-2\sqrt{2}$，這便可讓通訊者注意到加密的異常並放棄這次金鑰交換了。

值得注意的是Alice與Bob的金鑰存在誤差，這是因為Eve在測量完後將qubits發給破壞了糾纏態，使得Alice與Bob後續使用不同方向測量後有機率產生與預期不同的結果，讓最後的金鑰稱成產生了誤差。

## Additional information
創作者 : 莊懷德
參考自 : 
[> E91 quantum key distribution protocol](https://github.com/qiskit-community/qiskit-community-tutorials/blob/master/awards/teach_me_qiskit_2018/e91_qkd/e91_quantum_key_distribution_protocol.ipynb)