{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "a0f5b3c9-754e-47d4-8dc7-5af247070187",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Crear y transpilación contra backends personalizados\"\n",
        "description: \"Aprenda a crear sus propios backends personalizados y a transpilarlos contra ellos\"\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",
        "# Crear y transpilación contra backends personalizados\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=\"Versiones del paquete\">\n",
        "    El código de esta página se ha desarrollado teniendo en cuenta los siguientes requisitos.\n",
        "    Recomendamos utilizar estas versiones o versiones más recientes.\n",
        "\n",
        "    ```\n",
        "    qiskit[all]~=2.5.0\n",
        "    ```\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "678c28a1-d1ee-4179-8c7a-be5698e045bb",
      "metadata": {},
      "source": [
        "{/* cspell:ignore LOCC */}\n",
        "\n",
        "Una de las características más potentes de Qiskit es su capacidad para admitir configuraciones de dispositivo únicas.  Qiskit está construido para ser agnóstico al proveedor del hardware cuántico que se utilice, y los proveedores pueden configurar el objeto `BackendV2` a sus propias propiedades de dispositivo únicas.  Este tema demuestra cómo configurar tu propio backend y transpilar circuitos cuánticos contra ellos.\n",
        "\n",
        "Puede crear objetos `BackendV2` únicos con diferentes geometrías o puertas base y transpilar sus circuitos teniendo en cuenta esas configuraciones.  El ejemplo siguiente cubre un backend con un entramado de qubits disjuntos, cuyas puertas de base son diferentes a lo largo de los bordes desde dentro de la masa.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "77d43aaf-5f92-412d-b49a-30be82268d21",
      "metadata": {},
      "source": [
        "<span id=\"understand-the-provider-backendv2-and-target-interfaces\" />\n",
        "\n",
        "## Comprender las interfaces Provider, BackendV2 y Target\n",
        "\n",
        "Antes de empezar, es útil comprender el uso y la finalidad de la tecla [`Provider`](../api/qiskit/providers), [`BackendV2`](../api/qiskit/qiskit.providers.BackendV2)y [`Target`](../api/qiskit/qiskit.transpiler.Target) objetos.\n",
        "\n",
        "* Si tienes un dispositivo cuántico o un simulador que quieres integrar en el SDK de Qiskit, tienes que escribir tu propia clase `Provider` . Esta clase tiene un único propósito: obtener los objetos backend que tú le proporciones. Aquí es donde se gestionan todas las tareas de credenciales y/o autenticación necesarias. Una vez instanciado, el objeto proveedor proporcionará una lista de backends, así como la capacidad de adquirir/instanciar backends.\n",
        "\n",
        "* A continuación, las clases backend proporcionan la interfaz entre el SDK de Qiskit y el hardware o simulador que ejecutará los circuitos. Incluyen toda la información necesaria para describir un backend al transpilador, de modo que pueda optimizar cualquier circuito en función de sus restricciones. `BackendV2` consta de cuatro partes principales:\n",
        "  * A [`Target`](../api/qiskit/qiskit.transpiler.Target) que contiene una descripción de las restricciones del backend y proporciona un modelo del backend para el transpilador\n",
        "  * Una propiedad de `max_circuits` que define un límite en el número de circuitos que un backend puede ejecutar en un único trabajo\n",
        "  * Un método `run()` que acepta el envío de trabajos\n",
        "  * Un conjunto de `_default_options` para definir las opciones configurables por el usuario y sus valores por defecto\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0ad9dc45-97e9-4e47-b7c0-2fa536d4e624",
      "metadata": {},
      "source": [
        "<span id=\"create-a-custom-backendv2\" />\n",
        "\n",
        "## Crear un BackendV2 personalizado\n",
        "\n",
        "El objeto `BackendV2` es una clase abstracta utilizada para todos los objetos backend creados por un proveedor (ya sea dentro de `qiskit.providers` o de otra biblioteca como [`qiskit_ibm_runtime.IBMBackend`](../api/qiskit-ibm-runtime/ibm-backend)).  Como ya se ha mencionado, estos objetos contienen varios atributos, entre los que se incluye un atributo [`Target`](/docs/api/qiskit/qiskit.transpiler.Target). La dirección `Target` contiene información que especifica los atributos del backend - como la etiqueta [`Coupling Map`](/docs/api/qiskit/qiskit.transpiler.CouplingMap)lista de [`Instructions`](/docs/api/qiskit/qiskit.circuit.Instruction)y otros- al transpilador.  Además de `Target`, también se pueden definir detalles a nivel de pulso como el [`DriveChannel`](/docs/api/qiskit/1.4/qiskit.pulse.channels.DriveChannel) o [`ControlChannel`](/docs/api/qiskit/1.4/qiskit.pulse.channels.ControlChannel).\n",
        "\n",
        "El siguiente ejemplo demuestra esta personalización mediante la creación de un backend multi-chip simulado, donde cada chip posee una conectividad heavy-hex.  El ejemplo especifica que el conjunto de puertas de dos qubits del backend sea [`CZGates`](../api/qiskit/qiskit.circuit.library.CZGate) dentro de cada chip y [`CXGates`](../api/qiskit/qiskit.circuit.library.ECRGate) entre chips.  En primer lugar, cree su propio `BackendV2` y personalice su `Target` con puertas de uno y dos qubits de acuerdo con las restricciones descritas anteriormente.\n",
        "\n",
        "<Admonition type=\"tip\" title=\"biblioteca graphviz\">\n",
        "  Para trazar un mapa de acoplamiento es necesario tener instalada la biblioteca [`graphviz`](https://graphviz.org/) esté instalada.\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",
        "### Visualizar backends\n",
        "\n",
        "Puedes ver el gráfico de conectividad de esta nueva clase con el método [`plot_gate_map()`](../api/qiskit/qiskit.visualization.plot_gate_map) del módulo `qiskit.visualization` .  Este método, junto con [`plot_coupling_map()`](../api/qiskit/qiskit.visualization.plot_coupling_map) y [`plot_circuit_layout()`](../api/qiskit/qiskit.visualization.plot_circuit_layout)son herramientas útiles para visualizar la disposición de los qubits de un backend, así como la disposición de un circuito a través de los qubits de un backend.  Este ejemplo crea un backend que contiene tres pequeñas fichas de hexágono grueso. Especifica un conjunto de coordenadas para disponer los qubits, así como un conjunto de colores personalizados para las diferentes puertas de dos qubits.\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": [
        "Cada qubit está etiquetado y las flechas de color representan las puertas de dos qubits.  Las flechas grises son las compuertas CZ y las flechas negras son las compuertas inter-chip CX (conectan los qubits $6 \\rightarrow 21$ y $25 \\rightarrow 40$ ). La dirección de la flecha indica la dirección por defecto en la que se ejecutan estas puertas; especifican qué qubits son control/objetivo por defecto para cada canal de dos qubits.\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",
        "## Transpilar contra backends personalizados\n",
        "\n",
        "Ahora que se ha definido un backend personalizado con su propio y único [`Target`](../api/qiskit/qiskit.transpiler.Target) es sencillo transpilar circuitos cuánticos contra este backend, ya que todas las restricciones relevantes (puertas base, conectividad qubit, etc.) necesarias para los pases del transpilador están contenidas en este atributo. El siguiente ejemplo construye un circuito que crea un estado GHZ grande y lo transpila contra el backend construido anteriormente.\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: 204\n",
            "ECR gates: 8\n",
            "SX gates: 374\n",
            "RZ gates: 215\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": [
        "El circuito transpilado ahora contiene una mezcla de `CZ` puertas `ECR` y, que especificamos como puertas básicas en el backend `Target`.  También hay bastantes más puertas que al principio debido a la necesidad de insertar instrucciones SWAP después de elegir un diseño.  A continuación, se utiliza la herramienta [`plot_circuit_layout()`](/docs/api/qiskit/qiskit.visualization.plot_circuit_layout) de visualización para especificar qué qubits y canales de dos qubits se utilizaron en este 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",
        "## Crear backends únicos\n",
        "\n",
        "El paquete [rustworkx](https://www.rustworkx.org/) contiene una amplia biblioteca de gráficos diferentes y permite crear gráficos personalizados.  El código visualmente interesante que se muestra a continuación crea un backend inspirado en el código tórico. A continuación, puede visualizar el backend utilizando las funciones de la sección [Visualizar backends](#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: 563\n",
            "X gates: 6\n",
            "SX gates: 1182\n",
            "RZ gates: 1078\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
}