{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "ce3d197d-7b14-4c60-8d39-a202146d0663",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Entrées et sorties de l'exécuteur\"\n",
        "description: \"Comprendre les entrées et les sorties de la primitive Executor.\"\n",
        "---\n",
        "\n",
        "<span id=\"executor-inputs-and-outputs\" />\n",
        "\n",
        "# Entrées et sorties de l'exécuteur\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "44219323-9419-471a-86c4-488ef0162420",
      "metadata": {
        "tags": [
          "version-info"
        ]
      },
      "source": [
        "{/*\n",
        "  DO NOT EDIT THIS CELL!!!\n",
        "  This cell's content is generated automatically by a script. Anything you add\n",
        "  here will be removed next time the notebook is run. To add new content, create\n",
        "  a new cell before or after this one.\n",
        "  */}\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Versions de package\">\n",
        "    Le code présenté sur cette page a été développé en tenant compte des exigences suivantes.\n",
        "    Nous vous recommandons d'utiliser ces versions ou des versions plus récentes.\n",
        "\n",
        "    ```\n",
        "    qiskit[all]~=2.4.0\n",
        "    qiskit-ibm-runtime~=0.46.1\n",
        "    samplomatic~=0.18.0\n",
        "    ```\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "43c044dc-259d-4759-b6a7-a05bd9d3667f",
      "metadata": {},
      "source": [
        "La primitive Executor fait partie du [modèle d'exécution dirigée](/docs/guides/directed-execution-model), qui offre davantage de souplesse lors de la personnalisation d'un workflow de gestion des erreurs.\n",
        "\n",
        "Les entrées et les sorties de la primitive Executor sont très différentes de celles des primitives [Sampler](/docs/guides/sampler-input-output) et [Estimator](/docs/guides/estimator-input-output). Par exemple, au lieu de prendre une liste de PUB en entrée, Executor accepte un `QuantumProgram`, qui contient une liste `QuantumProgramItem` d'objets. Ces classes de conteneurs vous offrent davantage de flexibilité qu'un « PUB », qui est une simple structure de données de type tuple.\n",
        "\n",
        "La sortie de l'exécuteur est un `QuantumProgramResult`, qui est un objet itérable et contient un élément pour chaque entrée `QuantumProgramItem`.\n",
        "\n",
        "<span id=\"programs\" />\n",
        "\n",
        "<span id=\"inputs-quantum-programs\" />\n",
        "\n",
        "## Données d'entrée : programmes Quantum\n",
        "\n",
        "Comme indiqué précédemment, l'entrée d'une primitive Executor est un [`QuantumProgram`](/docs/api/qiskit-ibm-runtime/quantum-program-quantum-program), qui est un ensemble\n",
        "[`QuantumProgramItem`](/docs/api/qiskit-ibm-runtime/quantum-program-quantum-program-item) itérable d'objets.  Ces objets peuvent être de deux types :\n",
        "\n",
        "* `CircuitItem`, qui contient généralement un circuit et les valeurs de ses paramètres (le cas échéant).\n",
        "* `SamplexItem`, qui contient généralement les éléments suivants :\n",
        "  * Un circuit type\n",
        "  * Un objet « samplex », qui sert à générer des ensembles aléatoires de paramètres lors de l'exécution (par exemple pour effectuer un « twirling » ou injecter du bruit)\n",
        "  * Arguments du samplex, qui peuvent inclure les valeurs des paramètres du circuit d'origine\n",
        "\n",
        "Chacun de ces éléments correspond à une tâche distincte que l'Executor doit accomplir.\n",
        "\n",
        "<span id=\"before-you-begin\" />\n",
        "\n",
        "### Avant de commencer\n",
        "\n",
        "Certains des exemples de code présentés sur cette page utilisent `samplex`, qui fait partie du package Samplomatic.  Par conséquent, avant d'exécuter ces blocs de code, vous devez installer Samplomatic, comme indiqué dans le bloc de code suivant.  Pour plus d'informations, consultez la [documentation de Samplomatic](https://qiskit.github.io/samplomatic).\n",
        "\n",
        "```python\n",
        "pip install samplomatic\n",
        "\n",
        "# For visualization support, include the visualization dependencies.\n",
        "# pip install samplomatic[vis]\n",
        "```\n",
        "\n",
        "<span id=\"example-create-a-quantumprogram-with-two-different-tasks\" />\n",
        "\n",
        "### Exemple : Créer un `QuantumProgram` comprenant deux tâches différentes\n",
        "\n",
        "Commencez par initialiser votre programme quantique, puis ajoutez-y des éléments à l'aide de `append_circuit_item` ou `append_samplex_item` (si un samplex est présent), comme le montrent les exemples suivants.\n",
        "\n",
        "La cellule suivante initialise un `QuantumProgram` et précise qu'il doit effectuer 1 024 essais pour chaque configuration de chaque élément du programme.\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  Contrairement à Sampler, un `QuantumProgram` ne prend en charge qu'une seule valeur de prise de vue. `QuantumProgram`Si vous souhaitez une valeur de prise différente, vous devez créer un fichier distinct, ce qui constituerait une tâche à part entière.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "81fc7c9e-2cc9-416a-b3d5-74f45eab48fc",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "from qiskit_ibm_runtime.quantum_program import QuantumProgram\n",
        "from qiskit_ibm_runtime import Executor, QiskitRuntimeService\n",
        "from qiskit.circuit import Parameter, QuantumCircuit\n",
        "import numpy as np\n",
        "from samplomatic import build\n",
        "from samplomatic.transpiler import generate_boxing_pass_manager\n",
        "\n",
        "# Initialize an empty program\n",
        "program = QuantumProgram(shots=1024)\n",
        "\n",
        "# Initialize and transpile a 3-qubit quantum circuit with 2 parameters.\n",
        "circuit = QuantumCircuit(3)\n",
        "circuit.h(0)\n",
        "circuit.cx(0, 1)\n",
        "circuit.cx(1, 2)\n",
        "circuit.rz(Parameter(\"theta\"), 0)\n",
        "circuit.rz(Parameter(\"phi\"), 1)\n",
        "\n",
        "# `measure_all` adds a 3-bit classical register named \"meas\"\n",
        "circuit.measure_all()\n",
        "\n",
        "# Choose the least busy backend\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(operational=True, simulator=False)\n",
        "\n",
        "# Generate a preset pass manager\n",
        "# This will be used to convert the abstract circuit to an\n",
        "# equivalent Instruction Set Architecture (ISA) circuit.\n",
        "preset_pass_manager = generate_preset_pass_manager(\n",
        "    backend=backend, optimization_level=0\n",
        ")\n",
        "\n",
        "# Transpile the circuit\n",
        "isa_circuit = preset_pass_manager.run(circuit)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a1cc6580-3f31-4322-b0ea-f6bcf032a872",
      "metadata": {},
      "source": [
        "<span id=\"append-a-circuititem\" />\n",
        "\n",
        "### Ajouter un `CircuitItem`\n",
        "\n",
        "Ajoutez ensuite le circuit cible, qui a été transcompilé en fonction de l'architecture du jeu d'instructions (ISA) du backend, au fichier `QuantumProgram`. Comme ce circuit comporte deux paramètres, nous devons également indiquer leurs valeurs (10 paires dans cet exemple). L'exécution de cette commande `CircuitItem` est la première tâche que le programme va effectuer.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "e6f6762f-1627-450a-b74b-96ad4a3b1c9c",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Append the transpiled circuit and an array\n",
        "# containing 10 sets of parameter values to the program\n",
        "program.append_circuit_item(\n",
        "    isa_circuit,\n",
        "    circuit_arguments=np.random.rand(\n",
        "        10, 2\n",
        "    ),  # 10 sets of parameter values and 2 parameters\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "96f9ac43-8ce4-4389-bff4-5bd68b799974",
      "metadata": {},
      "source": [
        "<span id=\"append-a-samplexitem\" />\n",
        "\n",
        "### Ajouter un `SamplexItem`\n",
        "\n",
        "Les éléments du circuit sont exécutés sans aucune forme d'aléatoire. Au contraire, les éléments Samplex vous permettent de définir comment randomiser leur contenu. La cellule suivante utilise la `generate_boxing_pass_manager()` fonction pour regrouper les portes et les mesures du circuit dans des encadrés et ajouter une annotation tournoyante à chaque encadré. Il génère ensuite un circuit modèle et une paire de samplex à l'aide de la `build()` fonction.\n",
        "\n",
        "L'exécution de cette tâche `SamplexItem` est la deuxième opération que le programme va effectuer.\n",
        "\n",
        "Consultez la documentation [de l'API](https://github.com/Qiskit/samplomatic/) Samplomatic pour obtenir tous les détails concernant `samplex` et ses arguments. Consultez le [guide](https://qiskit.github.io/samplomatic/guides/transpiler.html) du Samplomatic Transpiler pour en savoir plus sur l'utilisation de la `generate_boxing_pass_manager()` fonction.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "4ac19f46-226b-4b13-b278-1e39e719484b",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "TensorInterface(<\n",
            "  - 'parameter_values' <float64[2]>: Input parameter values to use during sampling.\n",
            ">)\n"
          ]
        }
      ],
      "source": [
        "# Transpile the circuit, additionally grouping gates and measurements into annotated boxes\n",
        "preset_pass_manager = generate_preset_pass_manager(\n",
        "    backend=backend, optimization_level=0\n",
        ")\n",
        "\n",
        "# Use the boxing pass manager to group gates\n",
        "# and measurements into boxes and add\n",
        "# a`Twirl` annotation.\n",
        "preset_pass_manager.post_scheduling = generate_boxing_pass_manager(\n",
        "    # Add gate twirling\n",
        "    enable_gates=True,\n",
        "    # Add measurement twirling\n",
        "    enable_measures=True,\n",
        ")\n",
        "boxed_circuit = preset_pass_manager.run(circuit)\n",
        "\n",
        "# Build the template circuit and the samplex.  The template circuit has parametric gates\n",
        "# without fixed values and the samplex randomly generates the parameter\n",
        "# values on the server side at runtime to perform twirling.\n",
        "template_circuit, samplex = build(boxed_circuit)\n",
        "\n",
        "# Determine what arguments are required by the samplex.\n",
        "# Input the arguments in samplex_arguments.\n",
        "print(samplex.inputs())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "0e3b4d3d-12ea-47f2-a605-b64eed03cf95",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Append the template circuit and samplex as a samplex item\n",
        "program.append_samplex_item(\n",
        "    template_circuit,\n",
        "    samplex=samplex,\n",
        "    samplex_arguments={\n",
        "        # the arguments required by the samplex.sample method\n",
        "        \"parameter_values\": np.random.rand(10, 2),\n",
        "    },\n",
        "    shape=(28, 10),  # 28 randomizations and 10 sets of parameter values\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "eb850401-3d4f-4fac-8965-fb9ac4827ebd",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Initialize an Executor with the default options\n",
        "executor = Executor(mode=backend)\n",
        "\n",
        "# Submit the job\n",
        "job = executor.run(program)\n",
        "\n",
        "# Retrieve the result\n",
        "result = job.result()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "541f5d2d-b974-4ae5-81e0-ef2b6e1dbe82",
      "metadata": {},
      "source": [
        "<span id=\"outputs\" />\n",
        "\n",
        "## Sorties\n",
        "\n",
        "La sortie de l'exécuteur est un [`QuantumProgramResult`](/docs/api/qiskit-ibm-runtime/results-quantum-program-result), qui est un objet itérable. Il contient une entrée par entrée `QuantumProgramItem` , dans le même ordre que les éléments d'entrée. Chacun de ces éléments de sortie est un dictionnaire dont les clés sont des chaînes de caractères correspondant aux noms des registres classiques des circuits d'entrée (entre autres); vous n'avez donc plus besoin de mémoriser ces noms comme vous le faisiez avec la sortie Sampler. Les valeurs du dictionnaire sont de type `np.ndarray`.\n",
        "\n",
        "Le résultat de l'exemple précédent comprend les éléments suivants :\n",
        "\n",
        "<span id=\"circuititem-result\" />\n",
        "\n",
        "### `CircuitItem` résultat\n",
        "\n",
        "`CircuitItem`Le premier élément contient les résultats de l'exécution de la première tâche (a) du programme. Elle contient une seule clé, `meas`, qui correspond au nom du registre classique dans le circuit d'entrée. La valeur de cette clé correspond à un tableau `np.ndarray` de forme `(parameter sets, shots, register bits)`, qui est (10, 1024, 3) dans l'exemple ci-dessus.\n",
        "\n",
        "Le code suivant montre comment accéder à ces informations :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "d652d69a-7a4e-4e5c-8469-97cb9ec92010",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Result shape: (10, 1024, 3)\n"
          ]
        }
      ],
      "source": [
        "# Access the results of the classical register of task #0, a CircuitItem\n",
        "result_0 = result[0][\"meas\"]\n",
        "print(f\"Result shape: {result_0.shape}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7185353a-e7a0-4bb2-b605-664184e684ed",
      "metadata": {},
      "source": [
        "<span id=\"samplexitem-result\" />\n",
        "\n",
        "### `SamplexItem` résultat\n",
        "\n",
        "Le deuxième élément contient les résultats de l'exécution de la deuxième tâche (a `SamplexItem`) du programme. Cet article contient plusieurs clés. La `meas` clé, qui correspond au nom du registre classique du circuit d'entrée, est mise en correspondance avec le tableau de résultats de ce registre. Ce tableau a la forme `(randomizations, parameter sets, shots, classical bits)`, soit (28, 10, 1024, 3) dans cet exemple. De plus, la sortie contient une `measurement_flips.meas` clé qui correspond aux corrections par inversion de bits permettant d'annuler la distorsion de mesure pour le `meas` registre.  Dans notre exemple, cette matrice de sortie sera de forme (28, 10, 1, 3), car un seul passage suffit pour effectuer l'inversion de bits.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "884af6e2-614a-4b18-83dc-04505ddbc488",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Result shape: (28, 10, 1024, 3)\n",
            "Bit-flip corrections shape: (28, 10, 1, 3)\n"
          ]
        }
      ],
      "source": [
        "# Access the results of the classical register of task #1\n",
        "result_1 = result[1][\"meas\"]\n",
        "print(f\"Result shape: {result_1.shape}\")\n",
        "\n",
        "# Access the bit-flip corrections\n",
        "flips_1 = result[1][\"measurement_flips.meas\"]\n",
        "print(f\"Bit-flip corrections shape: {flips_1.shape}\")\n",
        "\n",
        "# Undo the bit flips via classical XOR\n",
        "unflipped_result_1 = result_1 ^ flips_1"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bb0c9225-9a52-4d94-b75d-6ae408f4e029",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Etapes suivantes\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recommandations\">\n",
        "  * Découvrez [des exemples](/docs/guides/executor-examples) d'utilisation d'Executor.\n",
        "  * Découvrez le [modèle d'exécution dirigée](/docs/guides/directed-execution-model).\n",
        "  * Comprendre [la diffusion par exécuteur](/docs/guides/executor-broadcasting).\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 4
}