教學課程:在 中實作 Quantum Fourier 轉換 Q#
注意
2024 年 6 月 30 日之後,將不再支援 Microsoft Quantum Development Kit (傳統 QDK) 。 如果您是現有的 QDK 開發人員,建議您轉換至新的 Azure Quantum Development Kit (Modern QDK) ,以繼續開發量子解決方案。 如需詳細資訊,請參閱 將您的程式 Q# 代碼移轉至新式 QDK。
本教學課程說明如何編寫和模擬在個別量子位元上運作的基本量子程式。
雖然 Q# 的建立主要是作為大規模量子程式的高階程式設計語言,但也可以用來探索下層的量子程式設計,即直接定址特定的量子位元。 具體而言,本教學課程會深入探討 Quantum Fourier Transform (QFT) ,這是許多較大量子演算法不可或缺的子程式。
在本教學課程中,您將了解如何:
- 在中 Q#定義量子作業。
- 撰寫 Quantum Fourier 轉換線路
- 模擬量子位配置到測量輸出的量子運算。
- 觀察量子系統的模擬波浪函數在整個作業中的演進方式。
注意
這項量子資訊處理的下層檢視通常以量子電路的字詞描述,代表系統特定量子位元閘道或作業的循序應用程式。 因此,您循序套用的單一和多量子位元作業,可以立即以電路圖表示。 例如,本教學課程中使用的完整三個量子四進位轉換具有下列表示法作為線路:
提示
如果您想要加速量子運算旅程,請參閱 使用 Azure Quantum 撰寫程式代碼,這是 Azure Quantum 網站的獨特功能。 在這裡,您可以執行內 Q# 建範例或自己的 Q# 程式、從提示產生新的 Q# 程序代碼、在 VS Code for the Web 中開啟並執行程式碼,按下滑鼠,並詢問 Copilot 量子運算的任何問題。
必要條件
最新版的 Visual Studio Code 或開啟 Web 上的 VS Code。
最新版的 Azure Quantum Development Kit 擴充功能。 如需安裝詳細數據,請參閱 在 VS Code 上安裝新式 QDK。
如果您想要使用 Jupyter Notebook,您也需要安裝 Python 和 Jupyter 擴充功能,以及最新的
qsharp
Python 套件。 若要這樣做,請開啟終端機並執行下列命令:$ pip install --upgrade qsharp
Create 新Q#檔案
- 在 VS Code 中,選取 [檔案>新文本檔]
- 將檔案儲存為 Tcircuit.qs。 此檔案將包含 Q# 程式的程序代碼。
- 開啟 ZLTcircuit.qs。
在中寫入 QFT 線路 Q#
本教學課程的第一個部分包含定義 Q# 作業 Perform3qubitQFT
,並在三個量子位元上執行量子傅立葉轉換。 DumpMachine
函式是用來觀察三量子位元系統的模擬波函式在整個作業如何演進。 在教學課程的第二個部分中,您將新增測量功能,並比較量子位元測量前後的狀態。
您將逐步組建作業。 將下列各節中的程式代碼複製並貼到 FNTcircuit.qs 檔案中。
您可以檢視此區段 的完整 Q# 程式代碼 作為參考。
存取其他 Q# 作業的命名空間
在 Q# 檔案中,定義編譯器存取的命名空間 NamespaceQFT
。
若要讓這項作業使用現有的 Q# 作業,請開啟相關 Microsoft.Quantum.*
命名空間。
namespace NamespaceQFT {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Arrays;
// operations go here
}
使用引數和傳回定義作業
接著,定義 Perform3qubitQFT
作業:
operation Perform3qubitQFT() : Unit {
// do stuff
}
這項作業目前不採用引數,並傳回 Unit
物件,類似傳回 C# 的 void
,或 Python 的 Tuple[()]
空 Tupie。
稍後,為傳回測量結果的陣列,您要修改作業。
配置量子位
在作業中 Q# ,使用 關鍵詞配置三個量子位的 use
緩存器。 使用 use
時,量子位元會自動配置為 $\ket{0}$ 狀態。
use qs = Qubit[3]; // allocate three qubits
Message("Initial state |000>:");
DumpMachine();
如同實際的量子運算中,Q# 不允許您直接存取量子位元狀態。 不過, DumpMachine
此作業會列印 target 計算機的目前狀態,因此當與完整狀態模擬器搭配使用時,它可提供偵錯和學習的寶貴見解。
套用單一量子位和受控制的作業
接下來,您會套用組成作業本身的 Perform3qubitQFT
作業。 在 Microsoft.Quantum.Intrinsic
命名空間中,Q# 已包含這些和許多其他的基本量子作業。
第一個套用的作業是第一個量子位元的 H
(Hadamard) 作業:
若要從暫存器套用作業至特定的量子位元 (例如,陣列 Qubit[]
的單一 Qubit
),請使用標準索引標記法。
換句話說,套用 H
作業至暫存器 qs
的第一個量子位元要採用下列格式:
H(qs[0]);
除了套用 H
作業至個別的量子位元外,QFT 電路主要是受控 R1
輪替。 一 R1(θ, <qubit>)
般作業會將量子位的 $\ket{0}$ 元件維持不變,同時將$e^{i\theta}$ 的旋轉套用至 $\ket{1}$ 元件。
Q# 讓您輕鬆在一或多個控制量子位元上決定作業的執行。 一般而言,以開頭 Controlled
的呼叫和作業引數會變更,如下所示:
Op(<normal args>)
$\to$ Controlled Op([<control qubits>], (<normal args>))
請注意,即使是單一量子位元的控制位元,控制量子位元引數仍須為陣列。
QFT 中的受控制作業是 R1
針對第一個量子位 (,並由第二和第三個量子位) 控制的作業:
在 Q# 檔案中,使用下列陳述式呼叫這些作業:
Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));
PI()
函式的用途是根據 pi 弧度定義輪替。
套用 SWAP 作業
將相關的 H
作業和受控制的旋轉套用至第二和第三個量子位之後,線路看起來會像這樣:
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
最後,您會將作業套用至第一個和第三個 SWAP
量子位,以完成線路。 這是必要的作業,因為量子傅立葉轉換的本質會反序輸出量子位元,所以交換讓副程式順利整合至較大的演算法。
SWAP(qs[2], qs[0]);
目前在 Q# 作業中,寫入量子傅立葉轉換的量子位元層級的作業已完成:
解除配置量子位元
最後一個步驟是再次呼叫 DumpMachine()
,查看作業後狀態,然後解除配置量子位元。 配置量子物原後,量子位元的狀態為 $\ket{0}$,所以必須使用 ResetAll
作業,重設量子位元為初始狀態。
要求所有量子位明確重設為 $\ket{0}$ 是 的基本功能 Q#,因為它可讓其他作業在開始使用這些相同的量子位時精確地知道其狀態, () 。 此外,這可確保這些量子位元不與系統中任何其他的量子位元纏結。 如果 use
配置區塊的結尾未執行重設,即可能擲回執行階段錯誤。
新增下列命令列至您的 Q# 檔案:
Message("After:");
DumpMachine();
ResetAll(qs); // deallocate qubits
完整的 QFT 作業
程式 Q# 已完成。 您的Tcircuit.qs 檔案現在看起來應該像這樣:
namespace NamespaceQFT {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Arrays;
operation Perform3qubitQFT() : Unit {
use qs = Qubit[3]; // allocate three qubits
Message("Initial state |000>:");
DumpMachine();
//QFT:
//first qubit:
H(qs[0]);
Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
SWAP(qs[2], qs[0]);
Message("After:");
DumpMachine();
ResetAll(qs); // deallocate qubits
}
}
執行QFT線路
目前, Perform3qubitQFT
作業不會傳回任何值 - 作業會傳 Unit
回值。 稍後,您將修改作業,以傳回 () 的度量結果 Result[]
陣列。
執行 Q# 程式時,您必須將 新增
EntryPoint
至 Q# 檔案。 這個屬性會告訴編譯程式此作業是程式的進入點。 在作業之前Perform3qubitQFT
,將下列這一行新增至檔案Q#頂端:@EntryPoint() operation Perform3qubitQFT() : Unit {
執行程式之前,您必須將 target 設定檔設定為 [不受限制]。 選取 [檢視 -> 命令選擇區],搜尋 QIR,選取 Q#:設定 Azure Quantum QIR target 配置檔,然後選取 Q#[不受限制]。
若要執行程式,請從右上方的播放圖示下拉式清單中選取 [ 執行 Q# 檔案 ],或按 Ctrl+F5。 程式會在預設模擬器上執行以 屬性標示
@EntryPoint()
的作業或函式。Message
和DumpMachine
輸出會出現在偵錯控制台中。
注意
target如果設定檔未設定為 [不受限制],當您執行程式時會收到錯誤。
瞭解 QFT 線路的輸出
在完整狀態模擬器上呼叫時,DumpMachine()
會提供這些量子狀態波函式的多種表示法。
$n$ 量子位系統的可能狀態可以表示為 $2^n$ 計算基礎狀態,每個狀態都有對應的複雜係數 (幅度和相位)。
計算基礎狀態對應長度 $n$ 所有可能的二進位字串,即量子位元狀態 $\ket{0}$ 和 $\ket{1}$ 所有可能的組合,且每個二進位元都對應個別量子位元。
第一個資料列依重要順序,提供包含對應量子位元識別碼的註解。
量子位元 2
「最重要」,即在基礎狀態向量 $\ket{i}$ 的二進位表示法中,量子位元 2
的狀態會對應到最左邊的位元。
例如,$\ket{6} = \ket{110}$ 構成在 $\ket{1}$ 中的量子位元 2
和 1
,及 $\ket{0}$ 中的量子位元 0
。
其餘的資料列會描述以笛卡兒和極座標格式,測量基礎狀態向量 $\ket{i}$ 的機率幅。 檢查輸入狀態 $\ket{000}$ 的第一個資料列:
|0>:
此資料列對應0
計算基礎狀態 (假設初始狀態配置後為 $\ket{000}$,即可預期是目前包含機率幅的唯一狀態)。1.000000 + 0.000000 i
:笛卡兒格式的機率幅。==
:equal
符號會分隔兩個等效表示法。********************
:數值的圖形化表示法。*
的數目與測量此狀態向量的機率成正比。[ 1.000000 ]
:數值表示法的數值。---
:幅度相位的圖形化表示法。[ 0.0000 rad ]
:相位 (弧度) 的數值。
數值和相位都以圖形化表示法顯示。 數值表示法很簡單:其顯示 *
的長條,即機率越高,長條越長。
顯示的輸出說明程式設計作業轉換狀態是從
$$ \ket{\psi}_{initial} = \ket{000} $$
to
$$ \begin{align} \ket{\psi}_{final} &= \frac{1}{\sqrt{8}} \left ( \ket + \{001}ket + \ket + \{010}ket{000} + \ket +{011}{100} \ket{101} + \ket + \ket{110}{111} \right) \\ &= \frac{1}{\sqrt{2^n}}\sum_{j=0}^{2^n-1} \ket{j}, \end{align} $$
而這正是三量子位傅立葉的轉換行為。
如果您想知道其他輸入狀態如何受到影響,建議您在轉換前,嘗試套用其他量子位元作業。
將度量新增至 QFT 線路
從 DumpMachine
函式會顯示作業的結果,但不巧量子力學的基石指出真正的量子系統不能使用這類 DumpMachine
函式。
但這項透過測量擷取的資訊,通常不僅無法提供完整量子狀態的資訊,也會大幅變更系統本身。
量子測量的種類很多,但這裡的範例著重基本:單一量子位元的投影測量。 使用指定的基礎測量時 (例如,計算基礎 $ { \ket{0}, \ket{1} } $),量子位元狀態會投射至測量的任何基礎狀態,因此終結兩者間的任何疊加。
修改 QFT 作業
若要在 Q# 程式中實作測量,請使用 M
作業傳回 Result
類型。
首先,修改 Perform3QubitQFT
作業傳回測量結果的陣列 (Result[]
),取代 Unit
。
operation Perform3QubitQFT() : Result[] {
定義並將 Result[]
陣列初始化
配置量子位之前,請針對每個量子位宣告並系結三個項目數位 (一 Result
個) :
mutable resultArray = [Zero, size = 3];
以 resultArray
開頭的 mutable
關鍵字讓您稍候 (例如,新增測量結果時) 可在程式碼中修改變數。
在 for
迴圈中執行測量,並新增結果至陣列
在 QFT 轉換作業之後,插入下列程式代碼:
for i in IndexRange(qs) {
set resultArray w/= i <- M(qs[i]);
}
在陣列上呼叫的 IndexRange
函式 (例如,量子位元的陣列、qs
) 會傳回陣列索引的範圍。
在此,函式用於 for
迴圈,並使用 M(qs[i])
陳述式依序測量每個量子位元。
接著使用更新並重新指定陳述式,新增每個測量的 Result
類型 (Zero
或 One
) 至 resultArray
中的對應索引位置。
注意
這個陳述式的語法對 Q# 而言是唯一的,但對應其他語言 (例如 F# 和 R) 所示,類似的變數重新指定 resultArray[i] <- M(qs[i])
。
關鍵字 set
使用 mutable
,並一律用來重新指定變數繫結。
傳回 resultArray
包含測量的所有三個量子位元,及結果新增至 resultArray
後,您可以和之前一樣,放心重設和解除配置量子位元。 若要傳回測量,請插入:
return resultArray;
使用測量執行 QFT 線路
接著變更 DumpMachine
函式的配置,輸出測量前後的狀態。
最後的 Q# 程式碼應如下所示:
namespace NamespaceQFT {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Arrays;
operation Perform3QubitQFT() : Result[] {
mutable resultArray = [Zero, size = 3];
use qs = Qubit[3];
//QFT:
//first qubit:
H(qs[0]);
Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
SWAP(qs[2], qs[0]);
Message("Before measurement: ");
DumpMachine();
for i in IndexRange(qs) {
set resultArray w/= i <- M(qs[i]);
}
Message("After measurement: ");
DumpMachine();
ResetAll(qs);
Message("Post-QFT measurement results [qubit0, qubit1, qubit2]: ");
return resultArray;
}
}
提示
回想一下,每次您引入程式碼變更后再再次執行檔案時,請記得儲存盤案。
EntryPoint
在工作之前Perform3qubitQFT
新增 :@EntryPoint() operation Perform3qubitQFT() : Unit {
將 target 配置檔設定為 [不受限制]。 按兩下 VS Code 視窗底部的 [QIR:基底 ] 按鈕,然後從下拉功能表中選取 [ 不受限制 ]。 target如果設定檔未設定為 [不受限制],當您執行程式時會收到錯誤。
若要執行程式,請從右上方的播放圖示下拉式清單中選取 [ 執行 Q# 檔案 ],或按 Ctrl+5。 程式會在預設模擬器上執行以 屬性標示的
@EntryPoint()
作業或函式。Message
和DumpMachine
輸出會出現在偵錯控制台中。
您的輸出看起來應該類似輸出:
Before measurement:
# wave function for qubits with ids (least to most significant): 0;1;2
|0>: 0.353553 + 0.000000 i == *** [ 0.125000 ] --- [ 0.00000 rad ]
|1>: 0.353553 + 0.000000 i == *** [ 0.125000 ] --- [ 0.00000 rad ]
|2>: 0.353553 + 0.000000 i == *** [ 0.125000 ] --- [ 0.00000 rad ]
|3>: 0.353553 + 0.000000 i == *** [ 0.125000 ] --- [ 0.00000 rad ]
|4>: 0.353553 + 0.000000 i == *** [ 0.125000 ] --- [ 0.00000 rad ]
|5>: 0.353553 + 0.000000 i == *** [ 0.125000 ] --- [ 0.00000 rad ]
|6>: 0.353553 + 0.000000 i == *** [ 0.125000 ] --- [ 0.00000 rad ]
|7>: 0.353553 + 0.000000 i == *** [ 0.125000 ] --- [ 0.00000 rad ]
After measurement:
# wave function for qubits with ids (least to most significant): 0;1;2
|0>: 0.000000 + 0.000000 i == [ 0.000000 ]
|1>: 0.000000 + 0.000000 i == [ 0.000000 ]
|2>: 0.000000 + 0.000000 i == [ 0.000000 ]
|3>: 1.000000 + 0.000000 i == ******************** [ 1.000000 ] --- [ 0.00000 rad ]
|4>: 0.000000 + 0.000000 i == [ 0.000000 ]
|5>: 0.000000 + 0.000000 i == [ 0.000000 ]
|6>: 0.000000 + 0.000000 i == [ 0.000000 ]
|7>: 0.000000 + 0.000000 i == [ 0.000000 ]
Post-QFT measurement results [qubit0, qubit1, qubit2]:
[One,One,Zero]
此輸出說明一些不同的狀態:
- 比較傳回的結果與預先測量
DumpMachine
後,顯然未說明 QFT 後疊加的基礎狀態。 測量只傳回單一基礎狀態,並以系統波函式中該狀態的幅度決定機率。 - 從測量後的
DumpMachine
,您可以看到測量會變更狀態本身,並從基礎狀態的初始疊加,投影該狀態至對應測量值的單一基礎狀態。
如果多次重複此作業,您會看到結果統計資料開始說明 QFT 後狀態的等權重疊加,導致每次投射的隨機結果。 但是,除了效率不佳及仍不完美外,這只會重現基礎狀態的相對振幅,而不是兩者間的相對相位。 後者不是此範例的問題,但如果提供 QFT 比 $\ket{000}$ 複雜的輸入,您會看到相對相位出現。
Q#使用作業來簡化 QFT 線路
如簡介所述,Q# 大部分的功能是讓您免於處理個別量子位元的麻煩。
如果您要開發全方位、可用的量子程式,請擔心 H
作業是特定輪替前或後執行,只會拖累進度。
Q#命名空間Microsoft.Quantum.Canon
包含ApplyQFT
作業,您可以使用此作業並套用至任意數目的量子位。
若要存取
ApplyQFT
作業,請在檔案開頭新增open
命名空間的 Q# 語句Microsoft.Quantum.Canon
:open Microsoft.Quantum.Canon;
從第一個
H
取代到SWAP
所取代的所有專案:ApplyQFT(qs);
Q#再次執行程式,並注意輸出與之前相同。
若要查看使用 Q# 作業的實際優點,請將量子位數目變更為 以外的
3
值:
mutable resultArray = [Zero, size = 4];
use qs = Qubit[4];
//...
因此,您可以在任何指定數目的量子位元套用適當的 QFT,而不必擔心弄亂每個量子位元的 H
新作業和輪替。
後續步驟
探索其他 Q# 教學課程:
- 量子隨機數產生器 示範如何撰寫 Q# 程式,以在迭加中產生量子位的隨機數。
- Grover 的搜尋演算法 示範如何撰寫 Q# 使用 Grover 搜尋演算法的程式。
- 量子糾纏 示範如何撰寫 Q# 可操作和測量量子位的程式,並示範迭加和糾纏的效果。
- Quantum Katas 是自我步調的教學課程和程式設計練習,旨在同時教學量子運算和Q#程序設計的專案。
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應