Tutorial: Implementar um gerador de número quântico aleatório em Q#

Observação

O Microsoft Quantum Development Kit (QDK clássico) não terá mais suporte após 30 de junho de 2024. Se você for um desenvolvedor de QDK existente, recomendamos fazer a transição para o novo QDK (QDK Moderno) do Azure Quantum Development Kit para continuar desenvolvendo soluções quânticas. Para obter mais informações, consulte Migrar seu Q# código para o QDK moderno.

Saiba como escrever um programa quântico básico no Q# que aproveita a natureza da mecânica quântica para produzir um número aleatório.

Neste tutorial, você irá:

  • Crie um Q# programa.
  • Examine os componentes main de um Q# programa.
  • Defina a lógica de um problema.
  • Combine operações clássicas e quânticas para resolver um problema.
  • Trabalhar com qubits e superposição para criar um gerador quântico de números aleatórios.

Dica

Se você quiser acelerar sua jornada de computação quântica, marcar Código com o Azure Quantum, um recurso exclusivo do site do Azure Quantum. Aqui, você pode executar exemplos internos Q# ou seus próprios Q# programas, gerar um novo Q# código de seus prompts, abrir e executar seu código no VS Code para a Web com um clique e fazer perguntas ao Copilot sobre computação quântica.

Pré-requisitos

Definir o problema

Computadores clássicos não produzem números aleatórios, mas sim números pseudoaleatórios. Um gerador de números pseudoaleatórios gera uma sequência de números determinística com base em algum valor inicial, chamado de semente. Para se aproximar melhor de valores aleatórios, essa semente geralmente é a hora atual do relógio da CPU.

Os computadores quânticos, por outro lado, podem gerar números verdadeiramente aleatórios. Isso ocorre porque a medida de um qubit na superposição é um processo probabilístico. O resultado da medida é aleatório e não há como prever o resultado. Esse é o princípio básico dos geradores de número aleatório quântico.

Um qubit é uma unidade de informações quânticas que podem estar em superposição. Quando medido, um qubit só pode estar no estado 0 ou no estado 1. No entanto, antes da medida, o estado do qubit representa a probabilidade de ler 0 ou 1 com uma medida.

Você começa colocando um qubit em um estado de base, por exemplo, zero. A primeira etapa do gerador de número aleatório é usar uma operação hadamard para colocar o qubit em uma superposição igual. A medida desse estado resulta em um zero ou um com 50% de probabilidade de cada resultado, um bit verdadeiramente aleatório.

Não há como saber o que você obterá após a medida do qubit na superposição e o resultado é um valor diferente sempre que o código é invocado. Mas como você pode usar esse comportamento para gerar números aleatórios maiores?

Digamos que você repita o processo quatro vezes, gerando esta sequência de dígitos binários:

$${0, 1, 1, 0}$$

Se você concatenar ou combinar esses bits em uma cadeia de caracteres de bits, poderá formar um número maior. Neste exemplo, a sequência de bits ${0110}$ é equivalente a seis em decimal.

$${0110_{\ binary} \equiv 6_{\ decimal}}$$

Se você repetir esse processo muitas vezes, poderá combinar vários bits para formar qualquer número grande. Agora você pode fornecer ao seu superior esse número como uma senha segura, já que você pode ter certeza de que nenhum hacker espacial conseguirá determinar os resultados da sequência de medições.

Definir a lógica do gerador de números aleatórios

Vamos descrever qual deve ser a lógica de um gerador de números aleatórios, desde que tenhamos um gerador de bits aleatório:

  1. Defina max como o número máximo que você deseja gerar.
  2. Defina o número de bits aleatórios que você precisa gerar. Isso é feito calculando de quantos bits, nBits, precisamos para expressar inteiros até max.
  3. Gere uma cadeia de caracteres de bits aleatória com comprimento nBits.
  4. Se a cadeia de caracteres de bits representar um número maior que max, volte para a etapa três.
  5. Caso contrário, o processo está concluído. Retorna o número gerado como um inteiro.

Como exemplo, vamos definir max como 12. Ou seja, 12 é o maior número que você deseja usar como senha segura.

Você precisa de ${\lfloor ln(12) / ln(2) + 1 \rfloor}$ ou quatro bits para representar um número entre 0 e 12. (Para fins de brevidade, não entraremos em detalhes sobre como derivar essa equação.)

Digamos que você gere a cadeia de caracteres de bits ${1101_{\ binary}}$, que é equivalente a ${13_{\ decimal}}$. Como 13 é maior que 12, você repete o processo.

Em seguida, você gera a cadeia de caracteres de bits ${0110_{\ binary}}$, que é equivalente a ${6_{\ decimal}}$. Já que 6 é menor que 12, o processo é concluído.

O gerador de número aleatório quântico retornará o número 6 como sua senha. Na prática, defina um número maior como o máximo, pois números menores são fáceis de decifrar, basta tentar todas as senhas possíveis. Na verdade, para aumentar a dificuldade de adivinhar ou decifrar sua senha, você pode usar o código ASCII para converter o formato binário em texto e gerar uma senha usando números, símbolos e letras maiúsculas e minúsculas.

Gravar um gerador de bits aleatório

A primeira etapa é gravar uma Q# operação que gere um bit aleatório. Essa operação será um dos blocos de construção do gerador de número aleatório.

operation GenerateRandomBit() : Result {
    // Allocate a qubit.
    use q = Qubit();

    // Set the qubit into superposition of 0 and 1 using the Hadamard 
    H(q);

    // At this point the qubit `q` has 50% chance of being measured in the
    // |0〉 state and 50% chance of being measured in the |1〉 state.
    // Measure the qubit value using the `M` operation, and store the
    // measurement value in the `result` variable.
    let result = M(q);

    // Reset qubit to the |0〉 state.
    // Qubits must be in the |0〉 state by the time they are released.
    Reset(q);

    // Return the result of the measurement.
    return result;
}

Agora, observe o novo código.

  • Você define a operação GenerateRandomBit, que não usa nenhuma entrada e produz um valor do tipo Result. O tipo Result representa o resultado de uma medida e pode ter dois valores possíveis: Zero e One.
  • Você aloca um único qubit com o use palavra-chave. Quando é alocado, um qubit está sempre no estado Zero.
  • Use a H operação para colocar o qubit em uma superposição igual.
  • Use a M operação para medir o qubit, retornar o valor medido (Zero ou One).
  • Use a Reset operação para redefinir o qubit para o estado |0〉.

Ao colocar o qubit em sobreposição com a operação H e medi-lo com a operação M, o resultado será um valor diferente cada vez que o código for invocado.

Visualizar o Q# código com a esfera Bloch

Na esfera Bloch, o polo norte representa o valor clássico 0 e o polo sul representa o valor clássico 1. Qualquer sobreposição pode ser representada por um ponto na esfera (representada por uma seta). Quanto mais próxima a extremidade da seta estiver de um polo, maior a probabilidade de o qubit recolher o valor clássico atribuído a esse polo quando medido. Por exemplo, o estado do qubit representado pela seta na figura abaixo terá uma probabilidade mais alta de dar o valor 0 se medirmos isso.

Um diagrama mostrando um estado qubit com uma alta probabilidade de medir zero.

Podemos usar essa representação para visualizar o que o código está fazendo:

  1. Primeiro, começamos com um qubit inicializado no estado 0 e aplicamos uma operação H para criar uma sobreposição igual na qual as probabilidades de 0 e 1 são as mesmas.

    Um diagrama mostrando a preparação de um qubit na superposição aplicando o portão hadamard.
  2. Em seguida, medimos o qubit e salvamos a saída:

    Um diagrama mostrando a medida de um qubit e salvando a saída.

Como o resultado da medida é aleatório e as probabilidades de medir 0 e 1 são as mesmas, você obteve um bit completamente aleatório. Podemos chamar essa operação várias vezes para criar inteiros. Por exemplo, se chamar a operação três vezes para obter três bits aleatórios, você poderá criar números aleatórios de 3 bits (ou seja, um número aleatório entre 0 e 7).

Gravar um gerador de número aleatório completo

  1. Primeiro, você precisa adicionar os namespaces necessários Q# ao programa. Para o gerador de número aleatório completo, você precisa incluir três Q# namespaces: Microsoft.Quantum.Math, Microsoft.Quantum.Intrinsice Microsoft.Quantum.Convert.

    open Microsoft.Quantum.Convert;
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Math;
    
  2. Em seguida, você define a GenerateRandomNumberInRange operação. Essa operação chama repetidamente a operação GenerateRandomBit para criar uma cadeia de caracteres de bits.

        /// Generates a random number between 0 and `max`.
        operation GenerateRandomNumberInRange(max : Int) : Int {
            // Determine the number of bits needed to represent `max` and store it
            // in the `nBits` variable. Then generate `nBits` random bits which will
            // represent the generated random number.
            mutable bits = [];
            let nBits = BitSizeI(max);
            for idxBit in 1..nBits {
                set bits += [GenerateRandomBit()];
            }
            let sample = ResultArrayAsInt(bits);
    
            // Return random number if it is within the requested range.
            // Generate it again if it is outside the range.
            return sample > max ? GenerateRandomNumberInRange(max) | sample;
        }
    
    

    Vamos dedicar um momento para analisar o novo código.

    • Você precisa calcular o número de bits necessários para expressar inteiros até max. A BitSizeI função do Microsoft.Quantum.Math namespace converte um inteiro no número de bits necessários para representá-lo.
    • A operação SampleRandomNumberInRange usa um loop for para gerar números aleatórios até que ele gere um igual a ou menor que max. O for loop funciona exatamente da mesma forma que um for loop em outras linguagens de programação.
    • A variável bits é uma variável mutável. Uma variável mutável é aquela que pode ser alterada durante a computação. Você usa a diretiva set para alterar o valor de uma variável mutável.
    • A ResultArrayAsInt função vem do Microsoft.Quantum.Convert namespace. Essa função converte a cadeia de caracteres de bits em um inteiro positivo.
  3. Por fim, você adiciona um ponto de entrada. Neste exemplo, a Main operação é o ponto de entrada do programa. Ele chama a GenerateRandomNumberInRange operação para gerar um número aleatório entre 0 e 100.

        @EntryPoint()
        operation Main() : Int {
            let max = 100;
            Message($"Sampling a random number between 0 and {max}: ");
    
            // Generate random number in the 0..max range.
            return GenerateRandomNumberInRange(max);
        }
    

    A diretiva let declara variáveis que não são alteradas durante a computação. Aqui, definimos o valor máximo como 100.

  4. O código completo para o gerador de número aleatório é o seguinte:

namespace QuantumRandomNumberGenerator {
    open Microsoft.Quantum.Convert;
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Math;

    @EntryPoint()
    operation Main() : Int {
        let max = 100;
        Message($"Sampling a random number between 0 and {max}: ");

        // Generate random number in the 0..max range.
        return GenerateRandomNumberInRange(max);
    }

    /// Generates a random number between 0 and `max`.
    operation GenerateRandomNumberInRange(max : Int) : Int {
        // Determine the number of bits needed to represent `max` and store it
        // in the `nBits` variable. Then generate `nBits` random bits which will
        // represent the generated random number.
        mutable bits = [];
        let nBits = BitSizeI(max);
        for idxBit in 1..nBits {
            set bits += [GenerateRandomBit()];
        }
        let sample = ResultArrayAsInt(bits);

        // Return random number if it is within the requested range.
        // Generate it again if it is outside the range.
        return sample > max ? GenerateRandomNumberInRange(max) | sample;
    }

    operation GenerateRandomBit() : Result {
        // Allocate a qubit.
        use q = Qubit();

        // Set the qubit into superposition of 0 and 1 using the Hadamard 
        H(q);

        // At this point the qubit `q` has 50% chance of being measured in the
        // |0〉 state and 50% chance of being measured in the |1〉 state.
        // Measure the qubit value using the `M` operation, and store the
        // measurement value in the `result` variable.
        let result = M(q);

        // Reset qubit to the |0〉 state.
        // Qubits must be in the |0〉 state by the time they are released.
        Reset(q);

        // Return the result of the measurement.
        return result;
    }
}

Executar o programa gerador de números aleatórios

Você pode executar o programa no Copilot no Azure Quantum e em Visual Studio Code como um aplicativo autônomo Q# ou usando um programa host python.

Você pode testar seu Q# código com o Copilot no Azure Quantum gratuitamente – tudo o que você precisa é de uma conta de email da Microsoft (MSA). Para obter mais informações sobre o Copilot no Azure Quantum, consulte Explorar o Azure Quantum.

  1. Abra o Copilot no Azure Quantum em seu navegador.

  2. Copie e cole o código a seguir no editor de código.

    namespace Tutorial {
        open Microsoft.Quantum.Convert;
        open Microsoft.Quantum.Intrinsic;
        open Microsoft.Quantum.Math;
    
        @EntryPoint()
        operation Main() : Int {
            let max = 100;
            Message($"Sampling a random number between 0 and {max}: ");
    
            // Generate random number in the 0..max range.
            return GenerateRandomNumberInRange(max);
        }
    
        /// # Summary
        /// Generates a random number between 0 and `max`.
        operation GenerateRandomNumberInRange(max : Int) : Int {
            // Determine the number of bits needed to represent `max` and store it
            // in the `nBits` variable. Then generate `nBits` random bits which will
            // represent the generated random number.
            mutable bits = [];
            let nBits = BitSizeI(max);
            for idxBit in 1..nBits {
                set bits += [GenerateRandomBit()];
            }
            let sample = ResultArrayAsInt(bits);
    
            // Return random number if it is within the requested range.
            // Generate it again if it is outside the range.
            return sample > max ? GenerateRandomNumberInRange(max) | sample;
        }
    
        /// # Summary
        /// Generates a random bit.
        operation GenerateRandomBit() : Result {
            // Allocate a qubit.
            use q = Qubit();
    
            // Set the qubit into superposition of 0 and 1 using the Hadamard 
            // operation `H`.
            H(q);
    
            // At this point the qubit `q` has 50% chance of being measured in the
            // |0〉 state and 50% chance of being measured in the |1〉 state.
            // Measure the qubit value using the `M` operation, and store the
            // measurement value in the `result` variable.
            let result = M(q);
    
            // Reset qubit to the |0〉 state.
            // Qubits must be in the |0〉 state by the time they are released.
            Reset(q);
    
            // Return the result of the measurement.
            return result;
    
            // Note that Qubit `q` is automatically released at the end of the block.
        }
    }
    
  3. Selecione o número de capturas a serem executadas e clique em Executar.

  4. Os resultados são exibidos no histograma e nos campos Resultados .

  5. Clique em Explicar código para solicitar que o Copilot explique o código para você.

Dica

No Copilot no Azure Quantum, você pode abrir seu programa no VS Code para a Web clicando no botão do logotipo do VS Code no canto direito do editor de código.

Observação

Esse snippet de código não é executado atualmente em nenhum hardware targetsdo Azure Quantum disponível, pois o callable ResultArrayAsInt requer uma QPU com perfil de computação completo.

Próximas etapas

Explore outros tutoriais Q#:

  • O emaranhamento quântico mostra como escrever um Q# programa que manipula e mede qubits e demonstra os efeitos da superposição e do emaranhamento.
  • O algoritmo de pesquisa de Grover mostra como escrever um Q# programa que usa o algoritmo de pesquisa de Grover.
  • O Quantum Fourier Transforms explora como escrever um Q# programa que aborda diretamente qubits específicos.
  • As Katas Quânticas são tutoriais e exercícios de programação com o objetivo de ensinar os elementos da computação quântica e Q# da programação ao mesmo tempo.