{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "d2c31ae8",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Coupe de fil pour l'estimation des valeurs attendues\"\n",
        "description: \"Utilisez la coupe de fils pour diviser les circuits en plusieurs sous-circuits plus petits.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore edgecolor Cutqc forall fontsize */}\n",
        "\n",
        "<span id=\"wire-cutting-for-expectation-values-estimation\" />\n",
        "\n",
        "# Coupe de fil pour l'estimation des valeurs attendues\n",
        "\n",
        "*Estimation du temps d'exécution : 22 secondes sur un processeur Heron (REMARQUE : il s'agit uniquement d'une estimation. (Votre temps d'exécution peut varier.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8bf80006",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## Résultats d'apprentissage\n",
        "\n",
        "À l'issue de ce tutoriel, les utilisateurs devraient être en mesure de :\n",
        "\n",
        "* Comment utiliser [`qiskit-addon-cutting`](https://github.com/Qiskit/qiskit-addon-cutting) pour diviser un grand circuit en sous-circuits plus petits, réduisant ainsi l'effet du bruit\n",
        "\n",
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## Prérequis\n",
        "\n",
        "Nous recommandons aux utilisateurs de se familiariser avec le sujet suivant avant de suivre ce tutoriel :\n",
        "\n",
        "* Utilisation de la primitive «[ Sampler](/docs/api/qiskit-ibm-runtime/sampler-v2) », qui est utilisée dans ce flux de travail\n",
        "\n",
        "<span id=\"background\" />\n",
        "\n",
        "## Arrière-plan\n",
        "\n",
        "Le « tricotage de circuits » est un terme générique qui désigne diverses méthodes permettant de diviser un circuit en plusieurs sous-circuits plus petits, comportant moins de portes logiques ou de qubits. Chacun des sous-circuits peut être exécuté indépendamment, et le résultat final est obtenu grâce à un post-traitement classique appliqué aux résultats de chaque sous-circuit. Cette technique est disponible dans [l](https://qiskit.github.io/qiskit-addon-cutting/index.html) 'extension « Circuit cutting » de Qiskit; consultez la [documentation](https://qiskit.github.io/qiskit-addon-cutting/explanation/index.html) ainsi que d'autres [ressources d'introduction](https://qiskit.github.io/qiskit-addon-cutting/tutorials/index.html) pour obtenir une explication détaillée de cette technique.\n",
        "\n",
        "Ce tutoriel porte sur une méthode appelée « **coupure de fil** », qui consiste à diviser le circuit le long du fil [\\[1\\], \\[2\\]](#references). Il convient de noter que le partitionnement est simple dans les circuits classiques, car le résultat au point de partition peut être déterminé de manière déterministe et prend la valeur 0 ou 1. Cependant, l'état du qubit au moment de la coupure est, en général, un état mixte. Par conséquent, chaque sous-circuit doit être mesuré à plusieurs reprises dans différentes bases (généralement une base tomographiquement complète, telle que la base de Pauli [\\[3\\], \\[4\\]](#references) ) et préparé en conséquence dans son état propre. La figure ci-dessous (source : [\\[7\\]](#references) ) illustre un exemple de découpage d'un état GHZ à quatre qubits en trois sous-circuits. Ici, $M_j$ désigne un ensemble de bases (généralement Pauli X, Y et Z), et $P_i$ désigne un ensemble d'états propres (généralement $|0\\rangle$, $|1\\rangle$, $|+\\rangle$ et $|+i\\rangle$ ).\n",
        "\n",
        "![wc-1.png](https://quantum.cloud.ibm.com/docs/images/tutorials/wire-cutting/0ce8857b-7f5f-400e-8536-6a496c724d50.avif)\n",
        "![wc-2.png](https://quantum.cloud.ibm.com/docs/images/tutorials/wire-cutting/cbce4455-4794-4c81-8630-3e3993e1b29f.avif)\n",
        "\n",
        "Étant donné que chaque sous-circuit comporte moins de qubits et de portes logiques, on s'attend à ce qu'ils soient moins sensibles au bruit. Ce tutoriel présente un exemple illustrant comment cette méthode peut être utilisée pour réduire efficacement le bruit dans le système.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "55b94021",
      "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",
        "* Module complémentaire Qiskit « Circuit cutting » v0.10.0 ou version ultérieure (`pip install qiskit-addon-cutting`)\n",
        "* Module complémentaire Qiskit utils 0.3 ou version ultérieure (`pip install qiskit-addon-utils`)\n",
        "* Qiskit Aer (`pip install qiskit-aer` )\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "7db2e559",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configuration\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "bc380c46",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "from qiskit.circuit import Parameter, ParameterVector, QuantumCircuit\n",
        "from qiskit.quantum_info import PauliList, SparsePauliOp\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "from qiskit_aer import AerSimulator\n",
        "from qiskit.result import sampled_expectation_value\n",
        "\n",
        "from qiskit_addon_cutting.instructions import CutWire\n",
        "from qiskit_addon_cutting import (\n",
        "    cut_wires,\n",
        "    expand_observables,\n",
        "    partition_problem,\n",
        "    generate_cutting_experiments,\n",
        "    reconstruct_expectation_values,\n",
        ")\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import SamplerV2, Batch"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "431a5bd2-e6ed-471b-ad9e-c4edd27784a8",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-simulator-example\" />\n",
        "\n",
        "## Exemple de simulateur à petite échelle\n",
        "\n",
        "Ce tutoriel met en œuvre un [modèle Qiskit](/docs/guides/intro-to-patterns) permettant de simuler un circuit de localisation à N corps (MBL) unidimensionnel (en 1D ). Le circuit MBL est un circuit efficace sur le plan matériel et est paramétré par deux paramètres : $\\theta$ et $\\vec{\\phi}$. Lorsque $\\theta$ est défini sur $0$ et que l'état initial est préparé dans $|0\\rangle$ pour tous les qubits, la valeur attendue idéale de $\\langle Z_i \\rangle$ est $+1$ pour chaque site de qubit $i$, quelles que soient les valeurs de $\\vec{\\phi}$. Vous trouverez plus de détails sur ce circuit dans cet [article](https://www.nature.com/articles/s41467-025-57623-x).\n",
        "\n",
        "Il convient de noter que, dans un simulateur sans bruit, la valeur attendue obtenue avec ou sans coupure du circuit sera la même.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "988ee237",
      "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",
        "<span id=\"construct-the-1d-mbl-circuit\" />\n",
        "\n",
        "#### Construire le circuit MBL de l' 1D\n",
        "\n",
        "Nous présentons tout d'abord une fonction permettant de construire le circuit MBL d' 1D.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "d2aa988a",
      "metadata": {},
      "outputs": [],
      "source": [
        "class MBLChainCircuit(QuantumCircuit):\n",
        "    def __init__(\n",
        "        self, num_qubits: int, depth: int, use_cut: bool = False\n",
        "    ) -> None:\n",
        "        super().__init__(\n",
        "            num_qubits, name=f\"MBLChainCircuit<{num_qubits}, {depth}>\"\n",
        "        )\n",
        "        evolution = MBLChainEvolution(num_qubits, depth, use_cut)\n",
        "        self.compose(evolution, inplace=True)\n",
        "\n",
        "\n",
        "class MBLChainEvolution(QuantumCircuit):\n",
        "    def __init__(self, num_qubits: int, depth: int, use_cut) -> None:\n",
        "        super().__init__(\n",
        "            num_qubits, name=f\"MBLChainEvolution<{num_qubits}, {depth}>\"\n",
        "        )\n",
        "\n",
        "        theta = Parameter(\"θ\")\n",
        "        phis = ParameterVector(\"φ\", num_qubits)\n",
        "\n",
        "        for layer in range(depth):\n",
        "            layer_parity = layer % 2\n",
        "            # print(\"layer parity\", layer_parity)\n",
        "            for qubit in range(layer_parity, num_qubits - 1, 2):\n",
        "                # print(qubit)\n",
        "                self.cz(qubit, qubit + 1)\n",
        "                self.u(theta, 0, np.pi, qubit)\n",
        "                self.u(theta, 0, np.pi, qubit + 1)\n",
        "                if (\n",
        "                    use_cut\n",
        "                    and layer_parity == 0\n",
        "                    and (\n",
        "                        qubit == num_qubits // 2 - 1\n",
        "                        or qubit == num_qubits // 2\n",
        "                    )\n",
        "                ):\n",
        "                    self.append(CutWire(), [num_qubits // 2])\n",
        "                if use_cut and layer < depth - 1 and layer_parity == 1:\n",
        "                    if qubit == num_qubits // 2:\n",
        "                        self.append(CutWire(), [qubit])\n",
        "            for qubit in range(num_qubits):\n",
        "                self.p(phis[qubit], qubit)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "a3debf65-06df-4277-933e-14b6f6170756",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/a3debf65-06df-4277-933e-14b6f6170756-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "num_qubits = 10\n",
        "depth = 2\n",
        "mbl = MBLChainCircuit(num_qubits, depth)\n",
        "mbl.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ef13f367",
      "metadata": {},
      "source": [
        "Nous calculons la valeur attendue moyenne $O = \\frac{1}{n} \\sum_i Z_i$ sur l'ensemble des qubits pour $\\theta = 0$. Étant donné que la valeur attendue idéale de $\\langle Z_i \\rangle = 1$ $\\forall$ $i$, la valeur attendue idéale de $O$ est également $1$. Les paramètres $\\phi$ sont choisis au hasard.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "b0f1e5fd",
      "metadata": {},
      "outputs": [],
      "source": [
        "np.random.seed(42)\n",
        "phis = list(np.random.rand(mbl.num_parameters - 1))\n",
        "theta = [0]\n",
        "params = theta + phis"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a81f0346",
      "metadata": {},
      "source": [
        "Pour diviser le circuit, il faut y insérer des annotations « **CutWire** » aux emplacements souhaités. Pour ce tutoriel, nous avons choisi un partage en parts égales. Le circuit MBL est conçu de telle sorte que le réglage `use_cut=True` dans la fonction insère correctement l'annotation après $\\frac{n}{2}$ qubits, où $n$ correspond au nombre de qubits dans le circuit d'origine. Nous avons également attribué les paramètres générés aléatoirement au circuit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "5208e0a8",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/5208e0a8-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 5,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "mbl_cut = MBLChainCircuit(num_qubits, depth, use_cut=True)\n",
        "mbl_cut.assign_parameters(params, inplace=True)\n",
        "mbl_cut.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ac6f36e3",
      "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",
        "<span id=\"cut-the-circuit-into-smaller-subcircuits\" />\n",
        "\n",
        "#### Divisez le circuit en sous-circuits plus petits\n",
        "\n",
        "Nous allons maintenant diviser le circuit en deux sous-circuits plus petits à l'aide de [`qiskit-addon-cutting`](https://qiskit.github.io/qiskit-addon-cutting/). `qiskit-addon-cutting` ajoute une porte virtuelle `Move` pour diviser l'emplacement de la coupure du fil en ajustant de manière appropriée le nombre de qubits. Nous allons maintenant créer le circuit à l'aide de cette porte logique virtuelle. Comme un fil a été coupé, le nombre de qubits associés augmentera d'une unité.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "1834cb22",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/1834cb22-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "mbl_move = cut_wires(mbl_cut)\n",
        "mbl_move.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1df16e6b",
      "metadata": {},
      "source": [
        "<span id=\"construct-and-expand-the-observable\" />\n",
        "\n",
        "#### Construire et étendre l'observable\n",
        "\n",
        "L'observable, telle que définie précédemment, correspondra à la moyenne de l' $Z$ e sur chaque qubit. Cependant, dès lors que l'on insère la porte virtuelle `Move` , le nombre effectif de qubits dans le circuit augmente. L'observable doit également être étendue en conséquence pour tenir compte de cette variation du nombre de qubits. Notez que l'observable agit toujours de manière triviale (comme dans $I$ ) sur le qubit supplémentaire ajouté pour la porte virtuelle `Move` .\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "3074b173",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "PauliList(['ZIIIIIIIII', 'IZIIIIIIII', 'IIZIIIIIII', 'IIIZIIIIII',\n",
              "           'IIIIZIIIII', 'IIIIIZIIII', 'IIIIIIZIII', 'IIIIIIIZII',\n",
              "           'IIIIIIIIZI', 'IIIIIIIIIZ'])"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "observable = PauliList(\n",
        "    [\"I\" * i + \"Z\" + \"I\" * (num_qubits - i - 1) for i in range(num_qubits)]\n",
        ")\n",
        "observable"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "32b7081b",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "PauliList(['ZIIIIIIIIII', 'IZIIIIIIIII', 'IIZIIIIIIII', 'IIIZIIIIIII',\n",
              "           'IIIIZIIIIII', 'IIIIIIZIIII', 'IIIIIIIZIII', 'IIIIIIIIZII',\n",
              "           'IIIIIIIIIZI', 'IIIIIIIIIIZ'])"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "new_obs = expand_observables(observable, mbl, mbl_move)\n",
        "new_obs"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0ad0886b",
      "metadata": {},
      "source": [
        "On peut désormais diviser le circuit le long de `Move` la porte et obtenir ainsi les sous-circuits, ainsi que la sous-observable, qui correspond à la partie de l'observable d'origine associée à chaque sous-circuit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "c8894a06",
      "metadata": {},
      "outputs": [],
      "source": [
        "partitioned_problem = partition_problem(circuit=mbl_move, observables=new_obs)\n",
        "subcircuits = partitioned_problem.subcircuits\n",
        "subobservables = partitioned_problem.subobservables"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8f47ab33",
      "metadata": {},
      "source": [
        "Nous représentons ici les deux sous-circuits :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "1b0e779f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/1b0e779f-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "subcircuits[0].draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "3c802f28",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/3c802f28-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 14,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "subcircuits[1].draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0e4c03fb",
      "metadata": {},
      "source": [
        "Pour étendre l'observable à l'aide de l'opération `Move` , il faut une `PauliList` structure de données. Pour calculer la valeur attendue du circuit d'origine, nous avons besoin de l'observable sous la `SparsePauliOp` forme\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "7dc51220",
      "metadata": {},
      "outputs": [],
      "source": [
        "M_z = SparsePauliOp(\n",
        "    [\"I\" * i + \"Z\" + \"I\" * (num_qubits - i - 1) for i in range(num_qubits)],\n",
        "    coeffs=[1 / num_qubits] * num_qubits,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2b5a5746",
      "metadata": {},
      "source": [
        "Comme nous l'avons vu précédemment, pour chaque coupure, le circuit en amont doit être mesuré dans une base de Pauli, et le circuit en aval doit être préparé dans l'état propre de cette base. La fonction `generate_cutting_experiments` crée tous les circuits nécessaires ainsi que les coefficients associés à chaque circuit requis pour la reconstruction. Pour plus de détails, consultez [cet article](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.125.150504).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "648519f0",
      "metadata": {},
      "outputs": [],
      "source": [
        "subexperiments, coefficients = generate_cutting_experiments(\n",
        "    circuits=subcircuits,\n",
        "    observables=subobservables,\n",
        "    num_samples=np.inf,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "43f58cfb",
      "metadata": {},
      "source": [
        "<span id=\"transpile-the-circuits-onto-the-backend\" />\n",
        "\n",
        "#### Transpiler les circuits vers le backend\n",
        "\n",
        "Pour le premier exemple, qui ne concerne que la simulation, nous transpilons le circuit vers l'ensemble de portes de base du backend :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "29d71cd3",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "<IBMBackend('ibm_fez')>\n"
          ]
        }
      ],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, simulator=False, min_num_qubits=133\n",
        ")\n",
        "\n",
        "print(backend)"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "b4d480b3",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### Étape 3 : Exécutez à l'aide d' Qiskit primitives\n",
        "\n",
        "Maintenant, lancez chaque sous-expérience :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "05fabb27",
      "metadata": {},
      "outputs": [],
      "source": [
        "pm_basis = generate_preset_pass_manager(\n",
        "    optimization_level=2, basis_gates=backend.configuration().basis_gates\n",
        ")\n",
        "basis_subexperiments = {\n",
        "    label: pm_basis.run(partition_subexpts)\n",
        "    for label, partition_subexpts in subexperiments.items()\n",
        "}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "b22a1b00",
      "metadata": {},
      "outputs": [],
      "source": [
        "sampler = SamplerV2(mode=AerSimulator())\n",
        "jobs = {\n",
        "    label: sampler.run(subsystem_subexpts, shots=2**12)\n",
        "    for label, subsystem_subexpts in basis_subexperiments.items()\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "50b94af2",
      "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",
        "Nous récupérons maintenant le résultat de chaque exécution de sous-expérience et reconstituons la valeur attendue du circuit non découpé :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "31dc35ea-6554-4ca7-9c3b-0b5394c46e4e",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Retrieve results\n",
        "results = {label: job.result() for label, job in jobs.items()}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "4972e4e4",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "np.float64(0.9953821063041687)"
            ]
          },
          "execution_count": 23,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "reconstructed_expval_terms = reconstruct_expectation_values(\n",
        "    results,\n",
        "    coefficients,\n",
        "    subobservables,\n",
        ")\n",
        "reconstructed_expval = np.dot(reconstructed_expval_terms, M_z.coeffs).real\n",
        "reconstructed_expval"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 32,
      "id": "fb5f955a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Text(0, 0.5, '$M_Z$')"
            ]
          },
          "execution_count": 32,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/fb5f955a-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "methods = [\n",
        "    \"Uncut\",\n",
        "    \"Wire cut\",\n",
        "]\n",
        "values = [\n",
        "    1,\n",
        "    reconstructed_expval,\n",
        "]  # since the ideal expectation value in noiseless simulation is +1\n",
        "\n",
        "ax = plt.gca()\n",
        "plt.bar(methods, values, color=\"#a56eff\", width=0.4, edgecolor=\"#8a3ffc\")\n",
        "ax.set_ylabel(r\"$M_Z$\", fontsize=12)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0d6db390-e7a8-4efe-902c-8d9a312170c6",
      "metadata": {},
      "source": [
        "<span id=\"large-scale-hardware-example\" />\n",
        "\n",
        "## Exemple de matériel à grande échelle\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ae69c5e0-32b1-4f03-ab13-7b95a9acfd25",
      "metadata": {},
      "source": [
        "Nous allons maintenant présenter la technique de « wire cutting » pour un circuit MBL de 60 qubits. Les circuits non découpés, tout comme les circuits découpés, seront réalisés sur du matériel d' IBM Quantum® :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "37834c72",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/37834c72-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "num_qubits = 60\n",
        "depth = 2\n",
        "\n",
        "# construct the circuit\n",
        "mbl = MBLChainCircuit(num_qubits, depth)\n",
        "\n",
        "# create parameters\n",
        "phis = list(np.random.rand(mbl.num_parameters - 1))\n",
        "theta = [0]\n",
        "params = theta + phis\n",
        "\n",
        "# construct the cut circuit\n",
        "mbl_cut = MBLChainCircuit(num_qubits, depth, use_cut=True)\n",
        "mbl_cut.assign_parameters(params, inplace=True)\n",
        "mbl_move = cut_wires(mbl_cut)\n",
        "\n",
        "# Define observable and expand to account for the wire cut\n",
        "observable = PauliList(\n",
        "    [\"I\" * i + \"Z\" + \"I\" * (num_qubits - i - 1) for i in range(num_qubits)]\n",
        ")\n",
        "new_obs = expand_observables(observable, mbl, mbl_move)\n",
        "\n",
        "# Construct a SparsePauliOp version of the observable for later use in reconstruction\n",
        "M_z = SparsePauliOp(\n",
        "    [\"I\" * i + \"Z\" + \"I\" * (num_qubits - i - 1) for i in range(num_qubits)],\n",
        "    coeffs=[1 / num_qubits] * num_qubits,\n",
        ")\n",
        "\n",
        "# Partition the circuit and get subcircuits and subobservables\n",
        "partitioned_problem = partition_problem(circuit=mbl_move, observables=new_obs)\n",
        "subcircuits = partitioned_problem.subcircuits\n",
        "subobservables = partitioned_problem.subobservables\n",
        "\n",
        "# Obtain subexperiments and coefficients\n",
        "subexperiments, coefficients = generate_cutting_experiments(\n",
        "    circuits=subcircuits,\n",
        "    observables=subobservables,\n",
        "    num_samples=np.inf,\n",
        ")\n",
        "\n",
        "# Transpile the subexperiments to the backend\n",
        "pm = generate_preset_pass_manager(optimization_level=2, backend=backend)\n",
        "isa_subexperiments = {\n",
        "    label: pm.run(partition_subexpts)\n",
        "    for label, partition_subexpts in subexperiments.items()\n",
        "}\n",
        "\n",
        "# Execute the subexperiments and retrieve results\n",
        "with Batch(backend=backend) as batch:\n",
        "    sampler = SamplerV2(mode=batch)\n",
        "    sampler.options.environment.job_tags = [\"TUT_WC\"]\n",
        "    jobs = {\n",
        "        label: sampler.run(subsystem_subexpts, shots=2**12)\n",
        "        for label, subsystem_subexpts in isa_subexperiments.items()\n",
        "    }\n",
        "results = {label: job.result() for label, job in jobs.items()}\n",
        "\n",
        "# Reconstruct the expectation value of the original observable\n",
        "reconstructed_expval_terms = reconstruct_expectation_values(\n",
        "    results,\n",
        "    coefficients,\n",
        "    subobservables,\n",
        ")\n",
        "reconstructed_expval = np.dot(reconstructed_expval_terms, M_z.coeffs).real\n",
        "\n",
        "# Compute the uncut circuit to obtain the noisy expectation value for comparison\n",
        "sampler = SamplerV2(mode=backend)\n",
        "sampler.options.environment.job_tags = [\"TUT_WC\"]\n",
        "\n",
        "if mbl.num_clbits == 0:\n",
        "    mbl.measure_all()\n",
        "isa_mbl = pm.run(mbl)\n",
        "\n",
        "pub = (isa_mbl, params)\n",
        "uncut_job = sampler.run([pub])\n",
        "\n",
        "uncut_counts = uncut_job.result()[0].data.meas.get_counts()\n",
        "uncut_expval = sampled_expectation_value(uncut_counts, M_z)\n",
        "\n",
        "# visualize the results\n",
        "ax = plt.gca()\n",
        "methods = [\"uncut\", \"cut\"]\n",
        "values = [uncut_expval, reconstructed_expval]\n",
        "\n",
        "plt.bar(methods, values, color=\"#a56eff\", width=0.4, edgecolor=\"#8a3ffc\")\n",
        "plt.axhline(y=1, color=\"k\", linestyle=\"--\")\n",
        "plt.text(0.3, 0.95, \"Exact result\")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 29,
      "id": "1445d099",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "0.9202473958333336"
            ]
          },
          "execution_count": 29,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "uncut_expval"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "75f48e6a-c7e4-46f3-9d39-a7a877427a04",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Etapes suivantes\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recommandations\">\n",
        "  Si ce travail vous a paru intéressant, les documents suivants pourraient vous intéresser :\n",
        "\n",
        "  * [Conditions aux limites périodiques avec coupure de circuit](/docs/tutorials/periodic-boundary-conditions-with-circuit-cutting)\n",
        "  * [Découpe en circuit pour réduire la profondeur](/docs/tutorials/depth-reduction-with-circuit-cutting)\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "23cd3042",
      "metadata": {},
      "source": [
        "<span id=\"references\" />\n",
        "\n",
        "## Références\n",
        "\n",
        "\\[1] Peng, T., Harrow, A. W., Ozols, M. et Wu, X. (2020). Simulation de grands circuits quantiques sur un petit ordinateur quantique. Physical review letters, 125(15), 150504.\n",
        "\n",
        "\\[2] Tang, W., Tomesh, T., Suchara, M., Larson, J. et Martonosi, M. (2021, avril). Cutqc : utilisation de petits ordinateurs quantiques pour l'évaluation de circuits quantiques de grande taille. In Proceedings of the 26th ACM International conference on architectural support for programming languages and operating systems (pp. 473-486).\n",
        "\n",
        "\\[3] Perlin, M. A., Saleem, Z. H., Suchara, M., et Osborn, J. C. (2021). Découpage de circuits quantiques par tomographie à maximum de vraisemblance. npj Quantum Information, 7(1), 64.\n",
        "\n",
        "\\[4] Majumdar, R., & Wood, C. J. (2022). Découpage de circuits quantiques avec atténuation des erreurs. arXiv preprint arXiv:2211.13431.\n",
        "\n",
        "\\[5] Khare, T., Majumdar, R., Sangle, R., Ray, A., Seshadri, P. V. et Simmhan, Y. (2023). Paralléliser les charges de travail classiques et quantiques : Profilage de l'impact des techniques de fractionnement. In 2023 IEEE International Conference on Quantum Computing and Engineering (QCE) (Vol. 1, pp. 990-1000). IEEE.\n",
        "\n",
        "\\[6] Bhoumik, D., Majumdar, R., Saha, A. et Sur-Kolay, S. (2023). Ordonnancement distribué de circuits quantiques avec optimisation du bruit et du temps. arXiv preprint arXiv:2309.06005.\n",
        "\n",
        "\\[7] Majumdar, R. (2024). Réduction efficace des ressources et du bruit dans les circuits d'informatique quantique discrète (Thèse de doctorat, Institut indien de statistique - Kolkata). [https://www.proquest.com/openview/b481def90b1cc80e6b58a77c99e8385c/1?pq-origsite=gscholar\\&cbl=2026366\\&diss=y](https://www.proquest.com/openview/b481def90b1cc80e6b58a77c99e8385c/1?pq-origsite=gscholar\\&cbl=2026366\\&diss=y)\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": 22
  },
  "nbformat": 4,
  "nbformat_minor": 5
}