在這個教學中,你會寫一個 Q# 程式,準備兩個處於特定量子態的量子位元,對這些量子位元進行操作使它們相互糾纏,並進行測量以展示疊加與糾纏的影響。 你要逐步建立 Q# 程式,以引入量子位元狀態、量子操作和測量。
在開始之前,請複習以下量子運算概念:
- 傳統位會保留單一二進位值,例如 0 或 1,但量子位元可以處於兩種狀態 0 和 1 的疊加狀態。 每個可能的量子位元狀態都會由一組機率幅度描述。
- 當您測量量子位元的狀態時,一律會得到 0 或 1。 每個結果的機率由測量時定義疊加狀態的機率振幅決定。
- 多個量子位元可能會糾纏在一起,因此您無法彼此獨立地描述它們。 當您測量糾纏配對中的一個量子位元時,您也會取得另一個量子位元的相關資訊,而不進行測量。
在本教學課程中,您將瞭解如何:
- 建立 Q# 作業,將量子位初始化為所需的狀態。
- 將量子位元置於疊加狀態。
- 糾纏一對量子位。
- 測量量子位並觀察結果。
提示
如果您想要加速量子計算的旅程,請參閱 Azure 量子程式碼,這是 Microsoft Quantum 網站 的一個獨特功能。 在這裡,您可以執行內建 Q# 範例或您自己的 Q# 程式,從提示產生新 Q# 程式碼,一鍵在 VS Code 網頁版 中開啟並執行程式碼,並向 Copilot 詢問有關量子運算的問題。
必要條件
要用 Azure Quantum 的 Copilot 執行程式碼範例,必須擁有 Microsoft(MSA)電子郵件帳號。
如需 Azure Quantum 的 Copilot 的詳細資訊,請參閱 探索 Azure Quantum。
將量子位初始化為已知狀態
第一步是定義一個 Q# 運算,將量子位元初始化為期望的經典狀態,為 0 或 1。 此操作測量一個處於一般量子態的量子位元,回傳Q#Result的型別值為ZeroOne或 。 若測量結果與期望狀態不同,則操作會將狀態反轉,使操作回傳期望狀態的 100%。
開啟 Azure Quantum 的 Copilot,清除預設程式碼,然後將以下程式碼複製到程式碼編輯器視窗。 你無法單獨執行這段程式碼,因為它還不是一個完整的 Q# 程式。
operation SetQubitState(desired : Result, target : Qubit) : Unit {
if desired != M(target) {
X(target);
}
}
程式碼範例引入了兩個標準 Q# 操作, MX與 ,分別轉換量子位元的狀態。
以下是 SetQubitState 操作運作方式的詳細描述:
- 需要兩個參數:
Result型別參數,其命名為desired,代表期望的量子位元狀態(Zero或One),以及一個Qubit型別參數。 - 執行一個測量運算,
M測量量子位元(Zero或One)的狀態,並將結果與你通過的desired值比較。 - 若測量結果與 的
desired值不符,則X會對量子位元進行操作。 此操作會將量子位元的狀態翻轉,從而反轉Zero和One的測量機率。
撰寫測試操作以測試 Bell 狀態
要在程式SetQubitState中呼叫該Q#操作,請建立另一個名為 Main的操作。 此操作分配兩個量子位元,呼叫 SetQubitState 將第一個量子位元設定為已知狀態,然後測量量子位元以觀察結果。
操作結束 SetQubitState 後,將以下程式碼複製到程式碼編輯器視窗。
operation Main() : (Int, Int, Int, Int) {
mutable numOnesQ1 = 0;
mutable numOnesQ2 = 0;
let count = 1000;
let initial = One;
// allocate the qubits
use (q1, q2) = (Qubit(), Qubit());
for test in 1..count {
SetQubitState(initial, q1);
SetQubitState(Zero, q2);
// measure each qubit
let resultQ1 = M(q1);
let resultQ2 = M(q2);
// Count the number of 'Ones' returned:
if resultQ1 == One {
numOnesQ1 += 1;
}
if resultQ2 == One {
numOnesQ2 += 1;
}
}
// reset the qubits
SetQubitState(Zero, q1);
SetQubitState(Zero, q2);
// Display the times that |0> is returned, and times that |1> is returned
Message($"Q1 - Zeros: {count - numOnesQ1}");
Message($"Q1 - Ones: {numOnesQ1}");
Message($"Q2 - Zeros: {count - numOnesQ2}");
Message($"Q2 - Ones: {numOnesQ2}");
return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
}
在程式代碼中, count 和 initial 變數會分別設定為 1000 和 One 。 這會將第一個量子位元初始化為 One,並測量每個量子位元 1000 次。
此 Main 工作會執行下列作業:
- 設定射擊次數(
count)及初始量子位元狀態(One)變數。 - 呼叫
use陳述式來初始化兩個量子位元。 - 重複進行實驗
count次。 - 在迴圈中,呼叫
SetQubitState將第一個量子位元的指定initial值設定為該狀態,然後再次呼叫SetQubitState將第二個量子位元設定為狀態Zero。 - 在迴圈中,應用
M運算來測量每個量子位元,然後儲存每個量子位元回傳One的測量次數。 - 迴圈結束後,再次呼叫
SetQubitState量子位元以重置為已知狀態(Zero)。 你必須重置你用use語句分配的量子位元。 - 請呼叫
Message函式以在輸出視窗中列印您的結果。
在適用於 Azure Quantum 的 Copilot 中執行程式碼
在撰寫疊加與糾纏的程式碼前,先測試你目前的程式,看看量子位元的初始化與測量情況。
要將你的程式碼當作獨立程式執行, Q# Copilot 的編譯器需要知道從哪裡開始程式。 因為你沒有指定命名空間,編譯器會將預設的入口點視為操作。Main 如需詳細資訊,請參閱 專案和隱含命名空間。
你的 Q# 程式現在看起來是這樣的:
operation SetQubitState(desired : Result, target : Qubit) : Unit {
if desired != M(target) {
X(target);
}
}
operation Main() : (Int, Int, Int, Int) {
mutable numOnesQ1 = 0;
mutable numOnesQ2 = 0;
let count = 1000;
let initial = One;
// allocate the qubits
use (q1, q2) = (Qubit(), Qubit());
for test in 1..count {
SetQubitState(initial, q1);
SetQubitState(Zero, q2);
// measure each qubit
let resultQ1 = M(q1);
let resultQ2 = M(q2);
// Count the number of 'Ones' returned:
if resultQ1 == One {
numOnesQ1 += 1;
}
if resultQ2 == One {
numOnesQ2 += 1;
}
}
// reset the qubits
SetQubitState(Zero, q1);
SetQubitState(Zero, q2);
// Display the times that |0> is returned, and times that |1> is returned
Message($"Q1 - Zeros: {count - numOnesQ1}");
Message($"Q1 - Ones: {numOnesQ1}");
Message($"Q2 - Zeros: {count - numOnesQ2}");
Message($"Q2 - Ones: {numOnesQ2}");
return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
}
將完整的程式碼範例複製貼上到 Copilot for Azure Quantum 的程式碼視窗,將射擊數量的滑桿設為「1」,然後選擇 執行。 結果會顯示在直方圖和 結果 欄位中。
Q1 - Zeros: 0
Q1 - Ones: 1000
Q2 - Zeros: 1000
Q2 - Ones: 0
你的程式還沒修改量子位元狀態,所以第一個量子位元的測量總是回傳 One,第二個量子位元總是回傳 Zero。
如果你將 的 initial 值改為 並 Zero 再次執行程式,那麼第一個量子位元也會返回 Zero。
Q1 - Zeros: 1000
Q1 - Ones: 0
Q2 - Zeros: 1000
Q2 - Ones: 0
將一個量子位元置於疊加態
目前,你程式中的量子位元處於經典狀態,可能是1或0,就像一般電腦上的位元一樣。 要糾纏這些量子位元,首先必須將其中一個量子位元置於相等疊加態。 在相等疊加態下測量量子位元有50%的機率回傳Zero,有50%的機率回傳One。
要將量子位元置於疊加態,請使用 Q#H或 Hadamard 操作。 這個 H 操作會將處於純 Zero 態或 One 狀態的量子位元轉換成介於 Zero 和 One之間的一種中間狀態。
請在 Main 操作中修改你的程式碼。 將初始值重設為 , One 並插入一行執行以下 H 操作:
for test in 1..count {
use (q1, q2) = (Qubit(), Qubit());
for test in 1..count {
SetQubitState(initial, q1);
SetQubitState(Zero, q2);
H(q1); // Add the H operation after initialization and before measurement
// measure each qubit
let resultQ1 = M(q1);
let resultQ2 = M(q2);
...
再跑一次你的程式。 因為第一個量子位元在測量時處於相等的疊加態,因此對於Zero和One,你得到的結果接近50/50。 舉例來說,你的輸出看起來像這樣:
Q1 - Zeros: 523
Q1 - Ones: 477
Q2 - Zeros: 1000
Q2 - Ones: 0
每次執行程式時,第一個量子位元的結果會略有變化,但接近 50% One 和 50% Zero,而第二個量子位元的結果仍然總是 Zero。
將第一個量子位元初始化為 ,Zero而不是 ,One然後再次執行程式。 你會得到類似的結果,因為這個 H 操作會將純 Zero 態和純態 One 都變成相等的疊加態。
注意
若要查看疊加結果在鏡頭分佈上的變化,請移動 Copilot for Azure Quantum 中的滑桿並增加鏡頭數。
糾纏兩個量子位
糾纏的量子位元彼此相關,無法彼此獨立描述。 當你測量一個糾纏量子位元的狀態時,你也知道另一個量子位元的狀態,即使沒有測量它。 這個教學範例是兩個糾纏的量子位元,但你也可以糾纏三個或以上的量子位元。
要建立糾纏狀態,請使用 Q#CNOT,或 Controlled-NOT 操作。 當你套用 CNOT 到兩個量子位元時,一個量子位元是控制量子位元,另一個是目標量子位元。 若控制量子位元的狀態為 One,則操作 CNOT 會翻轉目標量子比特的狀態。 否則, CNOT 對量子位元沒有任何影響。
在CNOT作業之後立即將H作業新增至程式。 你完整的課程內容如下:
operation SetQubitState(desired : Result, target : Qubit) : Unit {
if desired != M(target) {
X(target);
}
}
operation Main() : (Int, Int, Int, Int) {
mutable numOnesQ1 = 0;
mutable numOnesQ2 = 0;
let count = 1000;
let initial = Zero;
// allocate the qubits
use (q1, q2) = (Qubit(), Qubit());
for test in 1..count {
SetQubitState(initial, q1);
SetQubitState(Zero, q2);
H(q1);
CNOT(q1, q2); // Add the CNOT operation after the H operation
// measure each qubit
let resultQ1 = M(q1);
let resultQ2 = M(q2);
// Count the number of 'Ones' returned:
if resultQ1 == One {
numOnesQ1 += 1;
}
if resultQ2 == One {
numOnesQ2 += 1;
}
}
// reset the qubits
SetQubitState(Zero, q1);
SetQubitState(Zero, q2);
// Display the times that |0> is returned, and times that |1> is returned
Message($"Q1 - Zeros: {count - numOnesQ1}");
Message($"Q1 - Ones: {numOnesQ1}");
Message($"Q2 - Zeros: {count - numOnesQ2}");
Message($"Q2 - Ones: {numOnesQ2}");
return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
}
執行程式並查看輸出。 每次執行程式時,你的結果都會有些微不同。
Q1 - Zeros: 502
Q1 - Ones: 498
Q2 - Zeros: 502
Q2 - Ones: 498
第一個量子比特的統計數據仍顯示約有 50% 機率同時測量 One 和 Zero,但第二個量子比特的測量結果並不總是 Zero 現在。 每個量子位元都有相同數量的Zero結果和One結果。 第二個量子位元的測量結果總是與第一個量子位元相同,因為兩個量子比特是糾纏的。 若第一個量子位元被測量為 Zero,則糾纏量子比特也必須是 Zero。 若第一個量子位元被測量為 One,則糾纏量子比特也必須是 One。
必要條件
要在您的本地開發環境中開發並執行範例程式碼,請安裝以下工具:
- 請使用 最新版本的 Visual Studio Code(VS Code) 或開啟 VS Code for the Web。
- Azure Quantum Development Kit(QDK)擴充功能的最新版本。 如需安裝詳細資料,請參閱 設定 QDK 擴充功能。
建立新 Q# 檔案
- 在 VS Code 中,打開 檔案 選單並選擇 新文字檔案 以建立新檔案。
- 將檔案儲存為
CreateBellStates.qs。 這個檔案是你為程式撰寫 Q# 程式碼的地方。
將量子位初始化為已知狀態
第一步是定義一個 Q# 運算,將量子位元初始化為期望的經典狀態,為 0 或 1。 此操作測量一個處於一般量子態的量子位元,回傳Q#Result的型別值為ZeroOne或 。 若測量結果與期望狀態不同,則操作會將狀態反轉,使操作回傳期望狀態的 100%。
開啟 CreateBellStates.qs 並複製下列程式代碼:
operation SetQubitState(desired : Result, target : Qubit) : Unit {
if desired != M(target) {
X(target);
}
}
程式碼範例引入了兩個標準 Q# 操作, MX與 ,分別轉換量子位元的狀態。
以下是 SetQubitState 操作運作方式的詳細描述:
- 需要兩個參數:
Result型別參數,其命名為desired,代表期望的量子位元狀態(Zero或One),以及一個Qubit型別參數。 - 執行一個測量運算,
M測量量子位元(Zero或One)的狀態,並將結果與你通過的desired值比較。 - 若測量結果與 的
desired值不符,則X會對量子位元進行操作。 此操作會將量子位元的狀態翻轉,從而反轉Zero和One的測量機率。
撰寫測試操作以測試 Bell 狀態
要在程式SetQubitState中呼叫該Q#操作,請建立另一個名為 Main的操作。 此操作分配兩個量子位元,呼叫 SetQubitState 將第一個量子位元設定為已知狀態,然後測量量子位元以觀察結果。
在CreateBellStates.qs操作之後,將下列操作新增至您的SetQubitState檔案:
operation Main() : (Int, Int, Int, Int) {
mutable numOnesQ1 = 0;
mutable numOnesQ2 = 0;
let count = 1000;
let initial = One;
// allocate the qubits
use (q1, q2) = (Qubit(), Qubit());
for test in 1..count {
SetQubitState(initial, q1);
SetQubitState(Zero, q2);
// measure each qubit
let resultQ1 = M(q1);
let resultQ2 = M(q2);
// Count the number of 'Ones' returned:
if resultQ1 == One {
numOnesQ1 += 1;
}
if resultQ2 == One {
numOnesQ2 += 1;
}
}
// reset the qubits
SetQubitState(Zero, q1);
SetQubitState(Zero, q2);
// Display the times that |0> is returned, and times that |1> is returned
Message($"Q1 - Zeros: {count - numOnesQ1}");
Message($"Q1 - Ones: {numOnesQ1}");
Message($"Q2 - Zeros: {count - numOnesQ2}");
Message($"Q2 - Ones: {numOnesQ2}");
return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
}
在程式代碼中, count 和 initial 變數會分別設定為 1000 和 One 。 這會將第一個量子位元初始化為 One,並測量每個量子位元 1000 次。
此 Main 工作會執行下列作業:
- 設定射擊次數(
count)及初始量子位元狀態(One)變數。 - 呼叫
use陳述式來初始化兩個量子位元。 - 重複進行實驗
count次。 - 在迴圈中,呼叫
SetQubitState將第一個量子位元的指定initial值設定為該狀態,然後再次呼叫SetQubitState將第二個量子位元設定為狀態Zero。 - 在迴圈中,應用
M運算來測量每個量子位元,然後儲存每個量子位元回傳One的測量次數。 - 迴圈結束後,再次呼叫
SetQubitState量子位元以重置為已知狀態(Zero)。 你必須重置你用use語句分配的量子位元。 - 呼叫
Message函數以在主控台中列印結果。
執行程式碼
在撰寫疊加與糾纏的程式碼前,先測試你目前的程式,看看量子位元的初始化與測量情況。
要將程式碼作為獨立程式執行, Q# 編譯器需要知道從哪裡開始程式。 因為你沒有指定命名空間,編譯器會將預設的入口點視為操作。Main 如需詳細資訊,請參閱 專案和隱含命名空間。
你的 CreateBellStates.qs 檔案現在看起來是這樣:
operation SetQubitState(desired : Result, target : Qubit) : Unit {
if desired != M(target) {
X(target);
}
}
operation Main() : (Int, Int, Int, Int) {
mutable numOnesQ1 = 0;
mutable numOnesQ2 = 0;
let count = 1000;
let initial = One;
// allocate the qubits
use (q1, q2) = (Qubit(), Qubit());
for test in 1..count {
SetQubitState(initial, q1);
SetQubitState(Zero, q2);
// measure each qubit
let resultQ1 = M(q1);
let resultQ2 = M(q2);
// Count the number of 'Ones' returned:
if resultQ1 == One {
numOnesQ1 += 1;
}
if resultQ2 == One {
numOnesQ2 += 1;
}
}
// reset the qubits
SetQubitState(Zero, q1);
SetQubitState(Zero, q2);
// Display the times that |0> is returned, and times that |1> is returned
Message($"Q1 - Zeros: {count - numOnesQ1}");
Message($"Q1 - Ones: {numOnesQ1}");
Message($"Q2 - Zeros: {count - numOnesQ2}");
Message($"Q2 - Ones: {numOnesQ2}");
return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
}
要執行程式,請從操作前的程式碼鏡頭中選擇Main指令,或輸入 Ctrl + F5。 程式會在預設模擬器上執行 Main 作業。
您的輸出會出現在偵錯控制台中。
Q1 - Zeros: 0
Q1 - Ones: 1000
Q2 - Zeros: 1000
Q2 - Ones: 0
你的程式還沒修改量子位元狀態,所以第一個量子位元的測量總是回傳 One,第二個量子位元總是回傳 Zero。
如果你將 的 initial 值改為 並 Zero 再次執行程式,那麼第一個量子位元也會返回 Zero。
Q1 - Zeros: 1000
Q1 - Ones: 0
Q2 - Zeros: 1000
Q2 - Ones: 0
將一個量子位元置於疊加態
目前,你程式中的量子位元處於經典狀態,可能是1或0,就像一般電腦上的位元一樣。 要糾纏這些量子位元,首先必須將其中一個量子位元置於相等疊加態。 在相等疊加態下測量量子位元有50%的機率回傳Zero,有50%的機率回傳One。
要將量子位元置於疊加態,請使用 Q#H或 Hadamard 操作。 這個 H 操作會將處於純 Zero 態或 One 狀態的量子位元轉換成介於 Zero 和 One之間的一種中間狀態。
請在 Main 操作中修改你的程式碼。 將初始值重設為 , One 並插入一行執行以下 H 操作:
for test in 1..count {
use (q1, q2) = (Qubit(), Qubit());
for test in 1..count {
SetQubitState(initial, q1);
SetQubitState(Zero, q2);
H(q1); // Add the H operation after initialization and before measurement
// measure each qubit
let resultQ1 = M(q1);
let resultQ2 = M(q2);
...
再跑一次你的程式。 因為第一個量子位元在測量時處於相等疊加態,所以當你測量時,Zero 和 One 的結果接近 50/50。 舉例來說,你的輸出看起來像這樣:
Q1 - Zeros: 523
Q1 - Ones: 477
Q2 - Zeros: 1000
Q2 - Ones: 0
每次執行程式時,第一個量子位元的結果會略有變化,但接近 50% One 和 50% Zero,而第二個量子位元的結果仍然總是 Zero。
將第一個量子位元初始化為 ,Zero而不是 ,One然後再次執行程式。 你會得到類似的結果,因為這個 H 操作會將純 Zero 態和純態 One 都變成相等的疊加態。
糾纏兩個量子位
糾纏的量子位元彼此相關,無法彼此獨立描述。 當你測量一個糾纏量子位元的狀態時,你也知道另一個量子位元的狀態,即使沒有測量它。 這個教學範例是兩個糾纏的量子位元,但你也可以糾纏三個或以上的量子位元。
要建立糾纏狀態,請使用 Q#CNOT,或 Controlled-NOT 操作。 當你套用 CNOT 到兩個量子位元時,一個量子位元是控制量子位元,另一個是目標量子位元。 若控制量子位元的狀態為 One,則操作 CNOT 會翻轉目標量子比特的狀態。 否則, CNOT 對量子位元沒有任何影響。
在CNOT作業之後立即將H作業新增至程式。 你完整的課程內容如下:
operation SetQubitState(desired : Result, target : Qubit) : Unit {
if desired != M(target) {
X(target);
}
}
operation Main() : (Int, Int, Int, Int) {
mutable numOnesQ1 = 0;
mutable numOnesQ2 = 0;
let count = 1000;
let initial = Zero;
// allocate the qubits
use (q1, q2) = (Qubit(), Qubit());
for test in 1..count {
SetQubitState(initial, q1);
SetQubitState(Zero, q2);
H(q1);
CNOT(q1, q2); // Add the CNOT operation after the H operation
// measure each qubit
let resultQ1 = M(q1);
let resultQ2 = M(q2);
// Count the number of 'Ones' returned:
if resultQ1 == One {
numOnesQ1 += 1;
}
if resultQ2 == One {
numOnesQ2 += 1;
}
}
// reset the qubits
SetQubitState(Zero, q1);
SetQubitState(Zero, q2);
// Display the times that |0> is returned, and times that |1> is returned
Message($"Q1 - Zeros: {count - numOnesQ1}");
Message($"Q1 - Ones: {numOnesQ1}");
Message($"Q2 - Zeros: {count - numOnesQ2}");
Message($"Q2 - Ones: {numOnesQ2}");
return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
}
執行程式並查看輸出。 每次執行程式時,你的結果都會有些微不同。
Q1 - Zeros: 502
Q1 - Ones: 498
Q2 - Zeros: 502
Q2 - Ones: 498
第一個量子比特的統計數據仍顯示約有 50% 機率同時測量 One 和 Zero,但第二個量子比特的測量結果並不總是 Zero 現在。 每個量子位元都有相同數量的Zero結果和One結果。 第二個量子位元的測量結果總是與第一個量子位元相同,因為兩個量子比特是糾纏的。 若第一個量子位元被測量為 Zero,則糾纏量子比特也必須是 Zero。 若第一個量子位元被測量為 One,則糾纏量子比特也必須是 One。
繪製頻率直方圖
要繪製一個頻率直方圖,顯示多次執行程式時結果的分布,請完成以下步驟:
在 VS Code 中開啟你的
CreateBellStates.qs檔案。開啟 [檢視] 功能表,然後選擇 [命令面板]。
輸入 直方圖 即可跳出 QDK:執行檔案並顯示直方圖 選項。 或者,從操作
Main前的程式碼透鏡選項中選擇直方圖指令。 接著輸入拍攝數量(例如100張)。 Q#直方圖會在一個新分頁打開。直方圖中的每條條對應糾纏電路運行1000次時可能的結果。 條狀棒的高度代表該結果發生的次數。 例如,以下直方圖顯示一個包含50個獨特結果的分布。 請注意,對於每個結果,第一個和第二個量子位元的測量結果總是相同的。
提示
要放大直方圖,請使用滑鼠滾輪或觸控板手勢。 放大時要平移圖表,請按住 Alt 鍵並同時滾動滑鼠。
選擇一條條來顯示產生該結果的總投籃百分比。
請選擇左上角 的設定圖示 以顯示視覺化選項。
再跑一次程式碼,但這次用1000發子彈。 隨著射擊次數增加,結果分布趨近常態分布。
相關內容
探索其他 Q# 教學課程:
- Grover 的搜尋演算法 示範如何撰寫 Q# 使用 Grover 搜尋演算法的程式。
- Quantum Fourier Transform 會探索如何撰寫 Q# 直接尋址特定量子位的程式。
- Quantum Katas 是一系列的自學教程和編程練習,同時教授量子計算和Q#編程的要素。