Esercizio Parte 2: Creare un generatore quantistico di numeri casuali

Completato

In questa unità si implementa la seconda parte del generatore di numeri casuali quantistici. Si combinano più bit casuali per formare un numero casuale più grande. Questa parte si basa sul generatore di bit casuale già creato nell'unità precedente.

Combinare più bit casuali per formare un numero maggiore

Nell'unità precedente è stato creato un generatore di bit casuali che inserisce un qubit in uno stato di sovrapposizione e quindi misura tale qubit per generare un valore di bit casuale pari a 0 o 1, ognuno con 50% probabilità. Il valore di questo bit è veramente casuale, non c'è modo di sapere in anticipo quale sarà il risultato della misurazione. Ma come è possibile usare questo comportamento per generare numeri casuali più grandi?

Se si ripete il processo quattro volte, è possibile generare questa sequenza di cifre binarie:

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

Se si combinano questi bit in una stringa di bit, è possibile formare un numero maggiore. In questo esempio la sequenza di bit ${0110}$ in binary equivale al numero 6 in decimale.

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

Per generare un numero casuale arbitrariamente elevato, ripetere questo processo più volte. Combinare quindi tutti i bit in un numero binario e convertire il numero binario in un numero decimale.

Definire la logica del generatore di numeri casuali

Prima di scrivere il codice Q#, verrà delineata la logica per generare un numero casuale:

  1. Definire max come numero decimale massimo da generare.
  2. Determinare il numero di bit casuali, nBits, necessari per generare max.
  3. Generare una stringa di bit casuale con lunghezza pari a nBits.
  4. Se la stringa di bit rappresenta un numero maggiore di max, tornare al passaggio precedente.
  5. In caso contrario, il processo è completato. Restituisce il numero generato come numero decimale.

Ad esempio, definiamo max come 12. Ovvero, 12 è il numero più grande che il generatore di numeri casuali deve restituire.

Usare l'equazione seguente per determinare il numero di bit necessari per rappresentare il numero 12 in binario:

$${\lfloor ln(12) / ln(2) + 1 \rfloor}$$

In base a questa equazione, sono necessari 4 bit per rappresentare un numero compreso tra 0 e 12.

Si supponga, ad esempio, di generare un bit casuale quattro volte e ottenere la stringa di bit ${1101_{\ binary}}$. Questo valore in binary è uguale a 13 in decimale. Poiché 13 è maggiore di 12, si ripete il processo.

Successivamente, si genera la stringa di bit ${0110_{\ binary}}$, che è uguale a ${6_{\ decimal}}$. Poiché 6 è minore di 12, il processo è completato.

Il generatore di numeri casuali quantistici restituisce il numero 6.

Creare un generatore di numeri casuali completo in Q#

In questo caso si espande il Main.qs file della lezione precedente per compilare il generatore di numeri casuali.

Importare le librerie necessarie

Prima di tutto, importare i namespace dalla libreria standard di Q# che contengono le funzioni e le operazioni necessarie per scrivere il programma. Il compilatore Q# carica automaticamente molte funzioni e operazioni comuni. Tuttavia, per il generatore di numeri casuali quantistici sono necessarie alcune funzioni e operazioni aggiuntive da due spazi dei nomi Q#: Microsoft.Quantum.Math e Microsoft.Quantum.Convert.

Copiare e incollare le direttive seguenti import all'inizio del Main.qs file:

import Std.Convert.*;
import Std.Math.*;

Nota

È possibile usare Std anziché Microsoft.Quantum per importare funzioni e operazioni dalla libreria standard.

Rinominare l'operazione Main in GenerateRandomBit

Il programma generatore di numeri casuali usa l'operazione Main scritta nell'unità precedente per generare un bit casuale. Rinominare l'operazione Main in GenerateRandomBit in modo che questa operazione abbia un nome più descrittivo e non sia il punto di ingresso del programma.

Copiare e incollare il codice seguente in Main.qs:

import Std.Convert.*;
import Std.Math.*;

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

Definire l'operazione generatore di numeri casuali

Creare una nuova operazione denominata GenerateRandomNumberInRange. Questa operazione chiama ripetutamente l'operazione GenerateRandomBit per creare una stringa di bit.

Copiare il codice seguente e inserirlo direttamente prima dell'operazione GenerateRandomBit nel Main.qs file:

/// 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's within the requested range.
    // Generate it again if it's outside the range.
    return sample > max ? GenerateRandomNumberInRange(max) | sample;
}

Ecco una panoramica del codice in GenerateRandomNumberInRange:

  • Chiamare la funzione BitSizeI dalla libreria Std.Math per calcolare il numero di bit necessari a rappresentare l'intero archiviato in max.
  • Usare un for ciclo per generare un numero di bit casuali uguali a nBits. Chiamare l'operazione GenerateRandomBit per generare i bit casuali.
  • All'interno del for ciclo usare l'istruzione set per aggiornare la bits variabile con ogni nuovo bit casuale. La variabile è una variabile bits modificabile, il che significa che il valore di bits può cambiare durante il calcolo.
  • Chiamare la funzione ResultArrayAsInt dalla libreria Std.Convert per convertire l'array di bit in bits in un numero intero positivo memorizzato in sample.
  • Nell'istruzione return verificare se sample è maggiore di max. Se sample è maggiore di max, chiamare GenerateRandomNumberInRange di nuovo e ricominciare. In caso contrario, restituire il numero casuale archiviato in sample.

Aggiungere un punto di ingresso

Aggiungere infine un'operazione del punto di ingresso al codice in modo che il compilatore possa eseguire il programma. Per impostazione predefinita, il compilatore Q# cerca un'operazione Main e usa Main come punto di ingresso, indipendentemente da dove Main si trova nel file. In questo caso, l'operazione Main imposta un valore per max e chiama l'operazione GenerateRandomNumberInRange per generare un numero casuale compreso tra 0 e max.

Ad esempio, per generare un numero casuale compreso tra 0 e 100, copiare il codice seguente nel Main.qs file:

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

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

Programma finale

Ecco il codice Q# completo per il programma in Main.qs:

import Std.Convert.*;
import Std.Math.*;

operation Main() : Int {
    let max = 100;
    Message($"Generating 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's within the requested range.
    // Generate it again if it's 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;
}

Eseguire il programma

Provare il nuovo generatore di numeri casuali quantistici.

Per eseguire il programma, scegli la lente del codice Esegui dall'elenco dei comandi sopra l'operazione Main. In alternativa, premere CTRL+ F5. L'output viene visualizzato nella console di debug. Eseguire il programma più volte e notare come cambia il risultato.

Congratulazioni. È stato creato un generatore di numeri quantistici veramente casuali in Q#.

Esercizio bonus

Provare a modificare il programma in modo che richieda anche che il numero casuale generato sia maggiore di un numero minimo positivo, minanziché zero.