Udostępnij za pośrednictwem


Samouczek: eksplorowanie splątania kwantowego za pomocą polecenia Q#

W tym samouczku napiszesz program Q#, który manipuluje kubitami, mierzy je oraz demonstruje efekty superpozycji i splątania. Przygotowujesz dwa kubity w określonym stanie kwantowym, dowiesz się, jak działać na kubitach Q# , aby zmienić ich stan, i zademonstrować skutki superpozycji i splątania. Budujesz swój program Q# krok po kroku, aby wprowadzać stany, operacje i pomiary kubitów.

Poniżej przedstawiono kilka kluczowych pojęć, które należy zrozumieć przed rozpoczęciem:

  • Jeśli bity klasyczne przechowują pojedynczą wartość binarną, taką jak 0 lub 1, stan kubitu może być w superpozycji dwóch stanów kwantowych, 0 i 1. Każdy możliwy stan kwantowy ma skojarzona amplituda prawdopodobieństwa.
  • Czynność pomiaru kubitu generuje wynik binarny z pewnym prawdopodobieństwem i zmienia stan kubitu poza superpozycję.
  • Wiele kubitów można splątać w taki sposób, że nie można ich opisać niezależnie od siebie. Oznacza to, że cokolwiek się dzieje z jednym kubitem w splątanej parze również dzieje się z drugim kubitem.

Z tego samouczka dowiesz się, jak wykonywać następujące czynności:

  • Tworzenie Q# operacji w celu zainicjowania kubitu do żądanego stanu.
  • Umieść kubit w superpozycji.
  • Splątanie pary kubitów.
  • Mierzenie kubitu i obserwowanie wyników.

Napiwek

Jeśli chcesz przyspieszyć podróż 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 z 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

Aby uruchomić przykładowy kod w aplikacji Copilot dla usługi Azure Quantum, potrzebne są następujące elementy:

  • Konto e-mail Microsoft (MSA).

Aby uzyskać więcej informacji o Copilocie, zobacz Eksplorowanie usługi Azure Quantum.

Inicjowanie kubitu do znanego stanu

Pierwszym krokiem Q# jest zdefiniowanie operacji, która inicjuje kubit do znanego stanu. Tę operację można wywołać, aby ustawić kubit na stan klasyczny, co oznacza, że w przypadku pomiaru zwraca Zero 100% czasu lub zwraca One 100% czasu. Pomiar kubitu Q# zwraca typ Result, który może mieć tylko wartość Zero lub One.

Otwórz narzędzie Copilot dla usługi Azure Quantum i skopiuj następujący kod do okna edytora kodu. Nie wybieraj jeszcze Uruchom; Uruchomisz kod w dalszej części tego samouczka.

import Microsoft.Quantum.Intrinsic.*;
import Microsoft.Quantum.Canon.*;

operation SetQubitState(desired : Result, target : Qubit) : Unit {
    if desired != M(target) {
        X(target);
    }
}

W przykładzie kodu wprowadzono dwie standardowe operacje, M i X, które przekształcają stan kubitu.

SetQubitState Operacja:

  1. Przyjmuje dwa parametry: typ Resulto nazwie desired, który reprezentuje żądany stan kubitu, który ma być w (Zero lub One) i typem Qubit.
  2. Wykonuje operację pomiaru , Mktóra mierzy stan kubitu (Zero lub One) i porównuje wynik z wartością określoną w .desired
  3. Jeśli pomiar nie jest zgodny z porównywaną wartością, uruchamia operację X , która przerzuca stan kubitu, do którego prawdopodobieństwo zwracania Zero miary i One są odwrócone. W ten sposób SetQubitState zawsze umieszcza kubit docelowy w żądanym stanie.

Napisz operację testową do przetestowania stanu Bella

Następnie, aby zademonstrować efekt SetQubitState operacji, utwórz inną operację o nazwie Main. Ta operacja przydziela dwa kubity, wywołaj metodę SetQubitState , aby ustawić pierwszy kubit do znanego stanu, a następnie zmierzy kubity, aby wyświetlić wyniki.

Skopiuj poniższy kod do okna edytora kodu, poniżej operacji SetQubitState.

operation Main() : (Int, Int, Int, Int) {
    mutable numOnesQ1 = 0;
    mutable numOnesQ2 = 0;
    let count = 1000;
    let initial = One;

    // allocate the qubits
    use (q1, q2) = (Qubit(), Qubit());   
    for test in 1..count {
        SetQubitState(initial, q1);
        SetQubitState(Zero, q2);
        
        // measure each qubit
        let resultQ1 = M(q1);            
        let resultQ2 = M(q2);           

        // Count the number of 'Ones' returned:
        if resultQ1 == One {
            numOnesQ1 += 1;
        }
        if resultQ2 == One {
            numOnesQ2 += 1;
        }
    }

    // reset the qubits
    SetQubitState(Zero, q1);             
    SetQubitState(Zero, q2);
    

    // Display the times that |0> is returned, and times that |1> is returned
    Message($"Q1 - Zeros: {count - numOnesQ1}");
    Message($"Q1 - Ones: {numOnesQ1}");
    Message($"Q2 - Zeros: {count - numOnesQ2}");
    Message($"Q2 - Ones: {numOnesQ2}");
    return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
}

W kodzie zmienne count i initial są ustawione odpowiednio na 1000 i One. Inicjuje to pierwszy kubit do One i mierzy każdy kubit 1000 razy.

MainOperacja:

  1. Ustawia zmienne dla licznika i początkowy stan kubitu.
  2. Wywołuje instrukcję use, aby zainicjować dwa kubity.
  3. Pętle iteracji count . Dla każdej pętli
    1. Wywołuje SetQubitState, aby ustawić określoną initial wartość na pierwszym kubicie.
    2. Wywołuje ponownie SetQubitState, aby ustawić drugi kubit na stan Zero.
    3. Używa operacji M do mierzenia każdego kubitu.
    4. Przechowuje liczbę pomiarów dla każdego kubitu, który zwraca wartość One.
  4. Po zakończeniu pętli ponownie wywołuje SetQubitState, aby zresetować kubity do znanego stanu (Zero), umożliwiając innym przydzielanie kubitów w znanym stanie. Instrukcja use wymaga zresetowania.
  5. Na koniec używa funkcji Message do wyświetlania wyników w oknach wyjściowych Copilot, zanim zwróci wyniki.

Uruchom kod w Copilot dla Azure Quantum

Przed przejściem do procedur superpozycji i splątania możesz przetestować kod do tego momentu, aby zobaczyć inicjowanie i pomiar kubitów.

Aby uruchomić kod jako autonomiczny program, kompilator w Copilot musi wiedzieć, Q# gdzie uruchomić program. Ponieważ nie określono przestrzeni nazw, kompilator rozpoznaje domyślny punkt wejścia jako operację Main . Aby uzyskać więcej informacji, zobacz Projekty i niejawne przestrzenie nazw.

Twój Q# program powinien teraz wyglądać następująco:

import Microsoft.Quantum.Intrinsic.*;
import Microsoft.Quantum.Canon.*;

operation SetQubitState(desired : Result, target : Qubit) : Unit {
    if desired != M(target) {
        X(target);
    }
}

operation Main() : (Int, Int, Int, Int) {
    mutable numOnesQ1 = 0;
    mutable numOnesQ2 = 0;
    let count = 1000;
    let initial = One;

    // allocate the qubits
    use (q1, q2) = (Qubit(), Qubit());   
    for test in 1..count {
        SetQubitState(initial, q1);
        SetQubitState(Zero, q2);
        
        // measure each qubit
        let resultQ1 = M(q1);            
        let resultQ2 = M(q2);           

        // Count the number of 'Ones' returned:
        if resultQ1 == One {
            numOnesQ1 += 1;
        }
        if resultQ2 == One {
            numOnesQ2 += 1;
        }
    }

    // reset the qubits
    SetQubitState(Zero, q1);             
    SetQubitState(Zero, q2);
        
    
    // Display the times that |0> is returned, and times that |1> is returned
    Message($"Q1 - Zeros: {count - numOnesQ1}");
    Message($"Q1 - Ones: {numOnesQ1}");
    Message($"Q2 - Zeros: {count - numOnesQ2}");
    Message($"Q2 - Ones: {numOnesQ2}");
    return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );

}

Skopiuj i wklej kompletny przykładowy kod do okna kodu Copilot for Azure Quantum, ustaw suwak liczby zdjęć na wartość "1", a następnie wybierz pozycję Uruchom. Wyniki są wyświetlane w histogramie i w polach Wyniki .

Q1 - Zeros: 0
Q1 - Ones: 1000
Q2 - Zeros: 1000
Q2 - Ones: 0

Ponieważ kubity nie zostały jeszcze manipulowane, zachowały swoje początkowe wartości: pierwszy kubit zwraca One za każdym razem, a drugi kubit zwraca wartość Zero.

Jeśli zmienisz wartość initial na Zero i ponownie uruchomisz program, należy zauważyć, że pierwszy kubit zwraca Zero również za każdym razem.

Q1 - Zeros: 1000
Q1 - Ones: 0
Q2 - Zeros: 1000
Q2 - Ones: 0

Napiwek

Wybierz Ctrl-Z lub Edytuj > Cofnij i zapisz plik za każdym razem, gdy dokonasz zmiany testowej w kodzie przed ponownym uruchomieniem.

Umieść kubit w superpozycji

Obecnie kubity w programie znajdują się w stanie klasycznym , czyli są to 1 lub 0. Wiesz, że to dlatego, że program inicjuje kubity do znanego stanu i nie dodano żadnych procesów do manipulowania nimi. Przed splątaniem kubitów należy umieścić pierwszy kubit w stanie superpozycji, gdzie pomiar kubitu zwraca Zero z prawdopodobieństwem ~50% i One z prawdopodobieństwem ~50%. Koncepcyjnie, kubit można potraktować jako mający równą szansę na pomiar Zero lub One.

Aby umieścić kubit w superpozycji, Q# zapewnia operację H, lub Hadamard. X Przypomnij sobie operację z inicjowania kubitu do znanej procedury stanu wcześniej, która przerzuciła kubit z zakresu od 0 do 1 (lub odwrotnie); H operacja przerzuca kubit w połowie stanu równego prawdopodobieństwa Zero lub One. Po zmierzeniu, kubit w superpozycji powinien zwrócić mniej więcej taką samą liczbę wyników Zero i One.

Zmodyfikuj kod w Main operacji, resetując wartość początkową do One i wstawiając wiersz dla H operacji:

for test in 1..count {
    use (q1, q2) = (Qubit(), Qubit());   
    for test in 1..count {
        SetQubitState(initial, q1);
        SetQubitState(Zero, q2);
        
        H(q1);                // Add the H operation after initialization and before measurement

        // measure each qubit
        let resultQ1 = M(q1);            
        let resultQ2 = M(q2); 
        ...

Teraz po uruchomieniu programu można zobaczyć wyniki pierwszego kubitu w superpozycji.

Q1 - Zeros: 523            // results vary
Q1 - Ones: 477
Q2 - Zeros: 1000
Q2 - Ones: 0

Za każdym razem, gdy uruchamiasz program, wyniki pierwszego kubitu różnią się nieznacznie, ale są zbliżone do 50% One i 50% Zero, podczas gdy wyniki drugiego kubitu pozostają Zero przez cały czas.

Q1 - Zeros: 510           
Q1 - Ones: 490
Q2 - Zeros: 1000
Q2 - Ones: 0

Inicjalizacja pierwszego kubitu do Zero daje podobne wyniki.

Q1 - Zeros: 504           
Q1 - Ones: 496
Q2 - Zeros: 1000
Q2 - Ones: 0

Uwaga

Przesuwając suwak w copilot dla usługi Azure Quantum i zwiększając liczbę zdjęć, możesz zobaczyć, jak wyniki superpozycji różnią się nieznacznie w zależności od rozkładu zdjęć.

Splątanie dwóch kubitów

Jak wspomniano wcześniej, splątane kubity są połączone w taki sposób, że nie można ich opisać niezależnie od siebie. Oznacza to, że jakakolwiek operacja dotyczy jednego kubitu, również dotyczy splątanego kubitu. Dzięki temu można znać stan wynikowy jednego kubitu bez mierzenia go, po prostu mierząc stan drugiego kubitu. (W tym przykładzie użyto dwóch kubitów; jednak istnieje również możliwość splątania co najmniej trzech kubitów).

Aby włączyć splątanie, Q# udostępnia operację CNOT, która oznacza Controlled-NOT. Wynikiem uruchomienia tej operacji na dwóch kubitach jest odwrócenie drugiego kubitu, jeśli pierwszy kubit jest One.

Dodaj operację CNOT do programu natychmiast po H operacji. Pełny program powinien wyglądać następująco:

import Microsoft.Quantum.Intrinsic.*;
import Microsoft.Quantum.Canon.*;

    operation SetQubitState(desired : Result, target : Qubit) : Unit {
        if desired != M(target) {
            X(target);
        }
    }

operation Main() : (Int, Int, Int, Int) {
    mutable numOnesQ1 = 0;
    mutable numOnesQ2 = 0;
    let count = 1000;
    let initial = Zero;

    // allocate the qubits
    use (q1, q2) = (Qubit(), Qubit());   
    for test in 1..count {
        SetQubitState(initial, q1);
        SetQubitState(Zero, q2);
    
        H(q1);            
        CNOT(q1, q2);      // Add the CNOT operation after the H operation

        // measure each qubit
        let resultQ1 = M(q1);            
        let resultQ2 = M(q2);           

        // Count the number of 'Ones' returned:
        if resultQ1 == One {
            numOnesQ1 += 1;
        }
        if resultQ2 == One {
            numOnesQ2 += 1;
        }
    }

    // reset the qubits
    SetQubitState(Zero, q1);             
    SetQubitState(Zero, q2);
    

    // Display the times that |0> is returned, and times that |1> is returned
    Message($"Q1 - Zeros: {count - numOnesQ1}");
    Message($"Q1 - Ones: {numOnesQ1}");
    Message($"Q2 - Zeros: {count - numOnesQ2}");
    Message($"Q2 - Ones: {numOnesQ2}");
    return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );

    }

Teraz po uruchomieniu programu powinny zostać wyświetlone następujące elementy:

Q1 - Zeros: 502           // results will vary
Q1 - Ones: 498
Q2 - Zeros: 502
Q2 - Ones: 498

Zwróć uwagę, że statystyki dla pierwszego kubitu nie uległy zmianie (nadal istnieje prawdopodobieństwo ok. 50/50 na Zero lub One po pomiarze), ale wyniki pomiaru dla drugiego kubitu są zawsze takie same jak pomiar pierwszego kubitu, niezależnie od tego, ile razy uruchomisz program. Operacja CNOT splątała dwa kubity, przez co cokolwiek dzieje się z jednym z nich, dzieje się także z drugim.

Wymagania wstępne

Aby utworzyć i uruchomić przykładowy kod w lokalnym środowisku projektowym:

Tworzenie nowego Q# pliku

  1. Otwórz program Visual Studio Code i wybierz pozycję Plik > nowy plik tekstowy, aby utworzyć nowy plik.
  2. Zapisz plik jako CreateBellStates.qs. Ten plik będzie zawierać Q# kod programu.

Inicjowanie kubitu do znanego stanu

Pierwszym krokiem Q# jest zdefiniowanie operacji, która inicjuje kubit do znanego stanu. Tę operację można wywołać, aby ustawić kubit na stan klasyczny, co oznacza, że zwraca Zero 100% czasu lub zwraca One 100% czasu. Zero i One są Q# wartościami reprezentującymi tylko dwa możliwe wyniki pomiaru kubitu.

Otwórz CreateBellStates.qs i skopiuj następujący kod:

import Microsoft.Quantum.Intrinsic.*;
import Microsoft.Quantum.Canon.*;

operation SetQubitState(desired : Result, target : Qubit) : Unit {
    if desired != M(target) {
        X(target);
    }
}

W przykładzie kodu przedstawiono dwie standardowe operacje, M oraz X, które przekształcają stan kubitu.

Operacja SetQubitState:

  1. Przyjmuje dwa parametry: typ Resulto nazwie desired, który reprezentuje żądany stan kubitu, który ma być w (Zero lub One) i typem Qubit.
  2. Wykonuje operację pomiaru , Mktóra mierzy stan kubitu (Zero lub One) i porównuje wynik z wartością określoną w .desired
  3. Jeśli pomiar nie jest zgodny z porównywaną wartością, uruchamia operację X, która odwraca stan kubitu, tak że prawdopodobieństwo zwrócenia pomiaru Zero i One jest odwrócone. W ten sposób SetQubitState zawsze umieszcza kubit docelowy w żądanym stanie.

Napisać operację testową do przetestowania stanu Bella

Następnie, aby zademonstrować efekt SetQubitState operacji, utwórz inną operację o nazwie Main. Ta operacja przydziela dwa kubity, wywołaj metodę SetQubitState , aby ustawić pierwszy kubit do znanego stanu, a następnie zmierzy kubity, aby wyświetlić wyniki.

Dodaj następującą operację do CreateBellStates.qs pliku po SetQubitState operacji:

operation Main() : (Int, Int, Int, Int) {
    mutable numOnesQ1 = 0;
    mutable numOnesQ2 = 0;
    let count = 1000;
    let initial = One;

    // allocate the qubits
    use (q1, q2) = (Qubit(), Qubit());   
    for test in 1..count {
        SetQubitState(initial, q1);
        SetQubitState(Zero, q2);
        
        // measure each qubit
        let resultQ1 = M(q1);            
        let resultQ2 = M(q2);           

        // Count the number of 'Ones' returned:
        if resultQ1 == One {
            numOnesQ1 += 1;
        }
        if resultQ2 == One {
            numOnesQ2 += 1;
        }
    }

    // reset the qubits
    SetQubitState(Zero, q1);             
    SetQubitState(Zero, q2);
    

    // Display the times that |0> is returned, and times that |1> is returned
    Message($"Q1 - Zeros: {count - numOnesQ1}");
    Message($"Q1 - Ones: {numOnesQ1}");
    Message($"Q2 - Zeros: {count - numOnesQ2}");
    Message($"Q2 - Ones: {numOnesQ2}");
    return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
}

W kodzie zmienne count i initial są ustawione na 1000 i One odpowiednio. Ten krok inicjuje pierwszy kubit do One i mierzy każdy kubit 1000 razy.

MainOperacja:

  1. Przyjmuje dwa parametry: count, liczbę razy do uruchomienia pomiaru i initial, żądany stan inicjowania kubitu.
  2. Wywołuje instrukcję use, aby zainicjować dwa kubity.
  3. Pętle iteracji count . Dla każdej pętli, to
    1. Wywołuje SetQubitState, aby ustawić określoną wartość initial na pierwszym kubicie.
    2. Ponownie wywołuje SetQubitState polecenie, aby ustawić drugi kubit na stan Zero.
    3. Używa operacji M do mierzenia każdego kubitu.
    4. Przechowuje liczbę pomiarów dla każdego kubitu, który zwraca wartość One.
  4. Po zakończeniu pętli ponownie wywołuje SetQubitState, aby zresetować kubity do znanego stanu (Zero), co pozwala innym przydzielać kubity w znanym stanie. Zresetowanie kubitu jest wymagane przez instrukcję use .
  5. Na koniec używa Message funkcji do drukowania komunikatu w konsoli przed zwróceniem wyników.

Uruchamianie kodu

Przed przejściem do procedur superpozycji i splątania przetestuj kod aż do tego momentu, aby zobaczyć inicjowanie i pomiar kubitów.

Aby uruchomić kod jako autonomiczny program, kompilator musi wiedzieć, Q# gdzie uruchomić program. Ponieważ nie określono przestrzeni nazw, kompilator rozpoznaje domyślny punkt wejścia jako operację Main . Aby uzyskać więcej informacji, zobacz Projekty i przestrzenie nazw ukryte.

  1. Plik CreateBellStates.qs powinien teraz wyglądać następująco:

    import Microsoft.Quantum.Intrinsic.*;
    import Microsoft.Quantum.Canon.*;
    
    operation SetQubitState(desired : Result, target : Qubit) : Unit {
        if desired != M(target) {
            X(target);
        }
    }
    
    operation Main() : (Int, Int, Int, Int) {
        mutable numOnesQ1 = 0;
        mutable numOnesQ2 = 0;
        let count = 1000;
        let initial = One;
    
        // allocate the qubits
        use (q1, q2) = (Qubit(), Qubit());   
        for test in 1..count {
            SetQubitState(initial, q1);
            SetQubitState(Zero, q2);
    
            // measure each qubit
            let resultQ1 = M(q1);            
            let resultQ2 = M(q2);           
    
            // Count the number of 'Ones' returned:
            if resultQ1 == One {
                numOnesQ1 += 1;
            }
            if resultQ2 == One {
                numOnesQ2 += 1;
            }
        }
    
        // reset the qubits
        SetQubitState(Zero, q1);             
        SetQubitState(Zero, q2);
    
    
        // Display the times that |0> is returned, and times that |1> is returned
        Message($"Q1 - Zeros: {count - numOnesQ1}");
        Message($"Q1 - Ones: {numOnesQ1}");
        Message($"Q2 - Zeros: {count - numOnesQ2}");
        Message($"Q2 - Ones: {numOnesQ2}");
        return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
    }
    
  2. Przed uruchomieniem programu upewnij się, że profil docelowy ma ustawioną wartość Unrestricted. Wybierz Widok — paleta poleceń, wyszukaj pozycję QIR, wybierz Q#: Ustaw profil docelowy dla Azure Quantum QIR, a następnie wybierz Q#: nieograniczony.

    Uwaga

    Jeśli profil docelowy nie jest ustawiony na Unrestricted, podczas uruchamiania programu zostanie wyświetlony błąd.

  3. Aby uruchomić program, wybierz pozycję Uruchom Q# plik z listy rozwijanej ikona odtwarzania w prawym górnym rogu, wybierz pozycję Uruchom z listy poleceń poprzedzających Main operację lub naciśnij Ctrl+F5. Program uruchamia operację Main w domyślnym symulatorze.

  4. Dane wyjściowe są wyświetlane w konsoli debugowania.

    Q1 - Zeros: 0
    Q1 - Ones: 1000
    Q2 - Zeros: 1000
    Q2 - Ones: 0
    

    Ponieważ kubity nie zostały jeszcze manipulowane, zachowały swoje początkowe wartości: pierwszy kubit zwraca One za każdym razem, a drugi kubit zwraca wartość Zero.

  5. Jeśli zmienisz wartość initial na Zero i ponownie uruchomisz program, należy zauważyć, że pierwszy kubit zwraca Zero również za każdym razem.

    Q1 - Zeros: 1000
    Q1 - Ones: 0
    Q2 - Zeros: 1000
    Q2 - Ones: 0
    

Napiwek

Za każdym razem zaznacz Ctrl-Z lub Edytuj > Cofnij i zapisz plik, gdy wprowadzisz testową zmianę w kodzie przed jego ponownym uruchomieniem.

Umieść kubit w superpozycji

Obecnie kubity w programie są w stanie klasycznym, czyli są 1 lub 0. Wiesz, że to dlatego, że program inicjuje kubity do znanego stanu i nie dodano żadnych procesów do manipulowania nimi. Przed splątaniem kubitów należy umieścić pierwszy kubit w stanie superpozycji, gdzie pomiar kubitu zwraca Zero 50% czasu i One 50% czasu. Koncepcyjnie, kubit można uważać za znajdujący się w połowie drogi między Zero a One.

Aby umieścić kubit w superpozycji, Q# zapewnia operację H, lub Hadamard. Przypomnij sobie operację z procedury Inicjalizacji kubitu do znanego stanu wcześniejszej, która przekształciła kubit z Zero do One (lub odwrotnie); H operacja przekształca kubit do stanu równych prawdopodobieństw Zero lub One. Po zmierzeniu, kubit znajdujący się w superpozycji powinien zwrócić mniej więcej taką samą liczbę wyników Zero i wyników One.

  1. Zmodyfikuj kod w operacji Main, aby uwzględnić operację H.

    for test in 1..count {
        use (q1, q2) = (Qubit(), Qubit());   
        for test in 1..count {
            SetQubitState(initial, q1);
            SetQubitState(Zero, q2);
    
            H(q1);                // Add the H operation after initialization and before measurement
    
            // measure each qubit
            let resultQ1 = M(q1);            
            let resultQ2 = M(q2); 
            ...
    
  2. Teraz po uruchomieniu programu można zobaczyć wyniki superpozycji pierwszego kubitu.

    Q1 - Zeros: 523            // results will vary
    Q1 - Ones: 477
    Q2 - Zeros: 1000
    Q2 - Ones: 0
    
  3. Za każdym razem, gdy uruchomisz program, wyniki dla pierwszego kubitu nieznacznie się różnią, ale będą bliskie 50% One i 50% Zero, podczas gdy wyniki dla drugiego kubitu pozostają Zero przez cały czas.

    Q1 - Zeros: 510           
    Q1 - Ones: 490
    Q2 - Zeros: 1000
    Q2 - Ones: 0
    
  4. Inicjowanie pierwszego kubitu na Zero zwraca podobne wyniki.

    Q1 - Zeros: 504           
    Q1 - Ones: 496
    Q2 - Zeros: 1000
    Q2 - Ones: 0
    

Splątanie dwóch kubitów

Jak wspomniano wcześniej, splątane kubity są połączone w taki sposób, że nie można ich opisać niezależnie od siebie. To znaczy, że jakakolwiek operacja zachodzi na jednym kubicie, dzieje się również z kubitem splątanym. Dzięki temu można znać stan wynikowy jednego kubitu bez mierzenia go, po prostu mierząc stan drugiego kubitu. (W tym przykładzie użyto dwóch kubitów; jednak istnieje również możliwość splątania co najmniej trzech kubitów).

Aby włączyć splątanie, Q# zapewnia operację CNOT, która oznacza Controlled-NOT. Wynikiem wykonania tej operacji na dwóch kubitach jest odwrócenie stanu drugiego kubitu, jeśli pierwszy kubit to One.

  1. Dodaj operację CNOT do programu natychmiast po H operacji. Pełny program powinien wyglądać następująco:

    import Microsoft.Quantum.Intrinsic.*;
    import Microsoft.Quantum.Canon.*;
    
        operation SetQubitState(desired : Result, target : Qubit) : Unit {
            if desired != M(target) {
                X(target);
            }
        }
    
    operation Main() : (Int, Int, Int, Int) {
        mutable numOnesQ1 = 0;
        mutable numOnesQ2 = 0;
        let count = 1000;
        let initial = Zero;
    
        // allocate the qubits
        use (q1, q2) = (Qubit(), Qubit());   
        for test in 1..count {
            SetQubitState(initial, q1);
            SetQubitState(Zero, q2);
    
            H(q1);            
            CNOT(q1, q2);      // Add the CNOT operation after the H operation
    
            // measure each qubit
            let resultQ1 = M(q1);            
            let resultQ2 = M(q2);           
    
            // Count the number of 'Ones' returned:
            if resultQ1 == One {
                numOnesQ1 += 1;
            }
            if resultQ2 == One {
                numOnesQ2 += 1;
            }
        }
    
        // reset the qubits
        SetQubitState(Zero, q1);             
        SetQubitState(Zero, q2);
    
    
        // Display the times that |0> is returned, and times that |1> is returned
        Message($"Q1 - Zeros: {count - numOnesQ1}");
        Message($"Q1 - Ones: {numOnesQ1}");
        Message($"Q2 - Zeros: {count - numOnesQ2}");
        Message($"Q2 - Ones: {numOnesQ2}");
        return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );
    
        }
    
    
    Q1 - Zeros: 502           
    Q1 - Ones: 498       // results will vary
    Q2 - Zeros: 502
    Q2 - Ones: 498
    Result: "(502, 498, 502, 498)"
    

Statystyki dla pierwszego kubitu nie uległy zmianie (prawdopodobieństwo uzyskania wartości Zero lub One wynosi 50/50 po pomiarze), ale wyniki pomiaru drugiego kubitu są zawsze takie same jak wyniki pomiaru pierwszego kubitu. Operacja CNOT splątała dwa kubity, tak że cokolwiek się stanie z jednym z nich, stanie się również z drugim.

Wykreśl histogram częstotliwości

Zwizualizujmy rozkład wyników uzyskanych z wielokrotnego uruchamiania programu kwantowego. Histogram częstotliwości pomaga wizualizować rozkład prawdopodobieństwa tych wyników.

  1. Wybierz Widok — Paleta poleceń lub naciśnij Ctrl+Shift+P, a następnie wpisz "histogram", co powinno wywołać opcję Q#: Uruchom plik i wyświetl histogram. Możesz również wybrać pozycję Histogram z listy poleceń poprzedzających Main. Wybierz tę opcję, aby otworzyć okno histogramu Q# .

  2. Wprowadź liczbę zdjęć do wykonania programu, na przykład 100 zdjęć, a następnie naciśnij Enter. Histogram wyświetla się w oknie histogramu Q#.

  3. Każdy słupek w histogramie odpowiada możliwemu wynikowi, a jego wysokość reprezentuje liczbę obserwowanych wyników. W tym przypadku istnieje 50 różnych unikatowych wyników. Należy pamiętać, że dla każdego wyniku wyniki pomiaru dla pierwszego i drugiego kubitu są zawsze takie same.

    Zrzut ekranu przedstawiający okno histogramu Q# w programie Visual Studio Code.

    Napiwek

    Histogram można powiększyć za pomocą kółka przewijania myszy lub gestu klawiatury. Po powiększeniu wykres można przesuwać, naciskając Alt podczas przewijania.

  4. Wybierz pasek, aby wyświetlić procent tego wyniku.

  5. Wybierz ikonę ustawień w lewym górnym rogu, aby wyświetlić opcje. Możesz wyświetlić 10 pierwszych wyników, 25 pierwszych wyników lub wszystkie wyniki. Możesz również posortować wyniki z wysokiej do niskiej lub niskiej do wysokiej.

    Zrzut ekranu przedstawiający okno histogramu Q# w programie Visual Studio Code pokazujące sposób wyświetlania ustawień.

Zapoznaj się z innymi Q# samouczkami:

  • Algorytm wyszukiwania Grovera pokazuje, jak napisać Q# program korzystający z algorytmu wyszukiwania Grovera.
  • Quantum Fourier Transform bada sposób pisania Q# programu, który bezpośrednio dotyczy określonych kubitów.
  • Quantum Katas to samouczki samodzielne i ćwiczenia programistyczne mające na celu nauczanie elementów obliczeń kwantowych i Q# programowania w tym samym czasie.