{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "eb16d89d-f6fc-417f-9ce5-19251d039df7",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Découpe de circuits pour conditions aux limites périodiques\"\n",
        "description: \"Utilisez la découpe de circuit pour traiter un problème de chaîne périodique à l'échelle des services publics où les premier et dernier qubits ne sont pas voisins.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore fontsize edgecolor */}\n",
        "\n",
        "<span id=\"circuit-cutting-for-periodic-boundary-conditions\" />\n",
        "\n",
        "# Découpe de circuits pour conditions aux limites périodiques\n",
        "\n",
        "*Estimation de l'utilisation : Deux 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": "f053a0b1",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## Arrière-plan\n",
        "\n",
        "Dans ce cahier, nous considérons la simulation d'une chaîne périodique de qubits où il y a une opération de deux qubits entre tous les deux qubits adjacents, y compris le premier et le dernier. Les chaînes périodiques sont souvent présentes dans les problèmes de physique et de chimie tels que les modèles d'Ising et la simulation moléculaire.\n",
        "\n",
        "Les dispositifs actuels IBM Quantum® sont planaires. Il est possible d'intégrer directement dans la topologie des chaînes périodiques où le premier et le dernier qubit sont voisins. Cependant, pour des problèmes suffisamment importants, le premier et le dernier qubit peuvent être éloignés l'un de l'autre, ce qui nécessite de nombreuses portes SWAP pour l'opération à deux qubits entre ces deux qubits. Un tel problème de frontière périodique a été étudié à l'adresse <a href=\"https://arxiv.org/abs/2402.17833\">ce document</a>.\n",
        "\n",
        "Dans ce cahier, nous montrons l'utilisation du découpage de circuits pour traiter un tel problème de chaîne périodique à l'échelle de l'utilité où le premier et le dernier qubits ne sont pas voisins. En coupant cette connectivité à longue distance, on évite les portes SWAP supplémentaires au prix de l'exécution de plusieurs instances du circuit et d'un post-traitement classique. En résumé, le découpage peut être incorporé pour calculer logiquement les opérations à 2 qubits sur de longues distances. En d'autres termes, cette approche conduit à une augmentation effective de la connectivité de la carte de couplage, ce qui permet de réduire le nombre de portes SWAP.\n",
        "\n",
        "Notez qu'il existe deux types de coupures : couper le fil d'un circuit (appelé `wire cutting`) ou remplacer une porte à 2 qubits par plusieurs opérations à un qubit (appelé `gate cutting`). Dans ce cahier, nous nous concentrerons sur le découpage des portes. Pour plus de détails sur le découpage des portillons, consultez le site <a href=\"https://qiskit.github.io/qiskit-addon-cutting/explanation/index.html\">matériel explicatif</a> à l'adresse `qiskit-addon-cutting` et les références correspondantes. Pour plus de détails sur le découpage du fil, reportez-vous au didacticiel sur [le découpage du fil pour l'estimation des valeurs espérées](/docs/tutorials/wire-cutting) ou aux didacticiels disponibles sur le site <a href=\"https://qiskit.github.io/qiskit-addon-cutting/tutorials/index.html\">qiskit-addon-cutting</a>.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "120312e5-0eed-4168-9098-633a3d0e6e57",
      "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 v1.2 ou plus tard (`pip install qiskit`)\n",
        "* Qiskit Runtime v0.3 ou plus tard (`pip install qiskit-ibm-runtime`)\n",
        "* Add-on Qiskit pour la découpe de circuits v.9.0 ou version ultérieure (`pip install qiskit-addon-cutting`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f0af3c7d",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configuration\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "f01e3062",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "import matplotlib as mpl\n",
        "\n",
        "from qiskit.transpiler import PassManager\n",
        "from qiskit.transpiler.passes import (\n",
        "    BasisTranslator,\n",
        "    Optimize1qGatesDecomposition,\n",
        ")\n",
        "from qiskit.circuit.equivalence_library import (\n",
        "    SessionEquivalenceLibrary as sel,\n",
        ")\n",
        "from qiskit.converters import circuit_to_dag, dag_to_circuit\n",
        "from qiskit.result import sampled_expectation_value\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "from qiskit.circuit.library import TwoLocal\n",
        "\n",
        "from qiskit_addon_cutting import (\n",
        "    cut_gates,\n",
        "    generate_cutting_experiments,\n",
        "    reconstruct_expectation_values,\n",
        ")\n",
        "\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import SamplerV2, SamplerOptions, Batch"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6e7c685c-3e10-4cf1-a435-2b0fc761ebc4",
      "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",
        "Ici, nous allons générer un circuit TwoLocal et définir quelques observables.\n",
        "\n",
        "<ul>\n",
        "  <li>Entrée : Paramètres pour créer un circuit</li>\n",
        "  <li>Sortie : Circuit abstrait et observables</li>\n",
        "</ul>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8590c112-cf8a-4bc3-ab8b-411d1a1b010a",
      "metadata": {},
      "source": [
        "Nous considérons une solution matérielle efficace `entangler map` pour le circuit TwoLocal avec une connectivité périodique entre le dernier et le premier qubit du circuit `entangler map`. Cette interaction à longue portée peut conduire à des portes SWAP supplémentaires lors de la transpilation, augmentant ainsi la profondeur du circuit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0776185d-4ea8-4a8c-ab14-121636444d8f",
      "metadata": {},
      "source": [
        "<span id=\"select-backend-and-initial-layout\" />\n",
        "\n",
        "#### Sélectionnez le backend et la mise en page initiale\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "81c616a7-248a-412b-bac5-080eb9199760",
      "metadata": {},
      "outputs": [],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, simulator=False, min_num_qubits=127\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "15b9c4e5-7b25-4b95-9d79-bf73b9adad97",
      "metadata": {},
      "source": [
        "Pour ce cahier, nous considérerons une chaîne 1D périodique de 109 qubits, qui est la plus longue chaîne 1D dans la topologie d'un dispositif IBM Quantum de 127 qubits. Il n'est pas possible d'organiser une chaîne périodique de 109 qubits sur un dispositif de 127 qubits de manière à ce que le premier et le dernier qubit soient voisins sans incorporer des portes SWAP supplémentaires.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "6f8c4588-0532-41f9-9d6d-50e754466593",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "109"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "init_layout = [\n",
        "    13,\n",
        "    12,\n",
        "    11,\n",
        "    10,\n",
        "    9,\n",
        "    8,\n",
        "    7,\n",
        "    6,\n",
        "    5,\n",
        "    4,\n",
        "    3,\n",
        "    2,\n",
        "    1,\n",
        "    0,\n",
        "    14,\n",
        "    18,\n",
        "    19,\n",
        "    20,\n",
        "    21,\n",
        "    22,\n",
        "    23,\n",
        "    24,\n",
        "    25,\n",
        "    26,\n",
        "    27,\n",
        "    28,\n",
        "    29,\n",
        "    30,\n",
        "    31,\n",
        "    32,\n",
        "    36,\n",
        "    51,\n",
        "    50,\n",
        "    49,\n",
        "    48,\n",
        "    47,\n",
        "    46,\n",
        "    45,\n",
        "    44,\n",
        "    43,\n",
        "    42,\n",
        "    41,\n",
        "    40,\n",
        "    39,\n",
        "    38,\n",
        "    37,\n",
        "    52,\n",
        "    56,\n",
        "    57,\n",
        "    58,\n",
        "    59,\n",
        "    60,\n",
        "    61,\n",
        "    62,\n",
        "    63,\n",
        "    64,\n",
        "    65,\n",
        "    66,\n",
        "    67,\n",
        "    68,\n",
        "    69,\n",
        "    70,\n",
        "    74,\n",
        "    89,\n",
        "    88,\n",
        "    87,\n",
        "    86,\n",
        "    85,\n",
        "    84,\n",
        "    83,\n",
        "    82,\n",
        "    81,\n",
        "    80,\n",
        "    79,\n",
        "    78,\n",
        "    77,\n",
        "    76,\n",
        "    75,\n",
        "    90,\n",
        "    94,\n",
        "    95,\n",
        "    96,\n",
        "    97,\n",
        "    98,\n",
        "    99,\n",
        "    100,\n",
        "    101,\n",
        "    102,\n",
        "    103,\n",
        "    104,\n",
        "    105,\n",
        "    106,\n",
        "    107,\n",
        "    108,\n",
        "    112,\n",
        "    126,\n",
        "    125,\n",
        "    124,\n",
        "    123,\n",
        "    122,\n",
        "    121,\n",
        "    120,\n",
        "    119,\n",
        "    118,\n",
        "    117,\n",
        "    116,\n",
        "    115,\n",
        "    114,\n",
        "    113,\n",
        "]\n",
        "\n",
        "# the number of qubits in the circuit is governed by the length of the initial layout\n",
        "num_qubits = len(init_layout)\n",
        "num_qubits"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "da94a31f-3bce-40e5-a580-b938ff255425",
      "metadata": {},
      "source": [
        "<span id=\"build-the-entangler-map-for-the-twolocal-circuit\" />\n",
        "\n",
        "#### Construire la carte d'entrelacement pour le circuit d' TwoLocal\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "15bc3ac1-dbb6-4bb5-8d3e-63f3638f7a96",
      "metadata": {},
      "outputs": [],
      "source": [
        "coupling_map = [(i, i + 1) for i in range(0, len(init_layout) - 1)]\n",
        "coupling_map.append(\n",
        "    (len(init_layout) - 1, 0)\n",
        ")  # adding in the periodic connectivity"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b7b4970f-ac13-4644-a92f-1796de876767",
      "metadata": {},
      "source": [
        "TwoLocal permet de répéter plusieurs fois le `rotation_blocks` et le `entangler map` . Dans ce cas, le nombre de répétitions détermine le nombre de portes périodiques à couper. Étant donné que la charge d'échantillonnage augmente de façon exponentielle avec le nombre de coupes (pour plus de détails, voir le tutoriel sur [la coupe de fil pour l'estimation des valeurs espérées](/docs/tutorials/wire-cutting) ), nous fixerons le nombre de répétitions à 2 dans le présent manuel.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "0cd786ff-9798-4e20-937f-a258eea88077",
      "metadata": {},
      "outputs": [],
      "source": [
        "num_reps = 2\n",
        "entangler_map = []\n",
        "\n",
        "for even_edge in coupling_map[0 : len(coupling_map) : 2]:\n",
        "    entangler_map.append(even_edge)\n",
        "\n",
        "for odd_edge in coupling_map[1 : len(coupling_map) : 2]:\n",
        "    entangler_map.append(odd_edge)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "79428537-66cf-40ce-87cf-0f75f591cb4b",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/periodic-boundary-conditions-with-circuit-cutting/extracted-outputs/79428537-66cf-40ce-87cf-0f75f591cb4b-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 5,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "ansatz = TwoLocal(\n",
        "    num_qubits=num_qubits,\n",
        "    rotation_blocks=\"rx\",\n",
        "    entanglement_blocks=\"cx\",\n",
        "    entanglement=entangler_map,\n",
        "    reps=num_reps,\n",
        ").decompose()\n",
        "ansatz.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "18b2f9b3-07f0-47d0-88db-7182e03c5f53",
      "metadata": {},
      "source": [
        "Pour vérifier la qualité du résultat par découpage, il faut connaître le résultat idéal. Le circuit actuel de choix va au-delà de la simulation classique par force brute. C'est pourquoi nous fixons soigneusement les paramètres du circuit pour le rendre conforme à la norme \"clifford\".\n",
        "\n",
        "Nous attribuerons la valeur de paramètre $0$ aux deux premières couches de portes `Rx` et la valeur $\\pi$ à la dernière couche. Cela garantit que le résultat idéal de ce circuit est $|1\\rangle^{\\otimes n}$, $n$ étant le nombre de qubits. Par conséquent, les valeurs espérées de $\\langle Z_i \\rangle$ et $\\langle Z_i Z_{i+1} \\rangle$, où $i$ est l'indice du qubit, sont respectivement $-1$ et $+1$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "a0d70827-ae61-49a0-b14c-c10b27963262",
      "metadata": {},
      "outputs": [],
      "source": [
        "params_last_layer = [np.pi] * ansatz.num_qubits\n",
        "params = [0] * (ansatz.num_parameters - ansatz.num_qubits)\n",
        "params.extend(params_last_layer)\n",
        "\n",
        "ansatz.assign_parameters(params, inplace=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8f4289dd-30fd-4295-9d33-488f2fc03a3a",
      "metadata": {},
      "source": [
        "<span id=\"select-observables\" />\n",
        "\n",
        "#### Sélectionner les observables\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6a359888-c685-4b28-b2d4-c29b89448e3b",
      "metadata": {},
      "source": [
        "Pour quantifier les avantages de l'ouverture des vannes, nous mesurons les valeurs d'espérance des observables $\\frac{1}{n}\\sum_{i=1}^n \\langle Z_i \\rangle$ et $\\frac{1}{n-1}\\sum_{i=1}^{n-1} \\langle Z_i Z_{i+1} \\rangle$. Comme nous l'avons vu précédemment, les valeurs idéales sont respectivement $-1$ et $+1$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "87a6a367-2d4e-410c-8e5f-3ef069f968d8",
      "metadata": {},
      "outputs": [],
      "source": [
        "observables = []\n",
        "\n",
        "for i in range(num_qubits):\n",
        "    obs = \"I\" * (i) + \"Z\" + \"I\" * (num_qubits - i - 1)\n",
        "    observables.append(obs)\n",
        "\n",
        "for i in range(num_qubits):\n",
        "    if i == num_qubits - 1:\n",
        "        obs = \"Z\" + \"I\" * (num_qubits - 2) + \"Z\"\n",
        "    else:\n",
        "        obs = \"I\" * i + \"ZZ\" + \"I\" * (num_qubits - i - 2)\n",
        "    observables.append(obs)\n",
        "\n",
        "observables = SparsePauliOp(observables)\n",
        "paulis = observables.paulis\n",
        "coeffs = observables.coeffs"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "906440c1-4e5b-4f18-9bc7-d450dd7d2b24",
      "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",
        "<ul>\n",
        "  <li>Entrée : Circuit abstrait et observables</li>\n",
        "  <li>Résultat : Circuit cible et observables produits par le découpage des portes à longue portée</li>\n",
        "</ul>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2a7387e5-ead4-4dd8-a01f-974138cda3d9",
      "metadata": {},
      "source": [
        "<span id=\"transpile-the-circuit\" />\n",
        "\n",
        "#### Transpiler le circuit\n",
        "\n",
        "Il est à noter que le circuit peut être transposé à ce stade, ou après la découpe. Si nous transposons après le découpage, nous devrons transposer chacune des sous-expériences générées en raison de la surcharge d'échantillonnage. Il est donc plus prudent de transposer à ce stade afin de réduire la charge de travail liée à la transposition.\n",
        "\n",
        "Toutefois, si la transpilation est effectuée à ce stade avec la connectivité matérielle native, le transpileur ajoutera plusieurs portes SWAP pour placer l'opération périodique à 2 qubits, ce qui obscurcira les avantages de la coupure de circuit. Pour éviter ce problème, nous pouvons nous appuyer sur le fait que nous connaissons les portes exactes qui doivent être coupées. Plus précisément, nous pouvons créer une carte de couplage virtuel en ajoutant des connexions virtuelles entre des qubits éloignés pour accommoder ces portes périodiques à 2 qubits. Ainsi, le circuit pourra être transposé à ce stade sans incorporer les portes SWAP supplémentaires.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "c12bb07e-54a0-4597-8b4d-4d0fbf6a4c99",
      "metadata": {},
      "outputs": [],
      "source": [
        "coupling_map = backend.configuration().coupling_map\n",
        "\n",
        "# create a virtual coupling map with long range connectivity\n",
        "virtual_coupling_map = coupling_map.copy()\n",
        "virtual_coupling_map.append([init_layout[-1], init_layout[0]])\n",
        "virtual_coupling_map.append([init_layout[0], init_layout[-1]])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "ad38aa32-4613-46c5-bf62-da332a1b9dfb",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/periodic-boundary-conditions-with-circuit-cutting/extracted-outputs/ad38aa32-4613-46c5-bf62-da332a1b9dfb-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 9,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "pm_virtual = generate_preset_pass_manager(\n",
        "    optimization_level=1,\n",
        "    coupling_map=virtual_coupling_map,\n",
        "    initial_layout=init_layout,\n",
        "    basis_gates=backend.configuration().basis_gates,\n",
        ")\n",
        "\n",
        "virtual_mapped_circuit = pm_virtual.run(ansatz)\n",
        "virtual_mapped_circuit.draw(\"mpl\", fold=-1, idle_wires=False)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ecb4ae6e-f9a5-4453-a8a3-1a5aaaf954da",
      "metadata": {},
      "source": [
        "<span id=\"cut-the-long-range-periodic-connectivities\" />\n",
        "\n",
        "#### Couper les connexions périodiques à longue portée\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c155eda9-ba08-4069-9962-ece21926f1db",
      "metadata": {},
      "source": [
        "Nous coupons maintenant les portes du circuit transposé. Notez que les portes à 2 qubits qui doivent être coupées sont celles qui relient le dernier et le premier qubit du schéma.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "718ea31e-c8d8-4cf9-975b-fc0e77fb27c0",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Find the indices of the distant gates\n",
        "cut_indices = [\n",
        "    i\n",
        "    for i, instruction in enumerate(virtual_mapped_circuit.data)\n",
        "    if {virtual_mapped_circuit.find_bit(q)[0] for q in instruction.qubits}\n",
        "    == {init_layout[-1], init_layout[0]}\n",
        "]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c6091cb0-227d-454a-a2d8-7ba685b66121",
      "metadata": {},
      "source": [
        "Nous allons appliquer le schéma du circuit transposé à l'observable.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "af57d942-5997-4920-91c6-295fbfef478d",
      "metadata": {},
      "outputs": [],
      "source": [
        "trans_observables = observables.apply_layout(virtual_mapped_circuit.layout)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6a9a20e4-b722-456d-885b-d68ead3d341e",
      "metadata": {},
      "source": [
        "Enfin, les sous-expériences sont générées par échantillonnage sur différentes bases de mesure et de préparation.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "981f111c-69e6-44c2-a297-c7df302cbc0e",
      "metadata": {},
      "outputs": [],
      "source": [
        "qpd_circuit, bases = cut_gates(virtual_mapped_circuit, cut_indices)\n",
        "subexperiments, coefficients = generate_cutting_experiments(\n",
        "    circuits=qpd_circuit,\n",
        "    observables=trans_observables.paulis,\n",
        "    num_samples=np.inf,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "60582298-52ae-4f10-b8c2-bc754cb6d24c",
      "metadata": {},
      "source": [
        "Il convient de noter que la suppression des interactions à longue distance conduit à l'exécution de plusieurs échantillons du circuit qui diffèrent dans les bases de mesure et de préparation. De plus amples informations à ce sujet sont disponibles sur les sites <a href=\"https://arxiv.org/abs/1909.07534\">Construction d'une porte virtuelle à deux qubits par échantillonnage d'opérations à un qubit</a> et <a href=\"https://arxiv.org/abs/2312.11638\">Couper des circuits avec des unités multiples à deux qubits</a>.\n",
        "\n",
        "Le nombre de portes périodiques à découper est égal au nombre de répétitions de la couche `TwoLocal` , définie comme `num_reps` ci-dessus. Le surdébit d'échantillonnage du découpage des portes est de 6. Par conséquent, le nombre total de sous-expériences sera de $6^{num\\_reps}$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "be4a43b4-c035-4814-a486-45eb9fe23d86",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Number of subexperiments is 36 = 6**2\n"
          ]
        }
      ],
      "source": [
        "print(f\"Number of subexperiments is {len(subexperiments)} = 6**{num_reps}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "41a11eed-a8c0-4cce-b59d-ca49c54b52b1",
      "metadata": {},
      "source": [
        "<span id=\"transpile-the-subexperiments\" />\n",
        "\n",
        "#### Transpiler les sous-expériences\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5772d662-c030-4de6-9be4-e90cd599b173",
      "metadata": {},
      "source": [
        "À ce stade, les sous-expériences contiennent des circuits comportant des portes à 1 qubit qui ne font pas partie de l'ensemble de portes de base. En effet, les qubits coupés sont mesurés dans des bases différentes, et les portes de rotation utilisées à cet effet n'appartiennent pas nécessairement à l'ensemble des portes de base. Par exemple, la mesure dans la base X implique l'application d'une porte de Hadamard avant la mesure habituelle dans la base Z. Mais Hadamard ne fait pas partie de l'ensemble de portes de base.\n",
        "\n",
        "Au lieu d'appliquer l'ensemble du processus de transpilation à chacun des circuits des sous-expériences, nous pouvons utiliser des passes de transpilation spécifiques. Pour une description détaillée de toutes les passes de transpilation disponibles, consultez le site <a href=\"/docs/api/qiskit/transpiler_passes\">cette documentation</a>.\n",
        "\n",
        "Nous appliquerons les passes `BasisTranslator` puis `Optimize1qGatesDecomposition` pour nous assurer que toutes les portes de ces circuits appartiennent à l'ensemble de portes de base. L'utilisation de ces deux passages est plus rapide que l'ensemble du processus de transpilation, puisque d'autres étapes telles que le routage et la sélection initiale de l'agencement ne sont pas exécutées à nouveau.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "9c6d074f-5e48-4adb-9f4f-c5b03c9f7e36",
      "metadata": {},
      "outputs": [],
      "source": [
        "pass_ = PassManager(\n",
        "    [Optimize1qGatesDecomposition(basis=backend.configuration().basis_gates)]\n",
        ")\n",
        "\n",
        "subexperiments = pass_.run(\n",
        "    [\n",
        "        dag_to_circuit(\n",
        "            BasisTranslator(sel, target_basis=backend.basis_gates).run(\n",
        "                circuit_to_dag(circ)\n",
        "            )\n",
        "        )\n",
        "        for circ in subexperiments\n",
        "    ]\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4c5c0c60-3caa-4bd6-80aa-f7dc412680bb",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "## Étape 3 : Exécutez à l'aide d' Qiskit primitives\n",
        "\n",
        "<ul>\n",
        "  <li>Entrée : Circuits cibles</li>\n",
        "  <li>Sortie : Distributions de quasi-probabilité</li>\n",
        "</ul>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1fb3b96c-e55c-40e4-a5ca-4c327a192d73",
      "metadata": {},
      "source": [
        "Nous utilisons une primitive `SamplerV2` pour l'exécution des circuits de coupe. Nous désactivons `dynamical decoupling` et `twirling` afin que toute amélioration que nous obtenons dans le résultat soit uniquement due à l'application efficace de la coupure de porte pour ce type de circuit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "69a7cc63-173b-467c-87c5-f0924b943f34",
      "metadata": {},
      "outputs": [],
      "source": [
        "options = SamplerOptions()\n",
        "options.default_shots = 10000\n",
        "options.dynamical_decoupling.enable = False\n",
        "options.twirling.enable_gates = False\n",
        "options.twirling.enable_measure = False"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e9311a78-cdc7-4ce2-9716-a0cb5fc03589",
      "metadata": {},
      "source": [
        "Nous allons maintenant soumettre les travaux en mode batch.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "6c1ba1d4-b4ed-4781-99f5-41e3f93672d7",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Job ID cwxf7wq60bqg008pvt8g\n"
          ]
        }
      ],
      "source": [
        "with Batch(backend=backend) as batch:\n",
        "    sampler = SamplerV2(options=options)\n",
        "    cut_job = sampler.run(subexperiments)\n",
        "\n",
        "print(f\"Job ID {cut_job.job_id()}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "54ca98b9-7f31-45d2-910a-97ad29b37a0d",
      "metadata": {},
      "outputs": [],
      "source": [
        "result = cut_job.result()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "dd7cbb19-22ab-4e5a-bd18-3c7b06f67478",
      "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",
        "<ul>\n",
        "  <li>Entrée : Distributions de quasi-probabilité</li>\n",
        "  <li>Résultat : Valeurs attendues reconstruites</li>\n",
        "</ul>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "3ee4af4c-4585-4a8d-87a6-921c2cdb1bd2",
      "metadata": {},
      "outputs": [],
      "source": [
        "reconstructed_expvals = reconstruct_expectation_values(\n",
        "    result,\n",
        "    coefficients,\n",
        "    paulis,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f2d78c56-001c-4fd5-97eb-6d45b0f84bda",
      "metadata": {},
      "source": [
        "Nous calculons maintenant la moyenne des observables de type Z de weight-1 et weight-2.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "00714269-8c72-47eb-8651-3e2f5f65d505",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Average of weight-1 expectation values is -0.741733944954063\n",
            "Average of weight-2 expectation values is 0.6968862385320495\n"
          ]
        }
      ],
      "source": [
        "cut_weight_1 = np.mean(reconstructed_expvals[:num_qubits])\n",
        "cut_weight_2 = np.mean(reconstructed_expvals[num_qubits:])\n",
        "\n",
        "print(f\"Average of weight-1 expectation values is {cut_weight_1}\")\n",
        "print(f\"Average of weight-2 expectation values is {cut_weight_2}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1d15b180-107a-4078-afad-d306cf3098f5",
      "metadata": {},
      "source": [
        "<span id=\"cross-verify-obtain-uncut-expectation-value\" />\n",
        "\n",
        "### Vérification croisée : obtenir la valeur attendue non ajustée\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b6387268-cbd6-4766-a43d-154e461dfbc4",
      "metadata": {},
      "source": [
        "Il est utile de vérifier l'avantage de la technique de découpage du circuit par rapport à la technique non découpée. Nous calculerons ici les valeurs espérées sans couper le circuit. Notez qu'un tel circuit non coupé souffrira d'un grand nombre de portes SWAP nécessaires pour mettre en œuvre l'opération à 2 qubits entre le premier et le dernier qubit. Nous utiliserons la fonction `sampled_expectation_value` pour obtenir les valeurs d'espérance du circuit non coupé après avoir obtenu la distribution de probabilité via `SamplerV2`. Cela permet une utilisation homogène de la primitive dans toutes les instances. Toutefois, il convient de noter que nous aurions pu utiliser `EstimatorV2` pour calculer directement les valeurs d'espérance.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "0fcd5c53-703d-4e67-93d5-3ee4a7870753",
      "metadata": {},
      "outputs": [],
      "source": [
        "if ansatz.num_clbits == 0:\n",
        "    ansatz.measure_all()\n",
        "\n",
        "pm_uncut = generate_preset_pass_manager(\n",
        "    optimization_level=1, backend=backend, initial_layout=init_layout\n",
        ")\n",
        "\n",
        "transpiled_circuit = pm_uncut.run(ansatz)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "00e5b011-bec5-4917-a0d3-91943cad5927",
      "metadata": {},
      "outputs": [],
      "source": [
        "sampler = SamplerV2(mode=backend, options=options)\n",
        "uncut_job = sampler.run([transpiled_circuit])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "id": "ec68d688-7de4-4d95-8e52-23a1eea7d94e",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The job id for the uncut clifford circuit is cwxfads2ac5g008jhe7g\n"
          ]
        }
      ],
      "source": [
        "uncut_job_id = uncut_job.job_id()\n",
        "print(f\"The job id for the uncut clifford circuit is {uncut_job_id}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "id": "1e04489f-3dc9-4253-aa9a-719952f260e3",
      "metadata": {},
      "outputs": [],
      "source": [
        "uncut_result = uncut_job.result()[0]\n",
        "uncut_counts = uncut_result.data.meas.get_counts()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fb150558-6571-4475-aa81-08860c2782d6",
      "metadata": {},
      "source": [
        "Nous allons maintenant calculer les valeurs moyennes de l'espérance de toutes les observables de type Z weight-1 et weight-2 sans découpage.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "20991ea2-49a8-4258-9dd1-c064655674f1",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Average of weight-1 expectation values is -0.32494128440366965\n",
            "Average of weight-2 expectation values is 0.32340917431192656\n"
          ]
        }
      ],
      "source": [
        "uncut_expvals = [\n",
        "    sampled_expectation_value(uncut_counts, obs) for obs in paulis\n",
        "]\n",
        "\n",
        "uncut_weight_1 = np.mean(uncut_expvals[:num_qubits])\n",
        "uncut_weight_2 = np.mean(uncut_expvals[num_qubits:])\n",
        "\n",
        "print(f\"Average of weight-1 expectation values is {uncut_weight_1}\")\n",
        "print(f\"Average of weight-2 expectation values is {uncut_weight_2}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ed843ab6-9614-45a8-864a-932d897c0d22",
      "metadata": {},
      "source": [
        "<span id=\"visualize\" />\n",
        "\n",
        "### Visualiser\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "94eccfea-feb6-4a1c-a8fd-b7491ddb3b7d",
      "metadata": {},
      "source": [
        "Visualisons maintenant l'amélioration obtenue pour les observables weight-1 et weight-2 lorsque l'on utilise le découpage de porte pour le circuit en chaîne périodique\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2ba8913f-ba35-409c-bc4c-5f28e3698f20",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/periodic-boundary-conditions-with-circuit-cutting/extracted-outputs/2ba8913f-ba35-409c-bc4c-5f28e3698f20-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "mpl.rcParams.update(mpl.rcParamsDefault)\n",
        "\n",
        "fig = plt.subplots(figsize=(12, 8), dpi=200)\n",
        "width = 0.25\n",
        "labels = [\"Weight-1\", \"Weight-2\"]\n",
        "x = np.arange(len(labels))\n",
        "\n",
        "ideal = [-1, 1]\n",
        "cut = [cut_weight_1, cut_weight_2]\n",
        "uncut = [uncut_weight_1, uncut_weight_2]\n",
        "\n",
        "br1 = np.arange(len(ideal))\n",
        "br2 = [x + width for x in br1]\n",
        "br3 = [x + width for x in br2]\n",
        "\n",
        "plt.bar(\n",
        "    br1, ideal, width=width, edgecolor=\"k\", label=\"Ideal\", color=\"#4589ff\"\n",
        ")\n",
        "plt.bar(br2, cut, width=width, edgecolor=\"k\", label=\"Cut\", color=\"#a56eff\")\n",
        "plt.bar(\n",
        "    br3, uncut, width=width, edgecolor=\"k\", label=\"Uncut\", color=\"#009d9a\"\n",
        ")\n",
        "\n",
        "plt.axhline(y=0, color=\"k\", linestyle=\"-\")\n",
        "\n",
        "plt.xticks([r + width for r in range(len(ideal))], labels, fontsize=14)\n",
        "plt.yticks(fontsize=14)\n",
        "\n",
        "plt.legend(fontsize=14)\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "55de226b-1c87-48c8-b5e3-ba28e0640110",
      "metadata": {},
      "source": [
        "<span id=\"summary\" />\n",
        "\n",
        "### Récapitulatif\n",
        "\n",
        "En résumé, nous avons calculé les valeurs moyennes de l'espérance des observables des types weight-1 et weight-2 Z pour une chaîne 1D périodique de 109 qubits. Pour ce faire, nous\n",
        "\n",
        "* créé une carte de couplage virtuelle en ajoutant une connectivité à longue portée entre le premier et le dernier qubit de la chaîne 1D, et transposé le circuit.\n",
        "  * la transpilation à ce stade nous a permis d'éviter les frais généraux liés à la transpilation de chaque sous-expérience séparément après le découpage,\n",
        "  * l'utilisation de la carte de couplage virtuel nous a permis d'éviter les portes SWAP supplémentaires pour l'opération à 2 qubits entre le premier et le dernier qubit.\n",
        "* a supprimé la connectivité à longue portée du circuit transpilé en coupant les portes.\n",
        "* converti les circuits coupés en jeux de portes de base en appliquant les passes de transpilation appropriées.\n",
        "* a exécuté les circuits de coupe sur IBM Quantum device en utilisant une primitive `SamplerV2` .\n",
        "* a obtenu la valeur espérée en reconstruisant les résultats des circuits coupés.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a556c909-8cac-4500-ab52-9146b47194bf",
      "metadata": {},
      "source": [
        "<span id=\"inference\" />\n",
        "\n",
        "### Inférence\n",
        "\n",
        "Les résultats montrent que la moyenne des observables de type weight-1 $\\langle Z \\rangle$ et weight-2 $\\langle ZZ \\rangle$ est nettement améliorée par la suppression des portes périodiques. Il convient de noter que cette étude n'inclut aucune technique de suppression ou d'atténuation des erreurs. L'amélioration observée est uniquement due à l'utilisation correcte du découpage de la porte pour ce problème. Les résultats auraient pu être encore améliorés en utilisant les techniques d'atténuation et de suppression.\n",
        "\n",
        "Cette étude montre un exemple d'utilisation efficace du gate cutting pour améliorer les performances de calcul.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6b013b53",
      "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 nos offres de contenu et l'expérience des utilisateurs.\n",
        "\n",
        "[Lien vers l'enquête](https://your.feedback.ibm.com/jfe/form/SV_3fQQYAIjTxvIChg)\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": 120
  },
  "nbformat": 4,
  "nbformat_minor": 5
}