Compartilhar via


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

Neste tutorial, você aprende a escrever um programa quântico básico que Q# aproveita a natureza da mecânica quântica para produzir um número aleatório.

Neste tutorial, você irá:

  • Crie um Q# programa.
  • Revise os principais componentes 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, confira Código com o Azure Quantum, um recurso exclusivo do site do Azure Quantum. Aqui, você pode executar exemplos integrados Q# ou seus próprios Q# programas, gerar novo Q# código a partir 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

  • Para executar o exemplo de código no Copilot no Azure Quantum:

    • Uma conta de email da Microsoft (MSA).
  • Para desenvolver e executar o exemplo de código no Visual Studio Code:

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ânticos.

Um qubit é uma unidade de informação quântica que pode 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úmeros aleatórios é usar uma operação de Hadamard para colocar o qubit em uma superposição igual. A medição 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 medição do qubit em superposição, e o resultado é um valor diferente cada vez 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. Usando esse método, você pode criar um número para usar como senha segura, pois pode ter certeza de que nenhum hacker poderá 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:

  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 quantos bits, nBits, você precisa para expressar números 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.

Você precisa de ${\lfloor ln(12) / ln(2) + 1 \rfloor}$ ou quatro bits para representar um número entre 0 e 12. Podemos usar a função BitSizeIembutida , que pega qualquer número inteiro e retorna o número de bits necessários para representá-lo.

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úmeros aleatórios quânticos 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.

Escreva um gerador de bits aleatórios

O primeiro passo é escrever uma Q# operação que gere um bit aleatório. Esta operação será um dos blocos de construção do gerador de números aleatórios.

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 a use palavra-chave. Quando ele é alocado, um qubit está sempre no estado |0〉.
  • Use a H operação para colocar o qubit em uma superposição igual.
  • Use a M operação para medir o qubit, retorne 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.

Visualize o Q# código com a esfera de 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 de qubit com alta probabilidade de medir zero.

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

  1. Primeiro, comece com um qubit inicializado no estado |0〉 e aplique uma H operação para criar uma superposição igual na qual as probabilidades para 0 e 1 são as mesmas.

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

    Um diagrama mostrando a medição 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).

Escreva um gerador completo de números aleatórios

  1. Primeiro, você precisa importar os namespaces necessários da Q# biblioteca padrão para o programa. O Q# compilador carrega muitas funções e operações comuns automaticamente, no entanto, para o gerador de números aleatórios completo, você precisa de algumas funções e operações adicionais de dois Q# namespaces: Microsoft.Quantum.Mathe Microsoft.Quantum.Convert.

    import Microsoft.Quantum.Convert.*;
    import 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 {
            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 namespace converte Microsoft.Quantum.Math 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 loop for funciona exatamente como um loop for 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, do namespace padrão Microsoft.Quantum.Convert , converte a cadeia de bits em um inteiro positivo.
  3. Por fim, você adiciona um ponto de entrada ao programa. Por padrão, o Q# compilador procura uma Main operação e inicia o processamento lá. Ele chama a GenerateRandomNumberInRange operação para gerar um número aleatório entre 0 e 100.

    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 você define o valor máximo como 100.

    Para obter mais informações sobre a Main operação, consulte Pontos de entrada.

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

import Microsoft.Quantum.Convert.*;
import Microsoft.Quantum.Math.*;

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 {
        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 a Hadamard operation
    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 no Visual Studio Code como um aplicativo autônomo Q# ou usando um programa host do 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.

    import Microsoft.Quantum.Convert.*;
    import Microsoft.Quantum.Math.*;
    
    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 {
            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 disparos a serem executados e selecione Executar.

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

  5. Selecione 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 selecionando o botão do logotipo do VS Code no canto direito do editor de código.

Observação

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

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.
  • As Transformadas Quânticas de Fourier exploram como escrever um Q# programa que aborda diretamente qubits específicos.
  • Os Katas Quânticos são tutoriais individualizados e exercícios de programação destinados a ensinar os elementos da computação quântica e Q# da programação ao mesmo tempo.