Tutorial: Implementación de un generador cuántico de números aleatorios en Q#

Nota

Microsoft Quantum Development Kit (QDK clásico) ya no se admitirá después del 30 de junio de 2024. Si es un desarrollador de QDK existente, se recomienda realizar la transición al nuevo Azure Quantum Development Kit (QDK moderno) para seguir desarrollando soluciones cuánticas. Para obtener más información, consulte Migración Q# del código al QDK moderno.

Aprenda a escribir un programa cuántico básico en Q# que aproveche la naturaleza de la mecánica cuántica para generar un número aleatorio.

En este tutorial:

  • Create un Q# programa.
  • Revise los componentes principales de un Q# programa.
  • Defina la lógica de un problema.
  • Combine operaciones clásicas y cuánticas para resolver un problema.
  • Trabajar con bits cuánticos y con la superposición para crear un generador de números aleatorios cuántico

Sugerencia

Si desea acelerar el recorrido de la computación cuántica, consulte Código con Azure Quantum, una característica única del sitio web de Azure Quantum. Aquí puede ejecutar ejemplos integrados Q# o sus propios Q# programas, generar código nuevo Q# a partir de las indicaciones, abrir y ejecutar el código en VS Code para la Web con un solo clic y hacer preguntas a Copilot sobre la computación cuántica.

Requisitos previos

Definir el problema

Los equipos clásicos no generan números aleatorios, sino números seudoaleatorios. Un generador de números seudoaleatorios genera una secuencia determinista de números a partir de un valor inicial cualquiera, que es el valor de inicialización. Para aproximarse de mejor forma a los valores aleatorios, este valor de inicialización suele ser la hora actual del reloj de la CPU.

Por otro lado, los equipos cuánticos pueden generar números realmente aleatorios. Esto se debe a que la medición de un cúbit en superposición es un proceso probabilístico. El resultado de la medida es aleatorio y no hay ninguna manera de predecir el resultado. Este es el principio básico de los generadores de números aleatorios cuánticos.

Un cúbit es una unidad de información cuántica que puede estar en superposición. Cuando se mide, un cúbit solo puede estar en el estado 0 o en el estado 1. Sin embargo, antes de la medida, el estado del cúbit representa la probabilidad de leer un 0 o un 1 al tomar una medida.

Para comenzar, tome un cúbit en estado base, por ejemplo, cero. El primer paso del generador de números aleatorios es usar una operación Hadamard para colocar el cúbit en una superposición igual. La medida de este estado da como resultado un cero o uno con una probabilidad del 50 % de cada resultado, un bit realmente aleatorio.

No hay forma de saber lo que obtendrá después de la medición del cúbit en superposición y el resultado es un valor diferente cada vez que se invoca el código. ¿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. Ahora puede proporcionar su superior con ese número como contraseña segura, ya que puede estar seguro de que ningún hacker espacial podría determinar los resultados de la secuencia de medidas.

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

Vamos a describir la lógica de un generador de números aleatorios, siempre que tengamos un generador de bits aleatorio:

  1. Definir max como el número máximo que quiere generar.
  2. Definir el número de bits aleatorios que necesita generar. Esto se realiza calculando cuántos bits (nBits) necesitamos expresar en enteros hasta llegar a 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 usar como contraseña segura.

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, omitiremos 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ánticos devolverá el número 6 como contraseña. En la práctica, establezca un número más grande como máximo, ya que los números inferiores son fáciles de descifrar simplemente probando todas las contraseñas posibles. De hecho, para aumentar la dificultad de adivinar o descifrar la contraseña, puede usar código ASCII para convertir binario en texto y generar una contraseña mediante números, símbolos y letras en mayúsculas y minúsculas mezcladas.

Escribir un generador de bits aleatorio

El primer paso es escribir una Q# operación que genera un bit aleatorio. Esta operación será uno de los bloques de creación del generador de números aleatorios.

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

Ahora, eche un vistazo al nuevo código.

  • Defina la operación GenerateRandomBit, que no toma ninguna entrada y genera un valor de tipo Result. El tipo Result representa el resultado de una medición y puede tener dos valores posibles: Zero o One.
  • Asigne un solo cúbit con la use palabra clave . Cuando se asigna un cúbit, siempre está en el estado Zero.
  • La operación se usa H para colocar el cúbit en una superposición igual.
  • La operación se usa M para medir el cúbit, devolver el valor medido (Zero o One).
  • La operación se usa Reset para restablecer el cúbit al estado |0〉.

Al colocar el cúbit en superposición con la operación H y medirlo con la operación M, el resultado será un valor diferente cada vez que se invoque el código.

Visualización del Q# código con la esfera bloch

En la esfera Bloch, el polo norte representa el valor clásico 0 y el polo sur representa el valor clásico 1. Cualquier superposición se puede representar mediante un punto en la esfera (representado con una flecha). Cuanto más cerca esté el extremo de la flecha a un polo, mayor será la probabilidad de que el cúbit caiga en el valor clásico asignado a ese polo cuando se mida. Por ejemplo, el estado del cúbit representado por la flecha en la ilustración siguiente tiene más probabilidades de dar el valor 0 si lo medimos.

Diagrama que muestra un estado de cúbit con una probabilidad alta de medir cero.

Puede usar esta representación para visualizar lo que está haciendo el código:

  1. En primer lugar, empiece con un cúbit inicializado en el estado 0 y aplique una operación H para crear una superposición igual en la que las probabilidades de 0 y 1 sean las mismas.

    Diagrama que muestra la preparación de un cúbit en superposición aplicando la puerta hadamard.
  2. A continuación, se mide el cúbit y se guarda el resultado:

    Diagrama que muestra la medición de un cúbit y el guardado de la salida.

Como el resultado de la medición es aleatorio y las probabilidades de medir 0 y 1 son las mismas, ha obtenido un bit completamente aleatorio. Puede llamar a esta operación varias veces para crear enteros. Por ejemplo, si llama a la operación tres veces para obtener tres bits aleatorios, puede crear números de 3 bits aleatorios (es decir, un número aleatorio entre 0 y 7).

Escribir un generador de números aleatorios completo

  1. En primer lugar, debe agregar los espacios de nombres necesarios Q# al programa. Para el generador de números aleatorios completo, debe incluir tres Q# espacios de nombres: Microsoft.Quantum.Math, Microsoft.Quantum.Intrinsicy Microsoft.Quantum.Convert.

    open Microsoft.Quantum.Convert;
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Math;
    
  2. A continuación, defina la GenerateRandomNumberInRange operación. Esta operación llama repetidamente a la operación GenerateRandomBit para generar una cadena 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;
        }
    
    

    Paremos un momento a revisar el nuevo código.

    • Debe calcular el número de bits necesarios para expresar enteros hasta max. La BitSizeI función del Microsoft.Quantum.Math espacio de nombres convierte un entero en el número de bits necesarios para representarlo.
    • La operación SampleRandomNumberInRange usa un bucle for para generar números aleatorios hasta llegar a uno que sea igual o menor que max. El for bucle funciona exactamente igual que un for bucle en otros lenguajes de programación.
    • La variable es una variable bits 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 ResultArrayAsInt función procede del Microsoft.Quantum.Convert espacio de nombres . Esta función convierte la cadena de bits en un entero positivo.
  3. Por último, agregue un punto de entrada. En este ejemplo, la Main operación es el punto de entrada del programa. Llama a la GenerateRandomNumberInRange operación para generar un número aleatorio entre 0 y 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);
        }
    

    La directiva let declara variables que no cambian durante el cálculo. Aquí definimos el valor máximo como 100.

  4. El código completo del generador de números aleatorios es el siguiente:

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

Ejecución del programa generador de números aleatorios

Puede ejecutar el programa en Copilot en Azure Quantum y en Visual Studio Code como una aplicación independiente Q# o mediante un programa host de Python.

Puede probar el Q# código con Copilot en Azure Quantum de forma gratuita: todo lo que necesita es una cuenta de correo electrónico de Microsoft (MSA). Para más información sobre Copilot en Azure Quantum, consulte Exploración de Azure Quantum.

  1. Abra Copilot en Azure Quantum en el explorador.

  2. Copie y pegue el código siguiente en el 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. Seleccione el número de tomas que se van a ejecutar y haga clic en Ejecutar.

  4. Los resultados se muestran en el histograma y en los campos Resultados .

  5. Haga clic en Explicar código para preguntar a Copilot para explicarle el código.

Sugerencia

Desde Copilot en Azure Quantum, puede abrir el programa en VS Code para la Web haciendo clic en el botón del logotipo de VS Code en la esquina derecha del editor de código.

Nota

Este fragmento de código no se ejecuta actualmente en ningún hardware targetsde Azure Quantum disponible, ya que el invocable ResultArrayAsInt requiere una QPU con un perfil de cálculo completo.

Pasos siguientes

Explore otros tutoriales de Q#:

  • El entrelazamiento cuántico muestra cómo escribir un Q# programa que manipula y mide cúbits y muestra los efectos de la superposición y el entrelazamiento.
  • El algoritmo de búsqueda de Grover muestra cómo escribir un Q# programa que usa el algoritmo de búsqueda de Grover.
  • Quantum Fourier Transforms explora cómo escribir un Q# programa que direccione directamente cúbits específicos.
  • Quantum Katas son tutoriales autodirigido y ejercicios de programación destinados a enseñar los elementos de la computación cuántica y Q# la programación al mismo tiempo.