{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "710d86a8-3243-4133-b7bc-093306598512",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Escribir un paso de transpilador personalizado\"\n",
        "description: \"Aprenda a escribir su propio paso de transpilador utilizando Qiskit, incluida la representación de circuitos DAG\"\n",
        "---\n",
        "\n",
        "<span id=\"write-a-custom-transpiler-pass\" />\n",
        "\n",
        "# Escribir un paso de transpilador personalizado\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5a66bd4b-56b1-4f33-b1bf-b43dbc7c03a7",
      "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": "32806873-3c88-48fc-94ad-9d20e0f378f8",
      "metadata": {},
      "source": [
        "El SDK de Qiskit permite crear pases de transpilación personalizados y ejecutarlos en el objeto `PassManager` o añadirlos a un `StagedPassManager`. Aquí demostraremos cómo escribir un pase transpilador, centrándonos en la construcción de un pase que realice [el giro de Pauli](https://arxiv.org/abs/quant-ph/0606161) en las puertas cuánticas ruidosas de un circuito cuántico. Este ejemplo utiliza el DAG, que es el objeto manipulado por el tipo de pase `TransformationPass` .\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c49a2046-39b0-4e63-aa63-911845ffd315",
      "metadata": {},
      "source": [
        "<Accordion>\n",
        "  <AccordionItem title=\"Antecedentes: Representación DAG\">\n",
        "    Antes de construir un pase, es importante introducir la representación interna de los circuitos cuánticos en Qiskit, el [grafo acíclico dirigido (DAG)](../api/qiskit/qiskit.dagcircuit.DAGCircuit) (ver [este tutorial](https://qiskit.org/ecosystem/rustworkx/tutorial/dags.html) para una visión general).  Para seguir estos pasos, instale la [biblioteca `graphviz` ](https://graphviz.org/download/) para las funciones de trazado de DAG.\n",
        "\n",
        "    En Qiskit, dentro de las etapas de transpilación, los circuitos se representan utilizando un DAG.  En general, un DAG se compone de *vértices* (también conocidos como «nodos») y *aristas* dirigidas que conectan pares de vértices en una orientación determinada.  Esta representación se almacena utilizando `qiskit.dagcircuit.DAGCircuit` objetos que se componen de objetos `DagNode` individuales.  La ventaja de esta representación sobre una lista pura de puertas (es decir, una *lista de redes* ) es que el flujo de información entre operaciones es explícito, lo que facilita la toma de decisiones de transformación.\n",
        "\n",
        "    Este ejemplo ilustra el DAG mediante la creación de un circuito simple que prepara un estado Bell y aplica una rotación $R_Z$, dependiendo del resultado de la medición.\n",
        "\n",
        "    ```python\n",
        "    from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit\n",
        "    import numpy as np\n",
        "\n",
        "    qr = QuantumRegister(3, 'qr')\n",
        "    cr = ClassicalRegister(3, 'cr')\n",
        "    qc = QuantumCircuit(qr, cr)\n",
        "\n",
        "    qc.h(qr[0])\n",
        "    qc.cx(qr[0], qr[1])\n",
        "    qc.measure(qr[0], cr[0])\n",
        "    with qc.if_test((cr, 2)):\n",
        "        qc.rz(np.pi/2, qr[1])\n",
        "    qc.draw(output='mpl')\n",
        "\n",
        "    ```\n",
        "\n",
        "    ![Circuito que prepara un estado Bell y aplica una rotación R\\_Z en función del resultado de la medición.](https://quantum.cloud.ibm.com/docs/images/guides/custom-transpiler-pass/DAG_circ.avif \"Circuito\")\n",
        "\n",
        "    Utilice la función `qiskit.tools.visualization.dag_drawer()` para ver el DAG de este circuito.  Hay tres tipos de nodos gráficos: nodos qubit/clbit (verde), nodos de operación (azul) y nodos de salida (rojo).  Cada arista indica el flujo de datos (o dependencia) entre dos nodos.\n",
        "\n",
        "    ```python\n",
        "    from qiskit.converters import circuit_to_dag\n",
        "    from qiskit.visualization import dag_drawer\n",
        "\n",
        "    dag = circuit_to_dag(qc)\n",
        "    dag_drawer(dag)\n",
        "    ```\n",
        "\n",
        "    ![El DAG del circuito está formado por nodos conectados por aristas direccionales. Es una forma visual de representar los qubits o bits clásicos, las operaciones y la forma en que fluyen los datos.](https://quantum.cloud.ibm.com/docs/images/guides/custom-transpiler-pass/DAG.avif \"DAG\")\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7e2e2bc6-c900-4d3b-a948-3f5d154cdea2",
      "metadata": {},
      "source": [
        "<span id=\"transpiler-passes\" />\n",
        "\n",
        "## Pasadas del transpilador\n",
        "\n",
        "Las etapas del transpilador se clasifican como [`AnalysisPass`](../api/qiskit/qiskit.transpiler.AnalysisPass) o como [`TransformationPass`](../api/qiskit/qiskit.transpiler.TransformationPass). Las pasadas suelen funcionar con el [DAG](../api/qiskit/qiskit.dagcircuit.DAGCircuit) y el `property_set`, un objeto similar a un diccionario que sirve para almacenar las propiedades determinadas por las pasadas de análisis. Las etapas de análisis funcionan tanto con el DAG como con su `property_set`.  No pueden modificar el DAG, pero sí pueden modificar el `property_set`.  Esto contrasta con las etapas de transformación, que sí modifican el DAG y pueden leer (pero no escribir) en `property_set`él.  Por ejemplo, las pasadas de transformación convierten un circuito a su [ISA](/docs/guides/transpile#instruction-set-architecture) o realizan pasadas de enrutamiento para insertar puertas SWAP donde sea necesario.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f8ead728-b2b7-47b9-b3b1-f3578b594ffe",
      "metadata": {},
      "source": [
        "<span id=\"create-a-paulitwirl-transpiler-pass\" />\n",
        "\n",
        "## Crear una `PauliTwirl` pasada del transpilador\n",
        "\n",
        "El siguiente ejemplo construye un pase de transpilador que añade giros de Pauli. [El giro de Pauli](https://arxiv.org/abs/quant-ph/0606161) es una estrategia de supresión de errores que aleatoriza la forma en que los qubits experimentan los canales ruidosos, que en este ejemplo suponemos que son puertas de dos qubits (porque son mucho más propensas a errores que las puertas de un solo qubit). Los giros de Pauli no afectan al funcionamiento de las puertas de dos qubits. Se eligen de forma que los aplicados *antes de* la puerta de dos qubits (a la izquierda) sean contrarrestados por los aplicados *después de* la puerta de dos qubits (a la derecha). En este sentido, las operaciones de dos qubits son idénticas, pero la forma en que se realizan es diferente. Una ventaja del giro de Pauli es que convierte los errores coherentes en errores estocásticos, que pueden mejorarse promediando más.\n",
        "\n",
        "Los pases del Transpilador actúan sobre el [DAG](../api/qiskit/qiskit.dagcircuit.DAGCircuit), por lo que el método importante a anular es `.run()`, que toma el DAG como entrada. Inicializar pares de Paulis como se muestra preserva la operación de cada puerta de dos qubits. Esto se hace con el método de ayuda `build_twirl_set`, que recorre cada Pauli de dos qubits (como se obtiene de `pauli_basis(2)`) y encuentra el otro Pauli que preserva la operación.\n",
        "\n",
        "A partir del DAG, utiliza el método `op_nodes()` para devolver todos sus nodos. El DAG también puede utilizarse para recopilar ejecuciones, que son secuencias de nodos que se ejecutan ininterrumpidamente en un qubit. Se pueden recopilar como ejecuciones de un solo qubit con `collect_1q_runs`, ejecuciones de dos qubits con `collect_2q_runs`, y ejecuciones de nodos en los que los nombres de las instrucciones están en una namelist con `collect_runs`.  `DAGCircuit` dispone de numerosos métodos para buscar y recorrer un grafo.  Un método comúnmente utilizado es `topological_op_nodes`, que proporciona los nodos en un orden de dependencia. Otros métodos como `bfs_successors` se utilizan principalmente para determinar cómo interactúan los nodos con operaciones posteriores en un DAG.\n",
        "\n",
        "En el ejemplo, queremos sustituir cada nodo, que representa una instrucción, por un subcircuito construido como un mini DAG. Al mini DAG se le ha añadido un registro cuántico de dos qubits.  Las operaciones se añaden al mini DAG utilizando `apply_operation_back`, que coloca el `Instruction` en la salida del mini DAG (mientras que `apply_operation_front` lo colocaría en la entrada del mini DAG). A continuación, el nodo se sustituye por el mini DAG utilizando `substitute_node_with_dag`, y el proceso continúa sobre cada instancia de `CXGate` y `ECRGate` en el DAG (correspondientes a las puertas base de dos qubits en los backends de IBM® ).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "80546e59-dcd8-4b61-88b5-00b220409f6d",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.dagcircuit import DAGCircuit\n",
        "from qiskit.circuit import QuantumCircuit, QuantumRegister, Gate\n",
        "from qiskit.circuit.library import CXGate, ECRGate\n",
        "from qiskit.transpiler import PassManager\n",
        "from qiskit.transpiler.basepasses import TransformationPass\n",
        "from qiskit.quantum_info import Operator, pauli_basis\n",
        "\n",
        "import numpy as np\n",
        "\n",
        "from typing import Iterable, Optional"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "6bb6ff72-43dd-4f8c-a67c-b43af2548b2a",
      "metadata": {},
      "outputs": [],
      "source": [
        "class PauliTwirl(TransformationPass):\n",
        "    \"\"\"Add Pauli twirls to two-qubit gates.\"\"\"\n",
        "\n",
        "    def __init__(\n",
        "        self,\n",
        "        gates_to_twirl: Optional[Iterable[Gate]] = None,\n",
        "    ):\n",
        "        \"\"\"\n",
        "        Args:\n",
        "            gates_to_twirl: Names of gates to twirl. The default behavior is to twirl all\n",
        "                two-qubit basis gates, `cx` and `ecr` for IBM backends.\n",
        "        \"\"\"\n",
        "        if gates_to_twirl is None:\n",
        "            gates_to_twirl = [CXGate(), ECRGate()]\n",
        "        self.gates_to_twirl = gates_to_twirl\n",
        "        self.build_twirl_set()\n",
        "        super().__init__()\n",
        "\n",
        "    def build_twirl_set(self):\n",
        "        \"\"\"\n",
        "        Build a set of Paulis to twirl for each gate and store internally as .twirl_set.\n",
        "        \"\"\"\n",
        "        self.twirl_set = {}\n",
        "\n",
        "        # iterate through gates to be twirled\n",
        "        for twirl_gate in self.gates_to_twirl:\n",
        "            twirl_list = []\n",
        "\n",
        "            # iterate through Paulis on left of gate to twirl\n",
        "            for pauli_left in pauli_basis(2):\n",
        "                # iterate through Paulis on right of gate to twirl\n",
        "                for pauli_right in pauli_basis(2):\n",
        "                    # save pairs that produce identical operation as gate to twirl\n",
        "                    if (Operator(pauli_left) @ Operator(twirl_gate)).equiv(\n",
        "                        Operator(twirl_gate) @ pauli_right\n",
        "                    ):\n",
        "                        twirl_list.append((pauli_left, pauli_right))\n",
        "\n",
        "            self.twirl_set[twirl_gate.name] = twirl_list\n",
        "\n",
        "    def run(\n",
        "        self,\n",
        "        dag: DAGCircuit,\n",
        "    ) -> DAGCircuit:\n",
        "        # collect all nodes in DAG and proceed if it is to be twirled\n",
        "        twirling_gate_classes = tuple(\n",
        "            gate.base_class for gate in self.gates_to_twirl\n",
        "        )\n",
        "        for node in dag.op_nodes():\n",
        "            if not isinstance(node.op, twirling_gate_classes):\n",
        "                continue\n",
        "\n",
        "            # random integer to select Pauli twirl pair\n",
        "            pauli_index = np.random.randint(\n",
        "                0, len(self.twirl_set[node.op.name])\n",
        "            )\n",
        "            twirl_pair = self.twirl_set[node.op.name][pauli_index]\n",
        "\n",
        "            # instantiate mini_dag and attach quantum register\n",
        "            mini_dag = DAGCircuit()\n",
        "            register = QuantumRegister(2)\n",
        "            mini_dag.add_qreg(register)\n",
        "\n",
        "            # apply left Pauli, gate to twirl, and right Pauli to empty mini-DAG\n",
        "            mini_dag.apply_operation_back(\n",
        "                twirl_pair[0].to_instruction(), [register[0], register[1]]\n",
        "            )\n",
        "            mini_dag.apply_operation_back(node.op, [register[0], register[1]])\n",
        "            mini_dag.apply_operation_back(\n",
        "                twirl_pair[1].to_instruction(), [register[0], register[1]]\n",
        "            )\n",
        "\n",
        "            # substitute gate to twirl node with twirling mini-DAG\n",
        "            dag.substitute_node_with_dag(node, mini_dag)\n",
        "\n",
        "        return dag"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "da136beb-d1b2-4f68-8274-386e80a49860",
      "metadata": {},
      "source": [
        "<span id=\"use-the-paulitwirl-transpiler-pass\" />\n",
        "\n",
        "## Utiliza el `PauliTwirl` paso del transpilador\n",
        "\n",
        "El siguiente código utiliza el pase creado anteriormente para transpilear un circuito. Consideremos un circuito simple con `cx` puertas `ecr` y.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "9123905d-b4cb-4ae9-9695-4ad77e70bdab",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/guides/custom-transpiler-pass/extracted-outputs/9123905d-b4cb-4ae9-9695-4ad77e70bdab-0.svg\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "qc = QuantumCircuit(3)\n",
        "qc.cx(0, 1)\n",
        "qc.ecr(1, 2)\n",
        "qc.ecr(1, 0)\n",
        "qc.cx(2, 1)\n",
        "qc.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f112c2af-c99a-4733-8118-4786e2dfdfd3",
      "metadata": {},
      "source": [
        "Para aplicar el pase personalizado, cree un gestor de pases utilizando el pase `PauliTwirl` y ejecútelo en 50 circuitos.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "ea884cd2-2446-4b2d-a0f7-82ef574b11ee",
      "metadata": {},
      "outputs": [],
      "source": [
        "pm = PassManager([PauliTwirl()])\n",
        "twirled_qcs = [pm.run(qc) for _ in range(50)]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3d4d921e-1a43-4f9a-bc30-6c64092d8f6b",
      "metadata": {},
      "source": [
        "Cada puerta de dos qubits está ahora intercalada entre dos Paulis.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "e2515cf3-f8d9-4281-9673-d5a955d7aab9",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/guides/custom-transpiler-pass/extracted-outputs/e2515cf3-f8d9-4281-9673-d5a955d7aab9-0.svg\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 5,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "twirled_qcs[-1].draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "41cccfba-8b65-401c-9d30-66396a3fe3a1",
      "metadata": {},
      "source": [
        "Los operadores son los mismos si se utiliza `Operator` de `qiskit.quantum_info` :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "0f8b4ca1-4205-4b71-941d-0852eebbe917",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "np.True_"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "np.all([Operator(twirled_qc).equiv(qc) for twirled_qc in twirled_qcs])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0d680854-13f8-4ccd-9be1-595edb4f942c",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Próximos pasos\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recomendaciones\">\n",
        "  * Para aprender a utilizar la función `generate_preset_passmanager` en lugar de escribir sus propios pases, empiece por el tema [Ajustes por defecto y opciones de configuración de la transpilación](defaults-and-configuration-options).\n",
        "  * Prueba la guía [de configuración del transpilador Compare](/docs/guides/circuit-transpilation-settings).\n",
        "  * Revisa la [documentación de la API del transpilador](/docs/api/qiskit/transpiler).\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"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}