Ejercicio parte 2: Creación de un generador de números aleatorios cuántico

Completado

En esta unidad, pondremos en marcha la segunda fase de nuestro generador de números aleatorios cuántico: combinar varios bits aleatorios para generar un número aleatorio más grande. Esta fase se basa en el generador de bits aleatorios que ya se creó en la unidad anterior.

Combinación de varios bits aleatorios para formar un número mayor

En la unidad anterior, creaste un generador de bits aleatorio que genera un bit aleatorio colocando un cúbit en la superposición y midiéndolo.

Al medir el cúbit, obtendrás un bit aleatorio, ya sea 0 o 1, con una probabilidad igual del 50 %. El valor de este bit es genuinamente aleatorio; no hay forma alguna de saber lo que obtendrás después de la medida. ¿Pero cómo puede usar este comportamiento para generar números aleatorios mayores?

Supongamos que repetimos el proceso cuatro veces, generando así esta secuencia de dígitos binarios:

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

Si concatenamos, o combinamos, estos bits en una cadena de bits, podremos formar un número más grande. En este ejemplo, la secuencia de bits ${0110}$ equivale a seis decimal.

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

Si repite este proceso muchas veces, puede combinar varios bits para formar un número grande.

Definición de la lógica del generador de números aleatorios

Vamos a describir cuál debe ser la lógica de un generador de números aleatorios, siempre y cuando se proporcione el generador de bits aleatorio integrado en la unidad anterior:

  1. Definir max como el número máximo que quiere generar.
  2. Defina el número de bits aleatorios que necesita generar calculando cuántos bits, nBits, necesita para expresar enteros hasta max.
  3. Generar una cadena de bits aleatorios que tenga una longitud de nBits.
  4. Si la cadena de bits representa un número mayor que max, hay que volver al paso tres.
  5. Si no, el proceso habrá finalizado. Devolver el número generado como un entero.

Como ejemplo, vamos a establecer max en 12. Es decir, 12 es el número más grande que desea obtener del generador de números aleatorios.

Necesita ${\lfloor ln(12) / ln(2) + 1 \rfloor}$ o 4 bits que representen un número entre cero y 12. (En aras de una mayor brevedad, se omite el paso para derivar esta ecuación).

Supongamos que generamos la cadena de bits ${1101_{\ binario}}$, que equivale a ${13_{\ decimal}}$. Como 13 es mayor que 12, repetimos el proceso.

Ahora, generamos la cadena de bits ${0110_{\ binario}}$, que equivale a ${6_{\ decimal}}$. Dado que 6 es menor que 12, el proceso se ha completado.

El generador de números aleatorios cuántico devuelve el número 6.

Creación de un generador completo de números aleatorios

Aquí ampliaremos el archivo Main.qs para generar números aleatorios más grandes.

Importación de las bibliotecas necesarias

En primer lugar, es necesario importar los espacios de nombres requeridos de la biblioteca Estándar de Q# al programa. El compilador de Q# carga automáticamente muchas funciones y operaciones comunes, pero para el generador de números aleatorios cuánticos completo, se necesitan algunas funciones y operaciones adicionales de dos espacios de nombres de Q#: Microsoft.Quantum.Math y Microsoft.Quantum.Convert.

Copie y pegue las siguientes directivas de import en la parte superior del archivo Main.qs:

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

Cambie el nombre de la operación Main a GenerateRandomBit

Para el generador de números aleatorios completo, se volverá a usar la operación definida en la unidad anterior. Sin embargo, el nombre de la operación Main es el punto de entrada del programa y debe ser único. Para evitar confusiones, cambie el nombre de la operación de Main a GenerateRandomBit.

La operación GenerateRandomBit debería tener este aspecto:

    operation GenerateRandomBit() : Result {
        // Allocate a qubit.
        use q = Qubit();
    
        // Set the qubit into superposition of 0 and 1 using the Hadamard 
        H(q);
    
        // Measure the qubit and store the result.
    
        let result = M(q);
    
        // Reset qubit to the |0〉 state.
        Reset(q);
    
        // Return the result of the measurement.
        return result;
    }

Definición de la operación de número aleatorio cuántico

Aquí definiremos la operación de GenerateRandomNumberInRange. Esta operación llama repetidamente a la operación GenerateRandomBit para generar una cadena de bits.

Copie el código siguiente y péguelo antes de la operación GenerateRandomBit en el archivo Main.qs:

    /// 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;
    }

Paremos un momento a revisar el nuevo código.

  • Debe calcular el número de bits necesarios para expresar enteros hasta max. La función BitSizeI de la biblioteca Microsoft.Quantum.Math convierte un entero en el número de bits necesarios para representarlo.
  • La operación GenerateRandomNumberInRange usa un bucle for para generar números aleatorios hasta llegar a uno que sea igual o menor que max. El bucle for funciona exactamente igual que un bucle for en otros lenguajes de programación.
  • La variable bits es una variable mutable. Una variable mutable es aquella que puede cambiar durante el cálculo. Para cambiar el valor de una variable mutable, usamos la directiva set.
  • La función ResultArrayAsInt procede de la biblioteca Microsoft.Quantum.Convert. Esta función convierte la cadena de bits en un entero positivo.

Adición de un punto de entrada

Por último, agregue un punto de entrada al programa. De forma predeterminada, el compilador de Q# buscará una operación de Main e iniciará el procesamiento allí, independientemente de dónde se encuentre. La operación Main llama a la operación GenerateRandomNumberInRange para generar un número aleatorio entre 0 y un número max. En este ejemplo, se define el valor máximo como 100.

Copie y pegue el siguiente código en el archivo Main.qs:

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

Programa final

El archivo Main.qs debería tener este aspecto:

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 {
            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 operation
        H(q);
    
        // 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.
        Reset(q);
    
        // Return the result of the measurement.
        return result;
    }

Ejecución del programa

¡Vamos a probar nuestro nuevo generador de números aleatorios!

  1. Antes de ejecutar el programa, debe establecer el perfil de destino en Sin restricciones. Seleccione Vista>Paleta de comandos, busque QIR y seleccione Q#: establecimiento del perfil de destino de QIR de Azure Quantum y, después, seleccione Q#: sin restricciones.
  2. Para ejecutar el programa, seleccione Ejecutar en la lista de comandos que hay sobre la operación Main, o bien presione Ctrl+F5. La salida aparecerá en la consola de depuración.
  3. Vuelva a ejecutar el programa para ver un resultado diferente.

Nota:

Si el perfil de destino no está establecido en Sin restricciones, obtendrá un error al ejecutar el programa.

Felicidades. Ya sabe cómo combinar la lógica clásica con Q# para crear un generador de números aleatorios cuántico.

Ejercicio extra

Intenta modificar el programa para que sea necesario también que el número aleatorio generado sea mayor que un número mínimo, min, en lugar de cero.