Vai al contenuto principale

Algoritmo di Shor

Per questo modulo di Qiskit in Classrooms, gli studenti devono disporre di un ambiente Python funzionante con i seguenti pacchetti installati:

  • qiskit v2.1.0 o successivo
  • qiskit-ibm-runtime v0.40.1 o successivo
  • qiskit-aer v0.17.0 o successivo
  • qiskit.visualization
  • numpy
  • pylatexenc

Per configurare e installare i pacchetti indicati, consulta la guida Installare Qiskit. Per eseguire job su veri computer quantistici, gli studenti dovranno creare un account IBM Quantum® seguendo i passaggi descritti nella guida Configura il tuo account IBM Cloud.

Questo modulo è stato testato e ha utilizzato tre secondi di tempo QPU. Si tratta di una stima indicativa. Il consumo effettivo può variare.

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime
# Uncomment and modify this line as needed to install dependencies
#!pip install 'qiskit>=2.1.0' 'qiskit-ibm-runtime>=0.40.1' 'qiskit-aer>=0.17.0' 'numpy' 'pylatexenc'

Introduzione​

All'inizio degli anni '90 cresceva l'entusiasmo attorno al potenziale dei computer quantistici di risolvere problemi difficili per i computer classici. Alcuni talentuosi informatici avevano ideato algoritmi che dimostravano la potenza del calcolo quantistico per problemi di nicchia e artificiosi, ma nessuno aveva ancora trovato la "killer app" definitiva del calcolo quantistico, destinata a rivoluzionare il settore. Fino al 1994, quando Peter Shor formulò quello che oggi è chiamato algoritmo di Shor per la fattorizzazione di numeri grandi.

Era noto all'epoca che trovare i fattori primi di un numero grande fosse estremamente difficile per un computer classico. I protocolli di sicurezza su Internet si basavano proprio su questa difficoltà. Shor trovò un modo per trovare questi fattori in modo esponenzialmente più efficiente, delegando alcune delle operazioni più complesse a un futuro, teorico computer quantistico.

In questo modulo esploreremo l'algoritmo di Shor. Prima forniremo un po' di contesto sull'algoritmo, formalizzando il problema che risolve e spiegandone la rilevanza per la cybersecurity. Poi daremo un'introduzione alla matematica modulare e a come applicarla al problema della fattorizzazione, mostrando come la fattorizzazione si riduca a un altro problema chiamato "ricerca dell'ordine". Mostreremo come entrino in gioco la Quantum Fourier Transform e la Quantum Phase Estimation, studiate in un modulo precedente, e come usarle per risolvere il problema della ricerca dell'ordine.

Infine, eseguiremo l'algoritmo di Shor su un vero computer quantistico! Tieni presente, però, che questo algoritmo sarà davvero utile solo quando avremo a disposizione un grande computer quantistico fault-tolerant, che è ancora a qualche anno di distanza. Quindi, fattorizzeremo soltanto un numero piccolo per dimostrare il funzionamento dell'algoritmo.

Il problema della fattorizzazione​

L'obiettivo del problema della fattorizzazione è trovare i fattori primi di un numero NN. Per alcuni numeri NN questo è piuttosto semplice. Ad esempio, se NN è pari, uno dei suoi fattori primi sarà 2. Se NN è una potenza di un primo, cioè N=pkN=p^k per qualche numero primo pp, è anche abbastanza facile trovare pp: basta approssimare la radice kesimak^{\text{esima}} di NN e cercare i numeri primi vicini che potrebbero essere pp.

Il punto in cui i computer classici faticano, però, è quando NN è dispari e non è una potenza di un primo. Questo è il caso che l'algoritmo di Shor affronta. L'algoritmo trova due fattori pp e qq tali che N=pqN=pq. Può essere applicato ricorsivamente finché tutti i fattori non sono primi. Nelle sezioni successive vedremo come affrontare questo problema.

Rilevanza per la cybersecurity​

Molti schemi crittografici sono stati costruiti basandosi sul fatto che fattorizzare numeri grandi è difficile, tra cui uno ampiamente usato oggi, chiamato RSA. In RSA, una chiave pubblica viene creata moltiplicando due grandi numeri primi per ottenere N=p⋅qN = p\cdot q. Chiunque può usare questa chiave pubblica per cifrare dati. Ma solo chi possiede la chiave privata, cioè pp e qq, può decifrare quei dati.

Se NN fosse facile da fattorizzare, chiunque sarebbe in grado di determinare pp e qq e rompere la cifratura. Ma non è così. Si tratta di un problema notoriamente difficile. In effetti, i fattori primi di un numero chiamato RSA1024, lungo 1024 bit e 309 cifre decimali, non sono stati ancora trovati, nonostante un premio di $100.000 fosse stato offerto per la sua fattorizzazione già nel 1991.

La soluzione di Shor​

Nel 1994, Peter Shor si rese conto che un computer quantistico avrebbe potuto fattorizzare un numero grande in modo esponenzialmente più efficiente rispetto a un computer classico. La sua intuizione si basava sulla relazione tra questo problema di fattorizzazione e l'aritmetica modulare. Daremo una breve introduzione all'aritmetica modulare, poi vedremo come usarla per fattorizzare NN.

Aritmetica modulare​

L'aritmetica modulare è un sistema di conteggio ciclico: il conteggio inizia nel modo consueto, con gli interi 0, 1, 2, ecc., ma a un certo punto, dopo un periodo NN, ricomincia da capo. Vediamolo con un esempio. Supponiamo che il periodo sia 5. Allora, nel conteggio, dove normalmente arriveremmo a 5, ricominciamo invece da 0:

0,1,2,3,4,0,1,2,3,4,0,1,2,...0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, ...

Questo perché nel "mondo modulo-5", 5 è equivalente a 0. Si dice che 5 mod 5 =05\bmod 5 \ = 0. In effetti, tutti i multipli di 5 saranno equivalenti a 0 mod 50\bmod 5.

Metti alla prova la tua comprensione​

Leggi la/le domanda/e qui sotto, pensa alla risposta, poi fai clic sul triangolo per rivelare la soluzione.

Usa l'aritmetica modulare per risolvere il seguente problema:

Parti per un lungo viaggio in treno transcontinentale alle 8:00. Il viaggio dura 60 ore. A che ora arrivi a destinazione?

Risposta:

Il periodo è 24, poiché ci sono 24 ore in un giorno. Quindi, questo problema può essere scritto in aritmetica modulare come:

(8+60)mod(24)=20(8+60)\text{mod}(24) = 20

Quindi arriveresti a destinazione alle 20:00.

ZN\mathbb{Z}_N e ZN∗\mathbb{Z}_N^*​

Spesso è utile introdurre due insiemi, ZN\mathbb{Z}_N e ZN∗\mathbb{Z}_N^*. ZN\mathbb{Z}_N è semplicemente l'insieme dei numeri che esistono nel "mondo modulo-NN". Ad esempio, quando contavamo modulo-5, l'insieme sarebbe Z5={0,1,2,3,4}\mathbb{Z}_5=\{0,1,2,3,4\}. Un altro esempio: Z15={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14}\mathbb{Z}_{15} = \{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14\}. Possiamo eseguire addizione e moltiplicazione (modulo NN) sugli elementi di ZN\mathbb{Z}_N, e il risultato di ciascuna di queste operazioni è anch'esso un elemento di ZN\mathbb{Z}_N, rendendo ZN\mathbb{Z}_N un oggetto matematico chiamato anello.

Esiste un sottoinsieme speciale di ZN\mathbb{Z}_N di particolare interesse per l'algoritmo di Shor. Si tratta del sottoinsieme dei numeri in ZN\mathbb{Z}_N tali che il massimo comune divisore tra ciascun elemento e NN sia 1, cioè ogni elemento è "coprimo" con NN. Se prendiamo l'insieme di questi numeri insieme all'operazione di moltiplicazione modulare, otteniamo un altro oggetto matematico, chiamato gruppo. Chiamiamo questo gruppo ZN∗\mathbb{Z}_N^*. Si scopre che con ZN∗\mathbb{Z}_N^* (e con i gruppi finiti in generale), se scegliamo un qualsiasi elemento a∈ZN∗a \in \mathbb{Z}_N^* e lo moltiplichiamo ripetutamente per se stesso, otterremo sempre il numero 11. Il numero minimo di volte in cui occorre moltiplicare aa per se stesso per ottenere 11 è chiamato ordine di aa. Questo fatto sarà molto importante nella nostra discussione su come fattorizzare numeri.

Metti alla prova la tua comprensione​

Leggi la/le domanda/e qui sotto, pensa alla risposta, poi fai clic sul triangolo per rivelare la soluzione.

Cos'è Z15∗\mathbb{Z}_{15}^*?

Risposta:

Z15∗={1,2,4,7,8,11,13,14}\mathbb{Z}_{15}^* = \{1,2,4,7,8,11,13,14\}

Abbiamo escluso i seguenti numeri:

3:GCD(3,15)=35:GCD(5,15)=56:GCD(6,15)=39:GCD(9,15)=310:GCD(10,15)=512:GCD(12,15)=3\begin{aligned} 3: GCD(3,15)=3 \\ 5: GCD(5,15)=5 \\ 6: GCD(6,15)=3 \\ 9: GCD(9,15)=3 \\ 10: GCD(10,15)=5 \\ 12: GCD(12,15)=3 \\ \end{aligned}

Qual è l'ordine di ciascuno degli elementi di Z15∗\mathbb{Z}_{15}^*?

Risposta:

L'ordine rr è il numero più basso tale che armod(15)=1a^r\text{mod}(15)=1 per ogni elemento aa.

11mod(15)=1,r=124mod(15)=1,r=442mod(15)=1,r=274mod(15)=1,r=484mod(15)=1,r=4112mod(15)=1,r=2134mod(15)=1,r=4142mod(15)=1,r=2\begin{aligned} 1^1\text{mod}(15) = 1, r=1 \\ 2^4\text{mod}(15) = 1, r=4 \\ 4^2\text{mod}(15) = 1, r=2 \\ 7^4\text{mod}(15) = 1, r=4 \\ 8^4\text{mod}(15) = 1, r=4 \\ 11^2\text{mod}(15) = 1, r=2 \\ 13^4\text{mod}(15) = 1, r=4 \\ 14^2\text{mod}(15) = 1, r=2 \\ \end{aligned}

Nota che, sebbene siamo riusciti a trovare l'ordine dei numeri in Z15∗\mathbb{Z}_{15}^*, questo NON è un compito facile in generale, per NN più grandi. Questo è il punto cruciale del problema della fattorizzazione e il motivo per cui abbiamo bisogno di un computer quantistico. Lo vedremo man mano che percorreremo il resto del notebook.

Applicare l'aritmetica modulare al problema della fattorizzazione​

La chiave per trovare i fattori pp e qq tali che N=pqN=pq consiste nel trovare un altro intero xx tale che

x2≡1 mod Nx^2 \equiv 1 \bmod N e x≢±1 mod N.x \not\equiv \pm 1 \bmod N.

Come ci aiuta trovare xx a trovare i fattori pp e qq? Vediamo il ragionamento. Poiché x2≡1 mod Nx^2 \equiv 1 \bmod N, ciò significa che x2−1≡0 mod Nx^2 - 1 \equiv 0 \bmod N . In altre parole, x2−1x^2 - 1 è un multiplo di NN. Quindi, per qualche intero ll,

x2−1=lNx^2 - 1 = l N

Possiamo fattorizzare x2−1x^2 - 1 per ottenere:

(x+1)(x−1)=lN(x+1)(x-1) = l N

Dalle nostre ipotesi iniziali sappiamo che x≢±1 mod Nx \not\equiv \pm 1 \bmod N, quindi NN non divide esattamente né x+1x+1 né x−1x-1. Pertanto, i due fattori di NN, pp e qq, devono ciascuno dividere x−1x-1 e x+1x+1. O pp è un fattore di x−1x-1 e qq è un fattore di x+1x+1, o viceversa. Quindi, se calcoliamo il massimo comune divisore (MCD) tra NN e sia x−1x-1 che x+1x+1, otterremo i fattori pp e qq. Calcolare il MCD tra due numeri è un compito classicamente semplice che può essere svolto, ad esempio, con l'algoritmo di Euclide.

Metti alla prova la tua comprensione​

Leggi la/le domanda/e qui sotto, pensa alla risposta, poi fai clic sul triangolo per rivelare la soluzione.

Potrebbe non essere facile seguire ogni passaggio logico di cui sopra, quindi prova a ragionare con un esempio. Usa N=15N=15 e x=11x=11. Prima, verifica che x2≡1mod(N)x^2 \equiv 1 \text{mod}(N) e x≢±1 mod Nx \not\equiv \pm 1 \bmod N. Poi continua a verificare ogni passaggio. Infine, calcola GCD(11±1,15)\text{GCD}(11\pm1,15) e verifica che siano i fattori di 1515.

Risposta:

112=12111^2 = 121, che è 15∗8+115*8 + 1, quindi 112 mod 15=111^2\bmod 15 = 1. ✓\checkmark

11−1=10 11 - 1 = 10, che non è equivalente a 0 mod 150\bmod 15. ✓\checkmark

11+1=12 11 + 1 = 12, che non è equivalente a 0 mod 150\bmod 15. ✓\checkmark

Ora sappiamo che (x+1)(x−1)=lN(x+1)(x-1) = l N per qualche intero ll. Ciò si verifica inserendo xx e NN: (12)(10)=l15(12)(10) = l 15 quando l=8l = 8. ✓\checkmark

Ora dobbiamo calcolare GCD(12,15)\text{GCD}(12,15) e GCD(10,15)\text{GCD}(10,15).

GCD(12,15)=3GCD(10,15)=5\begin{aligned} \text{GCD}(12,15) = 3 \\ \text{GCD}(10,15) = 5 \end{aligned}

Abbiamo trovato i fattori di 1515!

L'algoritmo​

Ora che abbiamo visto come trovare un intero xx tale che x2≡1 mod Nx^2 \equiv 1\bmod N ci aiuti a fattorizzare NN, possiamo descrivere l'algoritmo di Shor. Essenzialmente si riduce a trovare xx:

  1. Scegli un intero casuale Scegli un intero casuale aa tale che 1<a<N1 < a < N.
  • Calcola GCD(a,N)\text{GCD}(a, N) in modo classico.
    • Se GCD(a,N)>1\text{GCD}(a, N) > 1, hai già trovato un fattore. Fermati.
    • Altrimenti, continua.
  1. Trova l'ordine rr di aa modulo NN Trova il più piccolo intero positivo rr che soddisfa ar≡1(modN)a^r \equiv 1 \pmod N.

  2. Verifica se l'ordine è pari

  • Se rr è dispari, torna al passaggio 1 e scegli un nuovo aa.
  • Se rr è pari, continua al passaggio 4.
  1. Calcola x=ar/2 mod Nx = a^{r/2} \bmod N
  • Verifica che x≢1(modN)x \not\equiv 1 \pmod N e x≢−1(modN)x \not\equiv -1 \pmod N.
    • Se x≡±1(modN)x \equiv \pm 1 \pmod N, torna al passaggio 1 e scegli un nuovo aa.
  • Altrimenti, calcola i MCD per estrarre i fattori:
p=GCD(x−1,N),q=GCD(x+1,N)p = \text{GCD}(x-1, N), \quad q = \text{GCD}(x+1, N)

Questi saranno fattori non banali di NN.

  1. Fattorizza ricorsivamente se necessario
  • Se pp e/o qq non sono primi, applica l'algoritmo ricorsivamente per fattorizzarli completamente.
  • Una volta che tutti i fattori sono primi, la fattorizzazione è completa.

In base a questa procedura, potrebbe non essere ovvio perché sia necessario un computer quantistico per completare il compito. È necessario perché il passaggio 2, trovare l'ordine di aa modulo NN, è classicamente un problema molto difficile. La complessità cresce esponenzialmente con NN. Ma con un computer quantistico, dobbiamo solo usare la Quantum Phase Estimation per risolverlo. Il passaggio 4, trovare il MCD di due interi, è in realtà un'operazione abbastanza semplice da fare in modo classico. Quindi, l'unico passaggio che richiede davvero la potenza di un computer quantistico è quello della ricerca dell'ordine. Si dice che il problema della fattorizzazione si "riduce" al problema della ricerca dell'ordine.

La parte difficile: la ricerca dell'ordine​

Ora vedremo come usare un computer quantistico per la ricerca dell'ordine. Prima di tutto, chiariamo cosa intendiamo per "ordine". Naturalmente, ti ho già spiegato il significato matematico dell'ordine: è il primo intero non nullo rr tale che ar=1(modN).a^r = 1 \pmod N. Ma vediamo se riusciamo a sviluppare un po' più di intuizione per questo concetto.

Per NN abbastanza piccoli, possiamo determinare l'ordine semplicemente calcolando ogni potenza di aa, prendendo il modulo NN di quel numero, poi fermandoci quando troviamo la potenza rr che soddisfa ar=1mod(N)a^r = 1 \text{mod}(N). È quello che abbiamo fatto con il nostro esempio, N=15N=15, sopra. Diamo un'occhiata ad alcuni grafici di queste potenze modulari per alcuni valori campione di aa e NN:

Valore di a alla potenza di k modulo N rispetto alla potenza k, con a=2 e N=15. Si vede che al crescere di k emerge uno schema ripetitivo, mostrando che a^k modulo N è periodico in k.

Valore di a alla potenza di k modulo N rispetto alla potenza k, con a=5 e N=21. Si vede che al crescere di k emerge uno schema ripetitivo, mostrando che a^k modulo N è periodico in k.

Noti qualcosa? Sono funzioni periodiche! E l'ordine rr è uguale al periodo! Quindi, la ricerca dell'ordine è equivalente alla ricerca del periodo.

I computer quantistici sono particolarmente adatti a trovare il periodo delle funzioni. Per questo, possiamo usare una subroutine algoritmica chiamata Quantum Phase Estimation. Abbiamo discusso della QPE e della sua relazione con la Quantum Fourier Transform nel modulo precedente. Per un ripasso dettagliato, vai al modulo sulla QFT o alla lezione di John Watrous sulla Quantum Phase Estimation nel suo corso di Algoritmi Quantistici. Qui riassumiamo il procedimento:

Nella Quantum Phase Estimation (QPE), partiamo da un unitario UU e da un autostato di quell'unitario ∣ψ⟩|\psi\rangle. Poi usiamo QPE per approssimare l'autovalore corrispondente, che, poiché l'operatore è unitario, sarà della forma e2πiθe^{2\pi i \theta}. Quindi, trovare l'autovalore è equivalente a trovare il valore di θ\theta nella funzione periodica. Il circuito ha questo aspetto:

Diagramma del circuito della procedura di Quantum Phase Estimation. Gli m qubit di controllo superiori sono preparati in sovrapposizione con porte Hadamard, poi porte unitarie controllate vengono applicate ai qubit inferiori, che si trovano in un autostato dell&#39;unitario. Infine, una trasformata di Fourier quantistica inversa viene applicata ai qubit superiori e questi vengono misurati.

dove il numero di qubit di controllo (gli mm qubit superiori nella figura sopra) determina la precisione dell'approssimazione.

Nell'algoritmo di Shor, usiamo QPE sull'operatore unitario MaM_a:

Ma∣y⟩≡∣aymod  N⟩. M_a|y\rangle \equiv |ay \mod N \rangle .

Qui, ∣y⟩|y\rangle denota uno stato della base computazionale del registro multi-qubit, dove il valore binario dei qubit corrisponde all'intero yy. Ad esempio, se N=15N=15 e y=2y = 2, allora ∣y⟩|y\rangle è rappresentato dallo stato della base a quattro qubit ∣0010⟩|0010\rangle, poiché quattro qubit sono necessari per codificare numeri fino a 15. (Se questo concetto non è familiare, consulta il modulo introduttivo di Qiskit in classrooms per un ripasso sulla codifica binaria degli stati quantistici.)

Ora dobbiamo trovare un autostato di questo unitario. Se partiamo dallo stato ∣1⟩|1\rangle, possiamo vedere che ogni successiva applicazione di UU moltiplicherà lo stato del nostro registro per a(modN)a \pmod N, e dopo rr applicazioni torneremo allo stato ∣1⟩|1\rangle. Ad esempio con a=3a = 3 e N=35N = 35:

M3∣1⟩=∣3⟩M32∣1⟩=∣9⟩M33∣1⟩=∣27⟩⋮M3(r−1)∣1⟩=∣12⟩M3r∣1⟩=∣1⟩\begin{aligned} M_3|1\rangle &= |3\rangle & \\ M_3^2|1\rangle &= |9\rangle \\ M_3^3|1\rangle &= |27\rangle \\ & \vdots \\ M_3^{(r-1)}|1\rangle &= |12\rangle \\ M_3^r|1\rangle &= |1\rangle \end{aligned}

Quindi le sovrapposizioni degli stati in questo ciclo (∣ψj⟩|\psi_j\rangle) della forma:

∣ψj⟩=1r∑k=0r−1e2πijkr∣ak⟩|\psi_j\rangle = \tfrac{1}{\sqrt{r}}\sum_{k=0}^{r-1}{e^{\frac{2 \pi i j k}{r}} |a^k \rangle}

sono tutti autostati di MaM_a. (Ci sono più autostati di questi. Ma a noi interessano solo quelli della forma precedente.)

Metti alla prova la tua comprensione​

Leggi la/le domanda/e qui sotto, pensa alla risposta, poi fai clic sul triangolo per rivelare la soluzione.

Trova un autostato dell'unitario corrispondente a a=2a=2 e N=15N = 15.

Risposta:

M2∣1⟩=∣2⟩M22∣1⟩=∣4⟩M23∣1⟩=∣8⟩M24∣1⟩=∣1⟩\begin{aligned} M_2|1\rangle &= |2\rangle & \\ M_2^2|1\rangle &= |4\rangle \\ M_2^3|1\rangle &= |8\rangle \\ M_2^4|1\rangle &= |1\rangle \\ \end{aligned}

Quindi, l'ordine è r=4r=4. Gli autostati che ci interessano saranno una sovrapposizione uguale di tutti gli stati attraversati sopra, con varie fasi:

∣ψ0⟩=12(∣1⟩+∣2⟩+∣4⟩+∣8⟩)∣ψ1⟩=12(e2πi04∣1⟩+e2πi14∣2⟩+e2πi24∣4⟩+e2πi34∣8⟩)=12(∣1⟩+i∣2⟩−∣4⟩−i∣8⟩)∣ψ2⟩=12(e2πi04∣1⟩+e2πi24∣2⟩+e2πi44∣4⟩+e2πi64∣8⟩)=12(∣1⟩−∣2⟩+∣4⟩−∣8⟩)∣ψ3⟩=12(e2πi04∣1⟩+e2πi34∣2⟩+e2πi64∣4⟩+e2πi94∣8⟩)=12(∣1⟩−i∣2⟩−∣4⟩+i∣8⟩)\begin{aligned} |\psi_0\rangle &= \frac{1}{2}(|1\rangle+|2\rangle+|4\rangle+|8\rangle) \\ |\psi_1\rangle &= \frac{1}{2}(e^{2 \pi i \frac{0}{4}}|1\rangle+e^{2 \pi i \frac{1}{4}}|2\rangle+e^{2 \pi i \frac{2}{4}}|4\rangle+e^{2 \pi i \frac{3}{4}}|8\rangle) \\ &= \frac{1}{2}(|1\rangle+i|2\rangle-|4\rangle-i|8\rangle) \\ |\psi_2\rangle &= \frac{1}{2}(e^{2 \pi i \frac{0}{4}}|1\rangle+e^{2 \pi i \frac{2}{4}}|2\rangle+e^{2 \pi i \frac{4}{4}}|4\rangle+e^{2 \pi i \frac{6}{4}}|8\rangle) \\ &= \frac{1}{2}(|1\rangle-|2\rangle+|4\rangle-|8\rangle) \\ |\psi_3\rangle &= \frac{1}{2}(e^{2 \pi i \frac{0}{4}}|1\rangle+e^{2 \pi i \frac{3}{4}}|2\rangle+e^{2 \pi i \frac{6}{4}}|4\rangle+e^{2 \pi i \frac{9}{4}}|8\rangle) \\ &= \frac{1}{2}(|1\rangle-i|2\rangle-|4\rangle+i|8\rangle) \\ \end{aligned}

Supponiamo di riuscire a inizializzare il nostro stato di qubit in uno di questi autostati (spoiler — non è possibile. O almeno non facilmente. Spiegheremo perché e cosa possiamo fare in alternativa a breve). Potremmo quindi usare QPE per stimare l'autovalore corrispondente, ωj=e2πiθj\omega_j = e^{2 \pi i \theta_j} dove θj=jr\theta_j = \frac{j}{r}. Poi saremo in grado di determinare l'ordine rr con la semplice equazione:

r=jθj.r = \frac{j}{\theta_j}.

Ma ricorda, ho detto che QPE stima θj\theta_j — non ci dà un valore esatto. La stima deve essere sufficientemente buona da distinguere tra rr e r+1r+1. Più qubit di controllo mm abbiamo, migliore sarà la stima. Nei problemi alla fine della lezione ti verrà chiesto di determinare il minimo mm necessario per fattorizzare un numero NN.

Ora dobbiamo affrontare un problema. Tutta la spiegazione precedente su come trovare rr inizia preparando l'autostato ∣ψj⟩=1r∑k=0r−1e2πijkr∣ak⟩|\psi_j\rangle = \tfrac{1}{\sqrt{r}}\sum_{k=0}^{r-1}{e^{\frac{2 \pi i j k}{r}} |a^k \rangle}. Ma non sappiamo come farlo senza già conoscere rr. Il ragionamento è circolare. Abbiamo bisogno di un modo per stimare l'autovalore senza inizializzare l'autostato.

Invece di partire da un autostato di MaM_a, possiamo preparare lo stato iniziale nello stato a nn qubit corrispondente a ∣1⟩|1\rangle in binario (cioè, ∣000...01⟩|000...01\rangle). Sebbene questo stato non sia ovviamente un autostato di MaM_a, è una sovrapposizione di tutti gli autostati ∣ψk⟩|\psi_k\rangle:

∣1⟩=1r∑k=0r−1∣ψk⟩|1\rangle = \frac{1}{\sqrt{r}} \sum\limits_{k=0}^{r-1}{|\psi_k\rangle}

Metti alla prova la tua comprensione​

Leggi la/le domanda/e qui sotto, pensa alla risposta, poi fai clic sul triangolo per rivelare la soluzione.

Verifica che ∣1⟩|1\rangle sia equivalente alla sovrapposizione degli autostati che hai trovato per N=15N=15 e a=2a=2 nella domanda di verifica precedente.

Risposta:

I quattro autostati erano:

∣ψ0⟩=12(∣1⟩+∣2⟩+∣4⟩+∣8⟩)∣ψ1⟩=12(∣1⟩+i∣2⟩−∣4⟩−i∣8⟩)∣ψ2⟩=12(∣1⟩−∣2⟩+∣4⟩−∣8⟩)∣ψ3⟩=12(∣1⟩−i∣2⟩−∣4⟩+i∣8⟩)\begin{aligned} |\psi_0\rangle &= \frac{1}{2}(|1\rangle+|2\rangle+|4\rangle+|8\rangle) \\ |\psi_1\rangle &= \frac{1}{2}(|1\rangle+i|2\rangle-|4\rangle-i|8\rangle) \\ |\psi_2\rangle &= \frac{1}{2}(|1\rangle-|2\rangle+|4\rangle-|8\rangle) \\ |\psi_3\rangle &= \frac{1}{2}(|1\rangle-i|2\rangle-|4\rangle+i|8\rangle) \\ \end{aligned}

Quindi,

1r∑k=0r−1∣ψk⟩=12(∣ψ0⟩+∣ψ1⟩+∣ψ2⟩+∣ψ3⟩)=14(∣1⟩+∣2⟩+∣4⟩+∣8⟩+∣1⟩+i∣2⟩−∣4⟩−i∣8⟩+∣1⟩−∣2⟩+∣4⟩−∣8⟩+∣1⟩−i∣2⟩−∣4⟩+i∣8⟩)=14(4∣1⟩)=∣1⟩\begin{aligned} \frac{1}{\sqrt{r}} \sum\limits_{k=0}^{r-1}{|\psi_k\rangle} &= \frac{1}{2}(|\psi_0\rangle + |\psi_1\rangle + |\psi_2\rangle + |\psi_3\rangle ) \\ &= \frac{1}{4}(|1\rangle+|2\rangle+|4\rangle+|8\rangle+|1\rangle+i|2\rangle-|4\rangle-i|8\rangle+|1\rangle-|2\rangle+|4\rangle-|8\rangle + |1\rangle-i|2\rangle-|4\rangle+i|8\rangle) \\ &= \frac{1}{4}(4|1\rangle) = |1\rangle \end{aligned}

Come ci permette questo di trovare l'ordine rr? Poiché lo stato iniziale è una sovrapposizione di tutti gli autostati della forma elencata sopra, l'algoritmo QPE stima simultaneamente ciascuno dei θk\theta_k corrispondenti a questi autostati. Quindi, la misurazione degli mm qubit di controllo alla fine produrrà un'approssimazione del valore k/rk/r, dove k∈{0,1,2,...,r−1}k \in \{0,1,2,...,r-1\} è uno degli autovalori scelto casualmente. Se ripetiamo questo circuito alcune volte e otteniamo alcuni campioni con valori diversi di kk, saremo in grado di dedurre rapidamente rr.

Implementazione in Qiskit​

Come accennato in precedenza, l'hardware attuale non è ancora in grado di fattorizzare numeri enormi come RSA1024. Fattorizzeremo quindi un numero piccolo per dimostrare come funziona l'algoritmo. Per questa demo useremo una versione semplificata del codice presentato nel tutorial sull'algoritmo di Shor. Se desideri maggiori dettagli, visita il tutorial.

Eseguiremo l'algoritmo usando il nostro framework standard per la risoluzione di problemi quantistici, il framework Qiskit patterns. Questo consiste in quattro passi:

  1. Mappare il problema su un circuito quantistico
  2. Ottimizzare il circuito per l'esecuzione sull'hardware quantistico
  3. Eseguire il circuito sul computer quantistico
  4. Post-elaborare le misurazioni

1. Mappatura​

Fattorizziamo N=15N=15, scegliendo a=2a=2 come intero coprimo.

Per prima cosa, dobbiamo costruire il circuito che implementa l'unitario di moltiplicazione modulare MaM_a. Questa è in realtà la parte più difficile dell'intera implementazione e può essere computazionalmente molto costosa, a seconda di come viene eseguita. Per questo, barereremo un po': sappiamo che iniziamo nello stato ∣1⟩|1\rangle e da una domanda di verifica precedente,

M2∣1⟩=∣2⟩M2∣2⟩=∣4⟩M2∣4⟩=∣8⟩M2∣8⟩=∣1⟩\begin{aligned} M_2|1\rangle &= |2\rangle & \\ M_2|2\rangle &= |4\rangle \\ M_2|4\rangle &= |8\rangle \\ M_2|8\rangle &= |1\rangle \\ \end{aligned}

Costruiremo quindi un unitario che esegue le operazioni corrette su questi quattro stati, lasciando invariati tutti gli altri. Questo è un trucco perché stiamo usando la nostra conoscenza dell'ordine di 2 mod 152\bmod 15 per semplificare l'unitario. Se stessimo davvero cercando di fattorizzare un numero i cui fattori ci sono sconosciuti, non potremmo farlo.

Verifica della comprensione​

Leggi la/le domanda/e seguenti, pensa alla tua risposta, poi clicca il triangolo per scoprire la soluzione.

Con la tua conoscenza di come l'operatore M2M_2 trasforma gli stati di cui sopra, costruisci l'operatore a partire da una serie di gate SWAP, che scambiano gli stati di due qubit. (Suggerimento: scrivere ogni stato ∣i⟩|i\rangle in binario può aiutare.)

Risposta:

Riscriviamo l'azione di M2M_2 sugli stati in binario:

M2∣0001⟩=∣0010⟩M2∣0010⟩=∣0100⟩M2∣0100⟩=∣1000⟩M2∣1000⟩=∣0001⟩\begin{aligned} M_2|0001\rangle &= |0010\rangle \\ M_2|0010\rangle &= |0100\rangle \\ M_2|0100\rangle &= |1000\rangle \\ M_2|1000\rangle &= |0001\rangle \\ \end{aligned}

Ciascuna di queste azioni può essere realizzata con un semplice SWAP. M2∣0001⟩M_2|0001\rangle si ottiene scambiando gli stati del qubit 00 e 11. M2∣0010⟩M_2|0010\rangle si ottiene scambiando gli stati del qubit 11 e 22. E così via. Possiamo quindi decomporre la matrice M2M_2 nella seguente serie di gate SWAP:

M2=SWAP(0,1)SWAP(1,2)SWAP(2,3)M_2 = SWAP(0,1)SWAP(1,2)SWAP(2,3)

Ricordando che gli operatori agiscono da destra a sinistra, verifichiamo che questo abbia l'effetto desiderato su ciascuno degli stati:

M2∣0001⟩=SWAP(0,1)SWAP(1,2)SWAP(2,3)∣0001⟩=SWAP(0,1)SWAP(1,2)∣0001⟩=SWAP(0,1)∣0001⟩=∣0010⟩✓M2∣0010⟩=SWAP(0,1)SWAP(1,2)SWAP(2,3)∣0010⟩=SWAP(0,1)SWAP(1,2)∣0010⟩=SWAP(0,1)∣0100⟩=∣0100⟩✓M2∣0100⟩=SWAP(0,1)SWAP(1,2)SWAP(2,3)∣0100⟩=SWAP(0,1)SWAP(1,2)∣1000⟩=SWAP(0,1)∣1000⟩=∣1000⟩✓M2∣1000⟩=SWAP(0,1)SWAP(1,2)SWAP(2,3)∣1000⟩=SWAP(0,1)SWAP(1,2)∣0100⟩=SWAP(0,1)∣0010⟩=∣0001⟩✓\begin{aligned} M_2|0001\rangle &= SWAP(0,1)SWAP(1,2)SWAP(2,3)|0001\rangle \\ &= SWAP(0,1)SWAP(1,2)|0001\rangle \\ &= SWAP(0,1)|0001\rangle \\ &=|0010\rangle \checkmark \\ M_2|0010\rangle &= SWAP(0,1)SWAP(1,2)SWAP(2,3)|0010\rangle \\ &= SWAP(0,1)SWAP(1,2)|0010\rangle \\ &= SWAP(0,1)|0100\rangle \\ &=|0100\rangle \checkmark \\ M_2|0100\rangle &= SWAP(0,1)SWAP(1,2)SWAP(2,3)|0100\rangle \\ &= SWAP(0,1)SWAP(1,2)|1000\rangle \\ &= SWAP(0,1)|1000\rangle \\ &=|1000\rangle \checkmark \\ M_2|1000\rangle &= SWAP(0,1)SWAP(1,2)SWAP(2,3)|1000\rangle \\ &= SWAP(0,1)SWAP(1,2)|0100\rangle \\ &= SWAP(0,1)|0010\rangle \\ &=|0001\rangle \checkmark \\ \end{aligned}

Possiamo ora programmare il circuito equivalente a questo operatore in Qiskit.

Per prima cosa, importiamo i pacchetti necessari:

# Import necessary packages

import numpy as np
from fractions import Fraction
from math import floor, gcd, log

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.library import QFTGate
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.visualization import plot_histogram

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2 as Sampler

Poi, costruiamo l'operatore M2M_2:

def M2mod15():
"""
M2 (mod 15)
"""
b = 2
U = QuantumCircuit(4)

U.swap(2, 3)
U.swap(1, 2)
U.swap(0, 1)

U = U.to_gate()
U.name = f"M_{b}"

return U
# Get the M2 operator
M2 = M2mod15()

# Add it to a circuit and plot
circ = QuantumCircuit(4)
circ.compose(M2, inplace=True)
circ.decompose(reps=2).draw(output="mpl", fold=-1)

Output of the previous code cell

L'algoritmo QPE usa un gate controlled-UU. Ora che abbiamo un circuito M2M_2, dobbiamo renderlo un circuito controlled-M2M_2:

def controlled_M2mod15():
"""
Controlled M2 (mod 15)
"""
b = 2
U = QuantumCircuit(4)

U.swap(2, 3)
U.swap(1, 2)
U.swap(0, 1)

U = U.to_gate()
U.name = f"M_{b}"
c_U = U.control()

return c_U
# Get the controlled-M2 operator
controlled_M2 = controlled_M2mod15()

# Add it to a circuit and plot
circ = QuantumCircuit(5)
circ.compose(controlled_M2, inplace=True)
circ.decompose(reps=1).draw(output="mpl", fold=-1)

Output of the previous code cell

Abbiamo ora il nostro gate controlled-UU. Ma per eseguire l'algoritmo di Quantum Phase Estimation, avremo bisogno di controlled-U2U^2, controlled-U4U^4, fino a controlled-U2m−1U^{2^{m-1}}, dove mm è il numero di qubit usati per stimare la fase. Più qubit ci sono, più precisa sarà la stima della fase. Useremo m=8m=8 qubit di controllo per la nostra procedura di stima della fase. Abbiamo quindi bisogno di:

Ma2k∣y⟩≡∣a2ky mod N⟩M_{a^{2^k}}|y\rangle \equiv |a^{2^k} y \bmod N \rangle

dove l'indice kk, con 0≤k≤m−1=70 \le k \le m-1 = 7, corrisponde al qubit di controllo. Ora calcoliamo a2k mod Na^{2^k}\bmod N per ogni valore di kk:

def a2kmodN(a, k, N):
"""Compute a^{2^k} (mod N) by repeated squaring"""
for _ in range(k):
a = int(np.mod(a**2, N))
return a
k_list = range(8)
b_list = [a2kmodN(2, k, 15) for k in k_list]

print(b_list)
[2, 4, 1, 1, 1, 1, 1, 1]

Poiché a2k mod N=1a^{2^k} \bmod N = 1 per k≥2k \ge 2, tutti gli operatori corrispondenti (M8M_8 e superiori) sono equivalenti all'identità. Dobbiamo quindi costruire solo un'altra matrice, M4.M_4.

Nota: Questa semplificazione funziona qui solo perché l'ordine di 2 mod 152 \bmod 15 è 44. Una volta raggiunto k=2k=2 (cioè 2k=42^k = 4), ogni potenza successiva dell'operatore è l'identità. In generale, per numeri NN più grandi o diverse scelte di aa, non è possibile saltare la costruzione delle potenze superiori. Questo è uno dei motivi per cui si tratta di un esempio giocattolo: i numeri piccoli consentono scorciatoie che non funzionerebbero per casi più grandi.

def M4mod15():
"""
M4 (mod 15)
"""
b = 4
U = QuantumCircuit(4)

U.swap(1, 3)
U.swap(0, 2)

U = U.to_gate()
U.name = f"M_{b}"

return U
# Get the M4 operator
M4 = M4mod15()

# Add it to a circuit and plot
circ = QuantumCircuit(4)
circ.compose(M4, inplace=True)
circ.decompose(reps=2).draw(output="mpl", fold=-1)

Output of the previous code cell

E come in precedenza, lo rendiamo un operatore controlled-M4M_4:

def controlled_M4mod15():
"""
Controlled M4 (mod 15)
"""
b = 4
U = QuantumCircuit(4)

U.swap(1, 3)
U.swap(0, 2)

U = U.to_gate()
U.name = f"M_{b}"
c_U = U.control()

return c_U
# Get the controlled-M4 operator
controlled_M4 = controlled_M4mod15()

# Add it to a circuit and plot
circ = QuantumCircuit(5)
circ.compose(controlled_M4, inplace=True)
circ.decompose(reps=1).draw(output="mpl", fold=-1)

Output of the previous code cell

Ora possiamo mettere tutto insieme per trovare l'ordine di 2 mod 152\bmod 15 con un circuito quantistico, usando la stima della fase:

# Order finding problem for N = 15 with a = 2
N = 15
a = 2

# Number of qubits
num_target = floor(log(N - 1, 2)) + 1 # for modular exponentiation operators
num_control = 2 * num_target # for enough precision of estimation

# List of M_b operators in order
k_list = range(num_control)
b_list = [a2kmodN(2, k, 15) for k in k_list]

# Initialize the circuit
control = QuantumRegister(num_control, name="C")
target = QuantumRegister(num_target, name="T")
output = ClassicalRegister(num_control, name="out")
circuit = QuantumCircuit(control, target, output)

# Initialize the target register to the state |1>
circuit.x(num_control)

# Add the Hadamard gates and controlled versions of the
# multiplication gates
for k, qubit in enumerate(control):
circuit.h(k)
b = b_list[k]
if b == 2:
circuit.compose(
M2mod15().control(), qubits=[qubit] + list(target), inplace=True
)
elif b == 4:
circuit.compose(
M4mod15().control(), qubits=[qubit] + list(target), inplace=True
)
else:
continue # M1 is the identity operator

# Apply the inverse QFT to the control register
circuit.compose(QFTGate(num_control).inverse(), qubits=control, inplace=True)

# Measure the control register
circuit.measure(control, output)

circuit.draw("mpl", fold=-1)

Output of the previous code cell

2. Ottimizzazione​

Ora che abbiamo mappato il nostro circuito, il passo successivo è ottimizzarlo per l'esecuzione su un particolare computer quantistico. Prima dobbiamo caricare il backend.

service = QiskitRuntimeService()

backend = service.backend("ibm_marrakesh")

Se non hai tempo disponibile sul tuo account o vuoi usare un simulatore per qualsiasi motivo, puoi eseguire la cella seguente per configurare un simulatore che imita il dispositivo quantistico che abbiamo selezionato sopra:

pm = generate_preset_pass_manager(optimization_level=2, backend=backend)

transpiled_circuit = pm.run(circuit)

print(f"2q-depth: {transpiled_circuit.depth(lambda x: x.operation.num_qubits==2)}")
print(f"2q-size: {transpiled_circuit.size(lambda x: x.operation.num_qubits==2)}")
print(f"Operator counts: {transpiled_circuit.count_ops()}")
transpiled_circuit.draw(output="mpl", fold=-1, style="clifford", idle_wires=False)
2q-depth: 188
2q-size: 281
Operator counts: OrderedDict({'sx': 548, 'rz': 380, 'cz': 281, 'measure': 8, 'x': 6})

Output of the previous code cell

3. Esecuzione​

# Sampler primitive to obtain the probability distribution
sampler = Sampler(backend)

# Turn on dynamical decoupling with sequence XpXm
sampler.options.dynamical_decoupling.enable = True
sampler.options.dynamical_decoupling.sequence_type = "XpXm"
# Enable gate twirling
sampler.options.twirling.enable_gates = True

pub = transpiled_circuit
job = sampler.run([pub], shots=1024)
result = job.result()[0]
counts = result.data["out"].get_counts()
plot_histogram(counts, figsize=(35, 5))

Output of the previous code cell

Vediamo quattro picchi chiari in corrispondenza di 00000000, 01000000, 10000000 e 11000000, con alcuni conteggi in altre stringhe di bit dovuti al rumore del computer quantistico. Li ignoreremo e terremo solo i quattro dominanti imponendo una soglia: solo i conteggi sopra questa soglia sono considerati un segnale vero al di sopra del rumore.

# Dictionary of bitstrings and their counts to keep
counts_keep = {}
# Threshold to filter
threshold = np.max(list(counts.values())) / 2

for key, value in counts.items():
if value > threshold:
counts_keep[key] = value

print(counts_keep)

4. Post-elaborazione​

Per l'algoritmo di Shor, gran parte dell'algoritmo viene eseguita classicamente. Quindi, metteremo il resto nel passo di "post-elaborazione", dopo aver ottenuto le misurazioni dal computer quantistico. Ciascuna delle misurazioni di cui sopra può essere convertita in interi che, dopo la divisione per 2m2^m, sono le nostre approssimazioni di kr\frac{k}{r}, dove kk è casuale ogni volta.

a = 2
N = 15

FACTOR_FOUND = False
num_attempt = 0

while not FACTOR_FOUND:
print(f"\nATTEMPT {num_attempt}:")
# Here, we get the bitstring by iterating over outcomes
# of a previous hardware run with multiple shots.
# Instead, we can also perform a single-shot measurement
# here in the loop.
bitstring = list(counts_keep.keys())[num_attempt]
num_attempt += 1
# Find the phase from measurement
decimal = int(bitstring, 2)
phase = decimal / (2**num_control) # phase = k / r
print(f"Phase: theta = {phase}")

# Guess the order from phase
frac = Fraction(phase).limit_denominator(N)
r = frac.denominator # order = r
print(f"Order of {a} modulo {N} estimated as: r = {r}")

if phase != 0:
# Guesses for factors are gcd(a^{r / 2} ± 1, 15)
if r % 2 == 0:
x = pow(a, r // 2, N) - 1
d = gcd(x, N)
if d > 1:
FACTOR_FOUND = True
print(f"*** Non-trivial factor found: {x} ***")
ATTEMPT 0:
Phase: theta = 0.0
Order of 2 modulo 15 estimated as: r = 1

ATTEMPT 1:
Phase: theta = 0.75
Order of 2 modulo 15 estimated as: r = 4
*** Non-trivial factor found: 3 ***

Conclusione​

Dopo aver percorso il modulo, potresti aver maturato una nuova ammirazione per la brillantezza di Peter Shor nell'aver ideato un algoritmo così ingegnoso. Ma speriamo che tu abbia anche raggiunto un livello più profondo di comprensione della sua semplicità ingannevole. Anche se l'algoritmo può sembrare impressionantemente (o intimidatoriamente) complesso, se lo scomponi in ogni singolo passaggio logico e lo affronti con calma, anche tu sarai in grado di eseguire l'algoritmo di Shor.

Sebbene siamo ancora lontani dall'usare questo algoritmo per fattorizzare numeri come RSA1024, i nostri computer quantistici migliorano ogni giorno e, una volta raggiunta una soglia chiamata tolleranza ai guasti, algoritmi come questi seguiranno presto. È un momento entusiasmante per imparare il calcolo quantistico!

Problemi​

Concetti fondamentali:​

  • I moderni sistemi crittografici si basano sulla difficoltà classica di fattorizzare interi di grandi dimensioni.
  • L'aritmetica modulare — incluse le strutture ZN\mathbb{Z}_N e ZN∗\mathbb{Z}_N^* — fornisce la base matematica per l'algoritmo di Shor.
  • Il problema della fattorizzazione di un intero NN può essere ricondotto al problema di trovare l'ordine di un numero modulo NN.
  • La ricerca dell'ordine quantistico utilizza tecniche di stima della fase quantistica per determinare il periodo della funzione axmod  Na^x \mod N.
  • L'algoritmo di Shor consiste in un flusso di lavoro ibrido classico-quantistico che seleziona una base, esegue la ricerca dell'ordine quantistico e poi calcola classicamente i fattori dal risultato.

Vero/Falso:​

  1. V/F L'efficienza dell'algoritmo di Shor minaccia la sicurezza della crittografia RSA.
  2. V/F L'algoritmo di Shor può essere eseguito efficientemente su qualsiasi computer quantistico moderno.
  3. V/F L'algoritmo di Shor utilizza la stima della fase quantistica (QPE) come subroutine chiave.
  4. V/F La parte classica dell'algoritmo di Shor prevede il calcolo del massimo comune divisore (MCD).
  5. V/F L'algoritmo di Shor funziona solo per la fattorizzazione di numeri pari.
  6. V/F Un'esecuzione riuscita dell'algoritmo di Shor garantisce sempre i fattori corretti.

Risposta breve:​

  1. Perché l'algoritmo di Shor è considerato una potenziale minaccia futura per la crittografia RSA?
  2. Perché trovare il periodo, o ordine, di una funzione esponenziale modulare è utile per fattorizzare un numero nell'algoritmo di Shor?

Problemi avanzati:​

  1. Di quanti qubit di controllo mm abbiamo bisogno per un dato numero NN che stiamo cercando di fattorizzare, al fine di ottenere la precisione nella QPE necessaria a trovare il valore corretto dell'ordine rr?

  2. Seguendo la procedura che abbiamo illustrato qui per fattorizzare 15, prova ora a fattorizzare 21.