Estrutura de um Q# programa

Este artigo explora os componentes gerais que compõem um Q# programa. Observe que Q# os programas escritos no Jupyter Notebooks não usam alguns desses componentes – essas diferenças são descritas em cada seção.

Considere o seguinte programa Q#:

namespace Superposition {

    @EntryPoint()
    operation MeasureOneQubit() : Result {
        // Allocate a qubit, by default it is in zero state      
        use q = Qubit();  
        // We apply a Hadamard operation H to the state
        // It now has a 50% chance of being measured 0 or 1  
        H(q);      
        // Now we measure the qubit in Z-basis.
        let result = M(q);
        // We reset the qubit before releasing it.
        Reset(q);
        // Finally, we return the result of the measurement.
        return result;
    }
}

Apenas lendo os comentários (//), você pode dizer que este programa aloca um qubit, aplica uma operação para colocá-lo em superposição, mede o estado do qubit, redefine-o e retorna o resultado.

Para executar este programa no Visual Studio Code, consulte Introdução a Q# programas e VS Code.

Namespaces de usuário

Q# os programas normalmente começam com um namespace nomeado pelo usuário, como

namespace Superposition {
    // Your code goes here.
}

Os namespaces ajudam você a organizar a funcionalidade relacionada. Os namespaces são nomeados pelo usuário e só pode haver um namespace por arquivo qsharp (*.qs).

A Q# biblioteca padrão tem namespaces predefinidos que contêm funções e operações que você pode usar em programas quânticos. Para obter mais informações, consulte Namespaces internos.

Os Jupyter Notebooks não usam namespaces de usuário.

EntryPoint()

O @EntryPoint() atributo informa ao Q# compilador onde começar a executar o programa. Em programas com várias definições de função e operação, o @EntryPoint() pode ser colocado antes que qualquer uma das funções ou operações e o fluxo do programa comece a partir daí e continue sequencialmente.

    ...
    @EntryPoint()
    operation MeasureOneQubit() : Result {
        ...

Os Jupyter Notebooks não usam pontos de entrada.

O comando %%qsharp

Por padrão, Q# os programas no Jupyter Notebooks usam o kernel do Python ipykernel . Para adicionar Q# código a uma célula do notebook, você precisa usar o %%qsharp comando , que está habilitado com o pacote do qsharp Python. Por exemplo, o código de exemplo anterior em um Jupyter Notebook tem esta aparência:

import qsharp
%%qsharp

    operation MeasureOneQubit() : Result {
        // Allocate a qubit, by default it is in zero state      
        use q = Qubit();  
        // We apply a Hadamard operation H to the state
        // It now has a 50% chance of being measured 0 or 1  
        H(q);      
        // Now we measure the qubit in Z-basis.
        let result = M(q);
        // We reset the qubit before releasing it.
        Reset(q);
        // Display the result
        Message($"Result is {result}");
        // Finally, we return the result of the measurement.
        return result;
    
    }
    MeasureOneQubit();

Observe a ausência de um namespace de usuário ou um @EntryPoint(), que não são necessários para Jupyter Notebooks. Em vez de um ponto de entrada, a operação é chamada diretamente na última linha. Observe também que uma Message instrução foi adicionada ao código Jupyter Notebook para exibir o resultado. Quando você executa o programa anterior Q# no VS Code, o simulador interno exibe o resultado por padrão.

Ao usar o %%qsharp comando :

  • Você deve executar import qsharp primeiro para habilitar o comando %%qsharp.
  • O %%qsharp comando tem como escopo toda a célula na qual ele aparece.
  • O código Q# que segue o comando deve aderir à sintaxe de codificação Q# padrão. Por exemplo, você denota comentários usando // em vez de dentro %%qsharp das # células, e as linhas de código devem terminar com um ponto e vírgula;.
  • O comando %%qsharp não pode ser precedido ou seguido por uma instrução Python na célula.

Para obter um exemplo de como trabalhar com um programa Jupyter Notebook, consulte Introdução a Q# programas e VS Code.

Tipos

Q# fornece muitos tipos internos que são comuns à maioria das linguagens, incluindo Int, Double, Boole String, juntamente com tipos específicos da computação quântica. Por exemplo, o Result tipo representa o resultado de qualquer medida de qubit e pode ter um dos dois valores definidos possíveis: One e Zero. No programa de exemplo, a operação MeasureOneQubit() espera um tipo de retorno de Result e a M operação mede o qubit e retorna o Result.

...
// operation definition expecting a return type of Result
operation MeasureOneQubit() : Result {
    ...
    // Now we measure the qubit in Z-basis, returning a Result type
    let result = M(q);
    ...
}

O Q# também fornece tipos que definem intervalos, matrizes e tuplas. Você pode até mesmo definir seus tipos personalizados.

Alocação de qubits

No Q#, qubits são alocados por meio da palavra-chave use.

Nosso exemplo define um único qubit:

// Allocate a qubit.
use q = Qubit();
...

mas você também pode alocar vários qubits e acessar cada um por meio de seu índice:

...
use qubits = Qubit[2];
X(qubits[1]);
H(qubits[0]);
...

Por padrão, cada qubit que você aloca com a palavra-chave use começa no estado zero. Cada qubit deve ser redefinido para o estado zero antes de ser lançado no final do programa. Falha ao redefinir um qubit dispara um erro de runtime.

// Reset a qubit.
Reset(q);
...

Operações quânticas

Uma vez alocado, um qubit pode ser passado para operações e funções, também conhecido como chamadores. Operações são os blocos de construção básicos de um programa em Q#. Uma operação Q# é uma sub-rotina quântica. Ou seja, é uma rotina que pode ser chamada e que contém operações quânticas que modificam o estado do registro do qubit.

Para definir uma operação de Q#, especifique um nome para a operação junto com as respectivas entradas e saída. Em nosso exemplo, a única operação é essencialmente o programa inteiro. Ele não usa parâmetros e espera um tipo de retorno de Result:

operation MeasureOneQubit() : Result {
    ...
}

Aqui está um exemplo básico que não usa parâmetros e não espera nenhum valor retornado. O Unit valor é equivalente a NULL em outros idiomas.

operation SayHelloQ() : Unit {
    Message("Hello quantum world!");
}

A Q# biblioteca padrão também fornece operações que você pode usar em seus programas, por exemplo, o Hadamard ou a H operação usada no programa de exemplo. Considerando um qubit em base Z, a operação H coloca o qubit em uma sobreposição uniforme. Uma vez em sobreposição, o qubit tem uma chance de 50% de ser medido como zero ou um.

Medindo qubits

Há muitos tipos de medidas quânticas, mas Q# se concentra em medidas projetivas em qubits únicos, também conhecidos como medidas Pauli. Na medida em uma determinada base (por exemplo, a base computacional $\ket{0},\ket{1}$), o estado do qubit é projetado para o estado base que foi medido, destruindo eventual sobreposição existente entre os dois.

Nosso programa de exemplo usa a M operação , que executa uma medida de um único qubit na base Pauli Z e retorna um Result tipo.

Namespaces internos

A biblioteca padrão Q# usa namespaces internos que contêm funções e operações que você pode usar em programas quânticos. Por exemplo, o namespace Microsoft.Quantum.Intrinsic contém operações e funções comumente usadas, como M, para medir os resultados e Message, para exibir mensagens de usuário em qualquer lugar do programa.

Você pode chamar uma função ou operação especificando o namespace completo ou usar uma instrução open para disponibilizar todas as funções e operações para esse namespace e facilitar a leitura do código. Esses dois exemplos chamam a mesma operação:

 Microsoft.Quantum.Intrinsic.Message("Hello quantum world!");
open Microsoft.Quantum.Intrinsic;
Message("Hello quantum world!");

Observe que, no programa de exemplo, não open há instruções ou chamadas com namespaces completos. Isso ocorre porque o Q# ambiente de desenvolvimento carrega automaticamente dois namespaces por padrão - Microsoft.Quantum.Core e Microsoft.Quantum.Intrinsic - que contêm funções e operações comumente usadas.

Você pode aproveitar o Microsoft.Quantum.Measurement namespace e usar a MResetZ operação para otimizar o código no programa de exemplo. MResetZ combina as operações de medida e redefinição em uma etapa, como no exemplo a seguir:

namespace Superposition {

    // open the namespace for the MResetZ operation
    open Microsoft.Quantum.Measurement;

    @EntryPoint()
    operation MeasureOneQubit() : Result {
        // Allocate a qubit, by default it is in zero state      
        use q = Qubit();  
        // We apply a Hadamard operation H to the state
        // It now has a 50% chance of being measured 0 or 1  
        H(q);   
        // Measure and reset the qubit, and return the result value   
        return MResetZ(q);
    }
    
}