{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "710d86a8-3243-4133-b7bc-093306598512",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Écrire une passe de transpilation personnalisée\"\n",
        "description: \"Apprenez à écrire votre propre passe de transpileur à l'aide de Qiskit, y compris la représentation du circuit DAG\"\n",
        "---\n",
        "\n",
        "<span id=\"write-a-custom-transpiler-pass\" />\n",
        "\n",
        "# Écrire une passe de transpilation personnalisée\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=\"Versions de package\">\n",
        "    Le code de cette page a été développé en tenant compte des exigences suivantes.\n",
        "    Nous recommandons d'utiliser ces versions ou des versions plus récentes.\n",
        "\n",
        "    ```\n",
        "    qiskit[all]~=2.5.1\n",
        "    ```\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "32806873-3c88-48fc-94ad-9d20e0f378f8",
      "metadata": {},
      "source": [
        "Qiskit SDK vous permet de créer des passes de transpilation personnalisées et de les exécuter dans l'objet `PassManager` ou de les ajouter à un objet `StagedPassManager`. Nous allons montrer ici comment écrire une passe de transpilation, en nous concentrant sur la construction d'une passe qui effectue un [tourbillon de Pauli](https://arxiv.org/abs/quant-ph/0606161) sur les portes quantiques bruyantes d'un circuit quantique. Cet exemple utilise le DAG, qui est l'objet manipulé par le type de passe `TransformationPass` .\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c49a2046-39b0-4e63-aa63-911845ffd315",
      "metadata": {},
      "source": [
        "<Accordion>\n",
        "  <AccordionItem title=\"Contexte : représentation DAG\">\n",
        "    Avant de construire une passe, il est important de présenter la représentation interne des circuits quantiques dans Qiskit, le [graphe acyclique dirigé (DAG)](../api/qiskit/qiskit.dagcircuit.DAGCircuit) (voir [ce tutoriel](https://qiskit.org/ecosystem/rustworkx/tutorial/dags.html) pour une vue d'ensemble).  Pour suivre ces étapes, installez la [bibliothèque `graphviz` ](https://graphviz.org/download/) pour les fonctions de traçage DAG.\n",
        "\n",
        "    Dans Qiskit, au cours des étapes de transpilation, les circuits sont représentés à l'aide d'un DAG.  En général, un DAG est composé de *sommets* (également appelés « nœuds ») et *d'arêtes* orientées qui relient des paires de sommets dans une orientation particulière.  Cette représentation est stockée à l'aide `qiskit.dagcircuit.DAGCircuit` d'objets composés d'objets individuels `DagNode` .  L'avantage de cette représentation par rapport à une simple liste de portes (c'est-à-dire une *liste d'interconnexions* ) est que le flux d'informations entre les opérations est explicite, ce qui facilite la prise de décisions en matière de transformation.\n",
        "\n",
        "    Cet exemple illustre le DAG en créant un circuit simple qui prépare un état Bell et applique une rotation $R_Z$, en fonction du résultat de la mesure.\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",
        "    ![Circuit préparant un état de cloche et appliquant une rotation R\\_Z en fonction du résultat de la mesure.](https://quantum.cloud.ibm.com/docs/images/guides/custom-transpiler-pass/DAG_circ.avif \"Circuit\")\n",
        "\n",
        "    Utilisez la fonction `qiskit.tools.visualization.dag_drawer()` pour visualiser le DAG de ce circuit.  Il existe trois types de nœuds de graphe : les nœuds de qubit/clbit (vert), les nœuds d'opération (bleu) et les nœuds de sortie (rouge).  Chaque arête indique le flux de données (ou la dépendance) entre deux nœuds.\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",
        "    ![Le DAG du circuit est constitué de nœuds reliés par des arêtes directionnelles. Il s'agit d'un moyen visuel de représenter les qubits ou les bits classiques, les opérations et la manière dont les données circulent.](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",
        "## Passages du transpilateur\n",
        "\n",
        "Les passes du transpileur sont classées soit comme [`AnalysisPass`](../api/qiskit/qiskit.transpiler.AnalysisPass) soit comme [`TransformationPass`](../api/qiskit/qiskit.transpiler.TransformationPass). Les passes fonctionnent généralement avec le [DAG](../api/qiskit/qiskit.dagcircuit.DAGCircuit) et le `property_set`, un objet de type dictionnaire servant à stocker les propriétés déterminées par les passes d'analyse. Les passes d'analyse fonctionnent aussi bien avec le DAG qu'avec son `property_set`.  Ils ne peuvent pas modifier le DAG, mais peuvent modifier le `property_set`.  Cela contraste avec les passes de transformation, qui modifient le DAG et peuvent lire (mais pas écrire) dans `property_set`celui-ci.  Par exemple, les passes de transformation convertissent un circuit en son [architecture d'instructions (ISA)](/docs/guides/transpile#instruction-set-architecture) ou effectuent des passes de routage pour insérer des portes SWAP là où cela est nécessaire.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f8ead728-b2b7-47b9-b3b1-f3578b594ffe",
      "metadata": {},
      "source": [
        "<span id=\"create-a-paulitwirl-transpiler-pass\" />\n",
        "\n",
        "## Créer une `PauliTwirl` passage de transpileur\n",
        "\n",
        "L'exemple suivant construit une passe de transpilateur qui ajoute des tourbillons de Pauli. Le [tournoiement de Pauli](https://arxiv.org/abs/quant-ph/0606161) est une stratégie de suppression des erreurs qui randomise la manière dont les qubits sont confrontés aux canaux bruyants, que nous supposons être des portes à deux qubits dans cet exemple (parce qu'elles sont beaucoup plus sujettes aux erreurs que les portes à un seul qubit). Les tourbillons de Pauli n'affectent pas le fonctionnement des portes à deux qubits. Ils sont choisis de telle sorte que ceux qui sont appliqués *avant la* porte à deux qubits (à gauche) sont contrés par ceux qui sont appliqués *après la* porte à deux qubits (à droite). En ce sens, les opérations à deux qubits sont identiques, mais la manière dont elles sont effectuées est différente. L'un des avantages du tourbillon de Pauli est qu'il transforme les erreurs cohérentes en erreurs stochastiques, qui peuvent être améliorées en calculant davantage de moyennes.\n",
        "\n",
        "Les passes Transpiler agissent sur le [DAG](../api/qiskit/qiskit.dagcircuit.DAGCircuit), donc la méthode importante à surcharger est `.run()`, qui prend le DAG en entrée. L'initialisation des paires de Paulis comme indiqué préserve le fonctionnement de chaque porte à deux qubits. Pour ce faire, on utilise la méthode d'aide `build_twirl_set`, qui passe en revue chaque Pauli à deux qubits (tel qu'obtenu à partir de `pauli_basis(2)`) et trouve l'autre Pauli qui préserve l'opération.\n",
        "\n",
        "À partir du DAG, utilisez la méthode `op_nodes()` pour retourner tous ses nœuds. Le DAG peut également être utilisé pour collecter des runs, qui sont des séquences de nœuds qui s'exécutent de manière ininterrompue sur un qubit. Elles peuvent être rassemblées sous forme d'exécutions de qubits simples avec `collect_1q_runs`, d'exécutions de qubits doubles avec `collect_2q_runs`, et d'exécutions de nœuds dont les noms d'instructions figurent dans une liste de noms avec `collect_runs`.  Le site `DAGCircuit` propose de nombreuses méthodes pour rechercher et parcourir un graphe.  Une méthode couramment utilisée est `topological_op_nodes`, qui fournit les nœuds dans un ordre de dépendance. D'autres méthodes, telles que `bfs_successors` , sont utilisées principalement pour déterminer comment les nœuds interagissent avec les opérations ultérieures sur un DAG.\n",
        "\n",
        "Dans l'exemple, nous voulons remplacer chaque nœud, représentant une instruction, par un sous-circuit construit comme un mini DAG. Un registre quantique à deux qubits est ajouté au mini DAG.  Les opérations sont ajoutées au mini DAG en utilisant `apply_operation_back`, qui place `Instruction` sur la sortie du mini DAG (alors que `apply_operation_front` le placerait sur l'entrée du mini DAG). Le nœud est ensuite remplacé par le mini DAG en utilisant `substitute_node_with_dag`, et le processus se poursuit pour chaque instance de `CXGate` et `ECRGate` dans le DAG (correspondant aux portes de base à deux qubits sur les backends 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",
        "## Utilisez le `PauliTwirl` passage du transpiler\n",
        "\n",
        "Le code suivant utilise le passe créé ci-dessus pour transcompiler un circuit. Considérons un circuit simple avec des portes `cx``ecr` et.\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": [
        "Pour appliquer le laissez-passer personnalisé, construisez un gestionnaire de laissez-passer en utilisant le laissez-passer `PauliTwirl` et faites-le fonctionner sur 50 circuits.\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": [
        "Chaque porte à deux qubits est maintenant prise en sandwich entre deux 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": [
        "Les opérateurs sont les mêmes si l'on utilise `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",
        "## Etapes suivantes\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recommandations\">\n",
        "  * Pour apprendre à utiliser la fonction `generate_preset_passmanager` au lieu d'écrire vos propres passes, commencez par la rubrique [Paramètres par défaut et options de configuration de la transpilation](defaults-and-configuration-options).\n",
        "  * Consultez le guide [Comparer les paramètres du transpiler](/docs/guides/circuit-transpilation-settings).\n",
        "  * Consultez [la documentation relative à l'API du transcompilateur](/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
}