Tutorial: Escreva e simula programas de nível qubit em Q#Tutorial: Write and simulate qubit-level programs in Q#

Bem-vindo ao tutorial do Kit de Desenvolvimento Quântico sobre a escrita e simulação de um programa quântico básico que opera em qubits individuais.Welcome to the Quantum Development Kit tutorial on writing and simulating a basic quantum program that operates on individual qubits.

Embora tenha Q# sido criada principalmente como uma linguagem de programação de alto nível para programas quânticos em larga escala, pode ser facilmente usada para explorar o nível mais baixo de programas quânticos: abordando diretamente qubits específicos.Although Q# was primarily created as a high-level programming language for large-scale quantum programs, it can just as easily be used to explore the lower level of quantum programs: directly addressing specific qubits. A flexibilidade Q# permite que os utilizadores se aproximem dos sistemas quânticos a partir de qualquer tal nível de abstração, e neste tutorial mergulhamos nos próprios qubits.The flexibility of Q# allows users to approach quantum systems from any such level of abstraction, and in this tutorial we dive into the qubits themselves. Especificamente, damos uma olhada sob o capuz da transformação quântica de Fourier,uma sub-rotina que é parte integrante de muitos algoritmos quânticos maiores.Specifically, we take a look under the hood of the quantum Fourier transform, a subroutine that is integral to many larger quantum algorithms.

Note-se que esta visão de baixo nível do processamento de informação quântica é frequentemente descrita em termos de "circuitos quânticos", que representam a aplicação sequencial dos portões a qubits específicos de um sistema.Note that this low-level view of quantum information processing is often described in terms of "quantum circuits," which represent the sequential application of gates to specific qubits of a system.

Assim, as operações de mono e múltiplos qubits que aplicamos sequencialmente podem ser facilmente representadas num "diagrama de circuito".Thus, the single- and multi-qubit operations we sequentially apply can be readily represented in a "circuit diagram." No nosso caso, vamos definir uma Q# operação para executar a transformação completa de Fourier quântico de três qubits, que tem a seguinte representação como um circuito:In our case, we will define a Q# operation to perform the full three-qubit quantum Fourier transform, which has the following representation as a circuit:


Three qubit quantum Fourier transform circuit diagram

Pré-requisitosPrerequisites

  • Instale o Kit de Desenvolvimento Quântico utilizando o seu ambiente de linguagem e desenvolvimento preferido.Install the Quantum Development Kit using your preferred language and development environment.
  • Se já tiver o QDK instalado, confirme que o atualizou para a versão mais recenteIf you already have the QDK installed, make sure you have updated to the latest version

Neste tutorial, irá aprender a:In this tutorial, you'll learn how to:

  • Definir operações quânticas em Q#Define quantum operations in Q#
  • Ligue Q# as operações diretamente a partir do pedido de comando ou usando um programa de anfitrião clássicoCall Q# operations directly from the command prompt or using a classical host program
  • Simular uma operação quântica da alocação de qubits à saída de mediçãoSimulate a quantum operation from qubit allocation to measurement output
  • Observe como a simulação de função de onda do sistema quântico evolui ao longo da operaçãoObserve how the quantum system's simulated wavefunction evolves throughout the operation

Executar um programa quântico com o Kit de Desenvolvimento Quântico da Microsoft é normalmente composto por duas partes:Running a quantum program with Microsoft's Quantum Development Kit typically consists of two parts:

  1. O programa em si, que é implementado usando a Q# linguagem de programação quântica, e depois invocado para funcionar em um computador quântico ou simulador quântico.The program itself, which is implemented using the Q# quantum programming language, and then invoked to run on a quantum computer or quantum simulator. Estes consistem deThese consist of
    • Q# operações: sub-rotinas que atuam nos registos quânticos, eQ# operations: subroutines acting on quantum registers, and
    • Q# funções: sub-rotinas clássicas utilizadas dentro do algoritmo quântico.Q# functions: classical subroutines used within the quantum algorithm.
  2. O ponto de entrada utilizado para chamar o programa quântico e especificar a máquina-alvo na qual deve ser executado.The entry point used to call the quantum program and specify the target machine on which it should be run. Isto pode ser feito diretamente a partir do pedido de comando, ou através de um programa de anfitrião escrito em uma linguagem de programação clássica como Python ou C#.This can be done directly from the command prompt, or through a host program written in a classical programming language like Python or C#. Este tutorial inclui instruções para o método que preferir.This tutorial includes instructions for whichever method you prefer.

Alocar qubits e definir operações quânticasAllocate qubits and define quantum operations

A primeira parte deste tutorial consiste em definir a Q# Perform3qubitQFT operação, que realiza a transformação quântica de Fourier em três qubits.The first part of this tutorial consists of defining the Q# operation Perform3qubitQFT, which performs the quantum Fourier transform on three qubits.

Além disso, usaremos a DumpMachine função para observar como a simulação de função de onda do nosso sistema de três qubits evolui em toda a operação.In addition, we will use the DumpMachine function to observe how the simulated wavefunction of our three qubit system evolves across the operation.

O primeiro passo é criar o seu Q# projeto e arquivo.The first step is creating your Q# project and file. Os passos para tal dependem do ambiente que utilizará para ligar para o programa, podendo encontrar os detalhes nos respetivos guias de instalação.The steps for this depend on the environment you will use to call the program, and you can find the details in the respective installation guides.

Vamos acompanhá-lo através dos componentes do ficheiro passo a passo, mas o código também está disponível como um bloco completo abaixo.We will walk you through the components of the file step-by-step, but the code is also available as a full block below.

Espaços de nome para aceder a outras Q# operaçõesNamespaces to access other Q# operations

Dentro do ficheiro, primeiro definimos o espaço de nome NamespaceQFT que será acedido pelo compilador.Inside the file, we first define the namespace NamespaceQFT which will be accessed by the compiler. Para a nossa operação de utilização das Q# operações existentes, abrimos os Microsoft.Quantum.<> espaços de nome relevantes.For our operation to make use of existing Q# operations, we open the relevant Microsoft.Quantum.<> namespaces.

namespace NamespaceQFT {
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Diagnostics;
    open Microsoft.Quantum.Math;
    open Microsoft.Quantum.Arrays;

    // operations go here
}

Definir operações com argumentos e devoluçõesDefine operations with arguments and returns

Em seguida, definimos a Perform3qubitQFT operação:Next, we define the Perform3qubitQFT operation:

    operation Perform3qubitQFT() : Unit {
        // do stuff
    }

Para já, a operação não aceita argumentos e não devolve nada--- neste caso escrevemos que devolve um Unit objeto, que é semelhante a void C# ou um tuple vazio, Tuple[()] em Python.For now, the operation takes no arguments and does not return anything---in this case we write that it returns a Unit object, which is akin to void in C# or an empty tuple, Tuple[()], in Python. Mais tarde, vamos modificá-lo para devolver uma série de resultados de medição, altura em que Unit será substituído por Result[] .Later, we will modify it to return an array of measurement results, at which point Unit will be replaced by Result[].

Alocar qubits com usingAllocate qubits with using

No âmbito da nossa Q# operação, atribuímos primeiro um registo de três qubits com a using declaração:Within our Q# operation, we first allocate a register of three qubits with the using statement:

        using (qs = Qubit[3]) {

            Message("Initial state |000>:");
            DumpMachine();

        }

Com using , os qubits são automaticamente atribuídos no estado $\ket {0} $.With using, the qubits are automatically allocated in the $\ket{0}$ state. Podemos verificar isto utilizando Message(<string>) e DumpMachine() , que imprimimos uma cadeia e o estado atual do sistema para a consola.We can verify this by using Message(<string>) and DumpMachine(), which print a string and the system's current state to the console.

Nota

As Message(<string>) DumpMachine() funções (de Microsoft.Quantum.Intrinsic Microsoft.Quantum.Diagnostics e, respectivamente) são ambas impressas diretamente para a consola.The Message(<string>) and DumpMachine() functions (from Microsoft.Quantum.Intrinsic and Microsoft.Quantum.Diagnostics, respectively) both print directly to the console. Tal como uma computação quântica real, Q# não nos permite aceder diretamente aos estados qubit.Just like a real quantum computation, Q# does not allow us to directly access qubit states. No entanto, à medida DumpMachine que imprime o estado atual da máquina-alvo, pode fornecer informações valiosas para depurar e aprender quando usado em conjunto com o simulador de estado completo.However, as DumpMachine prints the target machine's current state, it can provide valuable insight for debugging and learning when used in conjunction with the full state simulator.

Aplicação de portões mono-qubit e controladosApplying single-qubit and controlled gates

Em seguida, aplicamos os portões que compõem a própria operação.Next, we apply the gates which comprise the operation itself. Q# já contém muitos portões quânticos básicos como operações no Microsoft.Quantum.Intrinsic espaço de nome, e estes não são exceção.Q# already contains many basic quantum gates as operations in the Microsoft.Quantum.Intrinsic namespace, and these are no exception.

No âmbito de uma Q# operação, as declarações invocando os callables serão, naturalmente, executadas por ordem sequencial.Within a Q# operation, the statements invoking callables will, of course, be run in sequential order. Assim, o primeiro portão a aplicar é o H (Hadamard) ao primeiro qubit:Hence, the first gate to apply is the H (Hadamard) to the first qubit:


Circuit diagram for three qubit QFT through first Hadamard

Para aplicar uma operação a um qubit específico a partir de um registo (isto é, um único Qubit de uma matriz ), Qubit[] usamos a notação de índice padrão.To apply an operation to a specific qubit from a register (i.e. a single Qubit from an array Qubit[]) we use standard index notation. Assim, a aplicação H do primeiro qubit do nosso registo qs assume o formulário:So, applying the H to the first qubit of our register qs takes the form:

            H(qs[0]);

Além de aplicar o H portão (Hadamard) a qubits individuais, o circuito QFT consiste principalmente em R1 rotações controladas.Besides applying the H (Hadamard) gate to individual qubits, the QFT circuit consists primarily of controlled R1 rotations. Uma R1(θ, <qubit>) operação em geral deixa inalterado o componente $\ket {0} $ do qubit, ao mesmo tempo que aplica uma rotação de $e^{i\theta}$ para o componente $\ket {1} $.An R1(θ, <qubit>) operation in general leaves the $\ket{0}$ component of the qubit unchanged, while applying a rotation of $e^{i\theta}$ to the $\ket{1}$ component.

Operações controladasControlled operations

Q# torna extremamente fácil condicionar o funcionamento de uma operação em um ou múltiplos qubits de controlo.Q# makes it extremely easy to condition the run of an operation upon one or multiple control qubits. Em geral, limitamo-nos a prefaciar a chamada Controlled com, e os argumentos da operação mudam como:In general, we merely preface the call with Controlled, and the operation arguments change as:

Op(<normal args>) $\a$ Controlled Op([<control qubits>], (<normal args>)) a$ .Op(<normal args>) $\to$ Controlled Op([<control qubits>], (<normal args>)).

Note que os qubits de controlo devem ser fornecidos como uma matriz, mesmo que seja um único qubit.Note that the control qubits must be provided as an array, even if it is a single qubit.

Depois do H , vemos que os portões seguintes são os R1 portões agindo no primeiro qubit (e controlados pelo segundo/terceiro):After the H, we see that the next gates are the R1 gates acting on the first qubit (and controlled by the second/third):


Circuit diagram for three qubit QFT through first qubit

Chamamos isto comWe call these with

            Controlled R1([qs[1]], (PI()/2.0, qs[0]));
            Controlled R1([qs[2]], (PI()/4.0, qs[0]));

Note que usamos a PI() função do Microsoft.Quantum.Math espaço de nome para definir as rotações em termos de raios pi.Note that we use the PI() function from the Microsoft.Quantum.Math namespace to define the rotations in terms of pi radians. Além disso, dividimo-nos por um Double (por 2.0 exemplo) porque dividir por um inteiro 2 lançaria um erro tipo.Additionally, we divide by a Double (e.g. 2.0) because dividing by an integer 2 would throw a type error.

Dica

R1(π/2) e R1(π/4) são equivalentes às S T operações (também em Microsoft.Quantum.Intrinsic ).R1(π/2) and R1(π/4) are equivalent to the S and T operations (also in Microsoft.Quantum.Intrinsic).

Após a aplicação das H operações pertinentes e das rotações controladas ao segundo e terceiro qubits:After applying the relevant H operations and controlled rotations to the second and third qubits:

            //second qubit:
            H(qs[1]);
            Controlled R1([qs[2]], (PI()/2.0, qs[1]));

            //third qubit:
            H(qs[2]);

só precisamos de aplicar um SWAP portão para completar o circuito:we need only apply a SWAP gate to complete the circuit:

            SWAP(qs[2], qs[0]);

Isto é necessário porque a natureza do Quantum Fourier transforma as saídas dos qubits em ordem inversa, de modo que os swaps permitem uma integração perfeita da subbroutina em algoritmos maiores.This is necessary because the nature of the quantum Fourier transform outputs the qubits in reverse order, so the swaps allow for seamless integration of the subroutine into larger algorithms.

Assim, terminamos de escrever as operações de nível qubit do Quantum Fourier transformar em nossa Q# operação:Hence we have finished writing the qubit-level operations of the quantum Fourier transform into our Q# operation:

Three qubit quantum Fourier transform circuit diagram

No entanto, não podemos encerrar o dia ainda.However, we can't call it a day just yet. Os nossos qubits estavam no estado $\ket {0} $ quando os atribuímos, e tal como na vida, Q# devemos deixar as coisas da mesma forma que as encontramos (ou melhor!).Our qubits were in state $\ket{0}$ when we allocated them, and much like in life, in Q# we should leave things the same way we found them (or better!).

Qubits de deallocateDeallocate qubits

Ligamos DumpMachine() novamente para ver o estado pós-operação, e finalmente aplicamo-nos ao registo qubit para redefinir os ResetAll nossos qubits para $\ket {0} $ antes de concluir a operação:We call DumpMachine() again to see the post-operation state, and finally apply ResetAll to the qubit register to reset our qubits to $\ket{0}$ before completing the operation:

            Message("After:");
            DumpMachine();

            ResetAll(qs);

Exigir que todos os qubits translocados sejam explicitamente definidos para $\ket {0} $ é uma característica básica de , pois permite que Q# outras operações conheçam o seu estado precisamente quando começam a usar esses mesmos qubits (um recurso escasso).Requiring that all deallocated qubits be explicitly set to $\ket{0}$ is a basic feature of Q#, as it allows other operations to know their state precisely when they begin using those same qubits (a scarce resource). Além disso, isto garante que não se enredam com quaisquer outros qubits no sistema.Additionally, this assures that they not be entangled with any other qubits in the system. Se o reset não for efetuado no final de um bloco de using atribuição, será lançado um erro de tempo de execução.If the reset is not performed at the end of a using allocation block, a runtime error will be thrown.

O seu ficheiro completo Q# deve agora ser assim:Your full Q# file should now look like this:

namespace NamespaceQFT {
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Diagnostics;
    open Microsoft.Quantum.Math;
    open Microsoft.Quantum.Arrays;

    operation Perform3qubitQFT() : Unit {

        using (qs = Qubit[3]) {

            Message("Initial state |000>:");
            DumpMachine();

            //QFT:
            //first qubit:
            H(qs[0]);
            Controlled R1([qs[1]], (PI()/2.0, qs[0]));
            Controlled R1([qs[2]], (PI()/4.0, qs[0]));

            //second qubit:
            H(qs[1]);
            Controlled R1([qs[2]], (PI()/2.0, qs[1]));

            //third qubit:
            H(qs[2]);

            SWAP(qs[2], qs[0]);

            Message("After:");
            DumpMachine();

            ResetAll(qs);
        }
    }
}

Com o Q# ficheiro e a operação completos, o nosso programa quântico está pronto para ser chamado e simulado.With the Q# file and operation complete, our quantum program is ready to be called and simulated.

Execute o programaRun the program

Tendo definido a nossa Q# operação num .qs ficheiro, precisamos agora ligar para essa operação e observar quaisquer dados clássicos devolvidos.Having defined our Q# operation in a .qs file, we now need to call that operation and observe any returned classical data. Para já, não há nada devolvido (lembre-se que a nossa operação definida acima de Unit voltas), mas quando mais tarde modificarmos a Q# operação para devolver uma série de resultados de medição Result[] (), iremos abordar esta questão.For now, there isn't anything returned (recall that our operation defined above returns Unit), but when we later modify the Q# operation to return an array of measurement results (Result[]), we will address this.

Embora o Q# programa seja omnipresente em todos os ambientes usados para chamá-lo, a maneira de fazê-lo vai naturalmente variar.While the Q# program is ubiquitous across the environments used to call it, the manner of doing so will of course vary. Como tal, basta seguir as instruções no separador correspondente à sua configuração: trabalhar a partir da Q# aplicação ou utilizar um programa de anfitrião em Python ou C#.As such, simply follow the instructions in the tab corresponding to your setup: working from the Q# application or using a host program in Python or C#.

Executar o Q# programa a partir da origem do comando requer apenas uma pequena alteração no Q# ficheiro.Running the Q# program from the command prompt requires only a small change to the Q# file.

Basta adicionar @EntryPoint() a uma linha anterior à definição de operação:Simply add @EntryPoint() to a line preceding the operation definition:

    @EntryPoint()
    operation Perform3qubitQFT() : Unit {
        // ...

Para executar o programa, abra o terminal na pasta do seu projeto e insiraTo run the program, open the terminal in the folder of your project and enter

dotnet run

Após a conclusão, deverá ver as Message saídas e DumpMachine saídas abaixo impressas na sua consola.Upon completion, you should see the Message and DumpMachine outputs below printed in your console.

Esta saída ilustra algumas coisas diferentes:This output illustrates a few different things:

  1. Comparando o resultado devolvido com a pré-medição, DumpMachine claramente não ilustra a sobreposição pós-QFT sobre estados de base.Comparing the returned result to the pre-measurement DumpMachine, it clearly does not illustrate the post-QFT superposition over basis states. Uma medição apenas devolve um único estado base, com uma probabilidade determinada pela amplitude desse estado na função de onda do sistema.A measurement only returns a single basis state, with a probability determined by the amplitude of that state in the system's wavefunction.
  2. A partir da pós-medição, DumpMachine vemos que a medição muda o próprio estado, projetando-o desde a sobreposição inicial sobre estados base para o estado base único que corresponde ao valor medido.From the post-measurement DumpMachine, we see that measurement changes the state itself, projecting it from the initial superposition over basis states to the single basis state that corresponds to the measured value.

Se repetissemos esta operação muitas vezes, veríamos as estatísticas dos resultados começarem a ilustrar a superposição igualmente ponderada do estado pós-QFT que dá origem a um resultado aleatório em cada disparo.If we were to repeat this operation many times, we would see the result statistics begin to illustrate the equally weighted superposition of the post-QFT state that gives rise to a random result on each shot. No entanto, além de ser ineficiente e ainda imperfeito, isso só reproduziria as amplitudes relativas dos estados de base, e não as fases relativas entre eles.However , besides being inefficient and still imperfect, this would nevertheless only reproduce the relative amplitudes of the basis states, not the relative phases between them. Este último não é um problema neste exemplo, mas veríamos fases relativas aparecerem se lhes fosse dada uma entrada mais complexa para o QFT do que $\ket {000} $.The latter is not an issue in this example, but we would see relative phases appear if given a more complex input to the QFT than $\ket{000}$.

Medições parciaisPartial measurements

Para explorar como a medição de apenas alguns qubits do registo pode afetar o estado do sistema, tente adicionar o seguinte dentro do for loop, após a linha de medição:To explore how measuring only some qubits of the register can affect the system's state, try adding the following inside the for loop, after the measurement line:

                let iString = IntAsString(i);
                Message("After measurement of qubit " + iString + ":");
                DumpMachine();

Note que para aceder à IntAsString função terá de adicionarNote that to access the IntAsString function you will have to add

    open Microsoft.Quantum.Convert;

com o resto das declarações do espaço de open nome.with the rest of the namespace open statements.

Na saída resultante, você verá a projeção gradual em subespaços à medida que cada qubit é medido.In the resulting output, you will see the gradual projection into subspaces as each qubit is measured.

Use as Q# bibliotecasUse the Q# libraries

Como mencionamos na introdução, grande parte do poder assenta no facto de Q# permitir-lhe abstrair as preocupações de lidar com qubits individuais.As we mentioned in the introduction, much of Q#'s power rests in the fact that it allows you to abstract-away the worries of dealing with individual qubits. Na verdade, se quiser desenvolver programas quânticos em larga escala, aplicáveis, preocupar-se se uma H operação vai antes ou depois de uma determinada rotação só o atrasaria.Indeed, if you want to develop full-scale, applicable quantum programs, worrying about whether an H operation goes before or after a particular rotation would only slow you down.

As Q# bibliotecas contêm a operação QFT, que pode simplesmente tomar e solicitar qualquer número de qubits.The Q# libraries contain the QFT operation, which you can simply take and apply for any number of qubits. Para tentar, defina uma nova operação no seu Q# ficheiro que tenha o mesmo conteúdo Perform3QubitQFT de, mas com tudo, desde o primeiro H até ao substituído por SWAP duas linhas fáceis:To give it a try, define a new operation in your Q# file which has the same contents of Perform3QubitQFT, but with everything from the first H to the SWAP replaced by two easy lines:

            let register = BigEndian(qs);    //from Microsoft.Quantum.Arithmetic
            QFT(register);                   //from Microsoft.Quantum.Canon

A primeira linha simplesmente cria uma BigEndian expressão da matriz de qubits atribuído, que é o que a qs operação QFT toma como argumento.The first line simply creates a BigEndian expression of the allocated array of qubits, qs, which is what the QFT operation takes as an argument. Isto corresponde à ordem de índice dos qubits no registo.This corresponds to index ordering of the qubits in the register.

Para ter acesso a estas operações, adicione open declarações para os respetivos espaços de nome no início do Q# ficheiro:To have access to these operations, add open statements for their respective namespaces at the beginning of the Q# file:

    open Microsoft.Quantum.Canon;
    open Microsoft.Quantum.Arithmetic;

Agora, ajuste o seu programa de anfitrião para chamar o nome da sua nova operação (por PerformIntrinsicQFT exemplo), e dê-lhe um giro.Now, adjust your host program to call the name of your new operation (e.g. PerformIntrinsicQFT), and give it a whirl.

Para ver o verdadeiro benefício da utilização das operações da Q# biblioteca, altere o número de qubits para outra coisa que 3 não:To see the real benefit of using the Q# library operations, change the number of qubits to something other than 3:

        mutable resultArray = new Result[4];

        using (qs = Qubit[4]) {
            //...
        }

Pode assim aplicar o QFT adequado para qualquer número de qubits, sem ter que se preocupar com a confusão de novas H operações e rotações em cada qubit.You can thus apply the proper QFT for any given number of qubits, without having to worry about the mess of new H operations and rotations on each qubit.

Note que o simulador quântico leva exponencialmente mais tempo para correr à medida que aumenta o número de qubits---precisamente por que esperamos por hardware quântico real!Note that the quantum simulator takes exponentially more time to run as you increase the number of qubits---precisely why we look forward to real quantum hardware!