Come eseguire il debug e testare il codice quantistico

Come per la programmazione classica, è essenziale essere in grado di verificare che i programmi quantistici agiscano come previsto e di diagnosticare un comportamento non corretto. Questo articolo illustra gli strumenti offerti da Azure Quantum Development Kit per il test e il debug dei programmi quantistici.

Eseguire il debug del Q# programma

L'estensione Azure Quantum Development Kit (QDK) di Visual Studio Code include un debugger per Q# i programmi. È possibile impostare punti di interruzione, eseguire il codice e in ogni funzione o operazione e tenere traccia non solo delle variabili locali, ma anche dello stato quantistico dei qubit.

Nota

Il debugger di VS Code funziona solo con Q# i file (con estensione qs) e non funziona con Q# le celle in un notebook di Jupyter. Per testare le celle di Jupyter Notebook, vedere Testare il codice.

Nell'esempio seguente vengono illustrate le funzionalità di base del debugger. Per informazioni complete sull'uso dei debugger di VS Code, vedere Debug.

In VS Code creare e salvare un nuovo file con estensione qs con il codice seguente:

import Microsoft.Quantum.Arrays.*;
import Microsoft.Quantum.Convert.*;

operation Main() : Result {
    use qubit = Qubit();
    H(qubit);
    let result = M(qubit);
    Reset(qubit);
    return result;
}
  1. Impostare un punto di interruzione sulla riga H(qubit) facendo clic a sinistra del numero di riga.
  2. Selezionare l'icona del debugger per aprire il riquadro del debugger e selezionare Esegui e debug. I controlli del debugger vengono visualizzati nella parte superiore della schermata.
  3. Selezionare F5 per avviare il debug e continuare con il punto di interruzione. Nel riquadro Variabili del debugger espandere la categoria Stato quantistico. È possibile notare che il qubit è stato inizializzato nello stato |0> .
  4. Eseguire (F11) l'operazione H e il codice sorgente per l'operazione H viene visualizzato. Durante l'esecuzione dell'operazione, si noti che il valore quantistico cambia man mano che l'operazione H inserisce il qubit in sovrapposizione.
  5. Durante l'esecuzione M dell'operazione (F10), il valore quantistico viene risolto in |0> o |1> in seguito alla misurazione e viene visualizzato il valore della variabile result classica.
  6. Durante l'esecuzione dell'operazione Reset , il qubit viene reimpostato su |0>.

Eseguire test del codice

Anche se il debugger di VS Code Q# non è disponibile per Q# le celle in un notebook di Jupyter, Azure QDK fornisce alcune espressioni e funzioni che consentono di risolvere i problemi del codice.

Espressione non riuscita

L'espressione fail termina completamente il calcolo, corrispondente a un errore irreversibile che arresta il programma.

Si consideri questo semplice esempio che convalida un valore di parametro:

# 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 │     }   
   ╰────

In questo caso, l'espressione impedisce al programma di continuare l'esecuzione fail con dati non validi.

Funzione Fact()

È possibile implementare lo stesso comportamento dell'esempio precedente usando la Fact() funzione dallo spazio dei Microsoft.Quantum.Diagnostics nomi . La Fact() funzione valuta una determinata condizione classica e genera un'eccezione se è false.

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 │         }
    ╰────

Funzione DumpMachine()

DumpMachine() è una Q# funzione che consente di eseguire il dump delle informazioni sullo stato corrente del target computer nella console e di continuare a eseguire il programma.

Nota

Con il rilascio di Azure Quantum Development Kit, la DumpMachine() funzione usa ora l'ordinamento big-endian per l'output.

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   

funzione dump_machine()

dump_machine è una funzione Python che restituisce il numero di qubit allocato corrente e un dizionario Python di ampiezze di stato sparse che è possibile analizzare. L'uso di una di queste funzioni in jupyter Notebook consente di eseguire le operazioni in modo molto simile a un debugger. Uso del programma di esempio precedente:

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)

Operazioni CheckZero() e CheckAllZero()

CheckZero() e CheckAllZero() sono Q# operazioni che possono verificare se lo stato corrente di una matrice qubit o qubit è $\ket{0}$. CheckZero() restituisce true se il qubit è nello stato $\ket{0}$ e false se si trova in qualsiasi altro stato. CheckAllZero() restituisce true se tutti i qubit nella matrice si trovano nello stato $\ket{0}$ e false se i qubit si trovano in qualsiasi altro stato.

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");
    }
}

funzione dump_operation()

dump_operation è una funzione Python che accetta un'operazione o una definizione di operazione e un numero di qubit da usare e restituisce una matrice quadrata di numeri complessi che rappresenta l'output dell'operazione.

Si esegue l'importazione dump_operation da qsharp.utils.

import qsharp
from qsharp.utils import dump_operation

In questo esempio viene stampata la matrice di un gate di identità a qubit singolo e del gate Hadamard.

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)]]

È anche possibile definire una funzione o un'operazione usando qsharp.eval() e quindi farvi riferimento da dump_operation. Il singolo qubit rappresentato in precedenza può anche essere rappresentato come

qsharp.eval("operation SingleQ(qs : Qubit[]) : Unit { }")

res = dump_operation("SingleQ", 1)
print(res)
[[(1+0j), 0j], [0j, (1+0j)]]

Questo esempio usa un Controlled Ry gate per applicare una rotazione al secondo qubit

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)]]

Il codice seguente definisce Q# l'operazione ApplySWAP e stampa la matrice insieme a quella dell'operazione di identità a due qubit.

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)]]

Altri esempi di operazioni di test che usano dump_operation() sono disponibili nella pagina degli esempi Operazioni di test in QDK.