{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "e72c9e73-98e1-49ae-bc3c-511954eeb15c",
      "metadata": {
        "tags": [
          "remove_cell"
        ]
      },
      "source": [
        "---\n",
        "title: \"Algoritmo di Shor\"\n",
        "description: \"Questo tutorial si concentra sulla dimostrazione dell'algoritmo di Shor mediante la scomposizione in fattori del numero 15 su un computer quantistico.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore textrm */}\n",
        "\n",
        "<span id=\"shors-algorithm\" />\n",
        "\n",
        "# Algoritmo di Shor\n",
        "\n",
        "*Stima di utilizzo: Tre secondi su un processore Eagle r3 (NOTA: questa è solo una stima. Il tempo di esecuzione potrebbe variare)*\n",
        "\n",
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## Risultati di apprendimento\n",
        "\n",
        "Dopo aver seguito questo tutorial, gli utenti dovrebbero aver compreso:\n",
        "\n",
        "* I fondamenti matematici dell'algoritmo di Shor per la fattorizzazione dei numeri interi\n",
        "* Come eseguire un'istanza di esempio di questo algoritmo su un sistema hardware\n",
        "\n",
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## Prerequisiti\n",
        "\n",
        "Consigliamo agli utenti di acquisire familiarità con i seguenti argomenti prima di seguire questo tutorial:\n",
        "\n",
        "* [Fondamenti degli algoritmi quantistici](/learning/courses/fundamentals-of-quantum-algorithms).\n",
        "* [Stima delle fasi e fattorizzazione](/learning/courses/fundamentals-of-quantum-algorithms/phase-estimation-and-factoring/introduction). In questo tutorial trattiamo alcuni di questi argomenti.\n",
        "\n",
        "<span id=\"background\" />\n",
        "\n",
        "## Sfondo\n",
        "\n",
        "[L'algoritmo di Shor](https://epubs.siam.org/doi/abs/10.1137/S0036144598347011), sviluppato da Peter Shor nel 1994, è un rivoluzionario algoritmo quantistico che consente di scomporre i numeri interi in tempo polinomiale. La sua importanza risiede nella capacità di scomporre in fattori grandi numeri interi in modo esponenzialmente più veloce rispetto a qualsiasi algoritmo classico conosciuto, mettendo a rischio la sicurezza di sistemi crittografici ampiamente utilizzati come l'RSA, che si basano proprio sulla difficoltà di scomporre in fattori numeri di grandi dimensioni. Se questo problema venisse risolto in modo efficiente su un computer quantistico sufficientemente potente, l'algoritmo di Shor potrebbe rivoluzionare settori quali la crittografia, la sicurezza informatica e la matematica computazionale, mettendo in evidenza il potere trasformativo dell'informatica quantistica.\n",
        "\n",
        "Questa esercitazione si concentra sulla dimostrazione dell'algoritmo di Shor attraverso la fattorizzazione di 15 su un computer quantistico.\n",
        "\n",
        "In primo luogo, definiamo il problema di ricerca dell'ordine e costruiamo i circuiti corrispondenti a partire dal protocollo di stima della fase quantistica. Successivamente, eseguiamo i circuiti di ricerca dell'ordine su hardware reale, utilizzando i circuiti a profondità più breve che possiamo transpilare. L'ultima sezione completa l'algoritmo di Shor collegando il problema della ricerca degli ordini alla fattorizzazione degli interi.\n",
        "\n",
        "Concludiamo l'esercitazione con una discussione su altre dimostrazioni dell'algoritmo di Shor su hardware reale, concentrandoci sia sulle implementazioni generiche sia su quelle personalizzate per la fattorizzazione di numeri interi specifici, come 15 e 21.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "22f3a6f3-0ba5-4826-a4f3-9dcc62f51c70",
      "metadata": {},
      "source": [
        "Nota: questa esercitazione si concentra maggiormente sull'implementazione e sulla dimostrazione dei circuiti relativi all'algoritmo di Shor. Per una risorsa educativa approfondita sul materiale, consultare il corso [Fundamentals of quantum algorithms](/learning/courses/fundamentals-of-quantum-algorithms/phase-estimation-and-factoring/introduction) del Dr. John Watrous e i documenti nella sezione [Riferimenti](#references).\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b41b8639-ff72-4cd5-8bd3-3c16c01bedc0",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "### Requisiti\n",
        "\n",
        "Prima di iniziare questa esercitazione, assicuratevi di aver installato quanto segue:\n",
        "\n",
        "* Qiskit SDK v2.0 o versioni successive, con supporto [alla visualizzazione](/docs/api/qiskit/visualization)\n",
        "* Qiskit Runtime v0.40 o più tardi (`pip install qiskit-ibm-runtime`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "93dcb213-ddcd-4329-8bcb-7c61fa298ee5",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "### Configura\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "0860914d-cf1f-4bee-907d-3c442cd92cc6",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import pandas as pd\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 QFT, UnitaryGate\n",
        "from qiskit.transpiler import CouplingMap, 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": "a28eeaa5-2da0-4b6b-8877-547a86fbcd76",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "## Fase 1: mappare gli input classici su un problema quantistico\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c09ec908-51ba-4ea3-bc84-142e8f8002bf",
      "metadata": {},
      "source": [
        "L'algoritmo di Shor per la fattorizzazione degli interi utilizza un problema intermedio noto come problema di *ricerca dell'ordine*. In questa sezione dimostriamo come risolvere il problema della ricerca dell'ordine utilizzando la *stima quantistica della fase*.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e1aa8e70-a760-43ca-bcf2-def23d49ba36",
      "metadata": {},
      "source": [
        "<span id=\"phase-estimation-problem\" />\n",
        "\n",
        "### Problema di stima di fase\n",
        "\n",
        "Nel problema della stima di fase, ci viene dato uno stato quantistico $\\ket{\\psi}$ di $n$ qubit, insieme a un circuito quantistico unitario che agisce su $n$ qubit. Ci è stato promesso che $\\ket{\\psi}$ è un autovettore della matrice unitaria $U$ che descrive l'azione del circuito e il nostro obiettivo è calcolare o approssimare l'autovalore $\\lambda = e^{2 \\pi i \\theta}$ a cui corrisponde $\\ket{\\psi}$. In altre parole, il circuito deve fornire un'approssimazione del numero $\\theta \\in [0, 1)$ soddisfacente $U \\ket{\\psi}= e^{2 \\pi i \\theta} \\ket{\\psi}.$ L'obiettivo del circuito di stima della fase è quello di approssimare $\\theta$ in $m$ bit. In termini matematici, vorremmo trovare $y$ tale che $\\theta \\approx y / 2^m$, dove $y \\in {0, 1, 2, \\dots, 2^{m-1}}$. L'immagine seguente mostra il circuito quantistico che stima $y$ in $m$ bit effettuando una misura su $m$ qubit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7ae92a5f-55b4-4597-be58-fe47b450dc39",
      "metadata": {},
      "source": [
        "![Circuito di stima quantistica della fase](https://quantum.cloud.ibm.com/learning/images/courses/fundamentals-of-quantum-algorithms/phase-estimation-and-factoring/phase-estimation-procedure.svg)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c527ff28-8038-44fc-8d62-1c9dda2b600e",
      "metadata": {},
      "source": [
        "Nel circuito di cui sopra, i qubit superiori $m$ sono iniziati nello stato $\\ket{0^m}$ e i qubit inferiori $n$ sono iniziati nello stato $\\ket{\\psi}$, che è promesso essere un autovettore di $U$. Il primo ingrediente del circuito di stima della fase sono le operazioni controllate-unitarie che sono responsabili dell'esecuzione di un *contraccolpo di fase* al loro corrispondente qubit di controllo. Queste unità controllate vengono esponenziate in base alla posizione del qubit di controllo, che va dal bit meno significativo al bit più significativo. Poiché $\\ket{\\psi}$ è un autovalore di $U$, lo stato dei qubit inferiori di $n$ non è influenzato da questa operazione, ma l'informazione di fase dell'autovalore si propaga ai qubit superiori di $m$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "40b4a6e3-6f46-4971-8054-4a7ae9dd535e",
      "metadata": {},
      "source": [
        "Si scopre che, dopo l'operazione di contraccolpo di fase tramite le unità controllate, tutti i possibili stati dei qubit top $m$ sono ortonormali tra loro per ogni autovettore $\\ket{\\psi}$ dell'unità $U$. Pertanto, questi stati sono perfettamente distinguibili e possiamo ruotare la base che formano verso la base computazionale per effettuare una misura. Un'analisi matematica mostra che questa matrice di rotazione corrisponde alla trasformata quantistica di Fourier (QFT) inversa in $2^m$ spazio di Hilbert. L'intuizione è che la struttura periodica degli operatori di esponenziazione modulare è codificata nello stato quantistico e la QFT converte questa periodicità in picchi misurabili nel dominio della frequenza.\n",
        "\n",
        "Per una comprensione più approfondita del motivo per cui il circuito QFT è impiegato nell'algoritmo di Shor, rimandiamo il lettore al corso [Fondamenti di algoritmi quantistici](/learning/courses/fundamentals-of-quantum-algorithms/phase-estimation-and-factoring/introduction).\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "acfa31d3-7980-42ad-b418-77a24d16513a",
      "metadata": {},
      "source": [
        "Siamo ora pronti a utilizzare il circuito di stima della fase per la ricerca dell'ordine.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "535674d6-cc8a-45f5-9fc7-f5fd3053f4c4",
      "metadata": {},
      "source": [
        "<span id=\"order-finding-problem\" />\n",
        "\n",
        "### Problema con l'ordine\n",
        "\n",
        "Per definire il problema della ricerca di ordini, iniziamo con alcuni concetti di teoria dei numeri. In primo luogo, per ogni dato intero positivo $N$, definire l'insieme $\\mathbb{Z}_N$ come $\\mathbb{Z}_N = \\{0, 1, 2, \\dots, N-1\\}.$ Tutte le operazioni aritmetiche in $\\mathbb{Z}_N$ sono eseguite modulo $N$. In particolare, tutti gli elementi $a \\in \\mathbb{Z}_n$ che sono coprimi con $N$ sono speciali e costituiscono $\\mathbb{Z}^*_N$ come $\\mathbb{Z}^*_N = \\{ a \\in \\mathbb{Z}_N : \\mathrm{gcd}(a, N)=1 \\}.$ Per un elemento $a \\in \\mathbb{Z}^*_N$, il più piccolo intero positivo $r$ tale che $a^r \\equiv 1 \\; (\\mathrm{mod} \\; N)$ sia definito come l' *ordine* di $a$ modulo $N$. Come vedremo in seguito, trovare l'ordine di un $a \\in \\mathbb{Z}^*_N$ ci permetterà di fattorizzare $N$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6de4cbed-6591-40da-a994-2ff139c9f064",
      "metadata": {},
      "source": [
        "Per costruire il circuito di ricerca dell'ordine dal circuito di stima della fase, sono necessarie due considerazioni. In primo luogo, dobbiamo definire l'unità $U$ che ci permetterà di trovare l'ordine $r$, e in secondo luogo, dobbiamo definire un autovettore $\\ket{\\psi}$ di $U$ per preparare lo stato iniziale del circuito di stima della fase.\n",
        "\n",
        "Per collegare il problema dell'order finding alla stima di fase, consideriamo l'operazione definita su un sistema i cui stati classici corrispondono a $\\mathbb{Z}_N$, dove moltiplichiamo per un elemento fisso $a \\in \\mathbb{Z}^*_N$. In particolare, definiamo questo operatore di moltiplicazione $M_a$ tale che $M_a \\ket{x} = \\ket{ax \\; (\\mathrm{mod} \\; N)}$ per ogni $x \\in \\mathbb{Z}_N$. Si noti che è implicito che stiamo prendendo il prodotto modulo $N$ all'interno del ket sul lato destro dell'equazione. Un'analisi matematica dimostra che $M_a$ è un operatore unitario. Inoltre, si scopre che $M_a$ ha coppie di autovettori e autovalori che ci permettono di collegare l'ordine $r$ di $a$ al problema della stima della fase. In particolare, per qualsiasi scelta di $j \\in \\{0, \\dots, r-1\\}$, si ha che $\\ket{\\psi_j} = \\frac{1}{\\sqrt{r}} \\sum^{r-1}_{k=0} \\omega^{-jk}_{r} \\ket{a^k}$ è un autovalore di $M_a$ il cui corrispondente autovalore è $\\omega^{j}_{r}$, dove $\\omega^{j}_{r} = e^{2 \\pi i \\frac{j}{r}}.$\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "57942d8d-b2fc-4863-bfdd-f99aa941e0c0",
      "metadata": {},
      "source": [
        "Dall'osservazione, vediamo che una conveniente coppia autovettore/valore è lo stato $\\ket{\\psi_1}$ con $\\omega^{1}_{r} = e^{2 \\pi i \\frac{1}{r}}$. Pertanto, se riuscissimo a trovare l'autovalore $\\ket{\\psi_1}$, potremmo stimare la fase $\\theta=1/r$ con il nostro circuito quantistico e quindi ottenere una stima dell'ordine $r$. Tuttavia, non è facile farlo e dobbiamo considerare un'alternativa.\n",
        "\n",
        "Consideriamo il risultato del circuito se prepariamo lo stato computazionale $\\ket{1}$ come stato iniziale. Questo non è un autostato di $M_a$, ma è la sovrapposizione uniforme degli autostati appena descritti. In altre parole, vale la seguente relazione. $\\ket{1} = \\frac{1}{\\sqrt{r}} \\sum^{r-1}_{k=0} \\ket{\\psi_k}$ L'implicazione dell'equazione precedente è che se impostiamo lo stato iniziale su $\\ket{1}$, otterremo esattamente lo stesso risultato di misura che avremmo ottenuto se avessimo scelto $k \\in \\{ 0, \\dots, r-1\\}$ in modo uniforme e casuale e avessimo usato $\\ket{\\psi_k}$ come autovettore nel circuito di stima della fase. In altre parole, una misura dei primi $m$ qubit produce un'approssimazione $y / 2^m$ al valore $k / r$ dove $k \\in \\{ 0, \\dots, r-1\\}$ è scelto uniformemente a caso. Questo ci permette di imparare $r$ con un alto grado di confidenza dopo diverse esecuzioni indipendenti, che era il nostro obiettivo.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c037fd9d-2ad6-4258-a11d-bd91944e7f01",
      "metadata": {},
      "source": [
        "<span id=\"modular-exponentiation-operators\" />\n",
        "\n",
        "### Operatori di esponenziazione modulari\n",
        "\n",
        "Finora abbiamo collegato il problema della stima della fase al problema della ricerca dell'ordine definendo $U = M_a$ e $\\ket{\\psi} = \\ket{1}$ nel nostro circuito quantistico. Pertanto, l'ultimo ingrediente rimasto è trovare un modo efficiente per definire gli esponenziali modulari di $M_a$ come $M_a^k$ per $k = 1, 2, 4, \\dots, 2^{m-1}$. Per eseguire questo calcolo, scopriamo che per qualsiasi potenza $k$ scelta, possiamo creare un circuito per $M_a^k$ non iterando $k$ per il circuito per $M_a$, ma calcolando $b = a^k \\; \\mathrm{mod} \\; N$ e poi usando il circuito per $M_b$. Poiché ci servono solo le potenze che sono potenze di 2, possiamo eseguire questa operazione in modo classicamente efficiente utilizzando la quadratura iterativa.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "09d93ee7-ab42-43a8-a44f-ad303c9407c7",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-problem-for-quantum-hardware-execution\" />\n",
        "\n",
        "## Fase 2: Ottimizzazione del problema per l'esecuzione su hardware quantistico\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "80d39ec2-715f-4014-9dbc-37db001528b1",
      "metadata": {},
      "source": [
        "<span id=\"specific-example-with-$n-=-15$-and-$a=2$\" />\n",
        "\n",
        "### Esempio specifico con $N = 15$ e $a=2$\n",
        "\n",
        "Possiamo fermarci qui per discutere un esempio specifico e costruire il circuito di ricerca dell'ordine per $N=15$. Si noti che i possibili $a \\in \\mathbb{Z}_N^*$ non banali per $N=15$ sono $a \\in \\{2, 4, 7, 8, 11, 13, 14 \\}$. Per questo esempio, scegliamo $a=2$. Costruiremo l'operatore $M_2$ e gli operatori di esponenziazione modulare $M_2^k$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5fcd0c79-0c62-42f1-b6fa-c0f4c58e1994",
      "metadata": {},
      "source": [
        "L'azione di $M_2$ sugli stati base computazionali è la seguente. $M_2 \\ket{0} = \\ket{0} \\quad M_2 \\ket{5} = \\ket{10} \\quad M_2 \\ket{10} = \\ket{5}$ $M_2 \\ket{1} = \\ket{2} \\quad M_2 \\ket{6} = \\ket{12} \\quad M_2 \\ket{11} = \\ket{7}$ $M_2 \\ket{2} = \\ket{4} \\quad M_2 \\ket{7} = \\ket{14} \\quad M_2 \\ket{12} = \\ket{9}$ $M_2 \\ket{3} = \\ket{6} \\quad M_2 \\ket{8} = \\ket{1} \\quad M_2 \\ket{13} = \\ket{11}$ $M_2 \\ket{4} = \\ket{8} \\quad M_2 \\ket{9} = \\ket{3} \\quad M_2 \\ket{14} = \\ket{13}$ Osservando, si nota che gli stati base sono mescolati, quindi si ha una matrice di permutazione. Possiamo costruire questa operazione su quattro qubit con porte di scambio. Di seguito, si costruiscono le operazioni $M_2$ e $M_2$ controllate.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "ec5d416b-7fc9-43f3-b291-e0edc8ad195f",
      "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": 3,
      "id": "0a8885f1-91d4-40bd-912d-dc5eea05f5bd",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/shors-algorithm/extracted-outputs/0a8885f1-91d4-40bd-912d-dc5eea05f5bd-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 3,
          "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": "code",
      "execution_count": 4,
      "id": "d0ae7456-053a-4389-8653-a1c9c8ff757c",
      "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": 5,
      "id": "ab7fe331-2f9e-47ca-ba3b-f5d67992062a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/shors-algorithm/extracted-outputs/ab7fe331-2f9e-47ca-ba3b-f5d67992062a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 5,
          "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": "139b6d31-33d0-49ea-82b1-5bb50b5b6c9e",
      "metadata": {},
      "source": [
        "Le porte che agiscono su più di due qubit saranno ulteriormente scomposte in porte a due qubit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "13b4841d-a4ac-46bd-b4d0-d111b3017189",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/shors-algorithm/extracted-outputs/13b4841d-a4ac-46bd-b4d0-d111b3017189-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "circ.decompose(reps=2).draw(output=\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e361cc09-e357-4ba7-950b-9fa83898fa88",
      "metadata": {},
      "source": [
        "Ora dobbiamo costruire gli operatori di esponenziazione modulare. Per ottenere una precisione sufficiente nella stima della fase, utilizzeremo otto qubit per la misura della stima. Pertanto, è necessario costruire $M_b$ con $b = a^{2^k} \\; (\\mathrm{mod} \\; N)$ per ogni $k = 0, 1, \\dots, 7$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "72d989c8-ef62-44d7-887c-5fa13db818e9",
      "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": 8,
      "id": "69fa8c9f-4107-4339-96db-c6f950a71261",
      "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": "e28ed7e5-f7ca-4b7b-b875-f14c5229588c",
      "metadata": {},
      "source": [
        "Come possiamo vedere dall'elenco dei valori di $b$, oltre a $M_2$ che abbiamo costruito in precedenza, dobbiamo costruire anche $M_4$ e $M_1$. Si noti che $M_1$ agisce banalmente sugli stati della base computazionale, quindi è semplicemente l'operatore di identità.\n",
        "\n",
        "$M_4$ agisce sugli stati base computazionali come segue. $M_4 \\ket{0} = \\ket{0} \\quad M_4 \\ket{5} = \\ket{5} \\quad M_4 \\ket{10} = \\ket{10}$ $M_4 \\ket{1} = \\ket{4} \\quad M_4 \\ket{6} = \\ket{9} \\quad M_4 \\ket{11} = \\ket{14}$ $M_4 \\ket{2} = \\ket{8} \\quad M_4 \\ket{7} = \\ket{13} \\quad M_4 \\ket{12} = \\ket{3}$ $M_4 \\ket{3} = \\ket{12} \\quad M_4 \\ket{8} = \\ket{2} \\quad M_4 \\ket{13} = \\ket{7}$ $M_4 \\ket{4} = \\ket{1} \\quad M_4 \\ket{9} = \\ket{6} \\quad M_4 \\ket{14} = \\ket{11}$\n",
        "\n",
        "Pertanto, questa permutazione può essere costruita con la seguente operazione di scambio.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "4f3a5fe4-5449-4869-ae94-6fdefd6765f0",
      "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": 10,
      "id": "be041e3d-28b1-453e-983e-184c2366aeb9",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/shors-algorithm/extracted-outputs/be041e3d-28b1-453e-983e-184c2366aeb9-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 10,
          "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": "code",
      "execution_count": 11,
      "id": "0efb7000-7d13-4b73-95d4-f9f747ec5119",
      "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": 12,
      "id": "8d943b00-a502-4157-8a0d-13fb1f55e705",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/shors-algorithm/extracted-outputs/8d943b00-a502-4157-8a0d-13fb1f55e705-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 12,
          "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": "7d2a4f30-afb1-49fc-abbb-b9070b58fe8d",
      "metadata": {},
      "source": [
        "Le porte che agiscono su più di due qubit saranno ulteriormente scomposte in porte a due qubit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "68399eef-5e55-4c95-a8a4-c8efaebd34b9",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/shors-algorithm/extracted-outputs/68399eef-5e55-4c95-a8a4-c8efaebd34b9-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "circ.decompose(reps=2).draw(output=\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1f12fb24-257b-4b12-b7c5-e01b975e3216",
      "metadata": {},
      "source": [
        "Abbiamo visto che gli operatori di $M_b$ per un dato $b \\in \\mathbb{Z}^*_N$ sono operazioni di permutazione. A causa delle dimensioni relativamente ridotte del problema di permutazione che abbiamo qui, dato che $N=15$ richiede solo quattro qubit, siamo stati in grado di sintetizzare queste operazioni direttamente con le porte di `SWAP` mediante un'ispezione. In generale, questo potrebbe non essere un approccio scalabile. Invece, potrebbe essere necessario costruire esplicitamente la matrice di permutazione e utilizzare la classe `UnitaryGate` di Qiskit e i metodi di transpilazione per sintetizzare questa matrice di permutazione. Tuttavia, questo può portare a circuiti molto più profondi. Segue un esempio.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "33a328b8-11d8-4b0c-a277-5d76b2c07c5e",
      "metadata": {},
      "outputs": [],
      "source": [
        "def mod_mult_gate(b, N):\n",
        "    \"\"\"\n",
        "    Modular multiplication gate from permutation matrix.\n",
        "    \"\"\"\n",
        "    if gcd(b, N) > 1:\n",
        "        print(f\"Error: gcd({b},{N}) > 1\")\n",
        "    else:\n",
        "        n = floor(log(N - 1, 2)) + 1\n",
        "        U = np.full((2**n, 2**n), 0)\n",
        "        for x in range(N):\n",
        "            U[b * x % N][x] = 1\n",
        "        for x in range(N, 2**n):\n",
        "            U[x][x] = 1\n",
        "        G = UnitaryGate(U)\n",
        "        G.name = f\"M_{b}\"\n",
        "        return G"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "c184f6dd-9f80-4487-ac0b-0dd94170b0f0",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "qubits: 4\n",
            "2q-depth: 94\n",
            "2q-size: 96\n",
            "Operator counts: OrderedDict({'cx': 45, 'swap': 32, 'u': 24, 'u1': 7, 'u3': 4, 'unitary': 3, 'circuit-335': 1, 'circuit-338': 1, 'circuit-341': 1, 'circuit-344': 1, 'circuit-347': 1, 'circuit-350': 1, 'circuit-353': 1, 'circuit-356': 1, 'circuit-359': 1, 'circuit-362': 1, 'circuit-365': 1, 'circuit-368': 1, 'circuit-371': 1, 'circuit-374': 1, 'circuit-377': 1, 'circuit-380': 1})\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/shors-algorithm/extracted-outputs/c184f6dd-9f80-4487-ac0b-0dd94170b0f0-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 15,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Let's build M2 using the permutation matrix definition\n",
        "M2_other = mod_mult_gate(2, 15)\n",
        "\n",
        "# Add it to a circuit\n",
        "circ = QuantumCircuit(4)\n",
        "circ.compose(M2_other, inplace=True)\n",
        "circ = circ.decompose()\n",
        "\n",
        "# Transpile the circuit and get the depth\n",
        "coupling_map = CouplingMap.from_line(4)\n",
        "pm = generate_preset_pass_manager(coupling_map=coupling_map)\n",
        "transpiled_circ = pm.run(circ)\n",
        "\n",
        "print(f\"qubits: {circ.num_qubits}\")\n",
        "print(\n",
        "    f\"2q-depth: {transpiled_circ.depth(lambda x: x.operation.num_qubits==2)}\"\n",
        ")\n",
        "print(f\"2q-size: {transpiled_circ.size(lambda x: x.operation.num_qubits==2)}\")\n",
        "print(f\"Operator counts: {transpiled_circ.count_ops()}\")\n",
        "transpiled_circ.decompose().draw(\n",
        "    output=\"mpl\", fold=-1, style=\"clifford\", idle_wires=False\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fe8caaf6-4bb6-4d27-8177-71f1b530556d",
      "metadata": {},
      "source": [
        "Confrontiamo questi conteggi con la profondità del circuito compilato della nostra implementazione manuale della porta $M_2$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "0235c931-0adb-4972-9fce-32a0341822bf",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "qubits: 4\n",
            "2q-depth: 9\n",
            "2q-size: 9\n",
            "Operator counts: OrderedDict({'cx': 9})\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/shors-algorithm/extracted-outputs/0235c931-0adb-4972-9fce-32a0341822bf-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 16,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Get the M2 operator from our manual construction\n",
        "M2 = M2mod15()\n",
        "\n",
        "# Add it to a circuit\n",
        "circ = QuantumCircuit(4)\n",
        "circ.compose(M2, inplace=True)\n",
        "circ = circ.decompose(reps=3)\n",
        "\n",
        "# Transpile the circuit and get the depth\n",
        "coupling_map = CouplingMap.from_line(4)\n",
        "pm = generate_preset_pass_manager(coupling_map=coupling_map)\n",
        "transpiled_circ = pm.run(circ)\n",
        "\n",
        "print(f\"qubits: {circ.num_qubits}\")\n",
        "print(\n",
        "    f\"2q-depth: {transpiled_circ.depth(lambda x: x.operation.num_qubits==2)}\"\n",
        ")\n",
        "print(f\"2q-size: {transpiled_circ.size(lambda x: x.operation.num_qubits==2)}\")\n",
        "print(f\"Operator counts: {transpiled_circ.count_ops()}\")\n",
        "transpiled_circ.draw(\n",
        "    output=\"mpl\", fold=-1, style=\"clifford\", idle_wires=False\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c3f0f349-a26c-4176-aa9d-407e17184b91",
      "metadata": {},
      "source": [
        "Come si può notare, l'approccio della matrice di permutazione ha prodotto un circuito significativamente più profondo anche per un singolo gate $M_2$ rispetto alla nostra implementazione manuale. Pertanto, continueremo con la nostra precedente implementazione delle operazioni di $M_b$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0dafd632-797b-402c-a88a-821a62c7265a",
      "metadata": {},
      "source": [
        "Ora siamo pronti a costruire il circuito di ricerca dell'ordine completo utilizzando gli operatori di esponenziazione modulare controllata definiti in precedenza. Nel codice che segue, importiamo anche il [circuito QFT](/docs/api/qiskit/qiskit.circuit.library.QFT) dalla libreria Qiskit Circuit, che utilizza porte Hadamard su ogni qubit, una serie di porte controlled-U1 (o Z, a seconda della fase) e uno strato di porte swap.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "0e854aed-c11b-494c-8c80-adeb8eb0e8fe",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/shors-algorithm/extracted-outputs/0e854aed-c11b-494c-8c80-adeb8eb0e8fe-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 17,
          "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(QFT(num_control, inverse=True), 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": "78cf31ef-b2e7-4256-996a-5f89500efb52",
      "metadata": {},
      "source": [
        "Si noti che abbiamo omesso le operazioni di esponenziazione modulare controllata dai restanti qubit di controllo perché $M_1$ è l'operatore di identità.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "792a5cb7-2ac0-4964-b747-2780193a0401",
      "metadata": {},
      "source": [
        "Si noti che più avanti in questa esercitazione si eseguirà questo circuito sul backend `ibm_marrakesh` . A tal fine, transpiliamo il circuito in base a questo backend specifico e riportiamo la profondità del circuito e il numero di porte.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "95925dd5-7ba9-4746-b96e-ba50400fa5ac",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "2q-depth: 187\n",
            "2q-size: 260\n",
            "Operator counts: OrderedDict({'sx': 521, 'rz': 354, 'cz': 260, 'measure': 8, 'x': 4})\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/shors-algorithm/extracted-outputs/95925dd5-7ba9-4746-b96e-ba50400fa5ac-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 18,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.backend(\"ibm_marrakesh\")\n",
        "pm = generate_preset_pass_manager(optimization_level=2, backend=backend)\n",
        "\n",
        "transpiled_circuit = pm.run(circuit)\n",
        "\n",
        "print(\n",
        "    f\"2q-depth: {transpiled_circuit.depth(lambda x: x.operation.num_qubits==2)}\"\n",
        ")\n",
        "print(\n",
        "    f\"2q-size: {transpiled_circuit.size(lambda x: x.operation.num_qubits==2)}\"\n",
        ")\n",
        "print(f\"Operator counts: {transpiled_circuit.count_ops()}\")\n",
        "transpiled_circuit.draw(\n",
        "    output=\"mpl\", fold=-1, style=\"clifford\", idle_wires=False\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f96a2a7e-0363-49d6-bc21-f0f207a84610",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "## Passaggio 3: eseguire utilizzando Qiskit primitives\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a3fd5248-11f2-40b6-857a-6efc25e31bdb",
      "metadata": {},
      "source": [
        "In primo luogo, discutiamo ciò che si otterrebbe teoricamente se si eseguisse questo circuito su un simulatore ideale. Di seguito sono riportati i risultati della simulazione del circuito di cui sopra con 1024 scatti. Come si vede, si ottiene una distribuzione approssimativamente uniforme su quattro stringhe di bit sui qubit di controllo.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "c8720362-684f-4114-8abb-83d71d21c0df",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Obtained from the simulator\n",
        "counts = {\"00000000\": 264, \"01000000\": 268, \"10000000\": 249, \"11000000\": 243}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "0d6d2702-02e4-47de-8f7e-0b256657ef0f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/shors-algorithm/extracted-outputs/0d6d2702-02e4-47de-8f7e-0b256657ef0f-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 20,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "plot_histogram(counts)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5ddd6952-fb4d-447c-8781-968ca5bb88a3",
      "metadata": {},
      "source": [
        "Misurando i qubit di controllo, otteniamo una stima di fase a otto bit dell'operatore $M_a$. Possiamo convertire questa rappresentazione binaria in decimale per trovare la fase misurata. Come si può vedere dall'istogramma sopra riportato, sono state misurate quattro diverse stringhe di bit, ognuna delle quali corrisponde a un valore di fase come segue.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "59ae21fc-e3d0-48e3-8cc6-dbce59c747ca",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "            Register Output           Phase\n",
            "0  00000000(bin) =   0(dec)    0/256 = 0.00\n",
            "1  01000000(bin) =  64(dec)   64/256 = 0.25\n",
            "2  10000000(bin) = 128(dec)  128/256 = 0.50\n",
            "3  11000000(bin) = 192(dec)  192/256 = 0.75\n"
          ]
        }
      ],
      "source": [
        "# Rows to be displayed in table\n",
        "rows = []\n",
        "# Corresponding phase of each bitstring\n",
        "measured_phases = []\n",
        "\n",
        "for output in counts:\n",
        "    decimal = int(output, 2)  # Convert bitstring to decimal\n",
        "    phase = decimal / (2**num_control)  # Find corresponding eigenvalue\n",
        "    measured_phases.append(phase)\n",
        "    # Add these values to the rows in our table:\n",
        "    rows.append(\n",
        "        [\n",
        "            f\"{output}(bin) = {decimal:>3}(dec)\",\n",
        "            f\"{decimal}/{2 ** num_control} = {phase:.2f}\",\n",
        "        ]\n",
        "    )\n",
        "\n",
        "# Print the rows in a table\n",
        "headers = [\"Register Output\", \"Phase\"]\n",
        "df = pd.DataFrame(rows, columns=headers)\n",
        "print(df)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f0ca65bb-abb4-4f4b-bc32-4749f36e5780",
      "metadata": {},
      "source": [
        "Ricordiamo che la fase misurata corrisponde a $\\theta = k / r$ dove $k$ è campionata in modo uniformemente casuale da $\\{0, 1, \\dots, r-1 \\}$. Pertanto, possiamo usare l'algoritmo delle frazioni continue per cercare di trovare $k$ e l'ordine $r$. Python ha questa funzionalità incorporata. Possiamo usare il modulo `fractions` per trasformare un galleggiante in un oggetto `Fraction` , ad esempio:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "999f04bc-f912-465f-9cba-90d08bda3758",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Fraction(5998794703657501, 9007199254740992)"
            ]
          },
          "execution_count": 22,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "Fraction(0.666)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c5452b79-967f-4fe9-86d7-f9797987a8b5",
      "metadata": {},
      "source": [
        "Poiché fornisce frazioni che restituiscono esattamente il risultato (in questo caso, `0.6660000...`), può dare risultati strani come quello sopra. Possiamo utilizzare il metodo `.limit_denominator()` per ottenere la frazione che più si avvicina al nostro galleggiante, con un denominatore inferiore a un certo valore:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "1352aa9e-7c98-4862-8ac6-8d17fce61f3d",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Fraction(2, 3)"
            ]
          },
          "execution_count": 23,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Get fraction that most closely resembles 0.666\n",
        "# with denominator < 15\n",
        "Fraction(0.666).limit_denominator(15)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ab1ce9d5-7a24-4717-a94b-576f8c940ec2",
      "metadata": {},
      "source": [
        "Questo è molto più bello. L'ordine (r) deve essere inferiore a N, quindi il denominatore massimo sarà `15`:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "6c20bff2-29b7-45ea-b1ed-e802e0d1a0f9",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "   Phase Fraction  Guess for r\n",
            "0   0.00      0/1            1\n",
            "1   0.25      1/4            4\n",
            "2   0.50      1/2            2\n",
            "3   0.75      3/4            4\n"
          ]
        }
      ],
      "source": [
        "# Rows to be displayed in a table\n",
        "rows = []\n",
        "\n",
        "for phase in measured_phases:\n",
        "    frac = Fraction(phase).limit_denominator(15)\n",
        "    rows.append(\n",
        "        [phase, f\"{frac.numerator}/{frac.denominator}\", frac.denominator]\n",
        "    )\n",
        "\n",
        "# Print the rows in a table\n",
        "headers = [\"Phase\", \"Fraction\", \"Guess for r\"]\n",
        "df = pd.DataFrame(rows, columns=headers)\n",
        "print(df)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7590599a-bb21-4d9c-8a78-2ff406acc4af",
      "metadata": {},
      "source": [
        "Possiamo notare che due degli autovalori misurati ci hanno fornito il risultato corretto: $r=4$, e possiamo vedere che l'algoritmo di Shor per la ricerca dell'ordine ha la possibilità di fallire. Questi cattivi risultati sono dovuti al fatto che $k = 0$, o perché $k$ e $r$ non sono coprimari - e invece di $r$, ci viene dato un fattore di $r$. La soluzione più semplice è semplicemente ripetere l'esperimento finché non si ottiene un risultato soddisfacente per $r$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "174b446c-ca09-4816-8983-33f61eaecfa6",
      "metadata": {},
      "source": [
        "Finora abbiamo implementato il problema della ricerca dell'ordine per $N=15$ con $a=2$ utilizzando il circuito di stima della fase su un simulatore. L'ultimo passo dell'algoritmo di Shor consiste nel mettere in relazione il problema della ricerca dell'ordine con il problema della fattorizzazione degli interi. Quest'ultima parte dell'algoritmo è puramente classica e può essere risolta su un computer classico dopo che le misure di fase sono state ottenute da un computer quantistico. Pertanto, rimandiamo l'ultima parte dell'algoritmo a quando avremo dimostrato come eseguire il circuito di ricerca degli ordini su un hardware reale.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7c826de7-0caa-4337-826e-3e81024ce125",
      "metadata": {},
      "source": [
        "<span id=\"hardware-runs\" />\n",
        "\n",
        "### Esecuzioni hardware\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a8d6d6a9-9494-4733-89a2-790ac8adf929",
      "metadata": {},
      "source": [
        "Ora possiamo eseguire il circuito di ricerca dell'ordine che abbiamo precedentemente tradotto per `ibm_marrakesh`. In questo caso ci rivolgiamo al [disaccoppiamento dinamico](/docs/guides/error-mitigation-and-suppression-techniques#dynamical-decoupling) (DD) per la soppressione degli errori e al [gate twirling](/docs/guides/error-mitigation-and-suppression-techniques#pauli-twirling) per la mitigazione degli errori. Il DD comporta l'applicazione di sequenze di impulsi di controllo precisamente temporizzati a un dispositivo quantistico, eliminando in modo efficace le interazioni ambientali indesiderate e la decoerenza. Il gate twirling, invece, randomizza specifiche porte quantistiche per trasformare gli errori coerenti in errori di Pauli, che si accumulano linearmente anziché quadraticamente. Entrambe le tecniche sono spesso combinate per migliorare la coerenza e la fedeltà delle computazioni quantistiche.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "af61f1de-be24-4fc6-b2bb-af6845163c3c",
      "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",
        "# Assign tags before executing\n",
        "sampler.options.environment.job_tags = [\"TUT_SA\"]\n",
        "\n",
        "pub = transpiled_circuit\n",
        "job = sampler.run([pub], shots=1024)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "id": "e5f53a20-40a4-440f-a6ea-349c20efbc95",
      "metadata": {},
      "outputs": [],
      "source": [
        "result = job.result()[0]\n",
        "counts = result.data[\"out\"].get_counts()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "id": "559d7030-1f67-44e8-afa7-6afc7a334677",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/shors-algorithm/extracted-outputs/559d7030-1f67-44e8-afa7-6afc7a334677-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 26,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "plot_histogram(counts, figsize=(35, 5))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3614aa7e-0410-4e9e-b33a-3caf407e086a",
      "metadata": {},
      "source": [
        "Come possiamo vedere, abbiamo ottenuto le stesse bitstring con i conteggi più alti. Poiché l'hardware quantistico ha un rumore, c'è una certa dispersione in altre stringhe di bit, che possiamo filtrare statisticamente.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "id": "f9cf9a07-5251-47bc-9713-c802f8f1a37c",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{'00000000': 58, '01000000': 41, '11000000': 42, '10000000': 40}\n"
          ]
        }
      ],
      "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": "c32cca67-9c9d-4373-9a0a-3174e884dd91",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "## Fase 4: Post-elaborazione e restituzione del risultato nel formato classico desiderato\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5a7b116b-02d0-47dc-8cab-e792ba36d868",
      "metadata": {},
      "source": [
        "<span id=\"integer-factorization\" />\n",
        "\n",
        "### Fattorizzazione dei numeri interi\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "691df180-6ac3-49ac-ad4d-2d78348ceea7",
      "metadata": {},
      "source": [
        "Finora abbiamo discusso come implementare il problema della ricerca dell'ordine utilizzando un circuito di stima della fase. Ora colleghiamo il problema della ricerca dell'ordine alla fattorizzazione dei numeri interi, completando così l'algoritmo di Shor. Si noti che questa parte dell'algoritmo è classica.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fe3472a3-f8cd-4db9-8178-c025ccf2ec55",
      "metadata": {},
      "source": [
        "Lo dimostriamo ora con l'esempio di $N = 15$ e $a = 2$. Ricordiamo che la fase che abbiamo misurato è $k / r$, dove $a^r \\; (\\textrm{mod} \\; N) = 1$ e $k$ è un intero casuale tra $0$ e $r - 1$. Da questa equazione, abbiamo $(a^r - 1) \\; (\\textrm{mod} \\; N) = 0,$ che significa che $N$ deve dividere $a^r-1$. Se anche $r$ è pari, allora possiamo scrivere $a^r -1 = (a^{r/2}-1)(a^{r/2}+1).$ Se $r$ non è pari, non possiamo andare oltre e dobbiamo riprovare con un valore diverso per $a$; altrimenti, c'è un'alta probabilità che il massimo comun divisore di $N$ e $a^{r/2}-1$, o $a^{r/2}+1$ sia un fattore proprio di $N$.\n",
        "\n",
        "Poiché alcune esecuzioni dell'algoritmo falliranno statisticamente, ripeteremo l'algoritmo finché non sarà trovato almeno un fattore di $N$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "dda4ad11-ef2b-4bfb-ba3c-5cb1f5810871",
      "metadata": {},
      "source": [
        "La cella sottostante ripete l'algoritmo finché non viene trovato almeno un fattore di $N=15$. Utilizzeremo i risultati dell'esecuzione hardware di cui sopra per indovinare la fase e il fattore corrispondente in ogni iterazione.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "id": "5d67cd71-8651-4a10-a913-a5bdaa6d6b38",
      "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.25\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": "00f31b56-e1a9-479e-8a82-bb708263f1a3",
      "metadata": {},
      "source": [
        "<span id=\"discussion\" />\n",
        "\n",
        "## Discussione\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "69189c94-6e78-410d-a365-8649d4c69163",
      "metadata": {},
      "source": [
        "<span id=\"related-work\" />\n",
        "\n",
        "### Lavoro correlato\n",
        "\n",
        "In questa sezione, discutiamo altre pietre miliari che hanno dimostrato l'algoritmo di Shor su hardware reale.\n",
        "\n",
        "Il lavoro fondamentale [\\[3\\]](#references) di IBM® ha dimostrato per la prima volta l'algoritmo di Shor, fattorizzando il numero 15 nei suoi fattori primi 3 e 5 utilizzando un computer quantistico a risonanza magnetica nucleare (NMR) a sette qubit. Un altro esperimento [\\[4\\] ha](#references) fatto la fattorizzazione di 15 utilizzando qubit fotonici. Impiegando un singolo qubit riciclato più volte e codificando il registro di lavoro in stati a più alta dimensione, i ricercatori hanno ridotto il numero di qubit richiesto a un terzo di quello del protocollo standard, utilizzando un algoritmo compilato a due fotoni. Un documento significativo nella dimostrazione dell'algoritmo di Shor è [\\[5\\]](#references), che utilizza la tecnica di stima iterativa della fase di Kitaev [\\[8\\]](#references) per ridurre il requisito di qubit dell'algoritmo. Gli autori hanno utilizzato sette qubit di controllo e quattro qubit di cache, insieme all'implementazione di moltiplicatori modulari. Questa implementazione, tuttavia, richiede misure a metà circuito con operazioni di feed-forward e riciclo dei qubit con operazioni di reset. Questa dimostrazione è stata effettuata su un computer quantistico a trappola ionica.\n",
        "\n",
        "Un lavoro più recente [\\[6\\]](#references) si è concentrato sulla fattorizzazione di 15, 21 e 35 sull'hardware IBM Quantum®. Analogamente a quanto fatto in precedenza, i ricercatori hanno utilizzato una versione compilata dell'algoritmo che impiega una trasformata di Fourier quantistica semiclassica, come proposto da Kitaev, per ridurre al minimo il numero di qubit e porte fisiche. Un lavoro più recente [\\[7\\]](#references) ha anche eseguito una dimostrazione proof-of-concept per la fattorizzazione del numero intero 21. Questa dimostrazione prevedeva anche l'uso di una versione compilata della routine di stima della fase quantistica e si basava sulla precedente dimostrazione di [\\[4\\]](#references). Gli autori sono andati oltre questo lavoro utilizzando una configurazione di porte Toffoli approssimate con sfasamenti residui. L'algoritmo è stato implementato sui processori quantistici IBM utilizzando solo cinque qubit e la presenza di entanglement tra i qubit di controllo e di registro è stata verificata con successo.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2dab9ac1-d3e4-4b4e-babe-472d69b026ab",
      "metadata": {},
      "source": [
        "<span id=\"scaling-of-the-algorithm\" />\n",
        "\n",
        "### Scalabilità dell'algoritmo\n",
        "\n",
        "Si noti che la crittografia RSA comporta in genere chiavi di dimensioni dell'ordine di 2048-4096 bit. Il tentativo di fattorizzare un numero di 2048 bit con l'algoritmo di Shor comporterà un circuito quantistico con milioni di qubit, compreso l'overhead di correzione degli errori e una profondità del circuito dell'ordine del miliardo, che è al di là dei limiti di esecuzione dell'attuale hardware quantistico. Pertanto, l'algoritmo di Shor richiederà metodi ottimizzati di costruzione dei circuiti o una robusta correzione quantistica degli errori per essere praticamente praticabile per la violazione dei moderni sistemi crittografici. Rimandiamo a [\\[9\\]](#references) per una discussione più dettagliata sulla stima delle risorse per l'algoritmo di Shor.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "cb53f62c-e560-43cf-a614-d31f266a569c",
      "metadata": {},
      "source": [
        "<span id=\"challenge\" />\n",
        "\n",
        "## Sfida\n",
        "\n",
        "Congratulazioni per aver terminato il tutorial! Questo è un ottimo momento per verificare la vostra comprensione. Si può provare a costruire il circuito per la fattorizzazione di 21? È possibile selezionare un sito $a$ di propria scelta. Dovrete decidere la precisione in bit dell'algoritmo per scegliere il numero di qubit e dovrete progettare gli operatori di esponenziazione modulare $M_a$. Vi invitiamo a provare voi stessi e a leggere le metodologie illustrate nella Fig. 9 di [\\[6\\]](#references) e nella Fig. 2 di [\\[7\\]](#references).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "09d347b8-51a4-4eb5-8422-76508f7ba1ad",
      "metadata": {},
      "outputs": [],
      "source": [
        "def M_a_mod21():\n",
        "    \"\"\"\n",
        "    M_a (mod 21)\n",
        "    \"\"\"\n",
        "\n",
        "    # Your code here\n",
        "    pass"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "dc16f509-7441-4221-8113-e09b118397a0",
      "metadata": {},
      "source": [
        "<span id=\"references\" />\n",
        "\n",
        "## Riferimenti\n",
        "\n",
        "1. Shor, Peter W. \"[Algoritmi in tempo polinomiale per la fattorizzazione dei primi e i logaritmi discreti su un computer quantistico](https://epubs.siam.org/doi/abs/10.1137/S0036144598347011) \" Rassegna SIAM 41.2 (1999): 303-332.\n",
        "2. IBM Quantum Corso [\"Fondamenti degli algoritmi quantistici\"](/learning/courses/fundamentals-of-quantum-algorithms/phase-estimation-and-factoring/introduction) tenuto dal dott. John Watrous.\n",
        "3. Vandersypen, Lieven MK, et al. \"[Realizzazione sperimentale dell'algoritmo di fattorizzazione quantistica di Shor mediante risonanza magnetica nucleare](https://www.nature.com/articles/414883a) \" Nature 414.6866 (2001): 883-887.\n",
        "4. Martin-Lopez, Enrique, et al. \"[Realizzazione sperimentale dell'algoritmo di fattorizzazione quantistica di Shor utilizzando il riciclo dei qubit](https://www.nature.com/articles/nphoton.2012.259) \" Nature photonics 6.11 (2012): 773-776.\n",
        "5. Monz, Thomas, et al. \"[Realizzazione di un algoritmo Shor scalabile](https://www.science.org/doi/full/10.1126/science.aad9480) \" Science 351.6277 (2016): 1068-1070.\n",
        "6. Amico, Mirko, Zain H. Saleem e Muir Kumph. \"[Studio sperimentale dell'algoritmo di fattorizzazione di Shor utilizzando l'esperienza di IBM Q](https://journals.aps.org/pra/abstract/10.1103/PhysRevA.100.012305) \" Physical Review A 100.1 (2019): 012305.\n",
        "7. Skosana, Unathi e Mark Tame. \"[Dimostrazione dell'algoritmo di fattorizzazione di Shor per N=21 su processori quantistici IBM](https://www.nature.com/articles/s41598-021-95973-w) \" Rapporti scientifici 11.1 (2021): 16599.\n",
        "8. Kitaev, A. Yu. \"[Misure quantistiche e problema dello stabilizzatore abeliano](https://arxiv.org/abs/quant-ph/9511026) \" arXiv preprint quant-ph/9511026 (1995).\n",
        "9. Gidney, Craig e Martin Ekerå. \"[Come fattorizzare interi RSA a 2048 bit in 8 ore usando 20 milioni di qubit rumorosi](https://doi.org/10.22331/q-2021-04-15-433) \" Quantum 5 (2021): 433.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "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"
    },
    "hours": 1,
    "qpuSeconds": 3
  },
  "nbformat": 4,
  "nbformat_minor": 4
}