天天看點

使用開源量子程式設計架構ProjectQ進行量子計算資源估計與繪制量子線路圖

通過使用開源量子計算程式設計架構ProjectQ,我們可以編譯和生成量子線路,并将量子線路輸出為字元串或者latex代碼格式。根據得到的線路輸出,我們還可以對相應量子算法的實作進行資源估計,對于量子算法的科學研究有重要的啟發作用。

技術背景

在量子計算領域,基于量子晶片的算法設計(或簡稱為量子算法)是基于量子線路來設計的,類似于傳統計算中使用的​

​與​

​​門和​

​非​

​門之類的邏輯門。是以研究一個量子線路輸入後的編譯(可以簡化為數量更少的量子門組合,或者适配硬體上可實作的量子邏輯門操作),并且輸出編譯後的量子線路與量子線路圖,在各種場景下都會使用到。而且,量子線路編譯也能夠為量子計算資源估計帶來更加準确的結果預測。

量子計算與量子線路

針對于量子計算,這裡我們盡量的避免硬體上實作原理的解釋,因為那是屬于另外一個領域的研究課題。這裡我們僅從矩陣力學的角度來了解,讀者可以嘗試從這篇​​介紹量子系統模拟的部落格​​中先了解一部分量子門操作的實作。這些量子門的每一種排列組合,我們都可以将其視為一個量子線路,如下圖所示是一種量子線路的圖示。

使用開源量子程式設計架構ProjectQ進行量子計算資源估計與繪制量子線路圖

這裡的\(H\)門和\(CNOT\)門作用在不同的量子比特上,相當于執行了一項如下所示的任務:

\[\left|\psi_t\right>=U\left|\psi_0\right>

\]

這裡将\(U\)作用在初始的量子态\(\left|\psi_0\right>\)之後得到了一個新的量子态\(\left|\psi_t\right>\),當然,此時資訊還被存儲在量子态中。如果需要将量子态中的資訊讀取到經典資訊,就需要對量子态進行采樣,相關過程可以參考這篇​​介紹量子态采樣的部落格​​,最終我們得到的資訊是一系列二進制比特串的分布。

ProjectQ編譯與列印量子線路

我們先用ProjectQ量子程式設計架構寫一個不會被編譯優化的量子線路:

from projectq import MainEngine
from projectq.backends import CommandPrinter
from projectq.ops import H, CX, X, Z, Measure, All

circuit_backend = CommandPrinter()
eng=MainEngine(backend=circuit_backend)

qubits = eng.allocate_qureg(3)
All(H) | qubits
CX | (qubits[0], qubits[1])
CX | (qubits[1], qubits[2])
eng.flush()      

這個程式的基本邏輯是:使用​

​MainEngine​

​​類來執行編譯優化等工作;使用​

​CommandPrinter​

​​作為後端,表示隻列印量子線路而不進行計算;配置設定3個量子比特進行計算,最後作用一個線路在該量子系統上。這個程式的執行輸出如下,是一個包含有​

​Allocate​

​的量子線路:

Allocate | Qureg[0]
H | Qureg[0]
Allocate | Qureg[1]
H | Qureg[1]
CX | ( Qureg[0], Qureg[1] )
Allocate | Qureg[2]
H | Qureg[2]
CX | ( Qureg[1], Qureg[2] )      

這裡在額外介紹幾種優化操作:量子比特回收和量子線路編譯。由于該配置設定的比特已經在記憶體中注冊,需要手動的登出該量子系統所配置設定的量子比特:

from projectq import MainEngine
from projectq.backends import CommandPrinter
from projectq.ops import H, CX, X, Z, Measure, All

circuit_backend = CommandPrinter()
eng=MainEngine(backend=circuit_backend)
qubits = eng.allocate_qureg(3)

All(H) | qubits
CX | (qubits[0], qubits[1])
CX | (qubits[1], qubits[2])
eng.flush(deallocate_qubits=True)      

這裡python代碼中唯一的變化就是增加了​

​deallocate_qubits=True​

​​這個選項,最終輸出的量子線路裡面也就會包含有​

​Deallocate​

​的操作:

Allocate | Qureg[0]
H | Qureg[0]
Allocate | Qureg[1]
H | Qureg[1]
CX | ( Qureg[0], Qureg[1] )
Deallocate | Qureg[0]
Allocate | Qureg[2]
H | Qureg[2]
CX | ( Qureg[1], Qureg[2] )
Deallocate | Qureg[1]
Deallocate | Qureg[2]      

如果線上路中遇到一些可以簡化的子產品,比如連續作用兩次​

​H​

​門之後,相當于沒有執行任何的操作,即\(HH=I\)。這裡的\(H\)的矩陣形式如下所示:

\[H=\left(

\begin{array}{l}

\frac{\sqrt{2}}{2} & \frac{\sqrt{2}}{2}\\

\frac{\sqrt{2}}{2} & -\frac{\sqrt{2}}{2}

\end{array}

\right)

\]

如下代碼所示我們除了在所有量子比特上作用\(H\)門之外,在第二個量子比特上額外作用一個\(H\)門,這樣理論上說兩次連續作用的\(H\)門會被抵消:

from projectq import MainEngine
from projectq.backends import CommandPrinter
from projectq.ops import H, CX, X, Z, Measure, All

circuit_backend = CommandPrinter()
eng=MainEngine(backend=circuit_backend)
qubits = eng.allocate_qureg(3)

All(H) | qubits
H | qubits[1]
CX | (qubits[0], qubits[1])
CX | (qubits[1], qubits[2])
eng.flush(deallocate_qubits=True)      

可以看到相應的結果如下:

Allocate | Qureg[0]
H | Qureg[0]
Allocate | Qureg[1]
CX | ( Qureg[0], Qureg[1] )
Deallocate | Qureg[0]
Allocate | Qureg[2]
H | Qureg[2]
CX | ( Qureg[1], Qureg[2] )
Deallocate | Qureg[1]
Deallocate | Qureg[2]      

差別于上面的兩個例子我們可以發現,這裡僅有2個\(H\)門而上面的例子中都有3個\(H\)門,這就說明在量子編譯中兩個連續的\(H\)門成功的被抵消了。

ProjectQ輸出量子線路圖

from projectq import MainEngine
from projectq.backends import CircuitDrawer
from projectq.ops import H, CX, X, Z, Measure, All

circuit_backend = CircuitDrawer()
circuit_backend.set_qubit_locations({0: 2, 1: 1, 2:0})
eng=MainEngine(backend=circuit_backend)
qubits = eng.allocate_qureg(3)

All(H) | qubits
CX | (qubits[0], qubits[1])
CX | (qubits[1], qubits[2])
All(Measure) | qubits
eng.flush()

print (circuit_backend.get_latex())      

執行輸出的是一串完整的LaTex格式代碼,可以存儲為​

​file_name.tex​

​再進行編譯。

\documentclass{standalone}
\usepackage[margin=1in]{geometry}
\usepackage[hang,small,bf]{caption}
\usepackage{tikz}
\usepackage{braket}
\usetikzlibrary{backgrounds,shadows.blur,fit,decorations.pathreplacing,shapes}

\begin{document}
\begin{tikzpicture}[scale=0.8, transform shape]

\tikzstyle{basicshadow}=[blur shadow={shadow blur steps=8, shadow xshift=0.7pt, shadow yshift=-0.7pt, shadow scale=1.02}]\tikzstyle{basic}=[draw,fill=white,basicshadow]
\tikzstyle{operator}=[basic,minimum size=1.5em]
\tikzstyle{phase}=[fill=black,shape=circle,minimum size=0.1cm,inner sep=0pt,outer sep=0pt,draw=black]
\tikzstyle{none}=[inner sep=0pt,outer sep=-.5pt,minimum height=0.5cm+1pt]
\tikzstyle{measure}=[operator,inner sep=0pt,minimum height=0.5cm, minimum width=0.75cm]
\tikzstyle{xstyle}=[circle,basic,minimum height=0.35cm,minimum width=0.35cm,inner sep=-1pt,very thin]
\tikzset{
shadowed/.style={preaction={transform canvas={shift={(0.5pt,-0.5pt)}}, draw=gray, opacity=0.4}},
}
\tikzstyle{swapstyle}=[inner sep=-1pt, outer sep=-1pt, minimum width=0pt]
\tikzstyle{edgestyle}=[very thin]

\node[none] (line0_gate0) at (0.1,-0) {$\Ket{0}$};
\node[none] (line0_gate1) at (0.5,-0) {};
\node[none,minimum height=0.5cm,outer sep=0] (line0_gate2) at (0.75,-0) {};
\node[none] (line0_gate3) at (1.0,-0) {};
\draw[operator,edgestyle,outer sep=0.5cm] ([yshift=0.25cm]line0_gate1) rectangle ([yshift=-0.25cm]line0_gate3) node[pos=.5] {H};
\draw (line0_gate0) edge[edgestyle] (line0_gate1);
\node[none] (line1_gate0) at (0.1,-1) {$\Ket{0}$};
\node[none] (line1_gate1) at (0.5,-1) {};
\node[none,minimum height=0.5cm,outer sep=0] (line1_gate2) at (0.75,-1) {};
\node[none] (line1_gate3) at (1.0,-1) {};
\draw[operator,edgestyle,outer sep=0.5cm] ([yshift=0.25cm]line1_gate1) rectangle ([yshift=-0.25cm]line1_gate3) node[pos=.5] {H};
\draw (line1_gate0) edge[edgestyle] (line1_gate1);
\node[none] (line2_gate0) at (0.1,-2) {$\Ket{0}$};
\node[none] (line2_gate1) at (0.5,-2) {};
\node[none,minimum height=0.5cm,outer sep=0] (line2_gate2) at (0.75,-2) {};
\node[none] (line2_gate3) at (1.0,-2) {};
\draw[operator,edgestyle,outer sep=0.5cm] ([yshift=0.25cm]line2_gate1) rectangle ([yshift=-0.25cm]line2_gate3) node[pos=.5] {H};
\draw (line2_gate0) edge[edgestyle] (line2_gate1);
\node[xstyle] (line1_gate4) at (1.4000000000000001,-1) {};
\draw[edgestyle] (line1_gate4.north)--(line1_gate4.south);
\draw[edgestyle] (line1_gate4.west)--(line1_gate4.east);
\node[phase] (line2_gate4) at (1.4000000000000001,-2) {};
\draw (line2_gate4) edge[edgestyle] (line1_gate4);
\draw (line1_gate3) edge[edgestyle] (line1_gate4);
\draw (line2_gate3) edge[edgestyle] (line2_gate4);
\node[xstyle] (line0_gate4) at (1.9500000000000002,-0) {};
\draw[edgestyle] (line0_gate4.north)--(line0_gate4.south);
\draw[edgestyle] (line0_gate4.west)--(line0_gate4.east);
\node[phase] (line1_gate5) at (1.9500000000000002,-1) {};
\draw (line1_gate5) edge[edgestyle] (line0_gate4);
\draw (line0_gate3) edge[edgestyle] (line0_gate4);
\draw (line1_gate4) edge[edgestyle] (line1_gate5);
\node[measure,edgestyle] (line0_gate5) at (2.6000000000000005,-0) {};
\draw[edgestyle] ([yshift=-0.18cm,xshift=0.07500000000000001cm]line0_gate5.west) to [out=60,in=180] ([yshift=0.035cm]line0_gate5.center) to [out=0, in=120] ([yshift=-0.18cm,xshift=-0.07500000000000001cm]line0_gate5.east);
\draw[edgestyle] ([yshift=-0.18cm]line0_gate5.center) to ([yshift=-0.07500000000000001cm,xshift=-0.18cm]line0_gate5.north east);
\draw (line0_gate4) edge[edgestyle] (line0_gate5);
\node[measure,edgestyle] (line1_gate6) at (2.6000000000000005,-1) {};
\draw[edgestyle] ([yshift=-0.18cm,xshift=0.07500000000000001cm]line1_gate6.west) to [out=60,in=180] ([yshift=0.035cm]line1_gate6.center) to [out=0, in=120] ([yshift=-0.18cm,xshift=-0.07500000000000001cm]line1_gate6.east);
\draw[edgestyle] ([yshift=-0.18cm]line1_gate6.center) to ([yshift=-0.07500000000000001cm,xshift=-0.18cm]line1_gate6.north east);
\draw (line1_gate5) edge[edgestyle] (line1_gate6);
\node[measure,edgestyle] (line2_gate5) at (2.0500000000000003,-2) {};
\draw[edgestyle] ([yshift=-0.18cm,xshift=0.07500000000000001cm]line2_gate5.west) to [out=60,in=180] ([yshift=0.035cm]line2_gate5.center) to [out=0, in=120] ([yshift=-0.18cm,xshift=-0.07500000000000001cm]line2_gate5.east);
\draw[edgestyle] ([yshift=-0.18cm]line2_gate5.center) to ([yshift=-0.07500000000000001cm,xshift=-0.18cm]line2_gate5.north east);
\draw (line2_gate4) edge[edgestyle] (line2_gate5);

\end{tikzpicture}
\end{document}      

不過這裡推薦另外一種更加友善的做法,避免自己去安裝衆多的第三方tex包,可以直接使用​​Overleaf​​​平台來執行​

​tex​

​檔案的編譯操作,示範效果如下圖所示:

使用開源量子程式設計架構ProjectQ進行量子計算資源估計與繪制量子線路圖

在Overleaf裡面會自動的幫我們配置好相關的Latex環境,大大減輕我們自己的工作量。這裡的量子線路圖繪制時跟比特順序本身是相關的,比如這裡我們嘗試切換一種量子比特的映射編号,就會得到不同的線路圖(在數學和實體上是等價的):

from projectq import MainEngine
from projectq.backends import CircuitDrawer
from projectq.ops import H, CX, X, Z, Measure, All

circuit_backend = CircuitDrawer()
circuit_backend.set_qubit_locations({0: 1, 1: 2, 2:0})
eng=MainEngine(backend=circuit_backend)
qubits = eng.allocate_qureg(3)

All(H) | qubits
CX | (qubits[0], qubits[1])
CX | (qubits[1], qubits[2])
All(Measure) | qubits
eng.flush()

print (circuit_backend.get_latex())      

生成的latex代碼這裡就不做展示了,我們可以直接看下編譯後輸出的量子線路圖:

使用開源量子程式設計架構ProjectQ進行量子計算資源估計與繪制量子線路圖

ProjectQ輸出資源統計結果

說到量子計算的資源估計,這裡面就需要結合實際場景來分析,這是因為即使我們給出了一個非常精妙的線路,但是這個量子線路最終可能也很難真正在量子計算硬體上實作,這是由于不同的硬體體系本身所具備的局限性。這裡我們可以考慮在給定量子門集合的條件下的資源估計,我們先還是用給定的量子門集合來編譯上述的示例:

from projectq import MainEngine
from projectq.backends import CommandPrinter
from projectq.ops import H, CX, X, Z, Rx, Ry, Rz, Measure, All
from projectq.setups import linear

compiler_engines = linear.get_engine_list(num_qubits=16,
                                          one_qubit_gates=(Rx, Ry, Rz),
                                          two_qubit_gates=(CX, ))
circuit_backend = CommandPrinter()
eng=MainEngine(backend=circuit_backend, engine_list=compiler_engines)
qubits = eng.allocate_qureg(3)

All(H) | qubits
CX | (qubits[0], qubits[1])
CX | (qubits[1], qubits[2])
eng.flush(deallocate_qubits=True)      

這裡我們使用的是\(\{R_x,R_y,R_z,CX\}\)這個組合,而并不包含上述用例中使用到的\(H\)門,這裡我們可以看下輸出結果(其實就是用\(R_x\)和\(R_y\)的組合來建構一個\(H\)門):

Allocate | Qureg[1]
Ry(1.570796326795) | Qureg[1]
Rx(9.424777960769) | Qureg[1]
Allocate | Qureg[0]
Ry(1.570796326795) | Qureg[0]
Rx(9.424777960769) | Qureg[0]
CX | ( Qureg[0], Qureg[1] )
Allocate | Qureg[2]
Ry(1.570796326795) | Qureg[2]
Rx(9.424777960769) | Qureg[2]
CX | ( Qureg[1], Qureg[2] )
Deallocate | Qureg[1]
Deallocate | Qureg[2]
Deallocate | Qureg[0]      

針對于這種場景,我們切換下後端就可以實作量子計算資源的統計:

from projectq import MainEngine
from projectq.backends import ResourceCounter
from projectq.ops import H, CX, X, Z, Rx, Ry, Rz, Measure, All
from projectq.setups import linear

compiler_engines = linear.get_engine_list(num_qubits=16,
                                          one_qubit_gates=(Rx, Ry, Rz),
                                          two_qubit_gates=(CX, ))
circuit_backend = ResourceCounter()
eng=MainEngine(backend=circuit_backend, engine_list=compiler_engines)
qubits = eng.allocate_qureg(3)

All(H) | qubits
CX | (qubits[0], qubits[1])
CX | (qubits[1], qubits[2])
All(Measure) | qubits
eng.flush(deallocate_qubits=True)

print (circuit_backend)      

這個程式實際上就是統計上一個示例代碼中輸出的量子線路的門的數量,對應輸出結果為:

Gate class counts:
    AllocateQubitGate : 3
    CXGate : 2
    DeallocateQubitGate : 3
    MeasureGate : 3
    Rx : 3
    Ry : 3

Gate counts:
    Allocate : 3
    CX : 2
    Deallocate : 3
    Measure : 3
    Rx(9.424777960769) : 3
    Ry(1.570796326795) : 3

Max. width (number of qubits) : 3.      

這裡我們可以看到,統計中的\(R_x,R_y,CX\)的門的數量與上面輸出的結果是保持一緻的,那麼我們的示例到這裡就成功完成示範。

總結概要

這篇文章中,我們通過介紹開源量子計算程式設計架構ProjectQ的一些正常使用方法,來講解了如何使用程式來編譯和生成量子線路,以及将該量子線路作為字元串或者Latex格式代碼輸出,這同時也使得我們可以通過輸出結果來統計量子計算的資源,以用于預測在大規模場景下所需要的大緻的量子比特數以及門操作數量(或用量子線路深度來衡量)的量級。這種編譯的手段和資源估計的方法,在工程和科學研究上都有較大的意義。

版權聲明

作者ID:DechinPhy

繼續閱讀