天天看點

淺探LOCCNet:基于分層學習的機器學習架構

作者:量子發燒友

概述

量子糾纏在量子通信、量子計算以及其他量子技術中是一種很重要的資源。是以,能否在這些領域建構出實際的應用,很大程度上取決于我們能否有效地利用量子糾纏這一資源。在NISQ(noisy intermediate-scale quantum)時代,通過量子網絡實作兩個節點之間直接通訊量子資訊是一項艱巨的任務。是以在目前階段,通過本地操作和經典通訊(LOCC)來完成特定任務,是比全局操作(global operation)更為有效的方式。

所謂本地操作和經典通訊,是指幾個空間上分離的參與者隻能在自己的實驗室中執行本地操作,然後通過經典通訊的方式傳遞他們經典資訊(可以是測量結果)。然而,設計LOCC協定來進行糾纏操作以及分布式量子資訊處理是非常具有挑戰性的,因為LOCC的結構通常很複雜并且很難用數學方法描述。為了更好地探索如何在近期量子裝置上利用量子糾纏資源以及從長遠角度來看進行分布式量子資訊處理,我們設計了LOCCNet,一種用于LOCC協定設計的機器學習架構 。

什麼是LOCC?

正如上面所描述的,LOCC指代的是本地操作和經典通訊(local operations and classical communication),即一個多量子比特系統配置設定給位于不同位置的多個實驗室(參與方)。假如有N個實驗室,每個實驗室隻能做對他們手中的子系統`\mathit{K} \in \left [ 1,…,N \right ]`做量子操作`\left \{ \varepsilon _{j}^{k} \right \} _{j=0}^{r}`。這些實驗室之間允許傳輸包括測量結果在内的經典資訊。LOCC協定通常是根據通訊的輪數 r和實驗室的數量N進行分類的,記為`LOCC _{r} (N)`。比如量子隐形傳态協定就是一個一輪通訊兩個參與方的協定`LOCC _{1} (2)`,參與的兩方通常命名為Alice和Bob。這個協定的任務是把一個未知的量子态`\left | \psi \right \rangle`從Alice傳輸給Bob,圖1所示的流程圖具體闡述了如何實作這一任務。

淺探LOCCNet:基于分層學習的機器學習架構
淺探LOCCNet:基于分層學習的機器學習架構

圖 1:量子隐形傳态協定的電路圖(上)和樹狀圖(下)。

在量子隐形傳态中,隻有Alice對自己的量子比特進行了測量,Bob的所有本地操作均取決于Alice的測量結果`m_1 m_2 \in \left \{ 00,01,10,11 \right \}`。當`K^{th}`一方的測量結果`\left ( m_1 m_2 …m_n \right )`控制着後面的本地操作時,我們稱這類LOCC協定為Control-Type。當 Alice和Bob都對自己手中的量子比特進行測量時,協定就會變得複雜起來,因為他們可以選擇合作并決定下一步做什麼,這種協定被我們稱之為Cooperation-Type。比如圖2中描述的糾纏蒸餾協定。

淺探LOCCNet:基于分層學習的機器學習架構
淺探LOCCNet:基于分層學習的機器學習架構

圖 2:BBPSSW蒸餾協定是一種Cooperation-Type LOCC協定。上圖是電路圖,下圖是樹狀圖,`m_j ^\left ( k \right )`表示的是第`k^{th}`參與方的測量結果。當測量結果`m_1 ^\left ( 1 \right ) m_1 ^\left ( 2 \right )`為01或10時,判定協定失敗。特别地,這裡的本地操作為`\varepsilon _0 ^ \left ( 1 \right ) = \varepsilon _0 ^ \left ( 2 \right ) = CNOT`和 `\varepsilon _1 ^ \left ( 1 \right ) = \varepsilon _1 ^ \left ( 2 \right ) = I`。

這些協定看上去十分簡單,但是當參與方增多而且通訊輪數變多時,想要找到每一個參與方的最優的本地操作就會變得十分困難。現在我們大緻了解了為什麼說設計一個LOCC協定是一項艱巨的任務。即使如此困難,仍有許多重要的LOCC協定被科學家提了出來,比如:糾纏蒸餾(entanglement distillation),糾纏轉換(entanglement swapping)等。

LOCCNet的設計理念

我們從機器學習解決量子多體問題以及預測蛋白質折疊結構受到啟發,使用機器學習的方法從衆多可能的結果中搜尋最優的LOCC協定。為了實作上述目标,我們利用量子神經網絡(quantum neural networks, QNN)表示每個本地操作`\varepsilon_j ^\left ( k \right )`,這也就意味着樹狀圖中的每個節點都代表着一個量子神經網絡(QNN),也可以稱為參數化量子電路(parameterized quantum circuit, PQC)`U\left ( \theta \right )`。

在 Paddle Quantum 中,提供了多種QNN模闆以減少使用者的學習成本。在設定QNN之後,我們便可以規劃如何測量和通訊。下面需要做的就是學習目标函數,通常情況下,我們把目标函數編碼成損失函數L。舉個例子,在量子隐形傳态協定中,我們的學習目标是最大化Alice想要傳輸的态`\left | \psi \right \rangle`和Bob最終得到的态`\left | \phi \right \rangle`之間的保真度,也就是說`L = {\textstyle \sum_{m_1m_2}} \left ( 1-F\left ( \left | \psi \right \rangle ,\left | \phi \right \rangle \right ) \right )`。根據所處理的任務不同,損失函數會有相應的變化。

最後一步,使用經典的優化方法(主要是梯度下降)來訓練QNN中的參數。優化完成後,我們就獲得了一個近似最優的LOCC協定。從使用者的角度來說,LOCCNet這樣一個架構可以極大地減少設計LOCC協定所用的時間,而且得到的協定也是很容易被實驗驗證。

PS:目前版本下,LOCCNet僅支援密度矩陣形式。

功能簡介

這一部分,我們将解釋LOCCNet的主要函數,讓讀者明白如何使用該架構。首先,我們展示一段僞代碼:

from paddle_quantum.locc import LoccNet

class Net(LoccNet):
   def __init__(self):
       super(Net, self).__init__()
       # Step 0: 初始化系統
       # Step 1: 設定初始量子态
       # Step 2: 定義 QNNs
       
   def forward(self):
       # Step 3: 執行 QNNs
       # Step 4: 定義 protocol 的具體過程
       # Step 5: 計算損失函數
       return loss, final_status           

首先,我們需要建立一個類class Net(LoccNet)來儲存量子系統,與此同時,這個類也繼承了LoccNet中的函數。LOCC協定的主體部分都是在這個類Net()中實作的,它包含兩個函數:__init__() and forward()。 在__init__()函數中,我們需要初始化所有的參與方、量子态以及QNN。

  • self.add_new_party(qubits_number, party_name=None)是用于添加一個新的參與方的函數,第一個參數代表該參與方有幾個量子比特;第二個參數是可選參數,代表着參與者的名字。在協定中,我們可以選擇使用名字來指定參與方,也可以選擇用編号來指定。如果我們希望使用名字,那麼隻需要在add_new_party函數中給party_name命名;如果希望使用編号,那麼我們就不用給第二個參數指派,第一個參與方會自動編号為0,每增加一個參與方,其編号都會加一,同時該函數會将所添加的party的ID傳回,其值根據定義會是int或者str。
  • self.set_init_state(state, which_qubits)是用于設定協定的初始态的函數。第一個參數state是量子态,必須是密度矩陣的形式;第二個參數 which_qubits是定位量子比特(哪一參與方的第幾個量子比特,如("Alice", 0))。需要說明的是,我們必須初始化所有的量子比特,否則程式将出現錯誤。
  • self.create_ansatz(party_id)是為某一參與方建立本地量子電路的函數。是以參數party_id用來指定參與方。舉個例子cir1 = self.create_ansatz("Alice")為Alice建立了電路。之後,我們可以在電路中添加不同的操作比如X門、CNOT門等. 在forward()函數中,我們需要定義協定的流程。如果我們想要訓練一個模型,那麼需要定義損失函數,并設定為forward()的傳回值,這樣才能不斷更新參數使得損失函數最小化。如果我們僅僅是想要驗證某個協定的結果,我們就做上述的事情,隻需要把協定的流程定義清楚,就可以把我們感興趣的值設為傳回值。在forward()函數中,我們主要做兩件事情--量子操作和測量,我們為他們提供了相應的函數:
  • 運作電路,得到運作後的結果,如status_out = cir1(status)。
  • self.measure(status, which_qubits, results_desired,theta=None)是用來進行測量的函數。第一個參數 status是我們想要測量的态;第二個參數which_qubits代表着要測量的是哪一個量子比特。如果我們想測量的是Alice手中第0個量子比特,那麼就需要給第二個參數指派("Alice", 0)。如果我們想要同時測量兩個量子比特,比如Alice手中的第0個量子比特和Bob手中的第1個量子比特,那麼這個參數需要設為[("Alice", 0), ("Bob", 1)]。第三個參數results_desired是我們希望測量的結果,它隻可以為 "0","1",或者["0", "1"]。第四個參數theta是用于含參測量,如果我們不希望做含參測量操作,那麼就不用給它指派。
  • self.partial_state(status, which_qubits, is_desired=True) 是用來得到部分量子态的函數。在糾纏蒸餾中,我們可能隻有一部分量子态是我們想要的目标态。比如我們想要将Alice的第0個量子比特和Bob的第0個量子比特作為目标态,則我們可以通過status = self.partial_state(status, [("Alice", 0), ("Bob", 0)])來得到。
  • self.reset_state(status, state, which_qubits)可以重置部分量子态。有時候我們可能不想使用某些已經測量過的量子态,想将它重置為新的量子态來繼續進行LOCC。是以我們也提供了該功能。
  • LoccStatus:在LoccNet中,最小的資訊單元不是量子态,而是LoccStatus。它包含了量子态,從初始态得到該量子态的機率,以及測量結果。有時候,我們想要得到多個量子态,也就是說我們希望的測量結果是多個,比如在self.measure()函數中,results_desired設定為["0", "1"]。由此,我們能夠得到兩組LoccStatus,這種情況下,我們的函數傳回的是由LoccStatus組成的list。值得一提的是,不論是LoccStatus,還是由LoccStatus組成的list,我們的函數幾乎都可以對其進行正常執行。

實際案例

介紹完LOCCNet架構的基本資訊後,我們會通過一些案例來展示LOCCNet 架構的實際運用,比如量子隐形傳态協定如何通過Paddle Quantum實作。

我們都知道,量子隐形傳态協定是可以通過本地操作和經典通信(LOCC)協定完成的,這個協定完成的原理是借助提前制備好的糾纏資源在兩個空間上分離的通信節點之間傳輸量子資訊。現在讓我們一起來看看如何使用LOCCNet 訓練學習完成一個量子隐形傳态協定。

首先,我們需要導入依賴包,以完成環境适配:

import numpy as np
import paddle
from paddle import matmul, trace
import paddle_quantum
from paddle_quantum.locc import LoccNet
from paddle_quantum.qinfo import state_fidelity
from paddle_quantum.state import bell_state, isotropic_state, random_state
# 切換至密度矩陣模式
paddle_quantum.set_backend('density_matrix')           

初始化整個量子系統,然後定義量子電路和隐形傳态協定:

class LOCC(LoccNet):
   def __init__(self):
       super(LOCC, self).__init__()
       # 添加第一個參與方 Alice
       # 第一個參數 2 代表着 Alice 手裡有幾個量子比特
       # 第二個參數代表着參與方的名字
       self.add_new_party(2, party_name="Alice")
       # 添加第二個參與方 Bob
       # 第一個參數 1 代表着 Bob 手裡有幾個量子比特
       # 第二個參數代表着參與方的名字
       self.add_new_party(1, party_name="Bob")
​
       # 準備一個貝爾态
       _state = bell_state(2)
       # _state = isotropic_state(2, 0.8)     
       # 随機制備傳輸用的純态(rank=1)
       self.state_C = random_state(num_qubits=1, rank=1)
​
       # 通過配置設定上述制備好的量子态初始化整個量子系統
       # 這裡 ("Alice", 0) 即表示量子比特 C
       # 這裡 ("Alice", 1) 即表示量子比特 A
       # 這裡 ("Bob", 0) 即表示量子比特 B
       # print('提前配置設定好的糾纏态為:\n', _state.numpy())

       self.set_init_state(self.state_C, [("Alice", 0)])
       self.set_init_state(_state, [("Alice", 1), ("Bob", 0)])
​
       # 設定 Alice 的本地操作
       self.cirA = self.create_ansatz("Alice")
       self.cirA.cnot([0, 1])
       self.cirA.h(0)
       # 建立 Bob 的本地操作
       self.cirB = [self.create_ansatz("Bob") for _ in range(4)]
       self.cirB[1].x(0)
       self.cirB[2].z(0)
       self.cirB[3].x(0)
       self.cirB[3].z(0)
​
   def teleportation(self):
       status = self.init_status
       # 運作上述電路
       status = self.cirA(status)
       # Alice 在計算基上測量她所持有的兩個量子比特 C 還有 A
       # 得到并記錄四種結果 00,01,10,11
       status_A = self.measure(status, [("Alice", 0), ("Alice", 1)], ["00", "01", "10", "11"])
​
       # 用于記錄平均保真度
       fid_list = []
       # Bob 根據 Alice 的測量結果選擇不同的門作用在自己的量子比特上
       for i, s in enumerate(status_A):
           # 根據 Alice 的測量結果,進行不同操作
           cirB = self.cirB[int(status_A[i].measured_result, 2)]
           # 執行電路
           status_B = cirB(s)
           # 僅保留 Bob 的量子比特 B
           status_fin = self.partial_state(status_B, [("Bob", 0)])
           # 計算初始态和傳輸後态之間的保真度
           fid = state_fidelity(self.state_C, status_fin) ** 2
           fid_list.append(fid * status_fin.prob)
       fid_avg = sum(fid_list)
​
       return fid_avg           

接着随機生成200個量子純态,并使用态保真度F來衡量傳輸協定好壞,其中`F(\rho ,\sigma)\equiv tr(\sqrt{\sqrt{\rho }\sigma \sqrt{\rho } }) ^ 2`

SEED = 999              # 固定随機數
num_state = 200         # 設定随機态的生成數量
list_fid = []           # 用于記錄保真度
np.random.seed(SEED)
# 開始采樣 
for idx in range(num_state):
   list_fid.append(LOCC().teleportation().numpy())
​
print('平均保真度 =', np.around(sum(list_fid)[0] / len(list_fid), 4), ', 标準差 =', np.std(list_fid))           

輸出結果為:

平均保真度 = 1.0 , 标準差 = 4.0480725e-07           

完成上述操作後,我們将要開始訓練一個自定義的LOCC協定。一般的LOCC協定可以通過經典通信的回合數`r`進行分類,而在這次操作中,為了友善我們将通信回合數限制為單輪。與原始協定不同,我們将使用參數化量子電路把Bob作用在量子比特上的固定門`U \subseteq \left \{ X,Y \right \} `替換成布洛赫球面上的一個廣義旋轉門`U_3` ,其定義為:

`U_3(\theta ,\phi ,\varphi ) = \begin{bmatrix} \cos (\frac{\theta }{2} ) & -e^{i\varphi }\sin (\frac{\theta }{2} ) \\ e ^ {i\phi} \sin (\frac{\theta }{2} ) & e^{i(\phi +\varphi )\cos \frac{\theta }{2} } \end{bmatrix}`

訓練一個自定義LOCC協定流程大緻如下:

  1. Alice對它所持有的兩個量子比特作用兩量子比特通用門
  2. 接着在計算基上測量它的兩個量子比特,并通過經典信道與Bob交流
  3. 共計4種可能的測量結果:`m_1 m_2 \in \left \{ 00,01,10,11 \right \} `。Bob需要根據這些測量結果采取不同的本地操作。在Bob進行操作後,記錄它的量子态為 `\left | \psi \right \rangle B`。
  4. 計算 `\left | \psi \right \rangle B` 與 `\left | \psi \right \rangle C` 之間的量子态重疊,并記為 `O`。
  5. 将損失函數設定為4種可能測量結果的累加,即 `L = {\textstyle \sum_{m_1 m_2}}(1-Tr(\rho_C \rho_B))` 并使用基于梯度的優化方法更新Alice和Bob本地操作中的參數,進而使得損失函數最小化。
  6. 重複步驟1-5,直到損失函數收斂。
  7. 生成一組随機的态 `\left \{ \left | \psi _ C \right \rangle \right \} `,并以平均保真度訓練出的傳輸協定進行基準測試。
淺探LOCCNet:基于分層學習的機器學習架構

為了確定訓練出的LOCC協定對所有态都有效果,需要将訓練集設為4個線性獨立态,這4個獨立态在密度矩陣的表示如下:

`\rho _0 = \begin{bmatrix} 1 & 0\\ 0 & 0 \end{bmatrix} ,\rho _1 = \begin{bmatrix} 0 & 0\\ 0 & 1 \end{bmatrix},\rho _2 = \begin{bmatrix} 0.5 & 0.5 \\ 0.5 & 0.5 \end{bmatrix},\rho _3 = \begin{bmatrix} 0.5 & -0.5i\\ 0.5i & 0.5 \end{bmatrix}`

任何一個單量子比特量子态都可以寫為上述4個态的線性組合。具體操作的代碼如下:

class LOCC_Train(LoccNet):
   def __init__(self):
       super(LOCC_Train, self).__init__()
       # 初始化 LOCCNet
       self.parties = list()
       # 添加第一個參與方 Alice
       # 第一個參數 2 代表着 Alice 手裡有幾個量子比特
       # 第二個參數代表着參與方的名字
       self.add_new_party(2, party_name="Alice")
       # 添加第二個參與方 Bob
       # 第一個參數 1 代表着 Bob 手裡有幾個量子比特
       # 第二個參數代表着參與方的名字
       self.add_new_party(1, party_name="Bob")
​
       # 準備一個貝爾态
       _state = bell_state(2)
       # _state = isotropic_state(2, 0.8)
       # 訓練集: 4 個線性獨立态
       _state0 = paddle_quantum.State(np.array([[1, 0], [0, 0]], dtype=np.complex64))
       _state1 = paddle_quantum.State(np.array([[0, 0], [0, 1]], dtype=np.complex64))
       _state2 = paddle_quantum.State(np.array([[0.5, 0.5], [0.5, 0.5]], dtype=np.complex64))
       _state3 = paddle_quantum.State(np.array([[0.5, -0.5j], [0.5j, 0.5]], dtype=np.complex64))
       self.init_states = [_state0, _state1, _state2, _state3]
       # 通過配置設定上述制備好的量子态初始化整個量子系統
       self.set_init_state(_state, [("Alice", 1), ("Bob", 0)])
       self.set_init_state(_state0, [("Alice", 0)])
​
       # 定義 Alice 的本地操作
       self.cirA = self.create_ansatz("Alice")
       self.cirA.universal_two_qubits([0, 1])
       # 定義 Bob 的本地操作
       self.cirB = [self.create_ansatz("Bob") for _ in range(4)]
       for cir in self.cirB:
           # 作用單量子比特通用門
           cir.u3(0)
​

   def LOCCNet(self):
       # 定義訓練過程
       loss = 0
       temp_state = self.init_status
       # 開始訓練
       for init_state in self.init_states:
           # 重置 Alice 持有的量子比特 C 至訓練集中的量子态
           status = self.reset_state(temp_state, init_state, [("Alice", 0)])
           # 執行 Alice 的電路
           status = self.cirA(status)
            # 測量得到四個可能的結果
           status_A = self.measure(status, [("Alice", 0), ("Alice", 1)], ["00", "01", "10", "11"])
           

           # Bob 根據測量結果選擇不同的門作用在自己的量子比特上
           for i, s in enumerate(status_A):
               # 執行 Bob 的電路
               status_B = self.cirB[i](s)
               # 僅留下 Bob 的量子比特 B
               status_fin = self.partial_state(status_B, [("Bob", 0)])
               # 将所有的測量結果的損失函數進行累加
               loss += 1 - paddle.real(trace(matmul(init_state.data, status_fin.data)))
       
       return loss
​
   # 存儲訓練結束後的最優參數
   def save_module(self):
       theta_A = self.cirA.parameters()
       theta_B = [self.cirB[i].parameters() for i in range(4)]
       theta = theta_A + sum(theta_B, [])
       paddle.save(theta, 'parameters/QT_LOCCNet')           

輸出的結果展示如下:

ITR = 150   # 設定優化循環次數
LR = 0.2    # 設定學習速率
SEED = 999  # 固定本地操作中參數的初始化随機數種子
np.random.seed(SEED)
paddle.seed(SEED)
​
net = LOCC_Train()
params = net.cirA.parameters() + sum([net.cirB[i].parameters() for i in range(4)], [])
# 選擇 Adam 優化器
opt = paddle.optimizer.Adam(learning_rate=LR, parameters=params)
# 開始優化循環
for itr in range(ITR):
   # 向前傳播計算損失函數
   loss = net.LOCCNet()
   # 反向傳播優化損失函數
   loss.backward()
   opt.minimize(loss)
   # 清除梯度
   opt.clear_grad()
   if itr % 10 == 0:
       print("itr " + str(itr) + ":", loss.numpy()[0])
​
# 儲存參數
net.save_module()
itr 0: 7.721435
itr 10: 0.5636051
itr 20: 0.20643002
itr 30: 0.073403895
itr 40: 0.025113285
itr 50: 0.008655369
itr 60: 0.0035846233
itr 70: 0.0014438629
itr 80: 0.00045502186
itr 90: 0.00018626451
itr 100: 4.7028065e-05
itr 110: 1.4960766e-05
itr 120: 4.4703484e-06
itr 130: 3.1590462e-06
itr 140: 1.66893e-06           

至此,我們完成了對于自定義LOCC協定的訓練。

如果想要感受一下訓練成果,可以使用預先訓練好的電路參數來直接測試性能:

class LOCC_Test(LoccNet):
def __init__(self, theta_A, theta_B):
super(LOCC_Test, self).__init__()
self.parties = list()
self.add_new_party(2, party_name="Alice")
self.add_new_party(1, party_name="Bob")
_state = bell_state(2)
self._state0 = random_state(1)
self.set_init_state(_state, [("Alice", 1), ("Bob", 0)])
self.set_init_state(self._state0, [("Alice", 0)])
self.cirA = self.create_ansatz("Alice")
self.cirA.universal_two_qubits(qubits_idx=[0, 1], param=theta_A)
self.cirB = [self.create_ansatz("Bob") for _ in range(4)]
for i, cir in enumerate(self.cirB):
cir.u3(qubits_idx=0, param=theta_B[i])
def benchmark(self):
input_state = self.init_status
status = self.cirA(input_state)
status_A = self.measure(status, [("Alice", 0), ("Alice", 1)], ["00", "01", "10", "11"])
fid_list = []
for i, s in enumerate(status_A):
status_B = self.cirB[i](s)
status_fin = self.partial_state(status_B, [("Bob", 0)])
fid = state_fidelity(self._state0, status_fin) ** 2
fid_list.append(fid * status_fin.prob)
fid_ave = sum(fid_list)

return fid_ave
# 加載預先訓練的電路參數
para = paddle.load('parameters/QT_LOCCNet')
SEED = 999 # 固定生成傳輸态的随機種子
num_state = 200 # 設定随機态的生成數量
list_fid = [] # 用于記錄保真度
np.random.seed(SEED)
paddle.seed(SEED)
# 采樣
for idx in range(num_state):
list_fid.append(LOCC_Test(para[0], para[1:]).benchmark().numpy())
print('平均保真度 =', np.around(sum(list_fid)[0] / len(list_fid), 4), ', 标準差 =', np.std(list_fid))           

平均保真度和标準差的值為:

平均保真度 = 1.0 , 标準差 = 5.695904182530426e-07           

基于LOCCNet架構,我們成功讓機器學習出了量子隐形傳态協定。最初的隐形傳态協定是為了傳輸單量子比特量子态,無法直接推廣到多量子比特的情形。相比之下,LOCCNet為尋找多量子比特情形下的隐形傳态協定提供了可能,在未來會得到更廣泛的應用。

繼續閱讀