共用方式為


如何偵錯及測試量子程序代碼

測試和調試在量子程式設計中與在經典程式設計中一樣重要。 本文討論如何使用 Visual Studio Code (VS Code) 和 Jupyter Notebook 中的 Azure Quantum Development Kit (QDK) 偵錯和測試量子程式。

除錯量子程式

QDK 提供數個工具來偵錯程式碼。 如果您在 VS Code 中撰寫 Q# 或 OpenQASM 程式,則可以使用 VS Code 除錯器在程式中設定岔斷點並分析程式碼。 QDK 也提供一組轉儲函數,可用來取得程式中不同點的資訊。

如何使用 VS Code 偵錯工具

透過 VS Code 中的 QDK 延伸模組,您可以使用偵錯工具逐步執行程式碼並進入每個函式或作業、追蹤局部變數的值,以及追蹤量子位元的量子狀態。

下列範例示範如何在 Q# 程式中使用偵錯工具。 如需 VS Code 偵錯工具的完整資訊,請參閱 VS Code 網站上的 偵錯

  1. 在 VS Code 中,使用下列程式碼建立並儲存新 .qs 檔案:

    import Std.Arrays.*;
    import Std.Convert.*;
    
    operation Main() : Result {
        use qubit = Qubit();
        H(qubit);
        let result = M(qubit);
        Reset(qubit);
        return result;
    }
    
  2. 在第 6 行 上, H(qubit)按一下行號左側的 以設定中斷點。 出現一個紅色圓圈。

  3. 在 [主要側邊列] 中,選擇偵錯工具圖示以開啟偵錯工具窗格,然後選擇 [執行和偵錯]。 偵錯工具控制列隨即出現。

  4. F5 啟動偵錯工具,並繼續至岔斷點。 在偵錯工具窗格的 [變數] 功能表中,展開 [量子狀態] 下拉式清單,以查看量子位已在 $\ket{0}$ 狀態中初始化。

  5. F11 進入 H 作業。 作業的 H 原始程式碼隨即顯示。 請注意,當您逐步執行時,H會變更為疊加態。

  6. F10 以逐步執行 M 作業。 請注意, 量子狀態 在測量之後解析為 $\ket{0}$ 或 $\ket{1}$。 變數 result 也會顯示在 [區域變數] 底下。

  7. 再次按 F10 以略過Reset 作業。 請注意, 量子狀態 會重設為 $\ket{0}$。

當您完成探索偵錯工具時,請按 *Ctrl + F5 以結束偵錯工具。

注意

VS Code 除錯工具僅適用於 Q# (.qs) 和 OpenQASM (.qasm) 檔案。 您無法在 Jupyter Notebook 中的儲存格上使用 Q# VS Code 偵錯工具。

如何使用 QDK 傾印函數進行除錯

QDK 提供數個 Q# 以及 Python 函數,當您呼叫這些函數時,會輸出程式當前狀態的相關資訊。 使用這些轉儲功能中的資訊來檢查程式是否如預期般運作。

Q# DumpMachine函數

DumpMachine 是一個 Q# 函數,可讓您在程式執行時將量子位元系統目前狀態的相關資訊轉儲到控制台。 DumpMachine 不會在運行時間停止或中斷您的程序。

下列範例會在DumpMachine程式中兩個點呼叫Q#,並檢視結果。

  1. 在 VS Code 中,使用下列程式碼建立並儲存新 .qs 檔案:

    import Std.Diagnostics.*;
    
    operation Main() : Unit {
        use qubits = Qubit[2];
        X(qubits[1]);
        H(qubits[1]);
        DumpMachine();
    
        R1Frac(1, 2, qubits[0]);
        R1Frac(1, 3, qubits[1]);
        DumpMachine();
    
        ResetAll(qubits);
    }
    
  2. Ctrl + Shift + Y 打開 調試控制台

  3. Ctrl + F5 運行您的程序。 下列 DumpMachine 輸出會顯示在 偵錯主控台中:

    Basis | Amplitude      | Probability | Phase
    -----------------------------------------------
     |00⟩ |  0.7071+0.0000𝑖 |    50.0000% |   0.0000
     |01⟩ | −0.7071+0.0000𝑖 |    50.0000% |  -3.1416
    
    Basis | Amplitude      | Probability | Phase
    -----------------------------------------------
     |00⟩ |  0.7071+0.0000𝑖 |    50.0000% |   0.0000
     |01⟩ | −0.6533−0.2706𝑖 |    50.0000% |  -2.7489
    

DumpMachine 輸出顯示了量子位元系統的狀態在每組閘之後如何變化。

注意

DumpMachine 輸出會使用大端排序。

Python dump_machine 函數

函數是 dump_machine Python 套件中的 qsharp 函數。 此函式會傳回目前配置的量子位計數,以及包含量子位系統稀疏狀態幅度的字典。

下列範例會執行與上一個 DumpMachine 範例相同的程式,但在 Jupyter Notebook 中執行,而不是 .qs 在檔案中執行。

  1. 在 VS Code 中,按 Ctrl + Shift + P 開啟 命令面板

  2. 輸入 Create: New Jupyter Notebook ,然後按 Enter。 新的 Jupyter Notebook 索引標籤開啟。

  3. 在第一個儲存格中,複製並執行下列程式碼:

    from qdk import qsharp 
    
  4. 建立新的程式碼儲存格,然後複製並執行下列 Q# 程式碼:

    %%qsharp
    
    use qubits = Qubit[2];
    X(qubits[0]);
    H(qubits[1]);
    
  5. 建立新的程式碼儲存格。 複製並執行下列 Python 程式碼,以檢視程式中此時的量子位元狀態:

    dump = qsharp.dump_machine()
    dump
    

    函式會 dump_machine 顯示下列輸出:

    Basis State
    (|𝜓₁…𝜓ₙ⟩)  Amplitude       Measurement Probability  Phase
    |10⟩       0.7071+0.0000𝑖   50.0000%                 ↑  0.0000
    |11⟩       0.7071+0.0000𝑖   50.0000%                 ↑  0.0000
    
  6. 建立新的程式碼儲存格,然後複製並執行下列 Q# 程式碼:

    %%qsharp
    
    R1Frac(1, 2, qubits[0]);
    R1Frac(1, 3, qubits[1]);
    
  7. 建立新的程式碼儲存格。 複製並執行下列 Python 程式碼,以檢視程式中此時的量子位元狀態:

    dump = qsharp.dump_machine()
    dump
    

    函式會 dump_machine 顯示下列輸出:

    Basis State
    (|𝜓₁…𝜓ₙ⟩)  Amplitude      Measurement Probability  Phase
    |10⟩       0.5000+0.5000𝑖  50.0000%                 ↗  0.7854
    |11⟩       0.2706+0.6533𝑖  50.0000%                 ↗  1.1781
    
  8. 若要列印輸出的 dump_machine 縮寫版本,請建立新儲存格並執行下列 Python 程式碼:

    print(dump)
    
  9. 若要取得系統中的量子位元總數,請建立新的程式碼儲存格,並執行下列 Python 程式碼:

    dump.qubit_count
    
  10. 您可以存取具有非零振幅的個別量子位元狀態的振幅。 例如,建立新的程式碼儲存格並執行下列 Python 程式碼,以取得 $\ket$ 和 $\ket{10}{11}$ 狀態的個別振幅:

    print(dump[2])
    print(dump[3])
    

dump_operation 函式

函數是 dump_operation Python 套件中的 qsharp.utils 函數。 此函式會接受兩個輸入: Q# 運算或運算定義作為字串,以及運算中使用的量子位元數目。 輸出 dump_operation 是一個巢狀列表,代表對應於給定量子運算的複數方陣。 矩陣值位於計算基礎中,每個子清單代表矩陣的一列。

使用下列範例 dump_operation 顯示 1 量子位和 2 量子位系統的資訊。

  1. 在 VS Code 中,按 Ctrl + Shift + P 開啟 命令面板

  2. 輸入 Create: New Jupyter Notebook ,然後按 Enter。 新的 Jupyter Notebook 索引標籤開啟。

  3. 在第一個儲存格中,複製並執行下列程式碼:

    from qdk import qsharp
    from qsharp.utils import dump_operation
    
  4. 若要顯示單一量子位閘門的矩陣元素,請呼叫 dump_operation 並傳遞 1 以取得量子位元數目。 例如,在新的程式碼儲存格中複製並執行下列 Python 程式碼,以取得身分閘道和 Hadamard 閘道的矩陣元素:

    res = dump_operation("qs => ()", 1)
    print("Single-qubit identity gate:\n", res)
    print()
    
    res = dump_operation("qs => H(qs[0])", 1)
    print("Single-qubit Hadamard gate:\n", res)
    
  5. 您也可以呼叫函數 qsharp.eval ,然後參考 Q# 操作以 dump_operation 取得相同的結果。 例如,建立新的程式碼儲存格,然後複製並執行下列 Python 程式碼,以列印單一量子位元 Hadamard 閘的矩陣元素:

    qsharp.eval("operation SingleH(qs : Qubit[]) : Unit { H(qs[0]) }")
    
    res = dump_operation("SingleH", 1)
    print("Single-qubit Hadamard gate:\n", res)
    
  6. 若要顯示雙量子位閘門的矩陣元素,請呼叫 dump_operation 並傳遞 2 以取得量子位元數目。 例如,在新的程式碼儲存格中複製並執行下列 Python 程式碼,以取得受控 Ry 作業的矩陣元素,其中第二個量子位是 target 量子位:

    qsharp.eval ("operation ControlRy(qs : Qubit[]) : Unit { Controlled Ry([qs[0]], (0.5, qs[1])); }")
    
    res = dump_operation("ControlRy", 2)
    print("Controlled Ry rotation gate:\n", res)
    

如需如何測試及偵錯您的程式碼的更多範例,請查看 QDK 範例中的 dump_operation

測試您的量子程式碼

QDK 提供數個 Q# 函數和作業,可用來在程式碼執行時測試程式碼。 您也可以為程式撰寫 Q# 單元測試。

fail 運算式

運算式 fail 會立即結束您的程式。 若要將測試併入程式碼中,請使用條件語句內的 fail 運算式。

下列範例使用 fail 陳述式來測試量子位元陣列是否正好包含 3 個量子位元。 當測試未通過時,程式會以錯誤訊息結束。

  1. 在 VS Code 中,使用下列程式碼建立並儲存新 .qs 檔案:

    operation Main() : Unit {
        use qs = Qubit[6];
        let n_qubits = Length(qs);
    
        if n_qubits != 3 {
            fail $"The system should have 3 qubits, not {n_qubits}.";
        }  
    }
    
  2. Ctrl + F5 運行程序。 您的程式失敗,且下列輸出會出現在 偵錯主控台中:

    Error: program failed: The system should have 3 qubits, not 6.
    
  3. Qubit[6] 編輯 Qubit[3]程式碼,儲存檔案,然後按 Ctrl + F5 再次執行程式。 程式執行時沒有錯誤,因為測試通過了。

Fact 函式

您也可以使用 Q#Fact 命名空間中的 Std.Diagnostics 函式來測試程式碼。 此函式接受 Fact 布林運算式和一個錯誤訊息字串。 如果布林運算式為 true,則測試會通過,且程式會繼續執行。 如果布林運算式為 false,則 Fact 結束程式並顯示錯誤訊息。

若要在先前的程式碼中執行相同的陣列長度測試,使用 Fact 函數,請遵循下列步驟:

  1. 在 VS Code 中,使用下列程式碼建立並儲存新 .qs 檔案:

    import Std.Diagnostics.Fact;
    
    operation Main() : Unit {
        use qs = Qubit[6];
        let n_qubits = Length(qs);
    
        Fact(n_qubits == 3,  $"The system should have 3 qubits, not {n_qubits}.")
    }
    
  2. Ctrl + F5 運行程序。 中的 Fact 測試條件未通過,且錯誤訊息會出現在 偵錯主控台中。

  3. Qubit[6] 編輯 Qubit[3]程式碼,儲存檔案,然後按 Ctrl + F5 再次執行程式。 測試 Fact 條件會通過,且您的程式執行時不會發生錯誤。

使用標註 Q# 撰寫 @Test() 單元測試

在Q#程式中,您可以將@Test()註解套用至可呼叫項(函數或操作),從而將其轉換為單元測試。 這些單元測試會出現在 VS Code 的 [測試] 功能表中,以便您可以利用此 VS Code 功能。 只有在可呼叫項目不採用輸入參數時,您才能將可呼叫項目轉換為單元測試。

下列範例會將陣列長度測試程式碼包裝在作業中,並將該作業轉換為單元測試:

  1. 在 VS Code 中,使用下列程式碼建立並儲存新 .qs 檔案:

    import Std.Diagnostics.Fact;
    
    @Test()
    operation TestCase() : Unit {
        use qs = Qubit[3];
        let n_qubits = Length(qs);
    
        Fact(n_qubits == 3, $"The system should have 3 qubits, not {n_qubits}.");
    }
    

    @Test()作業定義之前TestCase行上的註釋會將作業轉換為 VS Code 單元測試。 作業定義行上會出現綠色箭頭。

  2. 選擇綠色箭號以執行 TestCase 並報告測試結果。

  3. 若要在 VS Code 測試檢視器中與單元測試互動,請選擇主要側邊列中的測試燒瓶圖示。

  4. 請將您的程式碼從Qubit[3]編輯為Qubit[6],然後再次執行單元測試,以查看測試資訊如何變更。

您可以在 VS Code 中撰寫和執行 Q# 單元測試,而不需要程式中的進入點作業。

注意

Std.Diagnostics 命名空間中的可調用項目與 QIR 生成不相容,因此請僅在模擬器上運行的程式碼中包含單元測試。 如果您想要從程式碼產生 Q# QIR,請勿在程式碼中包含單元測試。

這些CheckZeroCheckAllZero操作

CheckZero作業和CheckAllZeroQ#作業會檢查量子位元或量子位元陣列的目前狀態是否為 $\ket{0}$。 此CheckZero作業會採用單一量子位,只有在量子位處於 $\kettrue$ 狀態時才會傳回{0}。 這些CheckAllZero作業會採用量子位陣列,並且只有在陣列中的所有量子位都處於 $\kettrue$ 狀態時才會傳回{0}。 若要使用 CheckZeroCheckAllZero,請從命名空間匯 Std.Diagnostics 入它們。

下列範例會使用這兩個作業。 CheckZero 測試X作業是否能將第一個量子比特從 $\ket{0}$ 狀態翻轉至 $\ket{1}$ 狀態,而CheckAllZero作業測試兩個量子比特是否重設為 $\ket{0}$ 狀態。

在 VS Code 中,使用下列程式碼建立並儲存新 .qs 檔案,然後執行程式並在 偵錯主控台中檢查輸出。

import Std.Diagnostics.*;

operation Main() : Unit {
    use qs = Qubit[2];
    X(qs[0]); 

    if CheckZero(qs[0]) {
        Message("X operation failed");
    }
    else {
        Message("X operation succeeded");
    }

    ResetAll(qs);

    if CheckAllZero(qs) {
        Message("Reset operation succeeded");
    }
    else {
        Message("Reset operation failed");
    }
}