Samouczek: implementowanie kwantowego generatora liczb losowych w programie Q#

Uwaga

Zestaw Microsoft Quantum Development Kit (klasyczny zestaw QDK) nie będzie już obsługiwany po 30 czerwca 2024 r. Jeśli jesteś istniejącym deweloperem zestawu QDK, zalecamy przejście na nową platformę Azure Quantum Development Kit (modern QDK), aby kontynuować opracowywanie rozwiązań kwantowych. Aby uzyskać więcej informacji, zobacz Migrowanie Q# kodu do nowoczesnego zestawu QDK.

Dowiedz się, jak napisać podstawowy program kwantowy, który Q# wykorzystuje charakter mechaniki kwantowej do tworzenia liczby losowej.

W tym samouczku wykonasz następujące czynności:

  • Q# Twórca program.
  • Przejrzyj główne składniki Q# programu.
  • Zdefiniuj logikę problemu.
  • Łączenie klasycznych i kwantowych operacji w celu rozwiązania problemu.
  • Praca z kubitami i superpozycją w celu utworzenia kwantowego generatora liczb losowych.

Porada

Jeśli chcesz przyspieszyć swoją podróż do obliczeń kwantowych, zapoznaj się z kodem w usłudze Azure Quantum, unikatową funkcją witryny internetowej Azure Quantum. W tym miejscu możesz uruchamiać wbudowane Q# przykłady lub własne Q# programy, generować nowy Q# kod na podstawie monitów, otwierać i uruchamiać kod w programie VS Code dla sieci Web jednym kliknięciem i zadawać Copilot wszelkie pytania dotyczące obliczeń kwantowych.

Wymagania wstępne

Definiowanie problemu

Komputery klasyczne nie generują liczb losowych, ale raczej pseudolosowych . Generator liczb pseudorandom generuje deterministyczną sekwencję liczb na podstawie pewnej wartości początkowej, zwanej inicjatorem. Aby lepiej przybliżyć wartości losowe, ten inicjator często jest bieżącą godziną z zegara procesora CPU.

Z drugiej strony komputery kwantowe mogą generować prawdziwie losowe liczby. Wynika to z tego, że pomiar kubitu w superpozycji jest procesem probabilistycznym. Wynik pomiaru jest losowy i nie ma możliwości przewidywania wyniku. Jest to podstawowa zasada kwantowych generatorów liczb losowych.

Kubit to jednostka informacji kwantowych, które mogą znajdować się w superpozycji. W przypadku pomiaru kubit może być tylko w stanie 0 lub w stanie 1 . Jednak przed pomiarem stan kubitu reprezentuje prawdopodobieństwo odczytania wartości 0 lub 1 z pomiarem.

Zacznij od utworzenia kubitu w stanie podstawy, na przykład zero. Pierwszym krokiem generatora liczb losowych jest użycie operacji Hadamard, aby umieścić kubit w równej superpozycji. Pomiar tego stanu powoduje zero lub jeden z 50% prawdopodobieństwem każdego wyniku, prawdziwie losowy bit.

Nie ma możliwości poznania tego, co otrzymasz po pomiarze kubitu w superpozycji, a wynik jest inną wartością za każdym razem, gdy kod jest wywoływany. Ale jak można użyć tego zachowania, aby wygenerować większe liczby losowe?

Załóżmy, że powtarzasz ten proces cztery razy, generując następującą sekwencję cyfr binarnych:

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

Jeśli połączysz te bity w ciąg bitów, możesz utworzyć większą liczbę. W tym przykładzie sekwencja bitowa ${0110}$ jest równa liczbie sześć w postaci dziesiętnej.

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

W przypadku wielokrotnego powtarzania tego procesu można połączyć wiele bitów, aby utworzyć dowolną dużą liczbę. Teraz możesz zapewnić lepszą liczbę jako bezpieczne hasło, ponieważ możesz mieć pewność, że żaden haker nie może określić wyników sekwencji pomiarów.

Definiowanie logiki generatora liczb losowych

Przedstawimy logikę generatora liczb losowych, pod warunkiem że mamy generator bitów losowych:

  1. Zdefiniuj max jako maksymalną liczbę, którą chcesz wygenerować.
  2. Zdefiniuj liczbę bitów losowych, które należy wygenerować. W tym celu należy obliczyć liczbę bitów, nBits, potrzebną do przedstawienia liczby całkowitej o wartości wynoszącej maksymalnie max.
  3. Wygeneruj losowy ciąg bitowy o długości nBits.
  4. Jeśli ciąg bitowy reprezentuje liczbę większą niż max, wróć do kroku trzeciego.
  5. W przeciwnym razie proces zostanie zakończony. Zwróć wygenerowaną liczbę jako liczbę całkowitą.

Załóżmy na przykład, że wartość max wynosi 12. Oznacza to, że 12 jest największą liczbą, której chcesz użyć jako bezpiecznego hasła.

Potrzebujesz ${\lfloor ln(12) / ln(2) + 1 \rfloor}$, lub 4 bity do reprezentowania liczby z zakresu od 0 do 12. (Pominiemy sposób tworzenia tego równania na potrzeby zwięzłości).

Załóżmy, że wygenerujesz ciąg bitowy ${1101_{\ binary}}$, który jest odpowiednikiem ciągu ${13_{\ decimal}}$. Ponieważ 13 jest większe niż 12, należy powtórzyć proces.

Następnie wygenerujesz ciąg bitowy ${0110_{\ binary}}$, który jest odpowiednikiem ciągu ${6_{\ decimal}}$. Ponieważ 6 jest mniejsze niż 12, proces jest zakończony.

Kwantowy generator liczb losowych zwróci liczbę 6 jako hasło. W praktyce należy ustawić większą liczbę jako maksymalną, ponieważ mniejsze liczby są łatwe do złamania przez wypróbowanie wszystkich możliwych haseł. W rzeczywistości, aby zwiększyć trudności z odgadywaniem lub pękaniem hasła, można użyć kodu ASCII do konwersji binarnej na tekst i wygenerowania hasła przy użyciu cyfr, symboli i liter mieszanych.

Pisanie generatora bitów losowych

Pierwszym krokiem Q# jest napisanie operacji, która generuje bit losowy. Ta operacja będzie jednym z bloków konstrukcyjnych generatora liczb losowych.

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

Teraz przyjrzyj się nowej kodzie.

  • Zdefiniujesz operację GenerateRandomBit , która nie przyjmuje żadnych danych wejściowych i generuje wartość typu Result. Typ Result reprezentuje wynik miary i może mieć dwie możliwe wartości: Zero lub One.
  • Jeden kubit jest przydzielany za pomocą słowa kluczowego use . Po przydzieleniu kubit jest zawsze w Zero stanie .
  • Operacja służy do umieszczania H kubitu w równej superpozycji.
  • Operacja służy M do mierzenia kubitu, zwracania zmierzonej wartości (Zero lub One).
  • Operacja służy Reset do resetowania kubitu do stanu |0}.

Umieszczając kubit w superpozycji z operacją H i mierząc ją za M pomocą operacji, wynik jest inną wartością za każdym razem, gdy kod jest wywoływany.

Wizualizowanie kodu za Q# pomocą sfery Bloch

Na sferze Blocha biegun północny reprezentuje wartość klasyczną 0, a biegun południowy reprezentuje wartość klasyczną 1. Każdą superpozycję można przedstawić jako punkt na sferze (reprezentowany przez strzałkę). Im bliżej od końca strzałki do bieguna, tym większe jest prawdopodobieństwo, że przy pomiarze kubit ulegnie kolapsowi do wartości klasycznej przypisanej do tego bieguna. Na przykład stan kubitu reprezentowany przez strzałkę na poniższej ilustracji ma większe prawdopodobieństwo nadania wartości 0 , jeśli ją zmierzysz.

Diagram przedstawiający stan kubitu z wysokim prawdopodobieństwem mierzenia zera.

Możesz użyć tej reprezentacji, aby zwizualizować działanie kodu:

  1. Najpierw zacznij od kubitu zainicjowanego w stanie 0 i zastosuj operację H , aby utworzyć równą superpozycję, w której prawdopodobieństwa wartości 0 i 1 są takie same.

    Diagram przedstawiający przygotowanie kubitu w superpozycji przez zastosowanie bramy hadamarda.
  2. Następnie zmierz kubit i zapisz dane wyjściowe:

    Diagram przedstawiający pomiar kubitu i zapisywanie danych wyjściowych.

Ponieważ wynik pomiaru jest losowy, a prawdopodobieństwa pomiaru 0 i 1 są takie same, uzyskaliśmy całkowicie losowy bit. Tę operację można wywołać kilka razy, aby utworzyć liczby całkowite. Jeśli na przykład wywołasz operację trzy razy, aby uzyskać trzy losowe bity, możesz utworzyć losowe liczby 3-bitowe (czyli liczbę losową z zakresu od 0 do 7).

Pisanie kompletnego generatora liczb losowych

  1. Najpierw należy dodać wymagane Q# przestrzenie nazw do programu. W przypadku kompletnego generatora liczb losowych należy uwzględnić trzy Q# przestrzenie nazw: Microsoft.Quantum.Math, Microsoft.Quantum.Intrinsici Microsoft.Quantum.Convert.

    open Microsoft.Quantum.Convert;
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Math;
    
  2. Następnie zdefiniujesz operację GenerateRandomNumberInRange . Ta operacja wielokrotnie wywołuje operację GenerateRandomBit, aby utworzyć ciąg bitów.

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

    Poświęćmy chwilę na przejrzenie nowego kodu.

    • Należy obliczyć liczbę bitów potrzebnych do wyrażenia liczb całkowitych do max. Funkcja BitSizeI z Microsoft.Quantum.Math przestrzeni nazw konwertuje liczbę całkowitą na liczbę bitów potrzebnych do jej reprezentowania.
    • Operacja SampleRandomNumberInRange używa pętli for do generowania liczb losowych do momentu wygenerowania takiej liczby, która jest równa lub mniejsza niż max. Pętla for działa dokładnie tak samo jak pętla for w innych językach programowania.
    • Zmienna bits jest zmienną modyfikowaną. Zmienna modyfikowalna to taka, która może się zmienić podczas obliczania. Aby zmienić wartość zmiennej modyfikowalnej, należy użyć dyrektywy set.
    • Funkcja ResultArrayAsInt pochodzi z Microsoft.Quantum.Convert przestrzeni nazw. Ta funkcja konwertuje ciąg bitowy na dodatnią liczbę całkowitą.
  3. Na koniec dodasz punkt wejścia. W tym przykładzie Main operacja jest punktem wejścia programu. Wywołuje operację GenerateRandomNumberInRange w celu wygenerowania losowej liczby z zakresu od 0 do 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);
        }
    

    Dyrektywa let deklaruje zmienne, które nie zmieniają się podczas obliczania. W tym miejscu zdefiniujemy maksymalną wartość 100.

  4. Kompletny kod generatora liczb losowych jest następujący:

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

Uruchamianie programu generatora liczb losowych

Program można uruchomić w aplikacji Copilot w usłudze Azure Quantum, a w Visual Studio Code jako aplikacji autonomicznej Q# lub przy użyciu programu hosta języka Python.

Kod możesz przetestować za Q# pomocą rozwiązania Copilot w usłudze Azure Quantum bezpłatnie — wszystko, czego potrzebujesz, to konto e-mail firmy Microsoft (MSA). Aby uzyskać więcej informacji na temat rozwiązania Copilot w usłudze Azure Quantum, zobacz Eksplorowanie usługi Azure Quantum.

  1. Otwórz narzędzie Copilot w usłudze Azure Quantum w przeglądarce.

  2. Skopiuj i wklej następujący kod do edytora kodu.

    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. Wybierz liczbę zdjęć do uruchomienia, a następnie kliknij przycisk Uruchom.

  4. Wyniki są wyświetlane w histogramie i w polach Wyniki .

  5. Kliknij przycisk Wyjaśnij kod , aby wyświetlić monit Copilot, aby wyjaśnić ci kod.

Porada

W aplikacji Copilot w usłudze Azure Quantum możesz otworzyć program w programie VS Code dla sieci Web , klikając przycisk logo programu VS Code w prawym rogu edytora kodu.

Uwaga

Ten fragment kodu nie jest obecnie uruchamiany na żadnym dostępnym sprzęcie targetsAzure Quantum, ponieważ obiekt wywołujący ResultArrayAsInt wymaga funkcji QPU z pełnym profilem obliczeniowym.

Następne kroki

Zapoznaj się z innymi Q# samouczkami:

  • Splątanie kwantowe pokazuje, jak napisać Q# program, który manipuluje kubitami i mierzy je oraz demonstruje skutki superpozycji i splątania.
  • Algorytm wyszukiwania Grovera pokazuje, jak napisać Q# program, który używa algorytmu wyszukiwania Grovera.
  • Quantum Fourier Transforms bada sposób pisania Q# programu, który bezpośrednio adresuje określone kubity.
  • Quantum Kata to samouczki z własnym tempem i ćwiczenia programistyczne mające na celu nauczanie elementów obliczeń kwantowych i Q# programowania w tym samym czasie.