Partilhar via


Como depurar e testar seu código quântico

Tal como acontece com a programação clássica, é essencial ser capaz de verificar se os programas quânticos agem como pretendido, e ser capaz de diagnosticar o comportamento incorreto. Este artigo discute as ferramentas oferecidas pelo Azure Quantum Development Kit para testar e depurar programas quânticos.

Depurar seu Q# programa

A extensão do Azure Quantum Development Kit (QDK) Visual Studio Code inclui um depurador para Q# programas. Você pode definir pontos de interrupção, percorrer seu código e entrar em cada função ou operação e rastrear não apenas as variáveis locais, mas também o estado quântico dos qubits.

Nota

O depurador VS Code só funciona com Q# arquivos (.qs) e não funciona com Q# células em um Jupyter Notebook. Para testar células do Jupyter Notebook, consulte Testar seu código.

O exemplo a seguir demonstra os recursos básicos do depurador. Para obter informações completas sobre como usar depuradores do VS Code, consulte Depuração.

No VS Code, crie e salve um novo arquivo .qs com o seguinte código:

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. Defina um ponto de interrupção na linha H(qubit) clicando à esquerda do número da linha.
  2. Selecione o ícone do depurador para abrir o painel do depurador e selecione Executar e Depurar. Os controles do depurador são exibidos na parte superior da tela.
  3. Selecione F5 para iniciar a depuração e continuar até o ponto de interrupção. No painel Variáveis do depurador, expanda a categoria Estado Quântico. Você pode ver que o qubit foi inicializado no estado |0> .
  4. Entre em (F11) a H operação e o código-fonte da H operação são exibidos. Ao percorrer a operação, observe as mudanças de valor quântico à medida que a H operação coloca o qubit em superposição.
  5. À medida que você passa por cima (F10) da M operação, o valor quântico é resolvido para |0> ou |1> como resultado da medição, e o valor da variável result clássica é exibido.
  6. À medida que você passa sobre a Reset operação, o qubit é redefinido para |0>.

Teste o seu código

Embora o depurador VS Code Q# não esteja disponível para Q# células em um Jupyter Notebook, o Azure QDK fornece algumas expressões e funções que podem ajudar a solucionar problemas do seu código.

Expressão de falha

A fail expressão termina o cálculo inteiramente, correspondendo a um erro fatal que para o programa.

Considere este exemplo simples que valida um valor de parâmetro:

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

Aqui, a fail expressão impede que o programa continue a ser executado com dados inválidos.

Função Fact()

Você pode implementar o mesmo comportamento do exemplo anterior usando a Fact() função do Microsoft.Quantum.Diagnostics namespace. A Fact() função avalia uma determinada condição clássica e lança uma exceção se ela for falsa.

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

Função DumpMachine()

DumpMachine() é uma Q# função que permite despejar informações sobre o estado atual da target máquina para o console e continuar a executar o programa.

Nota

Com o lançamento do Azure Quantum Development Kit, a DumpMachine() função agora usa ordenação big-endian para sua saída.

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   

Função dump_machine()

dump_machine é uma função Python que retorna a contagem de qubit alocada atual e um dicionário Python de amplitudes de estado esparsas que você pode analisar. Usar qualquer uma dessas funções em um Jupyter Notebook permite que você percorra suas operações como um depurador. Usando o programa de exemplo anterior:

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)

Operações CheckZero() e CheckAllZero()

CheckZero() e CheckAllZero() são Q# operações que podem verificar se o estado atual de uma matriz de qubit ou qubit é $\ket{0}$. CheckZero() retorna true se o qubit estiver no estado $\ket{0}$ e false se estiver em qualquer outro estado. CheckAllZero() retorna true se todos os qubits na matriz estiverem no estado $\ket{0}$ e false se os qubits estiverem em qualquer outro estado.

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

Função dump_operation()

dump_operation é uma função Python que leva uma operação, ou definição de operação, e um número de qubits para usar, e retorna uma matriz quadrada de números complexos que representam a saída da operação.

Você importa do dump_operation qsharp.utils.

import qsharp
from qsharp.utils import dump_operation

Este exemplo imprime a matriz de uma porta de identidade de qubit único e a porta de 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)]]

Você também pode definir uma função ou operação usando qsharp.eval() e, em seguida, fazer referência a ela a partir de dump_operation. O qubit único representado anteriormente também pode ser representado como

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

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

Este exemplo usa uma Controlled Ry porta para aplicar uma rotação ao segundo 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)]]

O código a seguir define Q# a operação ApplySWAP e imprime sua matriz ao lado da operação de identidade de dois qubits.

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

Mais exemplos de operações de teste usando dump_operation() podem ser encontrados na página de exemplos Operações de teste no QDK.