{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "7ed4867f",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Découpe de circuits pour réduire la profondeur\"\n",
        "description: \"Construire un modèle Qiskit pour couper des portes dans un circuit quantique afin de réduire la profondeur du circuit.\"\n",
        "---\n",
        "\n",
        "<span id=\"circuit-cutting-for-depth-reduction\" />\n",
        "\n",
        "# Découpe de circuits pour réduire la profondeur\n",
        "\n",
        "*Estimation de l'utilisation : Huit minutes sur un processeur Eagle (NOTE : Il s'agit uniquement d'une estimation. Votre durée d'exécution peut varier.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "387c5e80",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## Arrière-plan\n",
        "\n",
        "Ce tutoriel montre comment construire un `Qiskit pattern` pour couper les portes d'un circuit quantique afin de réduire la profondeur du circuit. Pour une discussion plus approfondie sur le découpage des circuits, consultez la [documentation du module complémentaire Qiskit sur le découpage des circuits.](https://qiskit.github.io/qiskit-addon-cutting/)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a62460d9",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Exigences\n",
        "\n",
        "Avant de commencer ce tutoriel, assurez-vous que les éléments suivants sont installés :\n",
        "\n",
        "* Qiskit SDK v2.0 ou plus tard, avec prise en charge de [la visualisation](/docs/api/qiskit/visualization)\n",
        "* Qiskit Runtime v0.22 ou plus tard (`pip install qiskit-ibm-runtime`)\n",
        "* Add-on Qiskit pour la découpe de circuits v0.9.0 ou version ultérieure (`pip install qiskit-addon-cutting`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a89fe306",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configuration\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "c795c670",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "\n",
        "from qiskit.circuit.library import EfficientSU2\n",
        "from qiskit.quantum_info import PauliList, Statevector, SparsePauliOp\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "\n",
        "from qiskit_addon_cutting import (\n",
        "    cut_gates,\n",
        "    generate_cutting_experiments,\n",
        "    reconstruct_expectation_values,\n",
        ")\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "07f7f75d",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "## Étape 1 : Mettre en correspondance les entrées classiques avec un problème quantique\n",
        "\n",
        "Nous allons mettre en œuvre notre modèle Qiskit en suivant les quatre étapes décrites dans la [documentation.](/docs/guides/intro-to-patterns) Dans ce cas, nous simulerons les valeurs d'espérance sur un circuit d'une certaine profondeur en coupant des portes résultant en des portes d'échange et en exécutant des sous-expériences sur des circuits moins profonds. Le découpage de portes est pertinent pour les étapes 2 (optimiser le circuit pour l'exécution quantique en décomposant les portes éloignées) et 4 (post-traitement pour reconstruire les valeurs d'espérance sur le circuit d'origine).\n",
        "Dans un premier temps, nous allons générer un circuit à partir de la bibliothèque de circuits Qiskit et définir quelques observables.\n",
        "\n",
        "* Entrée : Paramètres classiques pour définir un circuit\n",
        "* Sortie : Circuit abstrait et observables\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "54ed0f13",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/depth-reduction-with-circuit-cutting/extracted-outputs/54ed0f13-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "circuit = EfficientSU2(num_qubits=4, entanglement=\"circular\").decompose()\n",
        "circuit.assign_parameters([0.4] * len(circuit.parameters), inplace=True)\n",
        "observables = PauliList([\"ZZII\", \"IZZI\", \"IIZZ\", \"XIXI\", \"ZIZZ\", \"IXIX\"])\n",
        "circuit.draw(\"mpl\", scale=0.8, style=\"iqp\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "080a2a8b",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-problem-for-quantum-hardware-execution\" />\n",
        "\n",
        "## Étape 2 : Optimiser le problème pour l'exécution sur du matériel quantique\n",
        "\n",
        "* Entrée : Circuit abstrait et observables\n",
        "* Résultat : Circuit cible et observables produits en coupant les portes éloignées pour réduire la profondeur du circuit transpilé\n",
        "\n",
        "Nous choisissons une disposition initiale qui nécessite deux permutations pour exécuter les portes entre les qubits 3 et 0 et deux autres permutations pour ramener les qubits à leur position initiale. Nous choisissons `optimization_level=3`, qui est le niveau d'optimisation le plus élevé disponible avec un gestionnaire de passe prédéfini.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "b394da7a",
      "metadata": {},
      "outputs": [],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, min_num_qubits=circuit.num_qubits, simulator=False\n",
        ")\n",
        "\n",
        "pm = generate_preset_pass_manager(\n",
        "    optimization_level=3, initial_layout=[0, 1, 2, 3], backend=backend\n",
        ")\n",
        "transpiled_qc = pm.run(circuit)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "68c00476",
      "metadata": {},
      "source": [
        "![Carte de couplage montrant les qubits qui devront être échangés](https://quantum.cloud.ibm.com/docs/images/tutorials/depth-reduction-with-circuit-cutting/swaps.avif)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "4fe4af43",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Transpiled circuit depth: 103\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/depth-reduction-with-circuit-cutting/extracted-outputs/4fe4af43-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "print(f\"Transpiled circuit depth: {transpiled_qc.depth()}\")\n",
        "transpiled_qc.draw(\"mpl\", scale=0.4, idle_wires=False, style=\"iqp\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "babfbd0f",
      "metadata": {},
      "source": [
        "*Rechercher et couper les portes distantes :* nous remplacerons les portes distantes (portes reliant les qubits non locaux, 0 et 3) par `TwoQubitQPDGate` des objets en spécifiant leurs indices. `cut_gates` remplacera les portes dans les indices spécifiés par `TwoQubitQPDGate` des objets et renverra également une liste `QPDBasis` d'instances, une pour chaque décomposition de porte. L'objet `QPDBasis` contient des informations sur la manière de décomposer les portes coupées en opérations à un seul qubit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "23e3d25e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/depth-reduction-with-circuit-cutting/extracted-outputs/23e3d25e-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 5,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Find the indices of the distant gates\n",
        "cut_indices = [\n",
        "    i\n",
        "    for i, instruction in enumerate(circuit.data)\n",
        "    if {circuit.find_bit(q)[0] for q in instruction.qubits} == {0, 3}\n",
        "]\n",
        "\n",
        "# Decompose distant CNOTs into TwoQubitQPDGate instances\n",
        "qpd_circuit, bases = cut_gates(circuit, cut_indices)\n",
        "\n",
        "qpd_circuit.draw(\"mpl\", scale=0.8)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "069eb942-e947-4eff-a250-9a99c5ec47f0",
      "metadata": {},
      "source": [
        "*Générer les sous-expériences à exécuter sur le backend* : `generate_cutting_experiments` accepte un circuit contenant des instances `TwoQubitQPDGate` et des observables en tant que `PauliList`.\n",
        "\n",
        "Pour simuler la valeur d'espérance du circuit complet, de nombreuses sous-expériences sont générées à partir de la distribution de quasiprobabilité commune des portes décomposées, puis exécutées sur un ou plusieurs backends. Le nombre d'échantillons prélevés dans la distribution est contrôlé par `num_samples`, et un coefficient combiné est donné pour chaque échantillon unique. Pour plus d'informations sur le calcul des coefficients, se référer au [matériel explicatif](https://qiskit.github.io/qiskit-addon-cutting/explanation/index.html).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "83b1efed-bafa-48c4-bbf0-cf7eb9027ac5",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Generate the subexperiments and sampling coefficients\n",
        "subexperiments, coefficients = generate_cutting_experiments(\n",
        "    circuits=qpd_circuit, observables=observables, num_samples=np.inf\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6929264d",
      "metadata": {},
      "source": [
        "*À titre de comparaison, nous constatons que les sous-expériences QPD seront moins profondes après avoir coupé les portes éloignées* : Voici un exemple de sous-expérience choisie arbitrairement et générée à partir du circuit QPD. Sa profondeur a été réduite de plus de la moitié. Un grand nombre de ces sous-expériences probabilistes doivent être générées et évaluées afin de reconstruire une valeur d'espérance du circuit profond.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "70e2f1b6",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Original circuit depth after transpile: 103\n",
            "QPD subexperiment depth after transpile: 46\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/depth-reduction-with-circuit-cutting/extracted-outputs/70e2f1b6-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Transpile the decomposed circuit to the same layout\n",
        "transpiled_qpd_circuit = pm.run(subexperiments[100])\n",
        "\n",
        "print(f\"Original circuit depth after transpile: {transpiled_qc.depth()}\")\n",
        "print(\n",
        "    f\"QPD subexperiment depth after transpile: {transpiled_qpd_circuit.depth()}\"\n",
        ")\n",
        "transpiled_qpd_circuit.draw(\n",
        "    \"mpl\", scale=0.6, style=\"iqp\", idle_wires=False, fold=-1\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "87368cb9",
      "metadata": {},
      "source": [
        "D' *autre part, la coupe entraîne la nécessité d'un échantillonnage supplémentaire*. Ici, nous coupons trois portes CNOT, ce qui entraîne une surcharge d'échantillonnage de $9^3$. Pour en savoir plus sur la surcharge d'échantillonnage induite par le découpage des circuits, reportez-vous à la [documentation de la boîte à outils pour le tricotage de circuits](https://qiskit-extensions.github.io/circuit-knitting-toolbox/circuit_cutting/explanation/index.html).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "2ab65bd4",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Sampling overhead: 729.0\n"
          ]
        }
      ],
      "source": [
        "print(f\"Sampling overhead: {np.prod([basis.overhead for basis in bases])}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fd9a126c",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "## Étape 3 : Exécutez à l'aide d' Qiskit primitives\n",
        "\n",
        "Exécutez les circuits cibles (« sous-expériences ») à l'aide de la primitive Sampler.\n",
        "\n",
        "* Entrée : Circuits cibles\n",
        "* Sortie : Distributions de quasi-probabilité\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "a437de20-2042-4e62-87a7-804058cff5db",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Transpile the subexperiments to the backend's instruction set architecture (ISA)\n",
        "isa_subexperiments = pm.run(subexperiments)\n",
        "\n",
        "# Set up the IBM Quantum Sampler primitive.  For a fake backend, this will use a local simulator.\n",
        "sampler = SamplerV2(backend)\n",
        "\n",
        "# Submit the subexperiments\n",
        "job = sampler.run(isa_subexperiments)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "ca53d638",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Retrieve the results\n",
        "results = job.result()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "32d35001",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "czypg1r6rr3g008mgp6g\n"
          ]
        }
      ],
      "source": [
        "print(job.job_id())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f04d9134-651b-446e-93f4-aa0281786200",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "## Étape 4 : Post-traitement et restitution du résultat dans le format classique souhaité\n",
        "\n",
        "Utiliser les résultats des sous-expériences, les sous-observables et les coefficients d'échantillonnage pour reconstruire la valeur d'espérance du circuit original.\n",
        "\n",
        "Entrée : Distributions de quasi-probabilité Sortie : Valeurs d'espérance reconstruites\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "ace12f7f",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Final reconstructed expectation value\n",
            "1.0751342773437473\n"
          ]
        }
      ],
      "source": [
        "reconstructed_expvals = reconstruct_expectation_values(\n",
        "    results,\n",
        "    coefficients,\n",
        "    observables,\n",
        ")\n",
        "# Reconstruct final expectation value\n",
        "final_expval = np.dot(reconstructed_expvals, [1] * len(observables))\n",
        "print(\"Final reconstructed expectation value\")\n",
        "print(final_expval)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "e6237a6f",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Ideal expectation value\n",
            "1.2283177520039992\n"
          ]
        }
      ],
      "source": [
        "ideal_expvals = [\n",
        "    Statevector(circuit).expectation_value(SparsePauliOp(observable))\n",
        "    for observable in observables\n",
        "]\n",
        "print(\"Ideal expectation value\")\n",
        "print(np.dot(ideal_expvals, [1] * len(observables)).real)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e366d14e",
      "metadata": {},
      "source": [
        "<span id=\"tutorial-survey\" />\n",
        "\n",
        "## Enquête tutorielle\n",
        "\n",
        "Veuillez répondre à cette courte enquête pour nous faire part de vos commentaires sur ce didacticiel. Vos commentaires nous aideront à améliorer notre offre de contenu et l'expérience des utilisateurs.\n",
        "\n",
        "[Lien vers l'enquête](https://your.feedback.ibm.com/jfe/form/SV_2ftYFf9t72yFNIO)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3"
    },
    "hours": 1.5,
    "qpuSeconds": 480
  },
  "nbformat": 4,
  "nbformat_minor": 5
}