Jak debugować i testować kod kwantowy
Podobnie jak w przypadku programowania klasycznego, niezbędne jest sprawdzenie, czy programy kwantowe działają zgodnie z oczekiwaniami i mogą diagnozować nieprawidłowe zachowanie. W tym artykule omówiono narzędzia oferowane przez platformę Azure Quantum Development Kit do testowania i debugowania programów kwantowych.
Q# Debugowanie programu
Rozszerzenie programu Visual Studio Code platformy Azure Quantum Development Kit (QDK) zawiera debuger dla Q# programów. Punkty przerwania można ustawić, przejść przez kod i do każdej funkcji lub operacji oraz śledzić nie tylko zmienne lokalne, ale także stan kwantowy kubitów.
Uwaga
Debuger programu VS Code działa tylko z plikami Q# (qs) i nie działa z komórkami Q# w notesie Jupyter Notebook. Aby przetestować komórki notesu Jupyter Notebook, zobacz Testowanie kodu.
W poniższym przykładzie przedstawiono podstawowe funkcje debugera. Aby uzyskać pełne informacje na temat korzystania z debugerów programu VS Code, zobacz Debugowanie.
W programie VS Code utwórz i zapisz nowy plik qs przy użyciu następującego kodu:
import Microsoft.Quantum.Arrays.*;
import Microsoft.Quantum.Convert.*;
operation Main() : Result {
use qubit = Qubit();
H(qubit);
let result = M(qubit);
Reset(qubit);
return result;
}
- Ustaw punkt przerwania w wierszu
H(qubit)
, klikając po lewej stronie numeru wiersza. - Wybierz ikonę debugera, aby otworzyć okienko debugera, a następnie wybierz pozycję Uruchom i Debuguj. Kontrolki debugera są wyświetlane w górnej części ekranu.
- Wybierz F5, aby rozpocząć debugowanie i przejdź do punktu przerwania. W okienku Zmienne debugera rozwiń kategorię Stan kwantowy. Widać, że kubit został zainicjowany w stanie |0> .
- Krok do (F11)
H
operacji i kodu źródłowegoH
dla operacji jest wyświetlany. Podczas wykonywania operacji zwróć uwagę, że wartość kwantowa zmienia się, gdyH
operacja umieszcza kubit w superpozycję. - Podczas przechodzenia przez operację (F10)
M
wartość kwantowa jest rozpoznawana jako |0> lub |1> w wyniku pomiaru, a wartość zmiennejresult
klasycznej jest wyświetlana. - Podczas przechodzenia przez operację
Reset
kubit jest resetowany do |0>.
testowanie kodu
Chociaż debuger programu VS Code Q# nie jest dostępny dla Q# komórek w notesie Jupyter Notebook, zestaw QDK platformy Azure udostępnia pewne wyrażenia i funkcje, które mogą pomóc w rozwiązywaniu problemów z kodem.
Wyrażenie niepowodzenia
Wyrażenie fail
kończy obliczenia całkowicie, co odpowiada krytycznemu błędowi, który zatrzymuje program.
Rozważmy ten prosty przykład, który weryfikuje wartość parametru:
# import qsharp package to access the %%qsharp magic command
import qsharp
// use the %%qsharp magic command to change the cell type from Python to Q#
%%qsharp
function PositivityFact(value : Int) : Unit {
if value <= 0 {
fail $"{value} isn't a positive number.";
}
}
PositivityFact(0);
Error: program failed: 0 isn't a positive number.
Call stack:
at PositivityFact in line_2
Qsc.Eval.UserFail
× runtime error
╰─▶ program failed: 0 isn't a positive number.
╭─[line_2:5:1]
5 │
6 │ fail $"{value} isn't a positive number.";
· ────────────────────┬───────────────────
· ╰── explicit fail
7 │ }
╰────
fail
W tym miejscu wyrażenie uniemożliwia programowi kontynuowanie uruchamiania z nieprawidłowymi danymi.
Fact(), funkcja
Możesz zaimplementować to samo zachowanie co w poprzednim przykładzie przy użyciu Fact()
funkcji z Microsoft.Quantum.Diagnostics
przestrzeni nazw. Funkcja Fact()
ocenia dany warunek klasyczny i zgłasza wyjątek, jeśli jest to fałsz.
import qsharp
%%qsharp
function PositivityFact(value : Int) : Unit {
Fact(value > 0, "Expected a positive number.");
}
PositivityFact(4);
Error: program failed: Expected a positive number.
Call stack:
at Microsoft.Quantum.Diagnostics.Fact in diagnostics.qs
at PositivityFact in line_4
Qsc.Eval.UserFail
× runtime error
╰─▶ program failed: Expected a positive number.
╭─[diagnostics.qs:29:1]
29 │ if (not actual) {
30 │ fail message;
· ──────┬─────
· ╰── explicit fail
31 │ }
╰────
DumpMachine(), funkcja
DumpMachine()
to Q# funkcja, która umożliwia zrzut informacji o bieżącym stanie target maszyny do konsoli i kontynuowanie uruchamiania programu.
Uwaga
Po wydaniu platformy Azure Quantum Development KitDumpMachine()
funkcja używa teraz kolejności big-endian dla danych wyjściowych.
import qsharp
%%qsharp
import Microsoft.Quantum.Diagnostics.*;
operation MultiQubitDumpMachineDemo() : 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);
}
MultiQubitDumpMachineDemo();
Basis State
(|𝜓₁…𝜓ₙ⟩) Amplitude Measurement Probability Phase
|00⟩ 0.7071+0.0000𝑖 50.0000% ↑ 0.0000
|01⟩ −0.7071+0.0000𝑖 50.0000% ↓ -3.1416
Basis State
(|𝜓₁…𝜓ₙ⟩) Amplitude Measurement Probability Phase
|00⟩ 0.7071+0.0000𝑖 50.0000% ↑ 0.0000
|01⟩ −0.6533−0.2706𝑖 50.0000% ↙ -2.7489
dump_machine(), funkcja
dump_machine
to funkcja języka Python, która zwraca bieżącą przydzieloną liczbę kubitów i słownik języka Python o rozrzedowanych amplitudach stanu, które można przeanalizować. Korzystanie z jednej z tych funkcji w notesie Jupyter Notebook umożliwia przechodzenie przez operacje podobnie jak debuger. Przy użyciu poprzedniego przykładowego programu:
import qsharp
%%qsharp
use qubits = Qubit[2];
X(qubits[0]);
H(qubits[1]);
dump = qsharp.dump_machine()
dump
Basis State
(|𝜓₁…𝜓ₙ⟩) Amplitude Measurement Probability Phase
|10⟩ 0.7071+0.0000𝑖 50.0000% ↑ 0.0000
|11⟩ 0.7071+0.0000𝑖 50.0000% ↑ 0.0000
%%qsharp
R1Frac(1, 2, qubits[0]);
R1Frac(1, 3, qubits[1]);
dump = qsharp.dump_machine()
dump
Basis State
(|𝜓₁…𝜓ₙ⟩) Amplitude Measurement Probability Phase
|10⟩ 0.5000+0.5000𝑖 50.0000% ↗ 0.7854
|11⟩ 0.2706+0.6533𝑖 50.0000% ↗ 1.1781
# you can print an abbreviated version of the values
print(dump)
STATE:
|10⟩: 0.5000+0.5000𝑖
|11⟩: 0.2706+0.6533𝑖
# you can access the current qubit count
dump.qubit_count
2
# you can access individual states by their index
dump[2]
(0.5+0.5000000000000001j)
dump[3]
(0.27059805007309845+0.6532814824381883j)
Operacje CheckZero() i CheckAllZero()
CheckZero()
i CheckAllZero()
są Q# operacjami, które mogą sprawdzić, czy bieżący stan tablicy kubitu lub kubitu wynosi $\ket{0}$. CheckZero()
Zwraca true
wartość , jeśli kubit jest w stanie $\ket{0}$ i false
czy jest w jakimkolwiek innym stanie. CheckAllZero()
Zwraca wartość true
, jeśli wszystkie kubity w tablicy znajdują się w stanie $\ket{0}$ i false
jeśli kubity znajdują się w jakimkolwiek innym stanie.
import Microsoft.Quantum.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");
}
}
dump_operation(), funkcja
dump_operation
to funkcja języka Python, która przyjmuje operację lub definicję operacji, oraz liczbę kubitów do użycia i zwraca macierz kwadratową liczb zespolonych reprezentujących dane wyjściowe operacji.
Importujesz dump_operation
z qsharp.utils
pliku .
import qsharp
from qsharp.utils import dump_operation
W tym przykładzie przedstawiono macierz bramy tożsamości z jednym kubitem i bramą hadamarda.
res = dump_operation("qs => ()", 1)
print(res)
res = dump_operation("qs => H(qs[0])", 1)
print(res)
[[(1+0j), 0j], [0j, (1+0j)]]
[[(0.707107+0j), (0.707107+0j)], [(0.707107+0j), (-0.707107-0j)]]
Możesz również zdefiniować funkcję lub operację przy użyciu polecenia qsharp.eval()
, a następnie odwołać się do niej z .dump_operation
Pojedynczy kubit reprezentowany wcześniej może być również reprezentowany jako
qsharp.eval("operation SingleQ(qs : Qubit[]) : Unit { }")
res = dump_operation("SingleQ", 1)
print(res)
[[(1+0j), 0j], [0j, (1+0j)]]
W tym przykładzie użyto Controlled Ry
bramy do zastosowania obrotu do drugiego kubitu
qsharp.eval ("operation ControlRy(qs : Qubit[]) : Unit {qs[0]; Controlled Ry([qs[0]], (0.5, qs[1]));}")
res = dump_operation("ControlRy", 2)
print(res)
[[(1+0j), 0j, 0j, 0j], [0j, (1+0j), 0j, 0j], [0j, 0j, (0.968912+0j), (-0.247404+0j)], [0j, 0j, (0.247404+0j), (0.968912+0j)]]
Poniższy kod definiuje Q# operację ApplySWAP
i drukuje jej macierz wraz z operacją tożsamości z dwoma kubitami.
qsharp.eval("operation ApplySWAP(qs : Qubit[]) : Unit is Ctl + Adj { SWAP(qs[0], qs[1]); }")
res = dump_operation("qs => ()", 2)
print(res)
res = dump_operation("ApplySWAP", 2)
print(res)
[[(1+0j), 0j, 0j, 0j], [0j, (1+0j), 0j, 0j], [0j, 0j, (1+0j), 0j], [0j, 0j, 0j, (1+0j)]]
[[(1+0j), 0j, 0j, 0j], [0j, 0j, (1+0j), 0j], [0j, (1+0j), 0j, 0j], [0j, 0j, 0j, (1+0j)]]
Więcej przykładów operacji testowania, których można użyć dump_operation()
, można znaleźć na stronie Przykładowe operacje testowania w zestawie QDK.