{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "24576595",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Kernel quantistici con porte frazionarie\"\n",
        "description: \"Utilizzare i gate frazionari, ovvero gate parametrizzati che eseguono direttamente rotazioni con angoli arbitrari, per ridurre la profondità e la durata dei circuiti a kernel quantistico.\"\n",
        "---\n",
        "\n",
        "<span id=\"quantum-kernels-with-fractional-gates\" />\n",
        "\n",
        "# Kernel quantistici con porte frazionarie\n",
        "\n",
        "*Stima di utilizzo: meno di 30 secondi su un processore Heron r2 (NOTA: questa è solo una stima. Il tempo di esecuzione può variare)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "983da41e",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## Risultati di apprendimento\n",
        "\n",
        "Dopo aver seguito questo tutorial, dovresti aver compreso:\n",
        "\n",
        "* Cosa sono i gate frazionari e in che modo riducono la profondità e la durata dei circuiti sulle QPU dell' IBM®\n",
        "* I vincoli legati all'uso delle porte frazionarie (in particolare, l'intervallo angolare RZZ)\n",
        "* Come creare un flusso di lavoro con kernel quantistico che utilizzi porte frazionarie con il servizio di calcolo \" IBM Quantum \"\n",
        "* Come confrontare le metriche di esecuzione hardware (profondità, durata, numero di gate non locali, fedeltà) con e senza gate frazionari\n",
        "* Come utilizzare solo gate RX frazionari mantenendo il flusso di lavoro standard di Qiskit\n",
        "\n",
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## Prerequisiti\n",
        "\n",
        "Ti consigliamo di acquisire familiarità con gli argomenti seguenti prima di seguire questo tutorial:\n",
        "\n",
        "* Il flusso di lavoro [dei modelli di](/docs/guides/intro-to-patterns) Qiskit\n",
        "* Guida [ai cancelli frazionari](/docs/guides/fractional-gates)\n",
        "* Il tutorial [sull'addestramento dei kernel quantistici](/docs/tutorials/quantum-kernel-training) e la lezione [sui kernel quantistici](/learning/courses/quantum-machine-learning/quantum-kernel-methods) del corso di machine learning quantistico\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "155eab76",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## Sfondo\n",
        "\n",
        "<span id=\"fractional-gates-on-ibm-qpus\" />\n",
        "\n",
        "### Gate frazionari su QPU IBM\n",
        "\n",
        "I gate frazionari sono gate quantistici parametrizzati che consentono l'esecuzione diretta di rotazioni con angoli arbitrari (entro limiti specifici),\n",
        "eliminando la necessità di scomporli in più gate di base.\n",
        "Sfruttando le interazioni native tra i qubit fisici, è possibile implementare determinati operatori unitari in modo più efficiente sull'hardware.\n",
        "\n",
        "IBM Le QPU Quantum® Heron supportano le seguenti porte frazionarie:\n",
        "\n",
        "* $R_{ZZ}(\\theta)$ per $0 < \\theta < \\pi / 2$\n",
        "* $R_X(\\theta)$ per qualsiasi valore reale $\\theta$\n",
        "\n",
        "Queste porte possono ridurre significativamente sia la profondità che la durata dei circuiti quantistici.\n",
        "Sono particolarmente vantaggiosi nelle applicazioni che si basano molto su $R_{ZZ}$ e $R_X$, come la simulazione hamiltoniana, il Quantum Approximate Optimization Algorithm (QAOA) e i metodi kernel quantistici.\n",
        "In questa esercitazione ci concentriamo sul kernel quantistico come esempio pratico.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fe5675f3",
      "metadata": {},
      "source": [
        "<span id=\"limitations\" />\n",
        "\n",
        "### Limitazioni\n",
        "\n",
        "Le porte frazionali sono attualmente una funzione sperimentale e presentano alcune limitazioni:\n",
        "\n",
        "* $R_{ZZ}$ è limitato agli angoli dell'intervallo $0 < \\theta < \\pi / 2$.\n",
        "* L'uso di porte frazionali non è supportato per i [circuiti dinamici](/docs/guides/classical-feedforward-and-control-flow), il [Pauli twirling](/docs/guides/error-mitigation-and-suppression-techniques#pauli-twirling), la [cancellazione probabilistica degli errori](/docs/guides/error-mitigation-and-suppression-techniques#probabilistic-error-cancellation-pec) (PEC) e l' [estrapolazione a rumore zero (ZNE](/docs/guides/error-mitigation-and-suppression-techniques#zero-noise-extrapolation-zne) ) (che utilizza l' [amplificazione probabilistica degli errori](/docs/guides/error-mitigation-and-suppression-techniques#probabilistic-error-amplification-pea) (PEA)).\n",
        "\n",
        "I gate frazionali richiedono un flusso di lavoro diverso rispetto all'approccio standard.\n",
        "Questa esercitazione spiega come lavorare con le porte frazionarie attraverso un'applicazione pratica.\n",
        "\n",
        "Per maggiori dettagli sui gate frazionari, vedere quanto segue.\n",
        "\n",
        "* [Porte frazionali](/docs/guides/fractional-gates)\n",
        "* [Quando *non* utilizzare i gate frazionari](/docs/guides/fractional-gates#when-not-to-use)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a4bdab87",
      "metadata": {},
      "source": [
        "<span id=\"workflow-approaches-for-the-rzz-angle-constraint\" />\n",
        "\n",
        "### Approcci basati sul flusso di lavoro per il vincolo dell'angolo RZZ\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "db213506",
      "metadata": {},
      "source": [
        "Il flusso di lavoro per l'utilizzo dei gate frazionari segue generalmente il flusso di lavoro [dei modelli di Qiskit](/docs/guides/intro-to-patterns).\n",
        "La differenza fondamentale sta nel fatto che tutti gli angoli RZZ devono soddisfare il vincolo $0 < \\theta \\leq \\pi/2$.\n",
        "Esistono due approcci per garantire il rispetto di questa condizione, come illustreremo di seguito. Consigliamo il secondo approccio e, in questo tutorial, lo illustriamo attraverso un esempio ispirato al metodo del kernel quantistico.\n",
        "Per comprendere meglio in quali contesti i kernel quantistici potrebbero rivelarsi utili, consigliamo di leggere [Liu, Arunachalam & Temme (2021)](https://www.nature.com/articles/s41567-021-01287-z).\n",
        "\n",
        "Puoi anche seguire il tutorial [di formazione sui kernel quantistici](/docs/tutorials/quantum-kernel-training) e la lezione [sui kernel quantistici](/learning/courses/quantum-machine-learning/quantum-kernel-methods) nel corso sul machine learning quantistico disponibile su IBM Quantum® Learning.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5797ed7e",
      "metadata": {},
      "source": [
        "<span id=\"1-generate-parameter-values-that-satisfy-the-rzz-angle-constraint\" />\n",
        "\n",
        "#### 1. Generare valori dei parametri che soddisfino il vincolo dell'angolo RZZ\n",
        "\n",
        "Se siete sicuri che tutti gli angoli RZZ rientrano nell'intervallo valido, potete seguire il flusso di lavoro standard dei modelli Qiskit.\n",
        "In questo caso, è sufficiente inviare i valori dei parametri come parte di un PUB. Il flusso di lavoro procede come segue.\n",
        "\n",
        "```python\n",
        "pm = generate_preset_pass_manager(backend=backend, ...)\n",
        "t_circuit = pm.run(circuit)\n",
        "t_observable = observable.apply_layout(t_circuit.layout)\n",
        "sampler.run([(t_circuit, parameter_values)])\n",
        "estimator.run([(t_circuit, t_observable, parameter_values)])\n",
        "```\n",
        "\n",
        "Se si tenta di inviare un PUB che include un gate RZZ con un angolo al di fuori dell'intervallo valido, verrà visualizzato un messaggio di errore del tipo:\n",
        "\n",
        "```\n",
        "'The instruction rzz is supported only for angles in the range [0, pi/2], but an angle (20.0) outside of this range has been requested; via parameter value(s) γ[0]=10.0, substituted in parameter expression 2.0*γ[0].'\n",
        "```\n",
        "\n",
        "Per evitare questo errore, utilizzare il secondo metodo descritto di seguito.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d3e00f37",
      "metadata": {},
      "source": [
        "<span id=\"2-assign-parameter-values-to-circuits-before-transpilation\" />\n",
        "\n",
        "#### 2. Assegnare i valori dei parametri ai circuiti prima della transpilazione\n",
        "\n",
        "Il `qiskit-ibm-runtime` pacchetto fornisce un passaggio di transpilazione specializzato denominato [`FoldRzzAngle`](/docs/api/qiskit-ibm-runtime/transpiler-passes-fold-rzz-angle).\n",
        "Questo passaggio trasforma i circuiti quantistici in modo che tutti gli angoli RZZ rispettino il vincolo relativo agli angoli RZZ.\n",
        "Se si fornisce il backend a `generate_preset_pass_manager` o `transpile`, Qiskit applica `FoldRzzAngle` automaticamente ai circuiti quantistici.\n",
        "Questo approccio richiede di assegnare i valori dei parametri ai circuiti quantistici prima della transpilazione.\n",
        "Il flusso di lavoro si svolge come segue.\n",
        "\n",
        "```python\n",
        "pm = generate_preset_pass_manager(backend=backend, ...)\n",
        "b_circuit = circuit.assign_parameters(parameter_values)\n",
        "t_circuit = pm.run(b_circuit)\n",
        "t_observable = observable.apply_layout(t_circuit.layout)\n",
        "sampler.run([(t_circuit,)])\n",
        "estimator.run([(t_circuit, t_observable)])\n",
        "```\n",
        "\n",
        "Si noti che questo flusso di lavoro comporta un costo computazionale maggiore rispetto al primo approccio, poiché prevede l’assegnazione di valori ai parametri dei circuiti quantistici e la memorizzazione locale dei circuiti associati a tali parametri.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b7cb1bd2",
      "metadata": {},
      "source": [
        "<Admonition type=\"caution\">\n",
        "  **Si prega di tenere presente un problema noto segnalato all'indirizzo `qiskit-ibm-runtime`v0.47.0**, secondo cui, in determinati scenari, i gate RZZ con angoli non validi potrebbero rimanere nei circuiti anche dopo la transpilazione.\n",
        "\n",
        "  Visita il sito [qiskit-ibm-runtime#2441](https://github.com/Qiskit/qiskit-ibm-runtime/issues/2441) per seguire gli sviluppi su questo argomento.\n",
        "  Fino a quando il problema non sarà risolto, consigliamo di adottare la seguente soluzione alternativa.\n",
        "\n",
        "  ```python\n",
        "  pm = generate_preset_pass_manager(backend=backend, ...)\n",
        "  pm.post_optimization = PassManager(\n",
        "      [\n",
        "          FoldRzzAngle(),\n",
        "          Optimize1qGatesDecomposition(target=backend.target),\n",
        "          RemoveIdentityEquivalent(target=backend.target),\n",
        "      ]\n",
        "  )\n",
        "  ... = pm.run(...)\n",
        "  ```\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "aaa153e7",
      "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.41 o versioni successive (`pip install qiskit-ibm-runtime`)\n",
        "* Qiskit Aer v0.17 o versioni successive (`pip install qiskit-aer`)\n",
        "* Costruttore di basi Qiskit (`pip install qiskit_basis_constructor`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5f43ce5d",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configura\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "a6a15694",
      "metadata": {},
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "from qiskit import QuantumCircuit, generate_preset_pass_manager\n",
        "from qiskit.circuit import ParameterVector\n",
        "from qiskit.circuit.library import UGate, n_local, unitary_overlap\n",
        "from qiskit.transpiler import Target, PassManager\n",
        "from qiskit.transpiler.passes import (\n",
        "    Optimize1qGatesDecomposition,\n",
        "    RemoveIdentityEquivalent,\n",
        ")\n",
        "from qiskit_aer.primitives import SamplerV2 as AerSampler\n",
        "from qiskit_basis_constructor import DEFAULT_EQUIVALENCE_LIBRARY\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2\n",
        "from qiskit_ibm_runtime.transpiler.passes import FoldRzzAngle"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3e94382d",
      "metadata": {},
      "source": [
        "<span id=\"enable-fractional-gates-and-check-basis-gates\" />\n",
        "\n",
        "### Abilita porte frazionarie e controlla le porte di base\n",
        "\n",
        "Per utilizzare i gate frazionari, è possibile ottenere un backend che li supporti impostando l'opzione `use_fractional_gates=True` .\n",
        "Se il backend supporta le porte frazionarie, si vedranno `rzz` e `rx` elencate tra le porte di base.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "fd577102",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Backend: ibm_marrakesh\n",
            "No fractional gates: ['cz', 'id', 'rz', 'sx', 'x']\n",
            "With fractional gates: ['cz', 'id', 'rx', 'rz', 'rzz', 'sx', 'x']\n"
          ]
        }
      ],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, simulator=False, min_num_qubits=133\n",
        ")  # backend should be a heron device or later\n",
        "backend_name = backend.name\n",
        "backend_c = service.backend(backend_name)  # w/o fractional gates\n",
        "backend_f = service.backend(\n",
        "    backend_name, use_fractional_gates=True\n",
        ")  # w/ fractional gates\n",
        "print(f\"Backend: {backend_name}\")\n",
        "print(f\"No fractional gates: {backend_c.basis_gates}\")\n",
        "print(f\"With fractional gates: {backend_f.basis_gates}\")\n",
        "if \"rzz\" not in backend_f.basis_gates:\n",
        "    print(f\"Backend {backend_name} does not support fractional gates\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3f11c5a3",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-simulator-example\" />\n",
        "\n",
        "## Esempio di simulatore su piccola scala\n",
        "\n",
        "In questa sezione illustreremo le quattro fasi del flusso di lavoro [dei modelli Qiskit](/docs/guides/intro-to-patterns) su un simulatore, utilizzando il circuito del kernel quantistico come esempio pratico.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7bfd6a97",
      "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",
        "<span id=\"quantum-kernel-circuit\" />\n",
        "\n",
        "#### Circuito quantistico a kernel\n",
        "\n",
        "In questa sezione, esploriamo il circuito del kernel quantistico utilizzando le porte RZZ per introdurre il flusso di lavoro per le porte frazionali.\n",
        "\n",
        "Cominciamo con la costruzione di un circuito quantistico per calcolare le singole voci della matrice kernel.\n",
        "Ciò avviene combinando circuiti di mappe di caratteristiche ZZ con una sovrapposizione unitaria.\n",
        "La funzione kernel prende i vettori nello spazio delle caratteristiche e restituisce il loro prodotto interno come voce della matrice kernel: $K(x, y) = \\langle \\Phi(x) | \\Phi(y) \\rangle,$ dove $|\\Phi(x)\\rangle$ rappresenta lo stato quantico mappato con le caratteristiche.\n",
        "\n",
        "Costruiamo manualmente un circuito di mappa delle caratteristiche ZZ utilizzando porte RZZ.\n",
        "`zz_feature_map`Sebbene Qiskit fornisca un'interfaccia integrata, al momento non supporta i gate RZZ, come indicato nella versione Qiskit v2.4.1 ( [vedi issue](https://github.com/Qiskit/qiskit/issues/14469) ).\n",
        "\n",
        "Successivamente, calcoliamo la funzione kernel per ingressi identici - ad esempio, $K(x, x) = 1$. Sui computer quantistici rumorosi, questo valore può essere inferiore a 1 a causa del rumore.\n",
        "Un risultato più vicino a 1 indica un minore rumore nell'esecuzione.\n",
        "In questa guida, ci si riferisce a questo valore come alla *fedeltà*, definita come $\\text{fidelity} = K(x, x).$\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "e7d5b52a",
      "metadata": {},
      "outputs": [],
      "source": [
        "optimization_level = 2\n",
        "shots = 2000\n",
        "reps = 3\n",
        "rng = np.random.default_rng(seed=123)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "2e9ab33d",
      "metadata": {},
      "outputs": [],
      "source": [
        "def my_zz_feature_map(num_qubits: int, reps: int = 1) -> QuantumCircuit:\n",
        "    x = ParameterVector(\"x\", num_qubits * reps)\n",
        "    qc = QuantumCircuit(num_qubits)\n",
        "    qc.h(range(num_qubits))\n",
        "    for k in range(reps):\n",
        "        K = k * num_qubits\n",
        "        for i in range(num_qubits):\n",
        "            qc.rz(x[i + K], i)\n",
        "        pairs = [(i, i + 1) for i in range(num_qubits - 1)]\n",
        "        for i, j in pairs[0::2] + pairs[1::2]:\n",
        "            qc.rzz((np.pi - x[i + K]) * (np.pi - x[j + K]), i, j)\n",
        "    return qc\n",
        "\n",
        "\n",
        "def quantum_kernel(num_qubits: int, reps: int = 1) -> QuantumCircuit:\n",
        "    qc = my_zz_feature_map(num_qubits, reps=reps)\n",
        "    inner_product = unitary_overlap(qc, qc, \"x\", \"y\", insert_barrier=True)\n",
        "    inner_product.measure_all()\n",
        "    return inner_product\n",
        "\n",
        "\n",
        "def random_parameters(inner_product: QuantumCircuit) -> np.ndarray:\n",
        "    return np.tile(rng.random(inner_product.num_parameters // 2), 2)\n",
        "\n",
        "\n",
        "def fidelity(result) -> float:\n",
        "    ba = result.data.meas\n",
        "    return ba.get_int_counts().get(0, 0) / ba.num_shots"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f676ff81",
      "metadata": {},
      "source": [
        "I circuiti kernel quantistici e i corrispondenti valori dei parametri sono generati per sistemi da 4 a 40 qubit e la loro fedeltà viene successivamente valutata.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "24116973",
      "metadata": {},
      "outputs": [],
      "source": [
        "qubits = list(range(4, 12, 2))\n",
        "circuits = [quantum_kernel(i, reps=reps) for i in qubits]\n",
        "params = [random_parameters(circ) for circ in circuits]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "30a7c442",
      "metadata": {},
      "source": [
        "Il circuito a quattro qubit è visualizzato di seguito.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "b3d6341a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/fractional-gates/extracted-outputs/b3d6341a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "circuits[0].draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "07f31cbe",
      "metadata": {},
      "source": [
        "Nel flusso di lavoro standard dei modelli Qiskit, i valori dei parametri sono tipicamente passati alla primitiva Sampler o Estimator come parte di un PUB.\n",
        "Tuttavia, quando si utilizza un backend che supporta porte frazionarie, questi valori dei parametri devono essere assegnati esplicitamente al circuito quantistico prima della transpilazione.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "6c9c1977",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/fractional-gates/extracted-outputs/6c9c1977-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "b_qc = [\n",
        "    circ.assign_parameters(param) for circ, param in zip(circuits, params)\n",
        "]\n",
        "b_qc[0].draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7513072b",
      "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": "804ba317",
      "metadata": {},
      "source": [
        "Quindi transpiliamo il circuito utilizzando il pass manager secondo lo schema standard di Qiskit.\n",
        "Fornendo un backend che supporta porte frazionarie a `generate_preset_pass_manager`, viene automaticamente incluso un passaggio specializzato chiamato `FoldRzzAngle` .\n",
        "Questo passaggio modifica il circuito per rispettare i vincoli dell'angolo RZZ.\n",
        "Di conseguenza, le porte RZZ con valori negativi nella figura precedente vengono trasformate in valori positivi e vengono aggiunte alcune porte X supplementari.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "6054bdea",
      "metadata": {},
      "outputs": [],
      "source": [
        "backend_f = service.backend(name=backend_name, use_fractional_gates=True)\n",
        "# pm_f includes `FoldRzzAngle` pass\n",
        "pm_f = generate_preset_pass_manager(\n",
        "    optimization_level=optimization_level, backend=backend_f\n",
        ")\n",
        "pm_f.post_optimization = PassManager(\n",
        "    [\n",
        "        FoldRzzAngle(),\n",
        "        Optimize1qGatesDecomposition(target=backend_f.target),\n",
        "        RemoveIdentityEquivalent(target=backend_f.target),\n",
        "    ]\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "a18e5c70",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "OrderedDict({'rz': 35, 'rzz': 18, 'x': 13, 'rx': 9, 'measure': 4, 'barrier': 2})\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/fractional-gates/extracted-outputs/a18e5c70-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 9,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "t_qc_f = pm_f.run(b_qc)\n",
        "print(t_qc_f[0].count_ops())\n",
        "t_qc_f[0].draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a4cd07d1",
      "metadata": {},
      "source": [
        "Per valutare l'impatto delle porte frazionali, valutiamo il numero di porte non locali (CZ e RZZ per questo backend), insieme alla profondità e alla durata dei circuiti e confrontiamo queste metriche con quelle di un flusso di lavoro standard.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "b5bcf9ad",
      "metadata": {},
      "outputs": [],
      "source": [
        "nnl_f = [qc.num_nonlocal_gates() for qc in t_qc_f]\n",
        "depth_f = [qc.depth() for qc in t_qc_f]\n",
        "duration_f = [\n",
        "    qc.estimate_duration(backend_f.target, unit=\"u\") for qc in t_qc_f\n",
        "]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "629406cc",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### Passaggio 3: eseguire utilizzando Qiskit primitives\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "87926539",
      "metadata": {},
      "source": [
        "Eseguiamo il circuito transpilato con il backend che supporta le porte frazionali.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "a68acf11",
      "metadata": {},
      "outputs": [],
      "source": [
        "sampler_f = AerSampler.from_backend(backend_f)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "a703b939",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "085ce928-767e-4200-93bf-3905e5411cfe\n"
          ]
        }
      ],
      "source": [
        "job = sampler_f.run(t_qc_f, shots=shots)\n",
        "print(job.job_id())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6c9fd10d",
      "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": "7a865036",
      "metadata": {},
      "source": [
        "È possibile ottenere il valore della funzione kernel $K(x, x)$ misurando la probabilità della stringa di bit tutti zero `00...00` nell'uscita.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "1f0d9c51",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[0.929, 0.882, 0.8645, 0.817]\n"
          ]
        }
      ],
      "source": [
        "result = job.result()\n",
        "fidelity_f = [fidelity(result=res) for res in result]\n",
        "print(fidelity_f)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a5bcd1a8",
      "metadata": {},
      "source": [
        "<span id=\"comparison-of-workflow-and-circuit-without-fractional-gates\" />\n",
        "\n",
        "### Confronto tra flusso di lavoro e circuito senza porte frazionarie\n",
        "\n",
        "In questa sezione presentiamo il flusso di lavoro standard di Qiskit Patterns utilizzando un backend che non supporta i gate frazionari.\n",
        "Confrontando i circuiti transpilati, noterete che la versione che utilizza porte frazionarie (della sezione precedente) è più compatta di quella che ne è priva.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "e97fd0d5",
      "metadata": {},
      "outputs": [],
      "source": [
        "# step 1: map classical inputs to quantum problem\n",
        "# `circuits` and `params` from the previous section are reused here"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "a10f2d95",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "OrderedDict({'rz': 130, 'sx': 80, 'cz': 36, 'measure': 4, 'barrier': 2})\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/fractional-gates/extracted-outputs/a10f2d95-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 15,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# step 2: optimize circuits\n",
        "backend_c = service.backend(backend_name)  # w/o fractional gates\n",
        "pm_c = generate_preset_pass_manager(\n",
        "    optimization_level=optimization_level, backend=backend_c\n",
        ")\n",
        "t_qc_c = pm_c.run(circuits)\n",
        "print(t_qc_c[0].count_ops())\n",
        "t_qc_c[0].draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "bb3475df",
      "metadata": {},
      "outputs": [],
      "source": [
        "nnl_c = [qc.num_nonlocal_gates() for qc in t_qc_c]\n",
        "depth_c = [qc.depth() for qc in t_qc_c]\n",
        "duration_c = [\n",
        "    qc.estimate_duration(backend_c.target, unit=\"u\") for qc in t_qc_c\n",
        "]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "e8d307c0",
      "metadata": {},
      "outputs": [],
      "source": [
        "# step 3: execute\n",
        "sampler_c = AerSampler.from_backend(backend_c)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "983dd26f",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "f2cca29d-7263-4976-9e51-13a91b75c3ae\n"
          ]
        }
      ],
      "source": [
        "job = sampler_c.run(pubs=zip(t_qc_c, params), shots=shots)\n",
        "print(job.job_id())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "a6a6fa77",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[0.8625, 0.7605, 0.702, 0.671]\n"
          ]
        }
      ],
      "source": [
        "# step 4: post-processing\n",
        "result = job.result()\n",
        "fidelity_c = [fidelity(res) for res in result]\n",
        "print(fidelity_c)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "765a7980",
      "metadata": {},
      "source": [
        "<span id=\"comparison-of-depths-durations-and-fidelities\" />\n",
        "\n",
        "### Confronto tra profondità, durata e fedeltà\n",
        "\n",
        "In questa sezione, confrontiamo il numero di porte non locali e le fedeltà tra circuiti con e senza porte frazionarie.\n",
        "Ciò evidenzia i potenziali vantaggi dell'uso di porte frazionali in termini di efficienza e qualità dell'esecuzione.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "ef343a53",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<matplotlib.legend.Legend at 0x116af3cb0>"
            ]
          },
          "execution_count": 20,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/fractional-gates/extracted-outputs/ef343a53-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.plot(qubits, depth_c, \"-o\", label=\"no fractional gates\")\n",
        "plt.plot(qubits, depth_f, \"-o\", label=\"with fractional gates\")\n",
        "plt.xlabel(\"number of qubits\")\n",
        "plt.ylabel(\"depth\")\n",
        "plt.title(\"Comparison of depths\")\n",
        "plt.grid()\n",
        "plt.legend()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "98bb2cd0",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<matplotlib.legend.Legend at 0x11ea4f4d0>"
            ]
          },
          "execution_count": 21,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/fractional-gates/extracted-outputs/98bb2cd0-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.plot(qubits, duration_c, \"-o\", label=\"no fractional gates\")\n",
        "plt.plot(qubits, duration_f, \"-o\", label=\"with fractional gates\")\n",
        "plt.xlabel(\"number of qubits\")\n",
        "plt.ylabel(\"duration (µs)\")\n",
        "plt.title(\"Comparison of durations\")\n",
        "plt.grid()\n",
        "plt.legend()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "1383b242",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<matplotlib.legend.Legend at 0x1247fc440>"
            ]
          },
          "execution_count": 22,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/fractional-gates/extracted-outputs/1383b242-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.plot(qubits, nnl_c, \"-o\", label=\"no fractional gates\")\n",
        "plt.plot(qubits, nnl_f, \"-o\", label=\"with fractional gates\")\n",
        "plt.xlabel(\"number of qubits\")\n",
        "plt.ylabel(\"number of non-local gates\")\n",
        "plt.title(\"Comparison of numbers of non-local gates\")\n",
        "plt.grid()\n",
        "plt.legend()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "8b4594f5",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<matplotlib.legend.Legend at 0x120b792b0>"
            ]
          },
          "execution_count": 23,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/fractional-gates/extracted-outputs/8b4594f5-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.plot(qubits, fidelity_c, \"-o\", label=\"no fractional gates\")\n",
        "plt.plot(qubits, fidelity_f, \"-o\", label=\"with fractional gates\")\n",
        "plt.xlabel(\"number of qubits\")\n",
        "plt.ylabel(\"fidelity\")\n",
        "plt.title(\"Comparison of fidelities\")\n",
        "plt.grid()\n",
        "plt.legend()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9f17acd5",
      "metadata": {},
      "source": [
        "<span id=\"large-scale-hardware-example\" />\n",
        "\n",
        "## Esempio di hardware su larga scala\n",
        "\n",
        "In questa sezione, effettuiamo un confronto prestazionale del flusso di lavoro del kernel quantistico con e senza gate frazionari su hardware quantistico con un massimo di 40 qubit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fd68378c",
      "metadata": {},
      "source": [
        "<span id=\"step-1-4-combined\" />\n",
        "\n",
        "### Passaggi da 1 a 4 combinati\n",
        "\n",
        "Il flusso di lavoro segue la stessa struttura dell'esempio su piccola scala. Effettuiamo la transpilazione di tutti i circuiti, sia con che senza porte frazionarie, raccogliamo le metriche e infine inviamo i circuiti all'hardware quantistico reale.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "4431bf56",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "job id (w/ fractional gates): d8uasitbh0os73eqnpig\n"
          ]
        }
      ],
      "source": [
        "# -------------------------Step 1-------------------------\n",
        "qubits = list(range(4, 44, 4))\n",
        "circuits = [quantum_kernel(i, reps=reps) for i in qubits]\n",
        "params = [random_parameters(circ) for circ in circuits]\n",
        "b_qc = [\n",
        "    circ.assign_parameters(param) for circ, param in zip(circuits, params)\n",
        "]\n",
        "\n",
        "\n",
        "def benchmark(b_qc, backend):\n",
        "    # -------------------------Step 2-------------------------\n",
        "    pm = generate_preset_pass_manager(optimization_level, backend=backend)\n",
        "    if \"rzz\" in backend.target.operation_names:\n",
        "        # workaround until https://github.com/Qiskit/qiskit-ibm-runtime/issues/2441 is resolved\n",
        "        pm.post_optimization = PassManager(\n",
        "            [\n",
        "                FoldRzzAngle(),\n",
        "                Optimize1qGatesDecomposition(target=backend.target),\n",
        "                RemoveIdentityEquivalent(target=backend.target),\n",
        "            ]\n",
        "        )\n",
        "    t_qc = pm.run(b_qc)\n",
        "    nnl = [qc.num_nonlocal_gates() for qc in t_qc]\n",
        "    depth = [qc.depth() for qc in t_qc]\n",
        "    duration = [\n",
        "        qc.estimate_duration(backend_f.target, unit=\"u\") for qc in t_qc\n",
        "    ]\n",
        "\n",
        "    # -------------------------Step 3-------------------------\n",
        "    sampler = SamplerV2(mode=backend)\n",
        "    sampler.options.dynamical_decoupling.enable = True\n",
        "    sampler.options.dynamical_decoupling.sequence_type = \"XY4\"\n",
        "    sampler.options.dynamical_decoupling.skip_reset_qubits = True\n",
        "    sampler.options.environment.job_tags = [\"TUT_FG\"]\n",
        "    job = sampler.run(t_qc, shots=shots)\n",
        "    job_id = job.job_id()\n",
        "    return nnl, depth, duration, job_id\n",
        "\n",
        "\n",
        "def postprocessing(job_id: str):\n",
        "    # -------------------------Step 4-------------------------\n",
        "    job = service.job(job_id)\n",
        "    result = job.result()\n",
        "    fidelities = [fidelity(result=res) for res in result]\n",
        "    usage = job.usage()\n",
        "    return fidelities, usage\n",
        "\n",
        "\n",
        "backend_f = service.backend(backend_name, use_fractional_gates=True)\n",
        "nnl_f, depth_f, duration_f, job_id_f = benchmark(\n",
        "    b_qc, backend_f\n",
        ")  # step 2 & 3\n",
        "print(\"job id (w/ fractional gates):\", job_id_f)\n",
        "fidelity_f, usage_f = postprocessing(job_id_f)  # step 4"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "id": "4a5d15a2",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "job id (w/o fractional gates): d8uav3lposuc738pruug\n"
          ]
        }
      ],
      "source": [
        "backend_c = service.backend(backend_name, use_fractional_gates=False)\n",
        "nnl_c, depth_c, duration_c, job_id_c = benchmark(b_qc, backend_c)\n",
        "print(\"job id (w/o fractional gates):\", job_id_c)\n",
        "fidelity_c, usage_c = postprocessing(job_id_c)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9f78441e",
      "metadata": {},
      "source": [
        "A questo punto mettiamo a confronto gli indicatori.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "id": "b409e8d3",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<matplotlib.legend.Legend at 0x12461e660>"
            ]
          },
          "execution_count": 26,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/fractional-gates/extracted-outputs/b409e8d3-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.plot(qubits, depth_c, \"-o\", label=\"no fractional gates\")\n",
        "plt.plot(qubits, depth_f, \"-o\", label=\"with fractional gates\")\n",
        "plt.xlabel(\"number of qubits\")\n",
        "plt.ylabel(\"depth\")\n",
        "plt.title(\"Comparison of depths\")\n",
        "plt.grid()\n",
        "plt.legend()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "id": "09f91f0f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<matplotlib.legend.Legend at 0x11f2ac980>"
            ]
          },
          "execution_count": 27,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/fractional-gates/extracted-outputs/09f91f0f-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.plot(qubits, duration_c, \"-o\", label=\"no fractional gates\")\n",
        "plt.plot(qubits, duration_f, \"-o\", label=\"with fractional gates\")\n",
        "plt.xlabel(\"number of qubits\")\n",
        "plt.ylabel(\"duration (µs)\")\n",
        "plt.title(\"Comparison of durations\")\n",
        "plt.grid()\n",
        "plt.legend()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "id": "c9308517",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<matplotlib.legend.Legend at 0x125c91be0>"
            ]
          },
          "execution_count": 28,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/fractional-gates/extracted-outputs/c9308517-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.plot(qubits, nnl_c, \"-o\", label=\"no fractional gates\")\n",
        "plt.plot(qubits, nnl_f, \"-o\", label=\"with fractional gates\")\n",
        "plt.xlabel(\"number of qubits\")\n",
        "plt.ylabel(\"number of non-local gates\")\n",
        "plt.title(\"Comparison of numbers of non-local gates\")\n",
        "plt.grid()\n",
        "plt.legend()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 29,
      "id": "234731d4",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<matplotlib.legend.Legend at 0x11fcf6e40>"
            ]
          },
          "execution_count": 29,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/fractional-gates/extracted-outputs/234731d4-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.plot(qubits, fidelity_c, \"-o\", label=\"no fractional gates\")\n",
        "plt.plot(qubits, fidelity_f, \"-o\", label=\"with fractional gates\")\n",
        "plt.xlabel(\"number of qubits\")\n",
        "plt.ylabel(\"fidelity\")\n",
        "plt.title(\"Comparison of fidelities\")\n",
        "plt.grid()\n",
        "plt.legend()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d38b9fe1",
      "metadata": {},
      "source": [
        "Confrontiamo il tempo di utilizzo della QPU con e senza porte frazionali. I risultati nella cella seguente mostrano che i tempi di utilizzo della QPU sono quasi identici.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 30,
      "id": "793326ca",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "no fractional gates: 8 seconds\n",
            "fractional gates: 8 seconds\n"
          ]
        }
      ],
      "source": [
        "print(f\"no fractional gates: {usage_c} seconds\")\n",
        "print(f\"fractional gates: {usage_f} seconds\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "efe18f80",
      "metadata": {},
      "source": [
        "<span id=\"advanced-topic-using-only-fractional-rx-gates\" />\n",
        "\n",
        "## Argomento avanzato: utilizzo esclusivo di porte RX frazionarie\n",
        "\n",
        "La necessità di modificare il flusso di lavoro quando si utilizzano porte frazionali deriva principalmente dalla limitazione degli angoli delle porte RZZ.\n",
        "Tuttavia, se si utilizzano solo le porte RX frazionali e si escludono le porte RZZ frazionali, è possibile continuare a seguire il flusso di lavoro standard dei modelli Qiskit.\n",
        "Questo approccio può ancora offrire vantaggi significativi, in particolare nei circuiti che coinvolgono un gran numero di porte RX e porte U, riducendo il numero complessivo di porte e migliorando potenzialmente le prestazioni.\n",
        "In questa sezione dimostriamo come ottimizzare i circuiti utilizzando solo porte RX frazionarie e omettendo le porte RZZ.\n",
        "\n",
        "A tale scopo, è disponibile una funzione di utilità che consente di disabilitare uno specifico gate di base in un oggetto Target.\n",
        "In questo caso, lo utilizziamo per disabilitare i gate RZZ.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 31,
      "id": "ab43ba23",
      "metadata": {},
      "outputs": [],
      "source": [
        "def remove_instruction_from_target(target: Target, gate_name: str) -> Target:\n",
        "    new_target = Target(\n",
        "        description=target.description,\n",
        "        num_qubits=target.num_qubits,\n",
        "        dt=target.dt,\n",
        "        granularity=target.granularity,\n",
        "        min_length=target.min_length,\n",
        "        pulse_alignment=target.pulse_alignment,\n",
        "        acquire_alignment=target.acquire_alignment,\n",
        "        qubit_properties=target.qubit_properties,\n",
        "        concurrent_measurements=target.concurrent_measurements,\n",
        "    )\n",
        "\n",
        "    for name, qarg_map in target.items():\n",
        "        if name == gate_name:\n",
        "            continue\n",
        "        instruction = target.operation_from_name(name)\n",
        "        if qarg_map == {None: None}:\n",
        "            qarg_map = None\n",
        "        new_target.add_instruction(instruction, qarg_map, name=name)\n",
        "    return new_target"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7f689a02",
      "metadata": {},
      "source": [
        "Utilizziamo come esempio un circuito composto da porte U, CZ e RZZ.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 32,
      "id": "6b812497",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/fractional-gates/extracted-outputs/6b812497-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 32,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "qc = n_local(3, \"u\", \"cz\", \"linear\", reps=1)\n",
        "qc.rzz(1.1, 0, 1)\n",
        "qc.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fa9dc1c4",
      "metadata": {},
      "source": [
        "Per prima cosa transpiliamo il circuito per un backend che non supporta porte frazionarie.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 33,
      "id": "9e8e0709",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "OrderedDict({'rz': 23, 'sx': 16, 'cz': 4})\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/fractional-gates/extracted-outputs/9e8e0709-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 33,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "pm_c = generate_preset_pass_manager(\n",
        "    optimization_level=optimization_level, backend=backend_c\n",
        ")\n",
        "t_qc = pm_c.run(qc)\n",
        "print(t_qc.count_ops())\n",
        "t_qc.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bd0e24da",
      "metadata": {},
      "source": [
        "Quindi, si traspone lo stesso circuito utilizzando porte RX frazionarie, escludendo le porte RZZ.\n",
        "Ciò comporta una leggera riduzione del numero totale di porte, grazie all'implementazione più efficiente delle porte RX.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 34,
      "id": "db45feb0",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "OrderedDict({'rz': 22, 'sx': 14, 'cz': 4, 'rx': 1})\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/fractional-gates/extracted-outputs/db45feb0-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 34,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "backend_f = service.backend(backend_name, use_fractional_gates=True)\n",
        "target = remove_instruction_from_target(backend_f.target, \"rzz\")\n",
        "pm_f = generate_preset_pass_manager(\n",
        "    optimization_level=optimization_level,\n",
        "    target=target,\n",
        ")\n",
        "t_qc = pm_f.run(qc)\n",
        "print(t_qc.count_ops())\n",
        "t_qc.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "15140636",
      "metadata": {},
      "source": [
        "<span id=\"optimize-u-gates-with-fractional-rx-gates\" />\n",
        "\n",
        "### Ottimizzare i gate U con gate RX frazionari\n",
        "\n",
        "In questa sezione dimostriamo come ottimizzare le porte U utilizzando porte RX frazionali, partendo dallo stesso circuito introdotto nella sezione precedente.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3f73cbda",
      "metadata": {},
      "source": [
        "Il circuito viene transpilato utilizzando solo porte RX frazionarie, escludendo le porte RZZ.\n",
        "Introducendo una regola di decomposizione personalizzata, come mostrato di seguito, è possibile ridurre il numero di porte a singolo qubit necessarie per implementare una porta U.\n",
        "\n",
        "Questa funzionalità è attualmente oggetto di discussione in questa [segnalazione](https://github.com/Qiskit/qiskit/issues/13455) su GitHub.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 35,
      "id": "0f0c6d87",
      "metadata": {},
      "outputs": [],
      "source": [
        "# special decomposition rule for UGate\n",
        "x = ParameterVector(\"x\", 3)\n",
        "zxz = QuantumCircuit(1)\n",
        "zxz.rz(x[2] - np.pi / 2, 0)\n",
        "zxz.rx(x[0], 0)\n",
        "zxz.rz(x[1] + np.pi / 2, 0)\n",
        "DEFAULT_EQUIVALENCE_LIBRARY.add_equivalence(UGate(x[0], x[1], x[2]), zxz)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "91c824d8",
      "metadata": {},
      "source": [
        "Successivamente, applichiamo il transpiler utilizzando `constructor-beta` la traduzione fornita dal `qiskit-basis-constructor` pacchetto.\n",
        "Di conseguenza, il numero totale di porte logiche risulta ridotto rispetto alla precedente transpilazione.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 36,
      "id": "b19aae7c",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "OrderedDict({'rz': 16, 'rx': 9, 'cz': 4})\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/fractional-gates/extracted-outputs/b19aae7c-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 36,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "pm_f = generate_preset_pass_manager(\n",
        "    optimization_level=optimization_level,\n",
        "    target=target,\n",
        "    translation_method=\"constructor-beta\",\n",
        ")\n",
        "t_qc = pm_f.run(qc)\n",
        "print(t_qc.count_ops())\n",
        "t_qc.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "23ad4615",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Passi successivi\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Suggerimenti\">\n",
        "  Se questo lavoro ti è sembrato interessante, potrebbero interessarti anche i seguenti contenuti:\n",
        "\n",
        "  * Guida [ai gate frazionari](/docs/guides/fractional-gates)\n",
        "  * [Quando *non* utilizzare i gate frazionari](/docs/guides/fractional-gates#when-not-to-use)\n",
        "  * [`FoldRzzAngle`](/docs/api/qiskit-ibm-runtime/transpiler-passes-fold-rzz-angle) Riferimento all'API del passaggio di transpiler\n",
        "  * Tutorial [sulla formazione del kernel Quantum](/docs/tutorials/quantum-kernel-training)\n",
        "  * La lezione [sui kernel quantistici](/learning/courses/quantum-machine-learning/quantum-kernel-methods) nel corso di apprendimento automatico quantistico\n",
        "</Admonition>\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": 30
  },
  "nbformat": 4,
  "nbformat_minor": 5
}