{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "2abbf9e4",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Estimation de phase quantique avec l' Qiskit Functions de Q-CTRL\"\n",
        "description: \"Implémenter l'algorithme d'estimation de phase quantique sur 35 qubits à l'aide de la fonction Q-CTRL Qiskit\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore eigenphase topk ylab boxstyle fontsize */}\n",
        "\n",
        "<span id=\"quantum-phase-estimation-with-q-ctrls-qiskit-functions\" />\n",
        "\n",
        "# Estimation de phase quantique avec l' Qiskit Functions de Q-CTRL\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4170a8f8",
      "metadata": {},
      "source": [
        "*Estimation d'utilisation : 40 secondes sur un processeur Heron r2. (NOTE : Il s'agit uniquement d'une estimation. Votre durée d'exécution peut varier.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b2f4a24e",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## Arrière-plan\n",
        "\n",
        "L'estimation de phase quantique (QPE) est un algorithme fondamental de l'informatique quantique qui constitue la base de nombreuses applications importantes telles que l'algorithme de Shor, l'estimation de l'énergie de l'état fondamental de la chimie quantique et les problèmes de valeurs propres.  QPE estime la phase $\\varphi$ associée à un état propre d'un opérateur unitaire, encodé dans la relation\n",
        "\n",
        "$U \\lvert \\varphi \\rangle = e^{2\\pi i \\varphi} \\lvert \\varphi \\rangle,$\n",
        "\n",
        "et la détermine avec une précision de $\\epsilon = O(1/2^m)$ en utilisant des qubits de comptage $m$ [\\[1\\].](#references) En préparant ces qubits en superposition, en appliquant des puissances contrôlées de $U$, puis en utilisant la transformée de Fourier quantique inverse (QFT) pour extraire la phase en résultats de mesure codés de manière binaire, le QPE produit une distribution de probabilité qui culmine à des chaînes de bits dont les fractions binaires se rapprochent de $\\varphi$. Dans le cas idéal, le résultat de mesure le plus probable correspond directement à l'expansion binaire de la phase, tandis que la probabilité des autres résultats diminue rapidement avec le nombre de qubits de comptage. Cependant, l'exécution de circuits QPE profonds sur du matériel présente des difficultés : le grand nombre de qubits et d'opérations d'enchevêtrement rend l'algorithme très sensible à la décohérence et aux erreurs de porte. Il en résulte des distributions élargies et décalées des chaînes de bits, masquant la véritable phase propre. Par conséquent, la chaîne de bits ayant la probabilité la plus élevée peut ne plus correspondre à l'expansion binaire correcte de $\\varphi$.\n",
        "\n",
        "Dans ce tutoriel, nous présentons une implémentation de l'algorithme QPE en utilisant les outils de gestion des performances et de suppression des erreurs Fire Opal de Q-CTRL, proposés en tant que fonction Qiskit (voir la [documentation Fire Opal](/docs/guides/q-ctrl-performance-management)). Fire Opal applique automatiquement des optimisations avancées, notamment le découplage dynamique, l'amélioration de la disposition des qubits et les techniques de suppression des erreurs, ce qui permet d'obtenir des résultats plus fidèles. Ces améliorations rapprochent les distributions de chaînes de bits matérielles de celles obtenues dans les simulations sans bruit, de sorte que vous pouvez identifier de manière fiable la phase propre correcte, même sous l'effet du bruit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5019f20e",
      "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.4 ou plus tard, avec prise en charge de [la visualisation](/docs/api/qiskit/visualization)\n",
        "* Qiskit Runtime v0.40 ou plus tard (`pip install qiskit-ibm-runtime`)\n",
        "* Qiskit Functions Catalog v0.9.0 (`pip install qiskit-ibm-catalog`)\n",
        "* Fire Opal SDK v9.0.2 ou plus récent (`pip install fire-opal`)\n",
        "* Q-CTRL Visualizer v8.0.2 ou plus récent (`pip install qctrl-visualizer`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "360f9871",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configuration\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "46eddb0f",
      "metadata": {},
      "source": [
        "Commencez par vous authentifier à l'aide de votre [clé API IBM Quantum](http://quantum.cloud.ibm.com/). Ensuite, sélectionnez la fonction Qiskit comme suit. (Ce code part du principe que vous avez déjà [enregistré votre compte](/docs/guides/functions-get-started#install-qiskit-functions-catalog-client) dans votre environnement local.)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "39896f05",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit import QuantumCircuit\n",
        "\n",
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "import qiskit\n",
        "from qiskit import qasm2\n",
        "from qiskit_aer import AerSimulator\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import SamplerV2 as Sampler\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "import qctrlvisualizer as qv\n",
        "from qiskit_ibm_catalog import QiskitFunctionsCatalog\n",
        "\n",
        "plt.style.use(qv.get_qctrl_style())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "9abf8775",
      "metadata": {},
      "outputs": [],
      "source": [
        "catalog = QiskitFunctionsCatalog(channel=\"ibm_quantum_platform\")\n",
        "\n",
        "# Access Function\n",
        "perf_mgmt = catalog.load(\"q-ctrl/performance-management\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6337a6c2",
      "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",
        "Dans ce tutoriel, nous illustrons le QPE pour récupérer la phase propre d'une unité connue à un seul qubit. L'unité dont nous voulons estimer la phase est la porte de phase du qubit unique appliquée au qubit cible :\n",
        "\n",
        "$$\n",
        "U(\\theta)=\n",
        "\\begin{pmatrix}\n",
        "1 & 0\\\\[2pt]\n",
        "0 & e^{i\\theta}\n",
        "\\end{pmatrix}\n",
        "= e^{i\\theta\\,|1\\rangle\\!\\langle 1|}.\n",
        "$$\n",
        "\n",
        "Nous préparons son état propre $|\\psi\\rangle=|1\\rangle$. Comme $|1\\rangle$ est un vecteur propre de $U(\\theta)$ avec la valeur propre $e^{i\\theta}$, la phase propre à estimer est :\n",
        "\n",
        "$$\n",
        "\\varphi = \\frac{\\theta}{2\\pi} \\pmod{1}\n",
        "$$\n",
        "\n",
        "Nous fixons $\\theta=\\tfrac{1}{6}\\cdot 2\\pi$, de sorte que la phase de vérité de base est $\\varphi=1/6$. Le circuit QPE met en œuvre les puissances contrôlées $U^{2^k}$ en appliquant des rotations de phase contrôlées avec des angles $\\theta\\cdot2^k$, puis applique la QFT inverse au registre de comptage et la mesure. Les chaînes de bits résultantes se concentrent autour de la représentation binaire de $1/6$.\n",
        "\n",
        "Le circuit utilise $m$ qubits de comptage (pour définir la précision de l'estimation) plus un qubit cible. Nous commençons par définir les éléments de base nécessaires à la mise en œuvre du QPE : la transformée de Fourier quantique (QFT) et son inverse, les fonctions utilitaires permettant de passer des fractions décimales aux fractions binaires de la phase propre, et les aides permettant de normaliser les comptes bruts en probabilités afin de comparer les résultats de la simulation et du matériel.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "4c5cd3d4",
      "metadata": {},
      "outputs": [],
      "source": [
        "def inverse_quantum_fourier_transform(quantum_circuit, number_of_qubits):\n",
        "    \"\"\"\n",
        "    Apply an inverse Quantum Fourier Transform the first `number_of_qubits` qubits in the\n",
        "    `quantum_circuit`.\n",
        "    \"\"\"\n",
        "    for qubit in range(number_of_qubits // 2):\n",
        "        quantum_circuit.swap(qubit, number_of_qubits - qubit - 1)\n",
        "    for j in range(number_of_qubits):\n",
        "        for m in range(j):\n",
        "            quantum_circuit.cp(-np.pi / float(2 ** (j - m)), m, j)\n",
        "        quantum_circuit.h(j)\n",
        "    return quantum_circuit"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "6d56f133",
      "metadata": {},
      "outputs": [],
      "source": [
        "def bitstring_count_to_probabilities(data, shot_count):\n",
        "    \"\"\"\n",
        "    This function turns an unsorted dictionary of bitstring counts into a sorted dictionary\n",
        "    of probabilities.\n",
        "    \"\"\"\n",
        "    # Turn the bitstring counts into probabilities.\n",
        "    probabilities = {\n",
        "        bitstring: bitstring_count / shot_count\n",
        "        for bitstring, bitstring_count in data.items()\n",
        "    }\n",
        "\n",
        "    sorted_probabilities = dict(\n",
        "        sorted(probabilities.items(), key=lambda x: x[1], reverse=True)\n",
        "    )\n",
        "\n",
        "    return sorted_probabilities"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d33b21fd",
      "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",
        "Nous construisons le circuit QPE en préparant les qubits de comptage en superposition, en appliquant des rotations de phase contrôlées pour coder la phase propre cible, et en terminant par une QFT inverse avant la mesure.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "79d62a79",
      "metadata": {},
      "outputs": [],
      "source": [
        "def quantum_phase_estimation_benchmark_circuit(\n",
        "    number_of_counting_qubits, phase\n",
        "):\n",
        "    \"\"\"\n",
        "    Create the circuit for quantum phase estimation.\n",
        "\n",
        "    Parameters\n",
        "    ----------\n",
        "    number_of_counting_qubits : The number of qubits in the circuit.\n",
        "    phase : The desired phase.\n",
        "\n",
        "    Returns\n",
        "    -------\n",
        "    QuantumCircuit\n",
        "        The quantum phase estimation circuit for `number_of_counting_qubits` qubits.\n",
        "    \"\"\"\n",
        "    qc = QuantumCircuit(\n",
        "        number_of_counting_qubits + 1, number_of_counting_qubits\n",
        "    )\n",
        "    target = number_of_counting_qubits\n",
        "\n",
        "    # |1> eigenstate for the single-qubit phase gate\n",
        "    qc.x(target)\n",
        "\n",
        "    # Hadamards on counting register\n",
        "    for q in range(number_of_counting_qubits):\n",
        "        qc.h(q)\n",
        "\n",
        "    # ONE controlled phase per counting qubit: cp(phase * 2**k)\n",
        "    for k in range(number_of_counting_qubits):\n",
        "        qc.cp(phase * (1 << k), k, target)\n",
        "\n",
        "    qc.barrier()\n",
        "\n",
        "    # Inverse QFT on counting register\n",
        "    inverse_quantum_fourier_transform(qc, number_of_counting_qubits)\n",
        "\n",
        "    qc.barrier()\n",
        "    for q in range(number_of_counting_qubits):\n",
        "        qc.measure(q, q)\n",
        "    return qc"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0e550ad5",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "## Étape 3 : Exécutez à l'aide d' Qiskit primitives\n",
        "\n",
        "Nous fixons le nombre de tirs et de qubits pour l'expérience et codons la phase cible $\\varphi = 1/6$ à l'aide de chiffres binaires $m$. Avec ces paramètres, nous construisons le circuit QPE qui sera exécuté sur la simulation, le matériel par défaut et les backends améliorés par Fire Opal.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "19e6dd45",
      "metadata": {},
      "outputs": [],
      "source": [
        "shot_count = 10000\n",
        "num_qubits = 35\n",
        "phase = (1 / 6) * 2 * np.pi\n",
        "circuits_quantum_phase_estimation = (\n",
        "    quantum_phase_estimation_benchmark_circuit(\n",
        "        number_of_counting_qubits=num_qubits, phase=phase\n",
        "    )\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6c53f759",
      "metadata": {},
      "source": [
        "<span id=\"run-mps-simulation\" />\n",
        "\n",
        "### Lancer la simulation MPS\n",
        "\n",
        "Tout d'abord, nous générons une distribution de référence à l'aide du simulateur `matrix_product_state` et convertissons les comptes en probabilités normalisées pour une comparaison ultérieure avec les résultats matériels.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "1fabc3a2",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Run the algorithm on the IBM Aer simulator.\n",
        "aer_simulator = AerSimulator(method=\"matrix_product_state\")\n",
        "\n",
        "# Transpile the circuits for the simulator.\n",
        "transpiled_circuits = qiskit.transpile(\n",
        "    circuits_quantum_phase_estimation, aer_simulator\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "99eca1f1",
      "metadata": {},
      "outputs": [],
      "source": [
        "simulated_result = (\n",
        "    aer_simulator.run(transpiled_circuits, shots=shot_count)\n",
        "    .result()\n",
        "    .get_counts()\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "263624e9",
      "metadata": {},
      "outputs": [],
      "source": [
        "simulated_result_probabilities = []\n",
        "\n",
        "simulated_result_probabilities.append(\n",
        "    bitstring_count_to_probabilities(\n",
        "        simulated_result,\n",
        "        shot_count=shot_count,\n",
        "    )\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "df203619",
      "metadata": {},
      "source": [
        "<span id=\"run-on-hardware\" />\n",
        "\n",
        "### Fonctionner sur du matériel\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "41bf548b",
      "metadata": {},
      "outputs": [],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(operational=True, simulator=False)\n",
        "\n",
        "pm = generate_preset_pass_manager(backend=backend, optimization_level=3)\n",
        "isa_circuits = pm.run(circuits_quantum_phase_estimation)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "b2bdb8b8",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Run the algorithm with IBM default.\n",
        "sampler = Sampler(backend)\n",
        "\n",
        "# Run all circuits using IBM Quantum Compute Service.\n",
        "ibm_default_job = sampler.run([isa_circuits], shots=shot_count)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "945967d0",
      "metadata": {},
      "source": [
        "<span id=\"run-on-hardware-with-fire-opal\" />\n",
        "\n",
        "### Fonctionne sur du matériel équipé de Fire Opal\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "ca892a3c",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Run the circuit using Sampler\n",
        "fire_opal_job = perf_mgmt.run(\n",
        "    primitive=\"sampler\",\n",
        "    pubs=[qasm2.dumps(circuits_quantum_phase_estimation)],\n",
        "    backend_name=backend.name,\n",
        "    options={\"default_shots\": shot_count},\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "000a119c",
      "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"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "dff71917",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Retrieve results.\n",
        "ibm_default_result = ibm_default_job.result()\n",
        "ibm_default_probabilities = []\n",
        "\n",
        "for idx, pub_result in enumerate(ibm_default_result):\n",
        "    ibm_default_probabilities.append(\n",
        "        bitstring_count_to_probabilities(\n",
        "            pub_result.data.c0.get_counts(),\n",
        "            shot_count=shot_count,\n",
        "        )\n",
        "    )"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "be4e50e5",
      "metadata": {},
      "outputs": [],
      "source": [
        "fire_opal_result = fire_opal_job.result()\n",
        "\n",
        "fire_opal_probabilities = []\n",
        "for idx, pub_result in enumerate(fire_opal_result):\n",
        "    fire_opal_probabilities.append(\n",
        "        bitstring_count_to_probabilities(\n",
        "            pub_result.data.c0.get_counts(),\n",
        "            shot_count=shot_count,\n",
        "        )\n",
        "    )"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "c0134366",
      "metadata": {},
      "outputs": [],
      "source": [
        "data = {\n",
        "    \"simulation\": simulated_result_probabilities,\n",
        "    \"default\": ibm_default_probabilities,\n",
        "    \"fire_opal\": fire_opal_probabilities,\n",
        "}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "a6dc6744",
      "metadata": {},
      "outputs": [],
      "source": [
        "def plot_distributions(\n",
        "    data,\n",
        "    number_of_counting_qubits,\n",
        "    top_k=None,\n",
        "    by=\"prob\",\n",
        "    shot_count=None,\n",
        "):\n",
        "    def nrm(d):\n",
        "        s = sum(d.values())\n",
        "        return {k: (v / s if s else 0.0) for k, v in d.items()}\n",
        "\n",
        "    def as_float(d):\n",
        "        return {k: float(v) for k, v in d.items()}\n",
        "\n",
        "    def to_space(d):\n",
        "        if by == \"prob\":\n",
        "            return nrm(as_float(d))\n",
        "        else:\n",
        "            if shot_count and 0.99 <= sum(d.values()) <= 1.01:\n",
        "                return {\n",
        "                    k: v * float(shot_count) for k, v in as_float(d).items()\n",
        "                }\n",
        "            else:\n",
        "                return as_float(d)\n",
        "\n",
        "    def topk(d, k):\n",
        "        items = sorted(d.items(), key=lambda kv: kv[1], reverse=True)\n",
        "        return items[: (k or len(d))]\n",
        "\n",
        "    phase = \"1/6\"\n",
        "\n",
        "    sim = to_space(data[\"simulation\"])\n",
        "    dft = to_space(data[\"default\"])\n",
        "    qct = to_space(data[\"fire_opal\"])\n",
        "\n",
        "    correct = max(sim, key=sim.get) if sim else None\n",
        "    print(\"Correct result:\", correct)\n",
        "\n",
        "    sim_items = topk(sim, top_k)\n",
        "    dft_items = topk(dft, top_k)\n",
        "    qct_items = topk(qct, top_k)\n",
        "\n",
        "    sim_keys, y_sim = zip(*sim_items) if sim_items else ([], [])\n",
        "    dft_keys, y_dft = zip(*dft_items) if dft_items else ([], [])\n",
        "    qct_keys, y_qct = zip(*qct_items) if qct_items else ([], [])\n",
        "\n",
        "    fig, axes = plt.subplots(3, 1, layout=\"constrained\")\n",
        "    ylab = \"Probabilities\"\n",
        "\n",
        "    def panel(ax, keys, ys, title, color):\n",
        "        x = np.arange(len(keys))\n",
        "        bars = ax.bar(x, ys, color=color)\n",
        "        ax.set_title(title)\n",
        "        ax.set_ylabel(ylab)\n",
        "        ax.set_xticks(x)\n",
        "        ax.set_xticklabels(keys, rotation=90)\n",
        "        ax.set_xlabel(\"Bitstrings\")\n",
        "        if correct in keys:\n",
        "            i = keys.index(correct)\n",
        "            bars[i].set_edgecolor(\"black\")\n",
        "            bars[i].set_linewidth(2)\n",
        "        return max(ys, default=0.0)\n",
        "\n",
        "    c_sim, c_dft, c_qct = (\n",
        "        qv.QCTRL_STYLE_COLORS[5],\n",
        "        qv.QCTRL_STYLE_COLORS[1],\n",
        "        qv.QCTRL_STYLE_COLORS[0],\n",
        "    )\n",
        "    m1 = panel(axes[0], list(sim_keys), list(y_sim), \"Simulation\", c_sim)\n",
        "    m2 = panel(axes[1], list(dft_keys), list(y_dft), \"Default\", c_dft)\n",
        "    m3 = panel(axes[2], list(qct_keys), list(y_qct), \"Q-CTRL\", c_qct)\n",
        "\n",
        "    for ax, m in zip(axes, (m1, m2, m3)):\n",
        "        ax.set_ylim(0, 1.05 * (m or 1.0))\n",
        "\n",
        "    for ax in axes:\n",
        "        ax.label_outer()\n",
        "    fig.suptitle(\n",
        "        rf\"{number_of_counting_qubits} counting qubits, $2\\pi\\varphi$={phase}\"\n",
        "    )\n",
        "    fig.set_size_inches(20, 10)\n",
        "    plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "593334d2",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Correct result: 00101010101010101010101010101010101\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/quantum-phase-estimation-qctrl/extracted-outputs/593334d2-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "experiment_index = 0\n",
        "phase_index = 0\n",
        "\n",
        "distributions = {\n",
        "    \"simulation\": data[\"simulation\"][phase_index],\n",
        "    \"default\": data[\"default\"][phase_index],\n",
        "    \"fire_opal\": data[\"fire_opal\"][phase_index],\n",
        "}\n",
        "\n",
        "plot_distributions(\n",
        "    distributions, num_qubits, top_k=100, by=\"prob\", shot_count=shot_count\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a5199fb3",
      "metadata": {},
      "source": [
        "La simulation établit la ligne de base pour la phase propre correcte. Les exécutions matérielles par défaut montrent un bruit qui obscurcit ce résultat, car le bruit répartit la probabilité sur de nombreuses chaînes de bits incorrectes. Avec la gestion des performances Q-CTRL, la distribution devient plus nette et le résultat correct est récupéré, ce qui permet un QPE fiable à cette échelle.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c72c6bb0",
      "metadata": {},
      "source": [
        "<span id=\"references\" />\n",
        "\n",
        "## Références\n",
        "\n",
        "\\[1] Lecture 7 : [Estimation de la phase et factorisation](/learning/courses/fundamentals-of-quantum-algorithms/phase-estimation-and-factoring/introduction). IBM Quantum Learning - Principes fondamentaux des algorithmes quantiques. Consulté le 3 octobre 2025.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4760c8b9",
      "metadata": {},
      "source": [
        "<span id=\"tutorial-survey\" />\n",
        "\n",
        "## Enquête tutorielle\n",
        "\n",
        "Veuillez prendre une minute pour nous faire part de vos commentaires sur ce tutoriel. 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_3BLFkNVEuh0QBWm)\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,
    "qpuSeconds": 40
  },
  "nbformat": 4,
  "nbformat_minor": 5
}