{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "78090c95-8fea-4731-a74a-515150e4f899",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Algoritmo di Shor\"\n",
        "description: \"Impara a usare l'algoritmo di Shor per scomporre i numeri composti in fattori primi.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore checkmark */}\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "64abf08d-8e69-41f9-b802-8c0fbbc37555",
      "metadata": {},
      "source": [
        "<span id=\"shors-algorithm\" />\n",
        "\n",
        "# Algoritmo di Shor\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c257cbc3-7730-40cc-a240-19e57033d582",
      "metadata": {},
      "source": [
        "Per questo modulo Qiskit in Classrooms, gli studenti devono disporre di un ambiente di Python lavoro con i seguenti pacchetti installati:\n",
        "\n",
        "* v2.1.0`qiskit` o più recente\n",
        "* v0.40.1`qiskit-ibm-runtime` o più recente\n",
        "* v0.17.0`qiskit-aer` o più recente\n",
        "* `qiskit.visualization`\n",
        "* `numpy`\n",
        "* `pylatexenc`\n",
        "\n",
        "Per configurare e installare i pacchetti sopra indicati, consultare la guida [Installazione di Qiskit](/docs/guides/install-qiskit).\n",
        "Per eseguire lavori su computer quantistici reali, gli studenti dovranno creare un account seguendo i passaggi indicati IBM Quantum® nella guida [Configura il tuo IBM Cloud account](/docs/guides/cloud-setup).\n",
        "\n",
        "Questo modulo è stato testato e ha utilizzato tre secondi di tempo QPU. Si tratta solo di una stima. L'utilizzo effettivo può variare.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "500d10c9-17ca-4787-90e0-4c57bf86c80d",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Uncomment and modify this line as needed to install dependencies\n",
        "#!pip install 'qiskit>=2.1.0' 'qiskit-ibm-runtime>=0.40.1' 'qiskit-aer>=0.17.0' 'numpy' 'pylatexenc'"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "72231ac3-5b60-4bcb-a072-0c2ecdd176aa",
      "metadata": {
        "jp-MarkdownHeadingCollapsed": true
      },
      "source": [
        "<span id=\"intro\" />\n",
        "\n",
        "## Introduzione\n",
        "\n",
        "All'inizio del 1990s, cresceva l'entusiasmo per il potenziale dei computer quantistici nel risolvere problemi difficili per i computer classici. Alcuni talentuosi scienziati informatici avevano ideato algoritmi che dimostravano la potenza dell'informatica quantistica per alcuni problemi di nicchia e artificiosi, ma nessuno aveva trovato una singola \"killer app\" dell'informatica quantistica che potesse rivoluzionare il settore. Questo fino al 1994, quando Peter Shor ideò quello che oggi è conosciuto come algoritmo di Shor per la scomposizione in fattori di numeri grandi.\n",
        "\n",
        "All'epoca era risaputo che trovare i fattori primi di un numero grande era estremamente difficile per un computer classico. Infatti, i protocolli di sicurezza Internet si basavano proprio su questa difficoltà. Shor ha trovato un modo per individuare questi fattori in modo esponenzialmente più efficiente, trasferendo alcuni dei passaggi più complessi su un futuro computer quantistico teorico.\n",
        "\n",
        "In questo modulo esploreremo l'algoritmo di Shor. Per prima cosa, forniremo qualche informazione in più sull'algoritmo, formalizzando il problema che risolve e spiegandone la rilevanza per la sicurezza informatica. Successivamente, forniremo una panoramica sulla matematica modulare e su come applicarla al problema della scomposizione in fattori, mostrando come la scomposizione in fattori si riduca a un altro problema chiamato \"ricerca dell'ordine\" Mostreremo come entrano in gioco la trasformata di Fourier quantistica e la stima di fase quantistica che abbiamo appreso nel modulo precedente e come utilizzarle per risolvere il problema della ricerca dell'ordine.\n",
        "\n",
        "Finalmente eseguiremo l'algoritmo di Shor su un vero computer quantistico! Tieni presente, però, che questo algoritmo sarà davvero utile solo quando avremo un computer quantistico grande e tollerante ai guasti, che è ancora lontano alcuni anni. Quindi, fattorizzeremo solo un numero piccolo per dimostrare come funziona l'algoritmo.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "22a38013-7bd5-46db-9a44-e89d66c2d06b",
      "metadata": {},
      "source": [
        "<span id=\"the-factoring-problem\" />\n",
        "\n",
        "## Il problema del factoring\n",
        "\n",
        "L'obiettivo del problema di fattorizzazione è trovare i fattori primi di un numero $N$. Per alcuni numeri $N$, questo è piuttosto facile. Ad esempio, se $N$ è pari, uno dei suoi fattori primi sarà 2. Se $N$ è una potenza prima, ovvero $N=p^k$ per un certo numero primo $p$, è anche abbastanza facile trovare $p$ : basta approssimare la radice $k^{\\text{th}}$ di $N$ e cercare i numeri primi vicini che potrebbero essere $p$.\n",
        "\n",
        "Tuttavia, i computer classici incontrano difficoltà quando $N$ è dispari e *non* è una potenza prima. Questo è il caso trattato dall'algoritmo di Shor. L'algoritmo trova due fattori $p$ e $q$ tali che $N=pq$. Può essere applicato ricorsivamente fino a quando tutti i fattori sono primi. Nelle prossime sezioni vedremo come viene affrontato questo problema.\n",
        "\n",
        "<span id=\"relevance-to-cyber-security\" />\n",
        "\n",
        "### Rilevanza per la sicurezza informatica\n",
        "\n",
        "Molti schemi crittografici sono stati sviluppati sulla base del fatto che la scomposizione in fattori di numeri grandi è difficile, compreso uno comunemente usato oggi, chiamato RSA. Nella crittografia RSA, una chiave pubblica viene creata moltiplicando due grandi numeri primi per ottenere $N = p\\cdot q$. Quindi, chiunque può utilizzare questa chiave pubblica per crittografare i dati. Ma solo qualcuno in possesso della chiave privata, $p$ e $q$, può decriptare quei dati.\n",
        "\n",
        "Se $N$ fosse facile da scomporre in fattori, allora chiunque sarebbe in grado di determinare quali sono $p$ $q$ e e violare la crittografia. Ma non è così. Questo è un problema notoriamente difficile. Infatti, i fattori primi di un numero chiamato RSA1024, lungo 1024 cifre binarie e 309 cifre decimali, non sono ancora stati trovati, nonostante nel 1991 fosse stato offerto un premio di 100.000 dollari per la sua scomposizione in fattori primi.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "01a0c5d4-922b-47af-8a8b-d87df8d520b0",
      "metadata": {},
      "source": [
        "<span id=\"shors-solution\" />\n",
        "\n",
        "## La soluzione di Shor\n",
        "\n",
        "Nel 1994, Peter Shor si rese conto che un computer quantistico poteva scomporre 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*. Faremo una breve introduzione all'aritmetica modulare, poi vedremo come possiamo usarla per scomporre in fattori $N$.\n",
        "\n",
        "<span id=\"modular-arithmetic\" />\n",
        "\n",
        "### Aritmetica modulare\n",
        "\n",
        "L'aritmetica modulare è un sistema di conteggio ciclico, il che significa che, sebbene il conteggio inizi nel modo consueto, con i numeri interi 0, 1, 2, ecc., ad un certo punto, dopo un periodo $N$, il conteggio ricomincia da capo. Vediamo come funziona con un esempio. Supponiamo che il nostro periodo sia 5. Quindi, mentre contiamo, dove normalmente arriveremmo a 5, ricominciamo invece da 0:\n",
        "\n",
        "$0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, ...$\n",
        "\n",
        "Questo perché nel mondo \" modulo-5 \" 5 equivale a 0. Diciamo che $5\\bmod 5 \\ = 0$. Infatti, tutti i multipli di 5 saranno equivalenti a $0\\bmod 5$.\n",
        "\n",
        "<span id=\"check-your-understanding\" />\n",
        "\n",
        "#### Verifica la tua comprensione\n",
        "\n",
        "Utilizza l'aritmetica modulare per risolvere il seguente problema:\n",
        "\n",
        "Parti per un lungo viaggio in treno transcontinentale alle 8 del mattino. Il viaggio in treno dura 60 ore. A che ora arrivi?\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Risposta\">\n",
        "    Il periodo è 24, poiché ci sono 24 ore in un giorno. Quindi, questo problema può essere scritto in aritmetica modulare come:\n",
        "\n",
        "    $(8+60)\\text{mod}(24) = 20$\n",
        "\n",
        "    Quindi arriveresti a destinazione alle 20:00, ovvero alle 8 di sera.\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n",
        "<span id=\"$mathbb{z}_n$-and-$mathbb{z}_n^*$\" />\n",
        "\n",
        "#### $\\mathbb{Z}_N$ e $\\mathbb{Z}_N^*$\n",
        "\n",
        "Spesso è utile introdurre due insiemi, $\\mathbb{Z}_N$ e $\\mathbb{Z}_N^*$. $\\mathbb{Z}_N$ è semplicemente l'insieme dei numeri che esistono in un mondo \"modulo- $N$ \". Ad esempio, quando stavamo contando modulo-5, l'insieme sarebbe $\\mathbb{Z}_5=\\{0,1,2,3,4\\}$. Un altro esempio: $\\mathbb{Z}_{15} = \\{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14\\}$. Possiamo eseguire addizioni e moltiplicazioni (modulo $N$ ) sugli elementi in $\\mathbb{Z}_N$, e il risultato di ciascuna di queste operazioni è anch'esso un elemento in $\\mathbb{Z}_N$, rendendo $\\mathbb{Z}_N$ un oggetto matematico chiamato *anello*.\n",
        "\n",
        "Esiste un sottoinsieme speciale di $\\mathbb{Z}_N$ che riveste particolare interesse per noi nell'ambito dell'algoritmo di Shor. Si tratta del sottoinsieme di numeri in $\\mathbb{Z}_N$ tale che il massimo comune divisore tra ciascun elemento e $N$ è 1, quindi ciascun elemento è \"coprimo\" rispetto a $N$. Se prendiamo l'insieme di questi numeri insieme all'operazione di moltiplicazione modulare, si forma un altro oggetto matematico, chiamato *gruppo*. Chiamiamo questo gruppo $\\mathbb{Z}_N^*$. Risulta che con $\\mathbb{Z}_N^*$ (e con i gruppi finiti in generale), se scegliamo un elemento qualsiasi e moltiplichiamo $a \\in \\mathbb{Z}_N^*$ ripetutamente $a$ per se stesso, alla fine otterremo sempre il numero $1$. Il numero minimo di volte che bisogna moltiplicare $a$ per se stesso per ottenere $1$ è chiamato **ordine** di $a$. Questo fatto sarà molto importante per la nostra discussione su come scomporre i numeri in seguito.\n",
        "\n",
        "<span id=\"check-your-understanding-1\" />\n",
        "\n",
        "#### Verifica la tua comprensione\n",
        "\n",
        "Che cos'è $\\mathbb{Z}_{15}^*$?\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Risposta\">\n",
        "    $\\mathbb{Z}_{15}^* = \\{1,2,4,7,8,11,13,14\\} $\n",
        "\n",
        "    Abbiamo escluso i seguenti numeri:\n",
        "\n",
        "    $$\n",
        "    \\begin{aligned}\n",
        "    3: GCD(3,15)=3 \\\\\n",
        "    5: GCD(5,15)=5 \\\\\n",
        "    6: GCD(6,15)=3 \\\\\n",
        "    9: GCD(9,15)=3 \\\\\n",
        "    10: GCD(10,15)=5 \\\\\n",
        "    12: GCD(12,15)=3 \\\\\n",
        "    \\end{aligned}\n",
        "    $$\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n",
        "Qual è l'ordine di ciascuno degli elementi in $\\mathbb{Z}_{15}^*$?\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Risposta\">\n",
        "    L'ordine $r$ è il numero più basso tale che $a^r\\text{mod}(15)=1$ per ogni elemento $a$.\n",
        "\n",
        "    $$\n",
        "    \\begin{aligned}\n",
        "    1^1\\text{mod}(15) = 1, r=1 \\\\\n",
        "    2^4\\text{mod}(15) = 1, r=4 \\\\\n",
        "    4^2\\text{mod}(15) = 1, r=2 \\\\\n",
        "    7^4\\text{mod}(15) = 1, r=4 \\\\\n",
        "    8^4\\text{mod}(15) = 1, r=4 \\\\\n",
        "    11^2\\text{mod}(15) = 1, r=2 \\\\\n",
        "    13^4\\text{mod}(15) = 1, r=4 \\\\\n",
        "    14^2\\text{mod}(15) = 1, r=2 \\\\\n",
        "    \\end{aligned}\n",
        "    $$\n",
        "\n",
        "    Si noti che, sebbene siamo riusciti a trovare l'ordine dei numeri in $\\mathbb{Z}_{15}^*$, questo NON è un compito facile in generale, per valori più grandi di $N$. Questo è il punto cruciale del problema della fattorizzazione e il motivo per cui abbiamo bisogno di un computer quantistico. Vedremo il perché man mano che procederemo con il resto del quaderno.\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c19d2032-3c40-48ee-823b-607b1ed8b4f0",
      "metadata": {},
      "source": [
        "<span id=\"apply-modular-arithmetic-to-the-factoring-problem\" />\n",
        "\n",
        "### Applicare l'aritmetica modulare al problema della scomposizione in fattori\n",
        "\n",
        "La chiave per trovare fattori $p$ e $q$ tali che $N=pq$ si riduce a trovare un *altro* numero intero $x$ tale che\n",
        "\n",
        "$x^2 \\equiv 1 \\bmod N$ e $x \\not\\equiv \\pm 1 \\bmod N.$\n",
        "\n",
        "In che modo trovare ci aiuta $x$ a trovare i fattori $p$ e $q$? Esaminiamo ora l'argomentazione. Poiché $x^2 \\equiv 1 \\bmod N$, ciò significa che $x^2 - 1 \\equiv 0 \\bmod N $. In altre parole, $x^2 - 1$ è un multiplo di $N$. Quindi, per un certo numero intero $l$,\n",
        "\n",
        "$x^2 - 1 = l N$\n",
        "\n",
        "Possiamo scomporre $x^2 - 1$ per ottenere:\n",
        "\n",
        "$(x+1)(x-1) = l N$\n",
        "\n",
        "Dalle nostre ipotesi iniziali sappiamo che $x \\not\\equiv \\pm 1 \\bmod N$, quindi $N$ non è divisibile in modo uniforme né per $x+1$ né $x-1$ per. Quindi, i due fattori di $N$, $p$ e $q$ devono essere entrambi divisibili per $x-1$ e $x+1$. O $p$ è un fattore di $x-1$ e $q$ è un fattore di $x+1$, o viceversa. Pertanto, se calcoliamo i massimi comuni divisori (MCD) tra $N$ e sia $x-1$ che $x+1$, otterremo i fattori $p$ e $q$. Il calcolo del MCD tra due numeri è un'operazione classicamente facile che può essere eseguita, ad esempio, utilizzando [l'algoritmo di Euclide](https://en.wikipedia.org/wiki/Euclidean_algorithm).\n",
        "\n",
        "<span id=\"check-your-understanding\" />\n",
        "\n",
        "#### Verifica la tua comprensione\n",
        "\n",
        "Potrebbe essere difficile comprendere ogni fase della logica sopra descritta, quindi provate a seguirla con un esempio. Utilizzare $N=15$ e $x=11$. Innanzitutto, verificare che $x^2 \\equiv 1 \\text{mod}(N)$ e $x \\not\\equiv \\pm 1 \\bmod N$. Quindi continuare a verificare ogni passaggio. Infine, calcola $\\text{GCD}(11\\pm1,15)$ e verifica che siano i fattori di $15$.\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Risposta\">\n",
        "    $11^2 = 121$, che è $15*8 + 1$, quindi $11^2\\bmod 15 = 1$. $\\checkmark$\n",
        "\n",
        "    $ 11 - 1 = 10$, che non è equivalente a $0\\bmod 15$. $\\checkmark$\n",
        "\n",
        "    $ 11 + 1 = 12$, che non è equivalente a $0\\bmod 15$. $\\checkmark$\n",
        "\n",
        "    Ora, sappiamo che $(x+1)(x-1) = l N$ per un certo numero intero $l$. Ciò è verificabile quando inseriamo $x$ e $N$ : $(12)(10) = l 15$ quando $l = 8$. $\\checkmark$\n",
        "\n",
        "    Ora, dobbiamo calcolare $\\text{GCD}(12,15)$ e $\\text{GCD}(10,15)$.\n",
        "\n",
        "    $$\n",
        "    \\begin{aligned}\n",
        "    \\text{GCD}(12,15) = 3 \\\\\n",
        "    \\text{GCD}(10,15) = 5\n",
        "    \\end{aligned}\n",
        "    $$\n",
        "\n",
        "    Quindi, abbiamo trovato i nostri fattori di $15$!\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n",
        "<span id=\"the-algorithm\" />\n",
        "\n",
        "### L'algoritmo\n",
        "\n",
        "Ora che abbiamo visto come trovare un numero intero $x$ tale che ci $x^2 \\equiv 1\\bmod N$ aiuta a scomporre $N$, possiamo passare all'algoritmo di Shor. In sostanza, si tratta di trovare $x$ :\n",
        "\n",
        "1. **Scegli un numero intero casuale**\n",
        "   Scegli un numero intero $a$ casuale tale che $1 < a < N$.\n",
        "\n",
        "* Calcolare $\\text{GCD}(a, N)$ in modo classico.\n",
        "  * Se $\\text{GCD}(a, N) > 1$, hai già trovato un fattore. Basta.\n",
        "  * Altrimenti, continua.\n",
        "\n",
        "2. **Trova l'ordine $r$ del $a$ modulo**\n",
        "   $N$ Trova il più piccolo numero intero positivo $r$ che soddisfa $a^r \\equiv 1 \\pmod N$.\n",
        "\n",
        "3. **Controlla se l'ordine è pari**\n",
        "\n",
        "* Se $r$ è dispari, torna al punto 1 e scegli un nuovo $a$.\n",
        "* Se $r$ è pari, passare al punto 4.\n",
        "\n",
        "4. **Calcolare $x = a^{r/2} \\bmod N$**\n",
        "\n",
        "* Verificare che $x \\not\\equiv 1 \\pmod N$ e $x \\not\\equiv -1 \\pmod N$.\n",
        "  * Se $x \\equiv \\pm 1 \\pmod N$, torna al punto 1 e scegli un nuovo $a$.\n",
        "* Altrimenti, calcola i gcd per estrarre i fattori:\n",
        "\n",
        "$$\n",
        "p = \\text{GCD}(x-1, N), \\quad q = \\text{GCD}(x+1, N)\n",
        "$$\n",
        "\n",
        "Questi saranno fattori non banali di $N$.\n",
        "\n",
        "5. **Se necessario, fattorizzare ricorsivamente**\n",
        "\n",
        "* Se $p$ e/o non $q$ sono numeri primi, applicare l'algoritmo in modo ricorsivo per scomporli completamente.\n",
        "* Una volta che tutti i fattori sono primi, il calcolo dei fattori è completo.\n",
        "\n",
        "Sulla base di questa procedura, potrebbe non essere ovvio il motivo per cui sia necessario un computer quantistico per completare questa operazione. È necessario perché il passaggio 2, trovare l'ordine di $a$ modulo $N$, è classicamente un problema molto difficile. La complessità cresce in modo esponenziale con il numero $N$. Ma con un computer quantistico, basta utilizzare la stima della fase quantistica per risolverlo. Il quarto passo, trovare il MCD di due numeri interi, è in realtà piuttosto facile da fare in modo classico. Quindi, l'unico passaggio che richiede effettivamente la potenza di un computer quantistico è quello della ricerca dell'ordine. Diciamo che il problema del factoring \"si riduce\" al problema della ricerca dell'ordine.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "d4771244-0604-4b2e-bd99-a52280456f43",
      "metadata": {},
      "source": [
        "<span id=\"the-hard-part-order-finding\" />\n",
        "\n",
        "### La parte difficile: trovare l'ordine\n",
        "\n",
        "Ora vedremo come utilizzare un computer quantistico per la ricerca. Innanzitutto, chiariamo cosa intendiamo per \"ordine\" Naturalmente, vi ho già spiegato il significato matematico dell'ordine: è il primo numero intero diverso da $r$ zero tale che $a^r = 1 \\pmod N.$ Ma vediamo se riusciamo a comprendere meglio questo concetto.\n",
        "\n",
        "Per valori sufficientemente piccoli $N$, possiamo semplicemente determinare l'ordine calcolando ogni potenza di $a$, prendendo il modulo $N$ di quel numero, quindi fermandoci quando troviamo la potenza $r$ che soddisfa $a^r = 1 \\text{mod}(N)$. È quello che abbiamo fatto con il nostro esempio, $N=15$, sopra. Diamo un'occhiata ad alcuni grafici di queste potenze modulari per alcuni valori campione di $a$ e $N$ :\n",
        "\n",
        "![Valore di a elevato alla potenza k modulo N rispetto alla potenza k, dove a=2 e N=15. Vediamo che all'aumentare di k emerge uno schema ripetitivo, che mostra che a^k modulo N è periodico in k.](https://quantum.cloud.ibm.com/learning/images/modules/computer-science/shors-algorithm/a2n15.avif)\n",
        "\n",
        "![Valore di a elevato alla potenza k modulo N rispetto alla potenza k, dove a=5 e N=21. Vediamo che all'aumentare di k emerge uno schema ripetitivo, che mostra che a^k modulo N è periodico in k.](https://quantum.cloud.ibm.com/learning/images/modules/computer-science/shors-algorithm/a5n21.avif)\n",
        "\n",
        "Noti qualcosa? Queste sono funzioni periodiche! E l'ordine $r$ è lo stesso del periodo! **Quindi, trovare l'ordine equivale a trovare il periodo.**\n",
        "\n",
        "I computer quantistici sono particolarmente adatti per trovare il periodo delle funzioni. A tal fine, possiamo utilizzare una subroutine algoritmica denominata Quantum Phase Estimation (Stima della fase quantistica). Nel modulo precedente abbiamo discusso della QPE e della sua relazione con la trasformata di Fourier quantistica. Per un ripasso dettagliato, consulta il modulo QFT o la lezione di John Watrous sulla [stima della fase quantistica](/learning/courses/fundamentals-of-quantum-algorithms/phase-estimation-and-factoring/shor-algorithm) nel suo corso sugli algoritmi quantistici. Ora esamineremo i punti salienti della procedura:\n",
        "\n",
        "Nella stima della fase quantistica (QPE), si parte da un operatore unitario $U$ e da uno stato proprio di tale operatore unitario $|\\psi\\rangle$. Quindi, si utilizza la QPE per approssimare il corrispondente autovalore che, poiché l'operatore è unitario, avrà la forma $e^{2\\pi i \\theta}$. Quindi, trovare l'autovalore equivale a trovare il valore di $\\theta$ nella funzione periodica. Il circuito ha questo aspetto:\n",
        "\n",
        "![Schema circuitale della procedura di stima della fase quantistica. I qubit di controllo superiori m vengono preparati in sovrapposizioni con porte Hadamard, quindi vengono applicate porte unitarie controllate ai qubit inferiori, che si trovano in uno stato proprio dell'unitaria. Infine, viene applicata una trasformata di Fourier quantistica inversa ai qubit superiori, che vengono poi misurati.](https://quantum.cloud.ibm.com/learning/images/modules/computer-science/shors-algorithm/QPE.avif)\n",
        "\n",
        "dove il numero di qubit di controllo (i qubit $m$ in alto nella figura sopra) determina la precisione dell'approssimazione.\n",
        "\n",
        "Nell'algoritmo di Shor, utilizziamo il QPE sull'operatore unitario $M_a$ :\n",
        "\n",
        "$ M_a|y\\rangle \\equiv |ay \\mod N \\rangle .$\n",
        "\n",
        "Qui, $|y\\rangle$ indica uno stato di base computazionale del registro multi-qubit, dove il valore binario dei qubit corrisponde al numero intero $y$. Ad esempio, se $N=15$ e $y = 2$, allora $|y\\rangle$ è rappresentato dallo stato di base a quattro $|0010\\rangle$ qubit, poiché sono necessari quattro qubit per codificare numeri fino a 15. (Se questo concetto non ti è familiare, consulta il [modulo introduttivo Qiskit nelle aule](/learning/modules/quantum-mechanics/get-started-with-qiskit) per un ripasso sulla codifica binaria degli stati quantistici.)\n",
        "\n",
        "Ora, dobbiamo capire uno stato proprio di questa unitaria. Se abbiamo iniziato dallo stato $|1\\rangle$, possiamo vedere che ogni applicazione successiva di $U$ moltiplicherà lo stato del nostro registro per $a \\pmod N$, e dopo $r$ applicazioni arriveremo nuovamente allo $|1\\rangle$ stato. Ad esempio con $a = 3$ e $N = 35$ :\n",
        "\n",
        "$$\n",
        "\\begin{aligned}\n",
        "M_3|1\\rangle &= |3\\rangle & \\\\\n",
        "M_3^2|1\\rangle &= |9\\rangle \\\\\n",
        "M_3^3|1\\rangle &= |27\\rangle \\\\\n",
        "& \\vdots \\\\\n",
        "M_3^{(r-1)}|1\\rangle &= |12\\rangle \\\\\n",
        "M_3^r|1\\rangle &= |1\\rangle\n",
        "\\end{aligned}\n",
        "$$\n",
        "\n",
        "Quindi sovrapposizioni degli stati in questo ciclo ( $|\\psi_j\\rangle$ ) della forma:\n",
        "\n",
        "$|\\psi_j\\rangle = \\tfrac{1}{\\sqrt{r}}\\sum_{k=0}^{r-1}{e^{\\frac{2 \\pi i j k}{r}} |a^k \\rangle} $\n",
        "\n",
        "sono tutti stati propri di $M_a$. (Esistono altri stati propri oltre a questi. Ma a noi interessano solo quelli della forma sopra indicata.)\n",
        "\n",
        "<span id=\"check-your-understanding\" />\n",
        "\n",
        "#### Verifica la tua comprensione\n",
        "\n",
        "Trova uno stato proprio dell'unitario corrispondente a $a=2$ e $N = 15$.\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Risposta\">\n",
        "    $$\n",
        "    \\begin{aligned}\n",
        "    M_2|1\\rangle &= |2\\rangle & \\\\\n",
        "    M_2^2|1\\rangle &= |4\\rangle \\\\\n",
        "    M_2^3|1\\rangle &= |8\\rangle \\\\\n",
        "    M_2^4|1\\rangle &= |1\\rangle \\\\\n",
        "    \\end{aligned}\n",
        "    $$\n",
        "\n",
        "    Quindi, l'ordine $r=4$. Gli stati propri che ci interessano saranno una sovrapposizione uguale di tutti gli stati che sono stati ciclicamente ripetuti sopra, con varie fasi:\n",
        "\n",
        "    $$\n",
        "    \\begin{aligned}\n",
        "    |\\psi_0\\rangle &= \\frac{1}{2}(|1\\rangle+|2\\rangle+|4\\rangle+|8\\rangle) \\\\\n",
        "    |\\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) \\\\\n",
        "    &= \\frac{1}{2}(|1\\rangle+i|2\\rangle-|4\\rangle-i|8\\rangle) \\\\\n",
        "    |\\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) \\\\\n",
        "    &= \\frac{1}{2}(|1\\rangle-|2\\rangle+|4\\rangle-|8\\rangle) \\\\\n",
        "    |\\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) \\\\\n",
        "    &= \\frac{1}{2}(|1\\rangle-i|2\\rangle-|4\\rangle+i|8\\rangle) \\\\\n",
        "    \\end{aligned}\n",
        "    $$\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n",
        "Supponiamo di essere riusciti a inizializzare lo stato del nostro qubit in uno di questi stati propri (spoiler: non ci siamo riusciti). O almeno, non facilmente. Spiegheremo tra poco perché e cosa possiamo fare invece). Quindi potremmo usare QPE per stimare l'autovalore corrispondente, $\\omega_j = e^{2 \\pi i \\theta_j}$ dove $\\theta_j = \\frac{j}{r}$. Quindi, saremo in grado di determinare l'ordine $r$ tramite la semplice equazione:\n",
        "\n",
        "$r = \\frac{j}{\\theta_j}.$\n",
        "\n",
        "Ma ricordate, ho detto che *si tratta di stime* QPE $\\theta_j$ — non ci forniscono un valore esatto. Abbiamo bisogno che la stima sia sufficientemente accurata da poter distinguere tra $r$ e $r+1$. Maggiore è il numero di qubit di controllo a nostra $m$ disposizione, migliore sarà la stima. Nei problemi alla fine della lezione, ti verrà chiesto di determinare il minimo $m$ necessario per scomporre un numero $N$.\n",
        "\n",
        "Ora dobbiamo risolvere un problema. Tutte le spiegazioni sopra riportate su come trovare $r$ iniziano con la preparazione dello stato proprio $|\\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 sapere già cosa $r$ sia. La logica è circolare. Abbiamo bisogno di un modo per stimare l'autovalore *senza* inizializzare lo stato proprio.\n",
        "\n",
        "Invece di iniziare con uno stato proprio di $M_a$, possiamo preparare lo stato iniziale nello stato $n$ a -qubit corrispondente a $|1\\rangle$ in binario (come in ) $|000...01\\rangle$. Sebbene questo stato non sia ovviamente uno stato proprio di $M_a$, è una sovrapposizione di tutti gli stati propri $|\\psi_k\\rangle$ :\n",
        "\n",
        "$|1\\rangle = \\frac{1}{\\sqrt{r}} \\sum\\limits_{k=0}^{r-1}{|\\psi_k\\rangle}$\n",
        "\n",
        "<span id=\"check-your-understanding-1\" />\n",
        "\n",
        "#### Verifica la tua comprensione\n",
        "\n",
        "Verifica che $|1\\rangle$ sia equivalente alla sovrapposizione sugli stati propri che hai trovato per $N=15$ e $a=2$ nella domanda di controllo precedente.\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Risposta\">\n",
        "    I quattro stati propri erano:\n",
        "\n",
        "    $$\n",
        "    \\begin{aligned}\n",
        "    |\\psi_0\\rangle &= \\frac{1}{2}(|1\\rangle+|2\\rangle+|4\\rangle+|8\\rangle) \\\\\n",
        "    |\\psi_1\\rangle &= \\frac{1}{2}(|1\\rangle+i|2\\rangle-|4\\rangle-i|8\\rangle) \\\\\n",
        "    |\\psi_2\\rangle &= \\frac{1}{2}(|1\\rangle-|2\\rangle+|4\\rangle-|8\\rangle) \\\\\n",
        "    |\\psi_3\\rangle &= \\frac{1}{2}(|1\\rangle-i|2\\rangle-|4\\rangle+i|8\\rangle) \\\\\n",
        "    \\end{aligned}\n",
        "    $$\n",
        "\n",
        "    Quindi,\n",
        "\n",
        "    $$\n",
        "    \\begin{aligned}\n",
        "    \\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 ) \\\\\n",
        "    &= \\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) \\\\\n",
        "    &= \\frac{1}{4}(4|1\\rangle) = |1\\rangle\n",
        "    \\end{aligned}\n",
        "    $$\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n",
        "In che modo questo ci permette di trovare l'ordine $r$? Poiché lo stato iniziale è una sovrapposizione di tutti gli stati propri della forma sopra elencata, l'algoritmo QPE stima simultaneamente ciascuno dei $\\theta_k$ corrispondenti a questi stati propri. Quindi, la misurazione dei qubit $m$ di controllo alla fine fornirà un'approssimazione del valore $k/r$ dove $k \\in \\{0,1,2,...,r-1\\}$ è uno degli autovalori scelti casualmente. Se ripetiamo questo circuito alcune volte e otteniamo alcuni campioni con valori diversi di $k$, saremo in grado di dedurre rapidamente $r$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "099389d1-1370-472c-af29-a642532beff4",
      "metadata": {},
      "source": [
        "<span id=\"implement-in-qiskit\" />\n",
        "\n",
        "## Implementare in Qiskit\n",
        "\n",
        "Come abbiamo detto prima, il nostro hardware non è ancora in grado di elaborare numeri enormi come RSA1024. Facciamo solo un piccolo calcolo per dimostrare come funziona l'algoritmo. Per questa demo, useremo una versione semplificata del codice presentato nel [tutorial sull'algoritmo di Shor](/docs/tutorials/shors-algorithm). Per ulteriori dettagli, consulta il tutorial.\n",
        "\n",
        "Eseguiremo l'algoritmo utilizzando il nostro framework standard per la risoluzione di problemi quantistici, chiamato Qiskit patterns framework. Si tratta di quattro passaggi:\n",
        "\n",
        "1. Mappare il problema su un circuito quantistico\n",
        "2. Ottimizzare il circuito da eseguire su hardware quantistico\n",
        "3. Esegui il tuo circuito sul computer quantistico\n",
        "4. Post-elaborazione delle misurazioni\n",
        "\n",
        "<span id=\"1-map\" />\n",
        "\n",
        "### 1. Mappa\n",
        "\n",
        "Facciamo il fattorizzazione $N=15$, scegliendo $a=2$ come nostro numero intero coprimo.\n",
        "\n",
        "In primo luogo, dobbiamo costruire il circuito che implementerà l'unità di moltiplicazione modulare. $M_a$ Questa è in realtà la parte più complessa dell'intera implementazione e può essere molto onerosa dal punto di vista computazionale, a seconda di come viene eseguita. Per questo, bareremo un po': sappiamo che stiamo iniziando dallo stato $|1\\rangle$, e da una domanda precedente,\n",
        "\n",
        "$$\n",
        "\\begin{aligned}\n",
        "M_2|1\\rangle &= |2\\rangle & \\\\\n",
        "M_2|2\\rangle &= |4\\rangle \\\\\n",
        "M_2|4\\rangle &= |8\\rangle \\\\\n",
        "M_2|8\\rangle &= |1\\rangle \\\\\n",
        "\\end{aligned}\n",
        "$$\n",
        "\n",
        "Quindi, costruiremo un'unità che esegua le operazioni corrette su questi quattro stati, ma lasci tutti gli altri stati invariati. Questo è un imbroglio perché stiamo usando la nostra conoscenza dell'ordine di $2\\bmod 15$ per semplificare l'unitario. Se stessimo effettivamente cercando di scomporre un numero i cui fattori ci sono sconosciuti, non saremmo in grado di farlo.\n",
        "\n",
        "<span id=\"check-your-understanding\" />\n",
        "\n",
        "#### Verifica la tua comprensione\n",
        "\n",
        "Conoscendo il modo in cui $M_2$ l'operatore trasforma gli stati sopra indicati, costruisci l'operatore a partire da una serie di porte SWAP, che scambiano gli stati di due qubit. (Suggerimento: scrivere ogni stato $|i\\rangle$ in binario ti aiuterà.)\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Risposta\">\n",
        "    Riscriviamo l'azione di $M_2$ sugli stati in binario:\n",
        "\n",
        "    $$\n",
        "    \\begin{aligned}\n",
        "    M_2|0001\\rangle &= |0010\\rangle \\\\\n",
        "    M_2|0010\\rangle &= |0100\\rangle \\\\\n",
        "    M_2|0100\\rangle &= |1000\\rangle \\\\\n",
        "    M_2|1000\\rangle &= |0001\\rangle \\\\\n",
        "    \\end{aligned}\n",
        "    $$\n",
        "\n",
        "    Ciascuna di queste azioni può essere eseguita con un semplice SWAP. $M_2|0001\\rangle$ si ottiene scambiando gli stati dei qubit $0$ e $1$. $M_2|0010\\rangle$ si ottiene scambiando gli stati dei qubit $1$ e $2$. E così via. Quindi, possiamo scomporre la $M_2$ matrice nella seguente serie di porte SWAP:\n",
        "\n",
        "    $$\n",
        "    M_2 = SWAP(0,1)SWAP(1,2)SWAP(2,3)\n",
        "    $$\n",
        "\n",
        "    Ricordando che gli operatori agiscono da destra a sinistra, verifichiamo che questo abbia l'effetto desiderato su ciascuno degli stati:\n",
        "\n",
        "    $$\n",
        "    \\begin{aligned}\n",
        "    M_2|0001\\rangle &= SWAP(0,1)SWAP(1,2)SWAP(2,3)|0001\\rangle \\\\\n",
        "    &= SWAP(0,1)SWAP(1,2)|0001\\rangle \\\\\n",
        "    &= SWAP(0,1)|0001\\rangle \\\\\n",
        "    &=|0010\\rangle  \\checkmark \\\\\n",
        "    M_2|0010\\rangle &= SWAP(0,1)SWAP(1,2)SWAP(2,3)|0010\\rangle \\\\\n",
        "    &= SWAP(0,1)SWAP(1,2)|0010\\rangle \\\\\n",
        "    &= SWAP(0,1)|0100\\rangle \\\\\n",
        "    &=|0100\\rangle  \\checkmark \\\\\n",
        "    M_2|0100\\rangle &= SWAP(0,1)SWAP(1,2)SWAP(2,3)|0100\\rangle \\\\\n",
        "    &= SWAP(0,1)SWAP(1,2)|1000\\rangle \\\\\n",
        "    &= SWAP(0,1)|1000\\rangle \\\\\n",
        "    &=|1000\\rangle  \\checkmark \\\\\n",
        "    M_2|1000\\rangle &= SWAP(0,1)SWAP(1,2)SWAP(2,3)|1000\\rangle \\\\\n",
        "    &= SWAP(0,1)SWAP(1,2)|0100\\rangle \\\\\n",
        "    &= SWAP(0,1)|0010\\rangle \\\\\n",
        "    &=|0001\\rangle  \\checkmark \\\\\n",
        "    \\end{aligned}\n",
        "    $$\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n",
        "Ora possiamo codificare il circuito equivalente a questo operatore in Qiskit.\n",
        "\n",
        "Per prima cosa, importiamo i pacchetti necessari:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 29,
      "id": "95c8d21b-d2e6-45c3-a2d2-a7288db4171f",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Import necessary packages\n",
        "\n",
        "import numpy as np\n",
        "from fractions import Fraction\n",
        "from math import floor, gcd, log\n",
        "\n",
        "from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister\n",
        "from qiskit.circuit.library import QFTGate\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "from qiskit.visualization import plot_histogram\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import SamplerV2 as Sampler"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f8d46443-d3d3-4d27-8026-27974f5d6702",
      "metadata": {},
      "source": [
        "Quindi, creiamo $M_2$ l'operatore:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 30,
      "id": "1396a7ff-718a-4169-9c63-85ae218b58a4",
      "metadata": {},
      "outputs": [],
      "source": [
        "def M2mod15():\n",
        "    \"\"\"\n",
        "    M2 (mod 15)\n",
        "    \"\"\"\n",
        "    b = 2\n",
        "    U = QuantumCircuit(4)\n",
        "\n",
        "    U.swap(2, 3)\n",
        "    U.swap(1, 2)\n",
        "    U.swap(0, 1)\n",
        "\n",
        "    U = U.to_gate()\n",
        "    U.name = f\"M_{b}\"\n",
        "\n",
        "    return U"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 31,
      "id": "e7521fba-fe3e-45bc-b9ce-fe876bddad33",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/shors-algorithm/extracted-outputs/e7521fba-fe3e-45bc-b9ce-fe876bddad33-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 31,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Get the M2 operator\n",
        "M2 = M2mod15()\n",
        "\n",
        "# Add it to a circuit and plot\n",
        "circ = QuantumCircuit(4)\n",
        "circ.compose(M2, inplace=True)\n",
        "circ.decompose(reps=2).draw(output=\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6f1e1a5d-bd34-4379-9237-d91ecba9d435",
      "metadata": {},
      "source": [
        "L'algoritmo QPE utilizza un gate $U$ controllato. Quindi, ora che abbiamo un $M_2$ circuito, dobbiamo renderlo un circuito $M_2$\\* controllato\\* :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 32,
      "id": "36d1020c-c0af-4211-bd50-be865d057f75",
      "metadata": {},
      "outputs": [],
      "source": [
        "def controlled_M2mod15():\n",
        "    \"\"\"\n",
        "    Controlled M2 (mod 15)\n",
        "    \"\"\"\n",
        "    b = 2\n",
        "    U = QuantumCircuit(4)\n",
        "\n",
        "    U.swap(2, 3)\n",
        "    U.swap(1, 2)\n",
        "    U.swap(0, 1)\n",
        "\n",
        "    U = U.to_gate()\n",
        "    U.name = f\"M_{b}\"\n",
        "    c_U = U.control()\n",
        "\n",
        "    return c_U"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 33,
      "id": "5bf4f10d-5d52-406d-b62d-ad4a8d366c02",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/shors-algorithm/extracted-outputs/5bf4f10d-5d52-406d-b62d-ad4a8d366c02-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 33,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Get the controlled-M2 operator\n",
        "controlled_M2 = controlled_M2mod15()\n",
        "\n",
        "# Add it to a circuit and plot\n",
        "circ = QuantumCircuit(5)\n",
        "circ.compose(controlled_M2, inplace=True)\n",
        "circ.decompose(reps=1).draw(output=\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fd98c560-530f-424f-b8f8-0f9fe9615c5e",
      "metadata": {},
      "source": [
        "Ora abbiamo il nostro gate $U$ controllato. Ma per eseguire l'algoritmo di stima della fase quantistica, avremo bisogno di controllato- $U^2$, controllato- $U^4$, fino a controllato- $U^{2^{m-1}}$, dove $m$ è il numero di qubit utilizzati per stimare la fase. Più qubit ci sono, più precisa sarà la stima della fase. Utilizzeremo qubit $m=8$ di controllo per la nostra procedura di stima di fase. Quindi, abbiamo bisogno di:\n",
        "\n",
        "$$\n",
        "M_{a^{2^k}}|y\\rangle \\equiv |a^{2^k} y \\bmod N \\rangle\n",
        "$$\n",
        "\n",
        "dove l'indice $k$, con $0 \\le k \\le m-1 = 7$, corrisponde al qubit di controllo. Ora calcoliamo $a^{2^k}\\bmod N $ per ogni valore di $k$ :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 34,
      "id": "1463f000-c7ab-4e09-b111-5cb3d279e06b",
      "metadata": {},
      "outputs": [],
      "source": [
        "def a2kmodN(a, k, N):\n",
        "    \"\"\"Compute a^{2^k} (mod N) by repeated squaring\"\"\"\n",
        "    for _ in range(k):\n",
        "        a = int(np.mod(a**2, N))\n",
        "    return a"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 35,
      "id": "ac0f44f1-e5fa-46b8-a1d2-5255ed63e3b9",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[2, 4, 1, 1, 1, 1, 1, 1]\n"
          ]
        }
      ],
      "source": [
        "k_list = range(8)\n",
        "b_list = [a2kmodN(2, k, 15) for k in k_list]\n",
        "\n",
        "print(b_list)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a2654cb9-a2ad-4947-8441-a1e2799501b8",
      "metadata": {},
      "source": [
        "Poiché $a^{2^k} \\bmod N = 1$ per $k \\ge 2$, tutti gli operatori corrispondenti ( $M_8$ e superiori) sono equivalenti all'identità. Quindi, dobbiamo solo costruire un'altra matrice, $M_4.$\n",
        "\n",
        "**Nota:** questa semplificazione funziona solo qui perché l'ordine di $2 \\bmod 15 $ è $4$. Una volta che $k=2$ (quindi $2^k = 4$ ), ogni potenza successiva dell'operatore è l'identità. In generale, per numeri più grandi $N$ o scelte diverse di $a$, non è possibile saltare la costruzione delle potenze superiori. Questo è uno dei motivi per cui questo è considerato un *esempio banale* : i numeri piccoli consentono scorciatoie che non funzionerebbero per casi più grandi.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 36,
      "id": "29bc2fb5-511d-4593-8c48-7b1c7c7eeed0",
      "metadata": {},
      "outputs": [],
      "source": [
        "def M4mod15():\n",
        "    \"\"\"\n",
        "    M4 (mod 15)\n",
        "    \"\"\"\n",
        "    b = 4\n",
        "    U = QuantumCircuit(4)\n",
        "\n",
        "    U.swap(1, 3)\n",
        "    U.swap(0, 2)\n",
        "\n",
        "    U = U.to_gate()\n",
        "    U.name = f\"M_{b}\"\n",
        "\n",
        "    return U"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 37,
      "id": "ea4fc641-e97c-400d-a761-5f67a0b7d65e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/shors-algorithm/extracted-outputs/ea4fc641-e97c-400d-a761-5f67a0b7d65e-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 37,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Get the M4 operator\n",
        "M4 = M4mod15()\n",
        "\n",
        "# Add it to a circuit and plot\n",
        "circ = QuantumCircuit(4)\n",
        "circ.compose(M4, inplace=True)\n",
        "circ.decompose(reps=2).draw(output=\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f6712a0c-a695-4c1d-a99a-a9fd67a95c92",
      "metadata": {},
      "source": [
        "E come prima, lo rendiamo un operatore $M_4$\\* controllato\\* :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 38,
      "id": "c8ec99c7-5e03-4623-b6f7-22dae558de04",
      "metadata": {},
      "outputs": [],
      "source": [
        "def controlled_M4mod15():\n",
        "    \"\"\"\n",
        "    Controlled M4 (mod 15)\n",
        "    \"\"\"\n",
        "    b = 4\n",
        "    U = QuantumCircuit(4)\n",
        "\n",
        "    U.swap(1, 3)\n",
        "    U.swap(0, 2)\n",
        "\n",
        "    U = U.to_gate()\n",
        "    U.name = f\"M_{b}\"\n",
        "    c_U = U.control()\n",
        "\n",
        "    return c_U"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 39,
      "id": "37caf888-276e-4f19-a0d0-59517c5ee44e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/shors-algorithm/extracted-outputs/37caf888-276e-4f19-a0d0-59517c5ee44e-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 39,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Get the controlled-M4 operator\n",
        "controlled_M4 = controlled_M4mod15()\n",
        "\n",
        "# Add it to a circuit and plot\n",
        "circ = QuantumCircuit(5)\n",
        "circ.compose(controlled_M4, inplace=True)\n",
        "circ.decompose(reps=1).draw(output=\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "db3f989d-ef17-48af-99c6-aae08eb59001",
      "metadata": {},
      "source": [
        "Ora possiamo mettere tutto insieme per trovare l'ordine di $2\\bmod 15$ con un circuito quantistico, utilizzando la stima di fase:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 48,
      "id": "d1b111c8-1a12-420b-bbc2-4ecdd1ac5a97",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/shors-algorithm/extracted-outputs/d1b111c8-1a12-420b-bbc2-4ecdd1ac5a97-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 48,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Order finding problem for N = 15 with a = 2\n",
        "N = 15\n",
        "a = 2\n",
        "\n",
        "# Number of qubits\n",
        "num_target = floor(log(N - 1, 2)) + 1  # for modular exponentiation operators\n",
        "num_control = 2 * num_target  # for enough precision of estimation\n",
        "\n",
        "# List of M_b operators in order\n",
        "k_list = range(num_control)\n",
        "b_list = [a2kmodN(2, k, 15) for k in k_list]\n",
        "\n",
        "# Initialize the circuit\n",
        "control = QuantumRegister(num_control, name=\"C\")\n",
        "target = QuantumRegister(num_target, name=\"T\")\n",
        "output = ClassicalRegister(num_control, name=\"out\")\n",
        "circuit = QuantumCircuit(control, target, output)\n",
        "\n",
        "# Initialize the target register to the state |1>\n",
        "circuit.x(num_control)\n",
        "\n",
        "# Add the Hadamard gates and controlled versions of the\n",
        "# multiplication gates\n",
        "for k, qubit in enumerate(control):\n",
        "    circuit.h(k)\n",
        "    b = b_list[k]\n",
        "    if b == 2:\n",
        "        circuit.compose(\n",
        "            M2mod15().control(), qubits=[qubit] + list(target), inplace=True\n",
        "        )\n",
        "    elif b == 4:\n",
        "        circuit.compose(\n",
        "            M4mod15().control(), qubits=[qubit] + list(target), inplace=True\n",
        "        )\n",
        "    else:\n",
        "        continue  # M1 is the identity operator\n",
        "\n",
        "# Apply the inverse QFT to the control register\n",
        "circuit.compose(QFTGate(num_control).inverse(), qubits=control, inplace=True)\n",
        "\n",
        "# Measure the control register\n",
        "circuit.measure(control, output)\n",
        "\n",
        "circuit.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b3659616-39b4-4544-8def-54012b40dbfa",
      "metadata": {},
      "source": [
        "<span id=\"2-optimize\" />\n",
        "\n",
        "### 2. Ottimizzare\n",
        "\n",
        "Ora che abbiamo mappato il nostro circuito, il passo successivo è ottimizzarlo per poterlo eseguire su un particolare computer quantistico. Per prima cosa dobbiamo caricare il backend.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 49,
      "id": "92b92fd3-0dca-43db-a304-0a933f293d8f",
      "metadata": {},
      "outputs": [],
      "source": [
        "service = QiskitRuntimeService()\n",
        "\n",
        "backend = service.backend(\"ibm_marrakesh\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a4e27bee-66de-4ffc-b1e3-40a458840e7a",
      "metadata": {},
      "source": [
        "Se non hai tempo disponibile sul tuo account o desideri utilizzare un simulatore per qualsiasi motivo, puoi eseguire la cella sottostante per configurare un simulatore che imiterà il dispositivo quantistico selezionato sopra:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 50,
      "id": "4aa86bd6-1240-49cb-aad0-c3dbc64602c5",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "2q-depth: 188\n",
            "2q-size: 281\n",
            "Operator counts: OrderedDict({'sx': 548, 'rz': 380, 'cz': 281, 'measure': 8, 'x': 6})\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/shors-algorithm/extracted-outputs/4aa86bd6-1240-49cb-aad0-c3dbc64602c5-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 50,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "pm = generate_preset_pass_manager(optimization_level=2, backend=backend)\n",
        "\n",
        "transpiled_circuit = pm.run(circuit)\n",
        "\n",
        "print(f\"2q-depth: {transpiled_circuit.depth(lambda x: x.operation.num_qubits==2)}\")\n",
        "print(f\"2q-size: {transpiled_circuit.size(lambda x: x.operation.num_qubits==2)}\")\n",
        "print(f\"Operator counts: {transpiled_circuit.count_ops()}\")\n",
        "transpiled_circuit.draw(output=\"mpl\", fold=-1, style=\"clifford\", idle_wires=False)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b0425c46-2f2e-436f-9d37-a29fbc3a0006",
      "metadata": {},
      "source": [
        "<span id=\"3-execute\" />\n",
        "\n",
        "### 3. Eseguire\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 51,
      "id": "6ee53841-d7e0-4a23-a3eb-bed31465374f",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Sampler primitive to obtain the probability distribution\n",
        "sampler = Sampler(backend)\n",
        "\n",
        "# Turn on dynamical decoupling with sequence XpXm\n",
        "sampler.options.dynamical_decoupling.enable = True\n",
        "sampler.options.dynamical_decoupling.sequence_type = \"XpXm\"\n",
        "# Enable gate twirling\n",
        "sampler.options.twirling.enable_gates = True\n",
        "\n",
        "pub = transpiled_circuit\n",
        "job = sampler.run([pub], shots=1024)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 52,
      "id": "7fb6178e-76bd-4067-9607-1d7e0ac051f3",
      "metadata": {},
      "outputs": [],
      "source": [
        "result = job.result()[0]\n",
        "counts = result.data[\"out\"].get_counts()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 53,
      "id": "6744e165-6929-46c6-9cad-55e78fe21f07",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/shors-algorithm/extracted-outputs/6744e165-6929-46c6-9cad-55e78fe21f07-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 53,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "plot_histogram(counts, figsize=(35, 5))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2eae47e9-1161-44e5-9e00-7b0d225bcf5f",
      "metadata": {},
      "source": [
        "Vediamo quattro picchi evidenti a `00000000`, `01000000`, `10000000` e `11000000`, con alcuni conteggi in altre stringhe di bit dovuti al rumore nel computer quantistico. Ignoreremo questi e manterremo solo i quattro dominanti imponendo una soglia: solo i conteggi superiori a questa soglia saranno considerati un segnale reale al di sopra del rumore.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "948f404b-a2a9-4645-a034-31b696ba24b7",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Dictionary of bitstrings and their counts to keep\n",
        "counts_keep = {}\n",
        "# Threshold to filter\n",
        "threshold = np.max(list(counts.values())) / 2\n",
        "\n",
        "for key, value in counts.items():\n",
        "    if value > threshold:\n",
        "        counts_keep[key] = value\n",
        "\n",
        "print(counts_keep)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f9bac651-2d89-4b4e-9930-ea585a4c042e",
      "metadata": {},
      "source": [
        "<span id=\"4-post-process\" />\n",
        "\n",
        "### 4. Post-elaborazione\n",
        "\n",
        "Per l'algoritmo di Shor, gran parte dell'algoritmo viene eseguito in modo classico. Quindi, inseriremo il resto nella fase di \"post-elaborazione\", dopo aver ottenuto le nostre misurazioni dal computer quantistico. Ciascuna delle misurazioni sopra riportate può essere convertita in numeri interi che, dopo averli divisi per $2^m$, costituiscono le nostre approssimazioni per $\\frac{k}{r}$, dove $k$ è casuale ogni volta.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 55,
      "id": "aad78e69-adb8-416f-bc68-c2086f900bef",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "ATTEMPT 0:\n",
            "Phase: theta = 0.0\n",
            "Order of 2 modulo 15 estimated as: r = 1\n",
            "\n",
            "ATTEMPT 1:\n",
            "Phase: theta = 0.75\n",
            "Order of 2 modulo 15 estimated as: r = 4\n",
            "*** Non-trivial factor found: 3 ***\n"
          ]
        }
      ],
      "source": [
        "a = 2\n",
        "N = 15\n",
        "\n",
        "FACTOR_FOUND = False\n",
        "num_attempt = 0\n",
        "\n",
        "while not FACTOR_FOUND:\n",
        "    print(f\"\\nATTEMPT {num_attempt}:\")\n",
        "    # Here, we get the bitstring by iterating over outcomes\n",
        "    # of a previous hardware run with multiple shots.\n",
        "    # Instead, we can also perform a single-shot measurement\n",
        "    # here in the loop.\n",
        "    bitstring = list(counts_keep.keys())[num_attempt]\n",
        "    num_attempt += 1\n",
        "    # Find the phase from measurement\n",
        "    decimal = int(bitstring, 2)\n",
        "    phase = decimal / (2**num_control)  # phase = k / r\n",
        "    print(f\"Phase: theta = {phase}\")\n",
        "\n",
        "    # Guess the order from phase\n",
        "    frac = Fraction(phase).limit_denominator(N)\n",
        "    r = frac.denominator  # order = r\n",
        "    print(f\"Order of {a} modulo {N} estimated as: r = {r}\")\n",
        "\n",
        "    if phase != 0:\n",
        "        # Guesses for factors are gcd(a^{r / 2} ± 1, 15)\n",
        "        if r % 2 == 0:\n",
        "            x = pow(a, r // 2, N) - 1\n",
        "            d = gcd(x, N)\n",
        "            if d > 1:\n",
        "                FACTOR_FOUND = True\n",
        "                print(f\"*** Non-trivial factor found: {x} ***\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c88c65c3-2dfb-47fd-bef9-59f02440f418",
      "metadata": {},
      "source": [
        "<span id=\"conclusion\" />\n",
        "\n",
        "## Conclusione\n",
        "\n",
        "Dopo aver completato il modulo, potresti rimanere colpito da un nuovo apprezzamento per la genialità di Peter Shor, che ha ideato un algoritmo così ingegnoso. Ma speriamo che anche voi abbiate raggiunto un nuovo livello di comprensione della sua semplicità ingannevole. Sebbene l'algoritmo possa sembrare incredibilmente (o intimidatoriamente) complesso, se lo scomponi in ogni singolo passaggio logico e lo esegui lentamente, anche tu sarai in grado di eseguire l'algoritmo di Shor.\n",
        "\n",
        "Sebbene siamo ancora lontani dall'utilizzare questo algoritmo per scomporre numeri come RSA1024, i nostri computer quantistici migliorano ogni giorno e, una volta raggiunta una soglia chiamata *tolleranza ai guasti*, algoritmi come questi saranno presto disponibili. È un momento entusiasmante per imparare qualcosa sul quantum computing!\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9f1e3bb2-9732-406f-9f17-862178724285",
      "metadata": {},
      "source": [
        "<span id=\"problems\" />\n",
        "\n",
        "## Problemi\n",
        "\n",
        "<span id=\"critical-concepts\" />\n",
        "\n",
        "### Concetti fondamentali:\n",
        "\n",
        "* I moderni sistemi crittografici si basano sulla classica difficoltà di scomporre grandi numeri interi in fattori primi.\n",
        "* L'aritmetica modulare — comprese le strutture $\\mathbb{Z}_N$ e $\\mathbb{Z}_N^*$ — fornisce le basi matematiche per l'algoritmo di Shor.\n",
        "* Il problema della fattorizzazione di un numero intero $N$ può essere ridotto al problema di trovare l'ordine di un numero modulo $N$.\n",
        "* La ricerca dell'ordine quantistico utilizza tecniche di stima della fase quantistica per determinare il periodo della funzione $a^x \\mod N$.\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 quindi calcola in modo classico i fattori dal risultato.\n",
        "\n",
        "<span id=\"true/false\" />\n",
        "\n",
        "### Vero/Falso:\n",
        "\n",
        "1. Vero/Falso L'efficienza dell'algoritmo di Shor minaccia la sicurezza della crittografia RSA.\n",
        "2. Vero/Falso L'algoritmo di Shor può essere eseguito in modo efficiente su qualsiasi computer quantistico moderno.\n",
        "3. T/F L'algoritmo di Shor utilizza la stima della fase quantistica (QPE) come subroutine chiave.\n",
        "4. Vero/Falso La parte classica dell'algoritmo di Shor prevede il calcolo del massimo comune divisore (MCD).\n",
        "5. Vero/Falso L'algoritmo di Shor funziona solo per la fattorizzazione dei numeri pari.\n",
        "6. Vero/Falso Un'esecuzione riuscita dell'algoritmo di Shor garantisce sempre i fattori corretti.\n",
        "\n",
        "<span id=\"short-answer\" />\n",
        "\n",
        "### Risposta breve:\n",
        "\n",
        "1. Perché l'algoritmo di Shor è considerato una potenziale minaccia futura per la crittografia RSA?\n",
        "2. Perché trovare il periodo, o ordine, di una funzione esponenziale modulare è utile per scomporre un numero nell'algoritmo di Shor?\n",
        "\n",
        "<span id=\"challenge-problems\" />\n",
        "\n",
        "### Problemi di sfida:\n",
        "\n",
        "1. Quanti qubit di controllo $m$ sono necessari per un dato numero $N$ che si sta cercando di scomporre per ottenere la precisione nel QPE necessaria per trovare il valore corretto dell'ordine $r$?\n",
        "\n",
        "2. Seguendo la procedura che abbiamo descritto qui per scomporre il 15, ora prova a scomporre il 21.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "in_page_toc_max_heading_level": 2,
    "in_page_toc_min_heading_level": 2,
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3"
    },
    "widgets": {
      "application/vnd.jupyter.widget-state+json": {
        "state": {},
        "version_major": 2,
        "version_minor": 0
      }
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}