Esercitazione: Esplorare l'entanglement quantistico con Q#

Questa esercitazione illustra come scrivere un Q# programma che modifica e misura qubit e illustra gli effetti della sovrapposizione e dell'entanglement.

  • Mentre i bit classici contengono un singolo valore binario come 0 o 1, lo stato di un qubit può essere una sovrapposizione di due stati quantistici, 0 e 1. A ogni stato quantico possibile è associata un'ampiezza di probabilità.
  • L'atto di misurare un qubit produce un risultato binario, 0 o 1, con una certa probabilità, e modifica lo stato del qubit fuori sovrapposizione.
  • Più qubit possono essere correlati in modo che non possano essere descritti in modo indipendente l'uno dall'altro. Ciò significa che qualsiasi cosa accada a un qubit in una coppia in correlazione (entanglement) accade anche all'altro qubit.

Questa esercitazione prevede la preparazione di due qubit in uno stato quantico specifico, per imparare come operare sui qubit con Q# per modificarne lo stato e mostrare gli effetti della sovrapposizione e dell'entanglement. Il programma Q# verrà compilato un'unità alla volta per introdurre stati, operazioni e misurazioni dei qubit.

Prerequisiti

Per completare questa esercitazione, è necessario disporre di:

In questa esercitazione si apprenderà come:

  • Creare operazioni Q# per misurare e inizializzare un qubit allo stato desiderato.
  • Creare qubit e testare il programma.
  • Posizionare un qubit in sovrapposizione.
  • Mettere in correlazione una coppia di qubit.

Creare un nuovo notebook nell'area di lavoro

  1. Accedere al portale di Azure e selezionare l'area di lavoro creata nel passaggio precedente.
  2. A sinistra selezionare Notebook.
  3. Fare clic su Notebook personali e quindi su Aggiungi nuovo.
  4. In Tipo di kernel selezionare IQ#.
  5. Digitare un nome per il file, ad esempio Entanglement.ipynb, e fare clic su Crea file.

Quando si apre il nuovo notebook, viene creato automaticamente il codice per la prima cella, in base alle informazioni sulla sottoscrizione e sull'area di lavoro.

%azure.connect "/subscriptions/\<subscription ID>/\<resource group>/providers/Microsoft.Quantum/Workspaces/\<workspace>" \<location>

Nota

%azure.connect è un comando magic IQ#, un set di comandi che semplificano le attività in Jupyter Notebook.

Se si esegue questa cella, verrà eseguita l'autenticazione nella sottoscrizione e verrà visualizzato un elenco dei provider disponibili e delle relative destinazioni.

Inizializzare un qubit usando la misurazione

Il primo passaggio consiste nel definire un'operazione Q# che inizializza un qubit su uno stato noto. Viene utilizzata per impostare un qubit su uno stato classico, ovvero restituisce Zero il 100% delle volte o restituisce One il 100% delle volte. Zero e One sono valori Q# che rappresentano gli unici due risultati possibili della misura di un qubit.

Fare clic su + Codice per aggiungere una nuova cella e aggiungere il codice seguente:

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

Nota

Gli spazi dei nomi Microsoft.Quantum.Intrinsic e Microsoft.Quantum.Canon, usati dalle operazioni in questo codice, vengono aperti automaticamente in ogni cella di un notebook Azure Quantum.

L'esempio di codice introduce due operazioni standard, M e X, che trasformano lo stato di un qubit.

L'operazione SetQubitState:

  1. Accetta due parametri: un tipo Result, denominato desired, che rappresenta lo stato desiderato per il qubit (0 o 1) e un tipo Qubit.
  2. Esegue un'operazione di misurazione, M, che misura lo stato del qubit (Zero o One) e confronta il risultato con il valore specificato in desired.
  3. Se la misura non corrisponde al valore confrontato, esegue un'operazione X che capovolge lo stato del qubit in cui le probabilità che una misura restituisca Zero e One sono invertite. In questo modo, SetQubitState inserisce sempre il qubit di destinazione nello stato desiderato.

Testare la misura

Successivamente, per illustrare l'effetto dell'operazione SetQubitState, creare un'altra operazione denominata TestBellState.

Aggiungere un'altra nuova cella e aggiungere il codice seguente:

operation TestBellState(count : Int, initial : Result) : (Int, Int, Int, Int) {
    mutable numOnesQ1 = 0;
    mutable numOnesQ2 = 0;

    // 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':
        if resultQ1 == One {
            set numOnesQ1 += 1;
        }
        if resultQ2 == One {
            set numOnesQ2 += 1;
        }
    }

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

    // Return number of |0> states, number of |1> states
    Message("q1:Zero, One  q2:Zero, One");
    return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );

}

L'operazione TestBellState:

  1. Accetta due parametri: count, il numero di volte in cui eseguire una misurazione e initial, lo stato desiderato per inizializzare il qubit.
  2. Chiama l'istruzione use per inizializzare due qubit.
  3. Esegue cicli per count iterazioni. Per ogni ciclo
    1. Chiama SetQubitState per impostare un valore initial specificato sul primo qubit.
    2. Chiama SetQubitState di nuovo per impostare il secondo qubit su uno stato Zero.
    3. Usa l'operazione M per misurare ogni qubit.
    4. Archivia il numero di misure per ogni qubit che restituisce One.
  4. Al termine del ciclo, chiama di nuovo SetQubitState per reimpostare i qubit su uno stato noto (Zero) per consentire ad altri di allocare i qubit in uno stato noto. Questo passaggio è richiesto dall'istruzione use.
  5. Infine, usa la funzione Message per visualizzare un messaggio nella console prima di restituire i risultati.

Testare il codice

Prima di passare alle procedure per la sovrapposizione e l'entanglement, testare il codice fino a questo punto per visualizzare l'inizializzazione e la misurazione dei qubit.

Per eseguire l'operazione TestBellState, usare il comando magic %simulate per chiamare il simulatore di stato completo Azure Quantum. È necessario specificare gli argomenti count e initial, ad esempio count=1000 e initial=1. In questo modo si inizializza il primo qubit in One e si misura ogni qubit 1000 volte. Aggiungere una nuova cella con il comando seguente e fare clic su Esegui tutto:

%simulate TestBellState count=1000 initial=1

e si dovrà osservare l'output seguente:

q1:Zero, One  q2:Zero, One
(0, 1000, 1000, 0)

Poiché i qubit non sono ancora stati manipolati, hanno mantenuto i valori iniziali: il primo qubit restituisce One ogni volta e il secondo qubit restituisce Zero.

Se si esegue di nuovo la cella con initial=0, si dovrebbe osservare che anche il primo qubit restituisce Zero ogni volta.

%simulate TestBellState count=1000 initial=0
q1:Zero, One q2:Zero, One
(1000, 0, 1000, 0)

Posizionare un qubit in sovrapposizione

Attualmente, i qubit nel programma sono tutti in uno stato classico, ovvero sono 1 o 0. Questo è noto perché il programma inizializza i qubit su uno stato noto e non sono stati aggiunti processi per modificarli. Prima di eseguire l'entanglement dei qubit, si inserirà il primo qubit in uno stato di sovrapposizione, in cui una misurazione del qubit restituirà Zero il 50% delle volte e One e il 50% delle volte. Concettualmente, il qubit può essere pensato come in una combinazione lineare di tutti gli stati tra Zeroe One.

Per inserire un qubit in sovrapposizione, Q# fornisce l'operazione H, o Hadamard. Richiamare l'operazione X dalla procedura Inizializzare un qubit usando la misurazione precedente, che ha capovolto un qubit da 0 a 1 (o viceversa). L'operazione H capovolge il qubit a metà strada in uno stato di probabilità uguali pari a 0 o 1. Quando viene misurato, un qubit in sovrapposizione deve restituire approssimativamente un numero uguale di risultati Zero e One .

Nella cella precedente con TestBellState, aggiungere l'operazione H all'interno del ciclo for:

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

Inizializzare di nuovo il qubit su 1 nel comando %simulate e fare clic su Esegui tutti. È possibile visualizzare i risultati del primo qubit in sovrapposizione:

%simulate TestBellState count=1000 initial=1
q1:Zero, One  q2:Zero, One
(523, 477, 1000, 0)      // results will vary

Ogni volta che si esegue il programma, i risultati per il primo qubit variano leggermente, ma saranno vicini al 50% One e al 50% Zero, mentre i risultati per il secondo qubit rimarranno sempre Zero.

Q1:Zero/One  Q2:Zero/One
(510, 490, 1000, 0)

L'inizializzazione del primo qubit in Zero restituisce risultati simili.

%simulate TestBellState count=1000 initial=0
Q1:Zero/One  Q2:Zero/One
(504, 496, 1000, 0)

Eseguire l'entanglement di due qubit

Come accennato in precedenza, i qubit in entanglement sono connessi in modo che non possano essere descritti in modo indipendente l'uno dall'altro. Ciò significa che qualsiasi operazione venga eseguita su un qubit in una coppia in correlazione (entanglement) viene eseguita anche sull'altro qubit. In questo modo è possibile conoscere lo stato risultante di un qubit senza misurarlo, semplicemente misurando lo stato dell'altro qubit. (Questo esempio usa due qubit, ma è anche possibile eseguire l'entanglement di tre o più qubit).

Per abilitare l'entanglement, Q# fornisce l'operazione CNOT, che sta per Controlled-NOT. Il risultato dell'esecuzione di questa operazione su due qubit è l'inversione del secondo qubit se il primo qubit è One.

Aggiungere l'operazione CNOT al ciclo for immediatamente dopo l'operazione H. L'aspetto dell'operazione TestBellState dovrebbe essere simile al seguente.

operation TestBellState(count : Int, initial : Result) : (Int, Int, Int, Int) {
    mutable numOnesQ1 = 0;
    mutable numOnesQ2 = 0;

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

        H(q1);
        CNOT(q1, q2);                   // added CNOT operation
        
        // measure each qubit
        let resultQ1 = M(q1);            
        let resultQ2 = M(q2);           

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

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

    // Return number of |0> states, number of |1> states
    Message("q1:Zero, One  q2:Zero, One");
    return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );

}

Fare clic su Esegui tutti per eseguire l'operazione aggiornata. Verrà visualizzato:

Q1:Zero/One  Q2:Zero/One
(502, 498, 502, 498)      // actual results will vary

Le statistiche per il primo qubit non sono cambiate (probabilità 50/50 di Zero o One dopo la misurazione), ma i risultati della misurazione per il secondo qubit sono sempre gli stessi della misurazione del primo qubit, indipendentemente dall'inizializzazione del qubit. L'operazione CNOT ha eseguito l'entanglement dei due qubit, così qualsiasi cosa accada a uno di essi, accade anche all'altro.

Prerequisiti

In questa esercitazione si apprenderà come

  • Creare operazioni Q# per misurare e inizializzare un qubit allo stato desiderato.
  • Creare qubit e testare il programma.
  • Posizionare un qubit in sovrapposizione.
  • Mettere in correlazione una coppia di qubit.

Inizializzare un qubit usando la misura

Il primo passaggio consiste nel definire un'operazione Q# che inizializzerà un qubit su uno stato noto. Questa può essere chiamata per impostare un qubit su uno stato classico, ovvero restituisce Zero il 100% delle volte o restituisce One il 100% delle volte. Zero e One sono valori Q# che rappresentano gli unici due risultati possibili della misura di un qubit.

Nel progetto, sostituire quindi il contenuto di Program.qs con il codice seguente:

   namespace Bell {
       open Microsoft.Quantum.Intrinsic;
       open Microsoft.Quantum.Canon;

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

L'esempio di codice introduce due operazioni standard, M e X, che trasformano lo stato di un qubit.

L'operazione SetQubitState:

  1. Accetta due parametri: un tipo Result, denominato desired, che rappresenta lo stato desiderato per il qubit (0 o 1) e un tipo Qubit.
  2. Esegue un'operazione di misurazione, M, che misura lo stato del qubit (Zero o One) e confronta il risultato con il valore specificato in desired.
  3. Se la misura non corrisponde al valore confrontato, esegue un'operazione X che capovolge lo stato del qubit in cui le probabilità che una misura restituisca Zero e One sono invertite. In questo modo, SetQubitState inserisce sempre il qubit di destinazione nello stato desiderato.

Testare la misura

Successivamente, per illustrare l'effetto dell'operazione SetQubitState, creare un'altra operazione denominata TestBellState.

Aggiungere l'operazione seguente al file Program.qs dopo l'operazione SetQubitState:

operation TestBellState(count : Int, initial : Result) : (Int, Int, Int, Int) {
    mutable numOnesQ1 = 0;
    mutable numOnesQ2 = 0;

    // 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' we saw:
        if resultQ1 == One {
            set numOnesQ1 += 1;
        }
        if resultQ2 == One {
            set numOnesQ2 += 1;
        }
    }

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

    // Return times we saw |0>, times we saw |1>
    Message("q1:Zero, One  q2:Zero, One");
    return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );

}

L'operazione TestBellState:

  1. Accetta due parametri: count, il numero di volte in cui eseguire una misurazione e initial, lo stato desiderato per inizializzare il qubit.
  2. Chiama l'istruzione use per inizializzare due qubit.
  3. Esegue cicli per count iterazioni. Per ogni ciclo
    1. Chiama SetQubitState per impostare un valore initial specificato sul primo qubit.
    2. Chiama SetQubitState di nuovo per impostare il secondo qubit su uno stato Zero.
    3. Usa l'operazione M per misurare ogni qubit.
    4. Archivia il numero di misure per ogni qubit che restituisce One.
  4. Al termine del ciclo, chiama di nuovo SetQubitState per reimpostare i qubit su uno stato noto (Zero) per consentire ad altri di allocare i qubit in uno stato noto. Questo passaggio è richiesto dall'istruzione use.
  5. Infine, usa la funzione Message per stampare un messaggio nella console prima di restituire i risultati.

Eseguire il codice dal prompt dei comandi

Prima di passare alle procedure per la sovrapposizione e l'entanglement, testare il codice fino a questo punto per visualizzare l'inizializzazione e la misurazione dei qubit.

Per eseguire il codice come programma autonomo, il compilatore Q# deve sapere dove avviare il programma quando si esegue il comando dotnet run. Questa operazione viene eseguita nel file Q# aggiungendo un elemento @EntryPoint() che precede direttamente l'operazione che si vuole eseguire, in questo caso l'operazione TestBellState.

Nota

@EntryPoint() è necessario solo per i programmi Q# autonomi. Quando si esegue un programma Q# in Jupyter Notebook o si chiama un programma Q# da un file host Python o .NET, non è necessario e genera un errore se incluso.

Il file program.qs dovrà risultare simile al seguente:

namespace Bell {
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Canon;

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

    @EntryPoint()
    operation TestBellState(count : Int, initial : Result) : (Int, Int, Int, Int) {
        mutable numOnesQ1 = 0;
        mutable numOnesQ2 = 0;

        // 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' we saw:
            if resultQ1 == One {
                set numOnesQ1 += 1;
            }
            if resultQ2 == One {
                set numOnesQ2 += 1;
            }
        }
    
        // reset the qubits
        SetQubitState(Zero, q1);             
        SetQubitState(Zero, q2);
        
    
        // Return times we saw |0>, times we saw |1>
        Message("q1:Zero, One  q2:Zero, One");
        return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );

    }
}

Per eseguire il programma, è necessario specificare gli argomenti count e initial dal prompt dei comandi. Ad esempio, --count 1000 e --initial One inizializzano il primo qubit su One e misurano ogni qubit 1000 volte. Eseguire il comando seguente:

dotnet run --count 1000 --initial One

si dovrebbe visualizzare l'output seguente:

Q1:Zero/One  Q2:Zero/One
(0, 1000, 1000, 0)

Poiché i qubit non sono ancora stati manipolati, hanno mantenuto i valori iniziali: il primo qubit restituisce One ogni volta e il secondo qubit restituisce Zero.

Se lo si esegue con --initial Zero, si dovrebbe osservare che anche il primo qubit restituisce Zero ogni volta.

dotnet run --count 1000 --initial Zero
Q1:Zero/One  Q2:Zero/One
(1000, 0, 1000, 0)

Posizionare un qubit in sovrapposizione

Attualmente, i qubit nel programma sono tutti in uno stato classico, ovvero sono 1 o 0. Questo è noto perché il programma inizializza i qubit su uno stato noto e non sono stati aggiunti processi per modificarli. Prima di eseguire l'entanglement dei qubit, si inserirà il primo qubit in uno stato di sovrapposizione, in cui una misurazione del qubit restituirà Zero il 50% delle volte e One il 50% delle volte. A livello concettuale, il qubit può essere considerato a metà tra Zero e One.

Per inserire un qubit in sovrapposizione, Q# fornisce l'operazione H, o Hadamard. Richiamare l'operazione X dalla procedura Inizializzare un qubit usando la misurazione precedente, che ha capovolto un qubit da 0 a 1 (o viceversa). L'operazione H capovolge il qubit a metà strada in uno stato di probabilità uguali pari a 0 o 1. Quando viene misurato, un qubit in sovrapposizione deve restituire approssimativamente un numero uguale di risultati Zero e One.

Modificare il codice nell'operazione TestBellState per includere l'operazione 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); 

A questo punto, quando si esegue il programma, è possibile visualizzare i risultati del primo qubit in sovrapposizione:

dotnet run --count 1000 --initial One
Q1:Zero/One  Q2:Zero/One
(523, 477, 1000, 0)      // results will vary

Ogni volta che si esegue il programma, i risultati per il primo qubit variano leggermente, ma saranno vicini al 50% One e al 50% Zero, mentre i risultati per il secondo qubit rimarranno sempre Zero.

dotnet run --count 1000 --initial One
Q1:Zero/One  Q2:Zero/One
(510, 490, 1000, 0)

L'inizializzazione del primo qubit in Zero restituisce risultati simili.

dotnet run --count 1000 --initial Zero
Q1:Zero/One  Q2:Zero/One
(504, 496, 1000, 0)

Eseguire l'entanglement di due qubit

Come accennato in precedenza, i qubit correlati tramite entanglement sono connessi in modo che non possano essere descritti in modo indipendente l'uno dall'altro. Ciò significa che qualsiasi operazione venga effettuata su un qubit, si verifica anche nel qubit correlato tramite entanglement. In questo modo è possibile conoscere lo stato risultante di un qubit senza misurarlo, semplicemente misurando lo stato dell'altro qubit. (Questo esempio usa due qubit, ma è anche possibile eseguire l'entanglement di tre o più qubit).

Per abilitare l'entanglement, Q# fornisce l'operazione CNOT, che sta per Controlled-NOT. Il risultato dell'esecuzione di questa operazione su due qubit è l'inversione del secondo qubit se il primo qubit è One.

Aggiungere l'operazione CNOT al programma immediatamente dopo l'operazione H. L'intero programma dovrebbe risultare simile al seguente:

namespace Bell {
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Canon;

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

    @EntryPoint()
    operation TestBellState(count : Int, initial : Result) : (Int, Int, Int, Int) {
        mutable numOnesQ1 = 0;
        mutable numOnesQ2 = 0;

        // 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' we saw:
            if resultQ1 == One {
                set numOnesQ1 += 1;
            }
            if resultQ2 == One {
                set numOnesQ2 += 1;
            }
        }
    
        // reset the qubits
        SetQubitState(Zero, q1);             
        SetQubitState(Zero, q2);
        
    
        // Return times we saw |0>, times we saw |1>
        Message("q1:Zero, One  q2:Zero, One");
        return (count - numOnesQ1, numOnesQ1, count - numOnesQ2, numOnesQ2 );

    }
}

Ora, quando si esegue il programma:

dotnet run --count 1000 --initial One
Q1:Zero/One  Q2:Zero/One
(502, 498, 502, 498)

Le statistiche per il primo qubit non sono cambiate (probabilità 50/50 di Zero o One dopo la misurazione), ma i risultati della misurazione per il secondo qubit sono sempre gli stessi della misura del primo qubit. L'operazione CNOT ha eseguito l'entanglement dei due qubit, così qualsiasi cosa accada a uno di essi, accade anche all'altro.

Passaggi successivi

Continuare a esplorare altri algoritmi quantistici e tecniche: