{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "a0f5b3c9-754e-47d4-8dc7-5af247070187",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Crea e transpilare su backend personalizzati\"\n",
        "description: \"Scopri come creare i tuoi backend personalizzati e transpile i circuiti su di essi\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore multichip interchip Lasciate ogne speranza voi ch'intrate */}\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9429c31d-84b1-4547-9227-0b2fcf8a0193",
      "metadata": {},
      "source": [
        "<span id=\"create-and-transpile-against-custom-backends\" />\n",
        "\n",
        "# Crea e transpilare su backend personalizzati\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "27349ef1-5e25-4762-8a6f-8b4c265763c9",
      "metadata": {
        "tags": [
          "version-info"
        ]
      },
      "source": [
        "{/*\n",
        "  DO NOT EDIT THIS CELL!!!\n",
        "  This cell's content is generated automatically by a script. Anything you add\n",
        "  here will be removed next time the notebook is run. To add new content, create\n",
        "  a new cell before or after this one.\n",
        "  */}\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Versioni del pacchetto\">\n",
        "    Il codice di questa pagina è stato sviluppato in base ai seguenti requisiti.\n",
        "    Si consiglia di utilizzare queste versioni o versioni più recenti.\n",
        "\n",
        "    ```\n",
        "    qiskit[all]~=2.5.1\n",
        "    ```\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "678c28a1-d1ee-4179-8c7a-be5698e045bb",
      "metadata": {},
      "source": [
        "{/* cspell:ignore LOCC */}\n",
        "\n",
        "Una delle caratteristiche più potenti di Qiskit è la capacità di supportare configurazioni uniche dei dispositivi.  Qiskit è costruito in modo da essere agnostico rispetto al fornitore dell'hardware quantistico utilizzato e i fornitori possono configurare l'oggetto `BackendV2` in base alle proprietà del proprio dispositivo.  Questo argomento mostra come configurare il proprio backend e transpilare i circuiti quantistici con esso.\n",
        "\n",
        "È possibile creare oggetti `BackendV2` unici con diverse geometrie o porte di base e transpilare i circuiti tenendo conto di queste configurazioni.  L'esempio seguente riguarda un backend con un reticolo di qubit disgiunto, le cui porte di base sono diverse lungo i bordi rispetto all'interno del bulk.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "77d43aaf-5f92-412d-b49a-30be82268d21",
      "metadata": {},
      "source": [
        "<span id=\"understand-the-provider-backendv2-and-target-interfaces\" />\n",
        "\n",
        "## Comprendere le interfacce Provider, BackendV2 e Target\n",
        "\n",
        "Prima di iniziare, è utile comprendere l'uso e lo scopo dei caratteri [`Provider`](../api/qiskit/providers), [`BackendV2`](../api/qiskit/qiskit.providers.BackendV2), e [`Target`](../api/qiskit/qiskit.transpiler.Target) e degli oggetti.\n",
        "\n",
        "* Se si dispone di un dispositivo o di un simulatore quantistico che si desidera integrare nell'SDK Qiskit, è necessario scrivere la propria classe `Provider` . Questa classe ha un unico scopo: ottenere gli oggetti backend forniti dall'utente. È qui che vengono gestite tutte le attività di credenziali e/o autenticazione richieste. Una volta istanziato, l'oggetto provider fornirà un elenco di backend e la possibilità di acquisire/instanziare backend.\n",
        "\n",
        "* Successivamente, le classi backend forniscono l'interfaccia tra l'SDK Qiskit e l'hardware o il simulatore che eseguirà i circuiti. Includono tutte le informazioni necessarie per descrivere un backend al transpiler, in modo che possa ottimizzare qualsiasi circuito in base ai suoi vincoli. Il sito `BackendV2` è costituito da quattro parti principali:\n",
        "  * A [`Target`](../api/qiskit/qiskit.transpiler.Target) che contiene una descrizione dei vincoli del backend e fornisce un modello del backend per il transpiler\n",
        "  * Una proprietà di `max_circuits` che definisce un limite al numero di circuiti che un backend può eseguire in un singolo lavoro\n",
        "  * Un metodo `run()` che accetta l'invio di un lavoro\n",
        "  * Un insieme di `_default_options` per definire le opzioni configurabili dall'utente e i loro valori predefiniti\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0ad9dc45-97e9-4e47-b7c0-2fa536d4e624",
      "metadata": {},
      "source": [
        "<span id=\"create-a-custom-backendv2\" />\n",
        "\n",
        "## Crea un BackendV2 personalizzato\n",
        "\n",
        "L'oggetto `BackendV2` è una classe astratta utilizzata per tutti gli oggetti di backend creati da un provider (all'interno di `qiskit.providers` o di un'altra libreria, come ad esempio [`qiskit_ibm_runtime.IBMBackend`](../api/qiskit-ibm-runtime/ibm-backend)).  Come già accennato, questi oggetti contengono diversi attributi, tra cui un elemento [`Target`](/docs/api/qiskit/qiskit.transpiler.Target). L'indirizzo `Target` contiene informazioni che specificano gli attributi del backend, come ad esempio gli attributi [`Coupling Map`](/docs/api/qiskit/qiskit.transpiler.CouplingMap), elenco di [`Instructions`](/docs/api/qiskit/qiskit.circuit.Instruction), e altri - al transpiler.  Oltre a `Target`, si possono definire anche dettagli a livello di impulso, come il valore di [`DriveChannel`](/docs/api/qiskit/1.4/qiskit.pulse.channels.DriveChannel) o [`ControlChannel`](/docs/api/qiskit/1.4/qiskit.pulse.channels.ControlChannel).\n",
        "\n",
        "L'esempio seguente dimostra questa personalizzazione creando un backend multi-chip simulato, in cui ogni chip possiede una connettività heavy-hex.  L'esempio specifica che il set di gate a due qubit del backend deve essere [`CZGates`](../api/qiskit/qiskit.circuit.library.CZGate) all'interno di ciascun chip e [`CXGates`](../api/qiskit/qiskit.circuit.library.ECRGate) tra i chip.  Per prima cosa, creare il proprio `BackendV2` e personalizzare il suo `Target` con porte a uno o due qubit secondo i vincoli precedentemente descritti.\n",
        "\n",
        "<Admonition type=\"tip\" title=\"libreria graphviz\">\n",
        "  Per tracciare una mappa di accoppiamento è necessario che la libreria [`graphviz`](https://graphviz.org/) sia installata.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "b346f50b-b127-4074-99fc-7c53e3fbc022",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import rustworkx as rx\n",
        "\n",
        "from qiskit.providers import BackendV2, Options\n",
        "from qiskit.transpiler import Target, InstructionProperties\n",
        "from qiskit.circuit.library import XGate, SXGate, RZGate, CZGate, ECRGate\n",
        "from qiskit.circuit import Measure, Delay, Parameter, Reset\n",
        "from qiskit import QuantumCircuit, transpile\n",
        "from qiskit.visualization import plot_gate_map\n",
        "\n",
        "\n",
        "class FakeLOCCBackend(BackendV2):\n",
        "    \"\"\"Fake multi chip backend.\"\"\"\n",
        "\n",
        "    def __init__(self, distance=3, number_of_chips=3):\n",
        "        \"\"\"Instantiate a new fake multi chip backend.\n",
        "\n",
        "        Args:\n",
        "            distance (int): The heavy hex code distance to use for each chips'\n",
        "                coupling map. This number **must** be odd. The distance relates\n",
        "                to the number of qubits by:\n",
        "                :math:`n = \\\\frac{5d^2 - 2d - 1}{2}` where :math:`n` is the\n",
        "                number of qubits and :math:`d` is the ``distance``\n",
        "            number_of_chips (int): The number of chips to have in the multichip backend\n",
        "                each chip will be a heavy hex graph of ``distance`` code distance.\n",
        "        \"\"\"\n",
        "        super().__init__(name=\"Fake LOCC backend\")\n",
        "        # Create a heavy-hex graph using the\n",
        "        # rustworkx library, then instantiate a new target\n",
        "        self._graph = rx.generators.directed_heavy_hex_graph(\n",
        "            distance, bidirectional=False\n",
        "        )\n",
        "        num_qubits = len(self._graph) * number_of_chips\n",
        "        self._target = Target(\n",
        "            \"Fake multi-chip backend\", num_qubits=num_qubits\n",
        "        )\n",
        "\n",
        "        # Generate instruction properties for single qubit gates and a measurement, delay,\n",
        "        #  and reset operation to every qubit in the backend.\n",
        "        rng = np.random.default_rng(seed=12345678942)\n",
        "        rz_props = {}\n",
        "        x_props = {}\n",
        "        sx_props = {}\n",
        "        measure_props = {}\n",
        "        delay_props = {}\n",
        "\n",
        "        # Add 1q gates. Globally use virtual rz, x, sx, and measure\n",
        "        for i in range(num_qubits):\n",
        "            qarg = (i,)\n",
        "            rz_props[qarg] = InstructionProperties(error=0.0, duration=0.0)\n",
        "            x_props[qarg] = InstructionProperties(\n",
        "                error=rng.uniform(1e-6, 1e-4),\n",
        "                duration=rng.uniform(1e-8, 9e-7),\n",
        "            )\n",
        "            sx_props[qarg] = InstructionProperties(\n",
        "                error=rng.uniform(1e-6, 1e-4),\n",
        "                duration=rng.uniform(1e-8, 9e-7),\n",
        "            )\n",
        "            measure_props[qarg] = InstructionProperties(\n",
        "                error=rng.uniform(1e-3, 1e-1),\n",
        "                duration=rng.uniform(1e-8, 9e-7),\n",
        "            )\n",
        "            delay_props[qarg] = None\n",
        "        self._target.add_instruction(XGate(), x_props)\n",
        "        self._target.add_instruction(SXGate(), sx_props)\n",
        "        self._target.add_instruction(RZGate(Parameter(\"theta\")), rz_props)\n",
        "        self._target.add_instruction(Measure(), measure_props)\n",
        "        self._target.add_instruction(Reset(), measure_props)\n",
        "\n",
        "        self._target.add_instruction(Delay(Parameter(\"t\")), delay_props)\n",
        "        # Add chip local 2q gate which is CZ\n",
        "        cz_props = {}\n",
        "        for i in range(number_of_chips):\n",
        "            for root_edge in self._graph.edge_list():\n",
        "                offset = i * len(self._graph)\n",
        "                edge = (root_edge[0] + offset, root_edge[1] + offset)\n",
        "                cz_props[edge] = InstructionProperties(\n",
        "                    error=rng.uniform(7e-4, 5e-3),\n",
        "                    duration=rng.uniform(1e-8, 9e-7),\n",
        "                )\n",
        "        self._target.add_instruction(CZGate(), cz_props)\n",
        "\n",
        "        cx_props = {}\n",
        "        # Add interchip 2q gates which are ecr (effectively CX)\n",
        "        # First determine which nodes to connect\n",
        "        node_indices = self._graph.node_indices()\n",
        "        edge_list = self._graph.edge_list()\n",
        "        inter_chip_nodes = {}\n",
        "        for node in node_indices:\n",
        "            count = 0\n",
        "            for edge in edge_list:\n",
        "                if node == edge[0]:\n",
        "                    count += 1\n",
        "            if count == 1:\n",
        "                inter_chip_nodes[node] = count\n",
        "        # Create inter-chip ecr props\n",
        "        cx_props = {}\n",
        "        inter_chip_edges = list(inter_chip_nodes.keys())\n",
        "        for i in range(1, number_of_chips):\n",
        "            offset = i * len(self._graph)\n",
        "            edge = (\n",
        "                inter_chip_edges[1] + (len(self._graph) * (i - 1)),\n",
        "                inter_chip_edges[0] + offset,\n",
        "            )\n",
        "            cx_props[edge] = InstructionProperties(\n",
        "                error=rng.uniform(7e-4, 5e-3),\n",
        "                duration=rng.uniform(1e-8, 9e-7),\n",
        "            )\n",
        "\n",
        "        self._target.add_instruction(ECRGate(), cx_props)\n",
        "\n",
        "    @property\n",
        "    def target(self):\n",
        "        return self._target\n",
        "\n",
        "    @property\n",
        "    def max_circuits(self):\n",
        "        return None\n",
        "\n",
        "    @property\n",
        "    def graph(self):\n",
        "        return self._graph\n",
        "\n",
        "    @classmethod\n",
        "    def _default_options(cls):\n",
        "        return Options(shots=1024)\n",
        "\n",
        "    def run(self, circuit, **kwargs):\n",
        "        raise NotImplementedError(\n",
        "            \"This backend does not contain a run method\"\n",
        "        )"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "64a418fa-7a7e-4a12-b9c9-ff7377bffed4",
      "metadata": {},
      "source": [
        "<span id=\"visualize-backends\" />\n",
        "\n",
        "### Visualizza i backend\n",
        "\n",
        "È possibile visualizzare il grafico di connettività di questa nuova classe con il metodo [`plot_gate_map()`](../api/qiskit/qiskit.visualization.plot_gate_map) del modulo `qiskit.visualization` .  Questo metodo, insieme a [`plot_coupling_map()`](../api/qiskit/qiskit.visualization.plot_coupling_map) e [`plot_circuit_layout()`](../api/qiskit/qiskit.visualization.plot_circuit_layout)sono strumenti utili per visualizzare la disposizione dei qubit di un backend e la disposizione di un circuito tra i qubit di un backend.  Questo esempio crea un backend contenente tre piccoli chip heavy-hex. Specifica un insieme di coordinate per la disposizione dei qubit e un insieme di colori personalizzati per le diverse porte a due qubit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "fcc3056a-4e89-4b35-a0d5-e5c676459cc2",
      "metadata": {},
      "outputs": [],
      "source": [
        "backend = FakeLOCCBackend(3, 3)\n",
        "\n",
        "\n",
        "target = backend.target\n",
        "coupling_map_backend = target.build_coupling_map()\n",
        "\n",
        "\n",
        "coordinates = [\n",
        "    (3, 1),\n",
        "    (3, -1),\n",
        "    (2, -2),\n",
        "    (1, 1),\n",
        "    (0, 0),\n",
        "    (-1, -1),\n",
        "    (-2, 2),\n",
        "    (-3, 1),\n",
        "    (-3, -1),\n",
        "    (2, 1),\n",
        "    (1, -1),\n",
        "    (-1, 1),\n",
        "    (-2, -1),\n",
        "    (3, 0),\n",
        "    (2, -1),\n",
        "    (0, 1),\n",
        "    (0, -1),\n",
        "    (-2, 1),\n",
        "    (-3, 0),\n",
        "]\n",
        "\n",
        "single_qubit_coordinates = []\n",
        "total_qubit_coordinates = []\n",
        "\n",
        "\n",
        "for coordinate in coordinates:\n",
        "    total_qubit_coordinates.append(coordinate)\n",
        "\n",
        "for coordinate in coordinates:\n",
        "    total_qubit_coordinates.append(\n",
        "        (-1 * coordinate[0] + 1, coordinate[1] + 4)\n",
        "    )\n",
        "\n",
        "for coordinate in coordinates:\n",
        "    total_qubit_coordinates.append((coordinate[0], coordinate[1] + 8))\n",
        "\n",
        "\n",
        "line_colors = [\"#adaaab\" for edge in coupling_map_backend.get_edges()]\n",
        "ecr_edges = []\n",
        "\n",
        "# Get tuples for the edges which have an ecr instruction attached\n",
        "for instruction in target.instructions:\n",
        "    if instruction[0].name == \"ecr\":\n",
        "        ecr_edges.append(instruction[1])\n",
        "\n",
        "for i, edge in enumerate(coupling_map_backend.get_edges()):\n",
        "    if edge in ecr_edges:\n",
        "        line_colors[i] = \"#000000\""
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "6dc04d04-7afb-46f2-8ee6-ac961e4583f5",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Fake LOCC backend\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/guides/custom-backend/extracted-outputs/6dc04d04-7afb-46f2-8ee6-ac961e4583f5-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "print(backend.name)\n",
        "plot_gate_map(\n",
        "    backend,\n",
        "    plot_directed=True,\n",
        "    qubit_coordinates=total_qubit_coordinates,\n",
        "    line_color=line_colors,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d61da191-caa0-4175-a5f4-741b60905cdc",
      "metadata": {},
      "source": [
        "Ogni qubit è etichettato e le frecce colorate rappresentano le porte a due qubit.  Le frecce grigie sono i gate CZ e le frecce nere sono i gate CX inter-chip (collegano i qubit $6 \\rightarrow 21$ e $25 \\rightarrow 40$ ). La direzione della freccia indica la direzione predefinita di esecuzione di questi gate; essi specificano quali qubit sono controllati/target per default per ogni canale a due qubit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "59dc66df-5fb8-4844-922c-714fa86f08d5",
      "metadata": {},
      "source": [
        "<span id=\"ex-count-ops\" />\n",
        "\n",
        "<span id=\"transpile-against-custom-backends\" />\n",
        "\n",
        "## Transpile rispetto ai backend personalizzati\n",
        "\n",
        "Ora che è stato definito un backend personalizzato con il suo unico [`Target`](../api/qiskit/qiskit.transpiler.Target) è stato definito, è semplice transpilare i circuiti quantistici contro questo backend, poiché tutti i vincoli rilevanti (porte di base, connettività dei qubit e così via) necessari per i passaggi del transpiler sono contenuti in questo attributo. L'esempio successivo costruisce un circuito che crea un grande stato GHZ e lo trasporta contro il backend costruito sopra.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "cf2b1e0f-e6a1-4274-af7b-e5d4d3df6add",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Pre-Transpilation: \n",
            "CX gates: 49\n",
            "H gates: 50\n",
            "\n",
            " ############################## \n",
            "\n"
          ]
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Post-Transpilation: \n",
            "CZ gates: 207\n",
            "ECR gates: 8\n",
            "SX gates: 379\n",
            "RZ gates: 211\n"
          ]
        }
      ],
      "source": [
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "\n",
        "num_qubits = 50\n",
        "ghz = QuantumCircuit(num_qubits)\n",
        "ghz.h(range(num_qubits))\n",
        "ghz.cx(0, range(1, num_qubits))\n",
        "op_counts = ghz.count_ops()\n",
        "\n",
        "print(\"Pre-Transpilation: \")\n",
        "print(f\"CX gates: {op_counts['cx']}\")\n",
        "print(f\"H gates: {op_counts['h']}\")\n",
        "print(\"\\n\", 30 * \"#\", \"\\n\")\n",
        "\n",
        "pm = generate_preset_pass_manager(optimization_level=3, backend=backend)\n",
        "transpiled_ghz = pm.run(ghz)\n",
        "op_counts = transpiled_ghz.count_ops()\n",
        "\n",
        "print(\"Post-Transpilation: \")\n",
        "print(f\"CZ gates: {op_counts['cz']}\")\n",
        "print(f\"ECR gates: {op_counts['ecr']}\")\n",
        "print(f\"SX gates: {op_counts['sx']}\")\n",
        "print(f\"RZ gates: {op_counts['rz']}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "58625e26-e078-4ee6-8549-362762450a3b",
      "metadata": {},
      "source": [
        "Il circuito transpilato ora contiene una combinazione di `CZ` porte `ECR` e, che abbiamo specificato come porte di base nel backend `Target`.  Ci sono anche molti più gate rispetto a quelli iniziali, poiché è necessario inserire istruzioni SWAP dopo aver scelto un layout.  Di seguito, lo strumento [`plot_circuit_layout()`](/docs/api/qiskit/qiskit.visualization.plot_circuit_layout) di visualizzazione viene utilizzato per specificare quali qubit e canali a due qubit sono stati utilizzati in questo circuito.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "b51657ed-bb37-4e1a-9dea-8189b4229d24",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/guides/custom-backend/extracted-outputs/b51657ed-bb37-4e1a-9dea-8189b4229d24-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit.visualization import plot_circuit_layout\n",
        "\n",
        "plot_circuit_layout(\n",
        "    transpiled_ghz, backend, qubit_coordinates=total_qubit_coordinates\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ec002958-7eda-4329-aabb-cdf1b4c61403",
      "metadata": {},
      "source": [
        "<span id=\"create-unique-backends\" />\n",
        "\n",
        "## Crea backend unici\n",
        "\n",
        "Il pacchetto [rustworkx](https://www.rustworkx.org/) contiene un'ampia libreria di grafici diversi e consente la creazione di grafici personalizzati.  Il codice visivamente interessante che segue crea un backend ispirato al codice torico. È quindi possibile visualizzare il backend utilizzando le funzioni della sezione [Visualizza backend](#visualize-backends).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "82b8fa47-b37f-4136-9e3a-ffa5bc72fdf5",
      "metadata": {},
      "outputs": [],
      "source": [
        "class FakeTorusBackend(BackendV2):\n",
        "    \"\"\"Fake multi chip backend.\"\"\"\n",
        "\n",
        "    def __init__(self):\n",
        "        \"\"\"Instantiate a new backend that is inspired by a toric code\"\"\"\n",
        "        super().__init__(name=\"Fake LOCC backend\")\n",
        "        graph = rx.generators.directed_grid_graph(20, 20)\n",
        "        for column in range(20):\n",
        "            graph.add_edge(column, 19 * 20 + column, None)\n",
        "        for row in range(20):\n",
        "            graph.add_edge(row * 20, row * 20 + 19, None)\n",
        "        num_qubits = len(graph)\n",
        "        rng = np.random.default_rng(seed=12345678942)\n",
        "        rz_props = {}\n",
        "        x_props = {}\n",
        "        sx_props = {}\n",
        "        measure_props = {}\n",
        "        delay_props = {}\n",
        "        self._target = Target(\"Fake Kookaburra\", num_qubits=num_qubits)\n",
        "        # Add 1q gates. Globally use virtual rz, x, sx, and measure\n",
        "        for i in range(num_qubits):\n",
        "            qarg = (i,)\n",
        "            rz_props[qarg] = InstructionProperties(error=0.0, duration=0.0)\n",
        "            x_props[qarg] = InstructionProperties(\n",
        "                error=rng.uniform(1e-6, 1e-4),\n",
        "                duration=rng.uniform(1e-8, 9e-7),\n",
        "            )\n",
        "            sx_props[qarg] = InstructionProperties(\n",
        "                error=rng.uniform(1e-6, 1e-4),\n",
        "                duration=rng.uniform(1e-8, 9e-7),\n",
        "            )\n",
        "            measure_props[qarg] = InstructionProperties(\n",
        "                error=rng.uniform(1e-3, 1e-1),\n",
        "                duration=rng.uniform(1e-8, 9e-7),\n",
        "            )\n",
        "            delay_props[qarg] = None\n",
        "        self._target.add_instruction(XGate(), x_props)\n",
        "        self._target.add_instruction(SXGate(), sx_props)\n",
        "        self._target.add_instruction(RZGate(Parameter(\"theta\")), rz_props)\n",
        "        self._target.add_instruction(Measure(), measure_props)\n",
        "        self._target.add_instruction(Reset(), measure_props)\n",
        "        self._target.add_instruction(Delay(Parameter(\"t\")), delay_props)\n",
        "        cz_props = {}\n",
        "        for edge in graph.edge_list():\n",
        "            cz_props[edge] = InstructionProperties(\n",
        "                error=rng.uniform(7e-4, 5e-3),\n",
        "                duration=rng.uniform(1e-8, 9e-7),\n",
        "            )\n",
        "        self._target.add_instruction(CZGate(), cz_props)\n",
        "\n",
        "    @property\n",
        "    def target(self):\n",
        "        return self._target\n",
        "\n",
        "    @property\n",
        "    def max_circuits(self):\n",
        "        return None\n",
        "\n",
        "    @classmethod\n",
        "    def _default_options(cls):\n",
        "        return Options(shots=1024)\n",
        "\n",
        "    def run(self, circuit, **kwargs):\n",
        "        raise NotImplementedError(\"Lasciate ogne speranza, voi ch'intrate\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "1d40864a-7695-438b-95bf-7724c34d92b4",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/guides/custom-backend/extracted-outputs/1d40864a-7695-438b-95bf-7724c34d92b4-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "backend = FakeTorusBackend()\n",
        "# We set `figsize` to a smaller size to make the documentation website faster\n",
        "# to load. Normally, you do not need to set the argument.\n",
        "plot_gate_map(backend, figsize=(4, 4))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "341a0256-4a76-4d84-b598-5fc4d9fbef19",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "CZ gates: 580\n",
            "X gates: 129\n",
            "SX gates: 936\n",
            "RZ gates: 840\n"
          ]
        }
      ],
      "source": [
        "num_qubits = int(backend.num_qubits / 2)\n",
        "full_device_bv = QuantumCircuit(num_qubits, num_qubits - 1)\n",
        "full_device_bv.x(num_qubits - 1)\n",
        "full_device_bv.h(range(num_qubits))\n",
        "full_device_bv.cx(range(num_qubits - 1), num_qubits - 1)\n",
        "full_device_bv.h(range(num_qubits))\n",
        "full_device_bv.measure(range(num_qubits - 1), range(num_qubits - 1))\n",
        "tqc = transpile(full_device_bv, backend, optimization_level=3)\n",
        "op_counts = tqc.count_ops()\n",
        "print(f\"CZ gates: {op_counts['cz']}\")\n",
        "print(f\"X gates: {op_counts['x']}\")\n",
        "print(f\"SX gates: {op_counts['sx']}\")\n",
        "print(f\"RZ gates: {op_counts['rz']}\")"
      ]
    },
    {
      "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"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}