Commit fde117e3 authored by Nicolas Ollinger's avatar Nicolas Ollinger

2025-2026

parent bed9adfe
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"cells": [
{
"cell_type": "markdown",
"id": "14a84dc4",
"metadata": {},
"source": [
"# Programmation quantique\n",
"## Notebook #2. Problème de Bernstein-Vazirani\n",
"\n",
"Ce notebook est à compléter avec vos réponses puis à déposer sur la page Celene du cours __programmation quantique__ dans le dépôt aproprié avant le 19 novembre."
]
},
{
"cell_type": "markdown",
"id": "fb088050",
"metadata": {},
"source": [
"**À vous de jouer !** Nous avons étudié en détail le problème de Bernstein-Vazirani, en cours et en TD. Dans ce notebook vous allez reproduire ce problème, simuler la solution et illustrer la démonstration. Nous vous fournissons la trame, nous attendons de vous bien plus que du code : il s'agit d'expliquer, de commenter et de coder."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "064383e8",
"metadata": {},
"outputs": [],
"source": [
"from qiskit import QuantumCircuit, transpile\n",
"from qiskit.providers.basic_provider import BasicProvider\n",
"from qiskit.visualization import plot_histogram\n",
"from qiskit.quantum_info import Operator\n",
"\n",
"# Un simulateur de circuit complet\n",
"sim = BasicProvider().get_backend('basic_simulator')\n",
"\n",
"# Afficher un circuit\n",
"def draw_circ(circ): display(circ.draw(output='mpl'))\n",
" \n",
"def show(circ):\n",
" draw_circ(circ)\n",
" display(Operator(circ).draw('latex'))"
]
},
{
"cell_type": "markdown",
"id": "aa08816b",
"metadata": {},
"source": [
"## 1. Produit scalaire\n",
"\n",
"Dans le problème de Bernstein-Vazirani, l'entrée du problème est un circuit quantique, considéré comme une boîte noire, dont on sait qu'il calcule un produit scalaire :\n",
"$$\n",
"f(\\left|x_1 x_2 \\cdots x_n\\right>) = x_1 s_1 \\oplus x_2 s_2 \\oplus \\cdots \\oplus x_n s_n\n",
"$$\n",
"\n",
"La manière dont le circuit calcule cette fonction n'a pas d'importance. Il peut utiliser des qubits auxiliaire initialisés à 0 du moment qu'il les retourne à 0 après utilisation."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "a58eb407",
"metadata": {},
"outputs": [],
"source": [
"### TODO écrire 2 mises en œuvre du calcul du produit scalaire :\n",
"### 1. la version directe qui calcule en enchaînant les CNOT sur les x_i pour s_i=1\n",
"### 2. une version plus alambiquée qui utilise des bits auxiliaires et mélange un peu le calcul\n",
"### Vos fonctions doivent prendre en paramètre le vecteur s (sous forme de tableau) \n",
"### et retourner un QuantumCircuit dont les n premiers bits attendent l'entrée, le n+1 eme produit\n",
"### la sortie et les bits restant sont les auxiliaires."
]
},
{
"cell_type": "markdown",
"id": "ffdc1763",
"metadata": {},
"source": [
"Pensez à vérifier par quelques tests que vos fonctions font bien ce qui est attendu. Vous pouvez par exemple tirer quelques vecteurs au hasard et comparer ce que donne la simulation quiskit par rapport à un calcul direct du produit scalaire.\n",
"\n",
"Prenez le temps de faire quelque chose de joli !"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "8eedb3c5",
"metadata": {},
"outputs": [],
"source": [
"### TODO faire des tests convaincants :)"
]
},
{
"cell_type": "markdown",
"id": "d821e90d",
"metadata": {},
"source": [
"## 2. Algorithme de Bernstein-Vazirani\n",
"\n",
"Expliquez comment l'algorithme fonctionne en quelques phrases puis codez-le !"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "bcb4daba",
"metadata": {},
"outputs": [],
"source": [
"### TODO écrire une fonction qui prend en paramètre la taille n du vecteur, \n",
"### un circuit boîte noire dont les n premiers bits attendent l'entrée et \n",
"### le n+1 eme retourne la sortie (les autres bits sont les auxiliaires)\n",
"### Cette fonction doit retourner le circuit complet pour Bernstein-Vazirani\n",
"### Avec des barrières avant et après le circuit boîte noire."
]
},
{
"cell_type": "markdown",
"id": "13e2beff",
"metadata": {},
"source": [
"Montrez par simulation, avec un unique run, que le circuit construit est capable de retrouver le vecteur."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "4d8e3932",
"metadata": {},
"outputs": [],
"source": [
"### TODO démonstration par simulation ! Testez votre fonction sur vos deux mises en œuvre !"
]
},
{
"cell_type": "markdown",
"id": "7708214b",
"metadata": {},
"source": [
"## 3. Démonstration\n",
"\n",
"La démonstration combinatoire repose sur deux égalités entre circuits. Présentez ces deux égalités en construisant les circuits et en montrant avec `Operator` qu'ils sont bien égaux."
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "72872895",
"metadata": {},
"outputs": [],
"source": [
"### TODO les deux égalités entre circuits qui sont au cœur de la démonstration"
]
},
{
"cell_type": "markdown",
"id": "9af745b6",
"metadata": {},
"source": [
"Expliquez la démonstration en illustrant la transformation progressive de la version directe du circuit de produit scalaire. Idéalement on voudrait visualier cette transformation étape par étape sur un exemple de 4 à 5 qubits d'entrée."
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "82721b76",
"metadata": {},
"outputs": [],
"source": [
"### TODO démonstration combinatoire par transformation de circuit"
]
},
{
"cell_type": "markdown",
"id": "663a2a1e",
"metadata": {},
"source": [
"## 4. Exécution à distance (BONUS)\n",
"\n",
"Utilisez `qiskit_ibm_runtime` pour effectuer une exécution de l'algorithme à distance sur un vrai processeur quantique."
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "9f43fc90",
"metadata": {},
"outputs": [],
"source": [
"### TODO exécution sur le runtime IBM"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ca0d66ba",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "qiskitenv",
"language": "python",
"name": "qiskitenv"
},
"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.9.20"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
{
"cells": [
{
"cell_type": "markdown",
"id": "9930deb1",
"metadata": {},
"source": [
"# Programmation quantique\n",
"# Notebook #3. Algorithme de Grover\n",
"\n",
"Ce notebook est à compléter avec vos réponses puis à déposer sur la page Celene du cours __programmation quantique__ dans le dépôt aproprié avant le __4 décembre__."
]
},
{
"cell_type": "markdown",
"id": "cf788cc0",
"metadata": {},
"source": [
"L'objectif est de proposer une implémentation de l'algorithme de Grover, et d'étudier son comportement. Ne pas hésiter à vous appuyer sur les notes de cours."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2a0466e2",
"metadata": {},
"outputs": [],
"source": [
"from qiskit import QuantumCircuit, transpile\n",
"from qiskit.providers.basic_provider import BasicProvider\n",
"from qiskit.visualization import plot_histogram\n",
"from qiskit.quantum_info import Operator\n",
"import math\n",
"import random\n",
"\n",
"# Un simulateur de circuit complet\n",
"sim = BasicProvider().get_backend('basic_simulator')\n",
"\n",
"# Afficher un circuit\n",
"def draw_circ(circ): display(circ.draw(output='mpl'))\n",
" \n",
"def show(circ):\n",
" draw_circ(circ)\n",
" display(Operator(circ).draw('latex'))"
]
},
{
"cell_type": "markdown",
"id": "9d98df4e",
"metadata": {},
"source": [
"## 1. Fonction mistère. Le cas “simple”\n",
"\n",
"Rappelons que l'algorithme de Grover prend en entrée une fonction $f:\\{0,1\\}^n \\mapsto \\{0,1\\}$ et produit en sortie un vecteur booléen $x_1 \\in \\{0,1\\}^n$ tel que $f(x_1)=1$, si un tel vecteur existe.\n",
"\n",
"Commençons par le cas “simple”, où ce vecteur $x_1$ est unique. "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "94b3f95b",
"metadata": {},
"outputs": [],
"source": [
"### Construire le circuit de la fonction f telle que décrite ci-dessus.\n",
"### Votre fonction prend en entrée un vecteur booléen x1 de taille n et retourne \n",
"### le circuit U_f correspondant. Rappelons que f(x1)=1, et f(x)=0 pour tout x != x1.\n",
"\n",
"def fGrov(x1):\n",
" \"Construit un circuit à n+1 qubits qui simule une fonction f vallant 0 partout sauf en x1\"\n",
" pass"
]
},
{
"cell_type": "markdown",
"id": "26914209",
"metadata": {},
"source": [
"Pensez à tester votre fonction !"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1e90d267",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "ab6c61a1",
"metadata": {},
"source": [
"## 2. Opérateur de diffusion de Grover\n",
"\n",
"Rappelons que l'opérateur de diffusion de Grover, sur $n$ qubits, correspond à $H^{\\oplus n} Z_{\\operatorname{OR}} H^{\\oplus n}$, où $\\operatorname{OR}$ est la fonction booléenne OU sur $n$ bits.\n",
"\n",
"Néanmoins, lors de l'implémentation, on utilisera un circuit sur $n+1$ qubits, et le dernier qubit sera laissé en permanence dans l'état $|-\\rangle = \\frac{|0\\rangle - |1\\rangle}{\\sqrt{2}}$."
]
},
{
"cell_type": "markdown",
"id": "a8b28beb",
"metadata": {},
"source": [
"Commençons donc par le circuit de la fonction $\\operatorname{OR}$ sur $n$ bits. Il a été déjà vu lors du premier TP !"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "69230b37",
"metadata": {},
"outputs": [],
"source": [
"def nor_gate(n):\n",
" \"Construit un circuit à n+1 qubits qui simule n-OR avec des portes C^nX\"\n",
" pass"
]
},
{
"cell_type": "markdown",
"id": "d531c542",
"metadata": {},
"source": [
"Construire l'opérateur de diffusion de Grover. "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "203c34d1",
"metadata": {},
"outputs": [],
"source": [
"### Construire l'opérateur de diffusion de Grover sur n qubits,\n",
"### en utilisant un qubit auxiliaire qui sera maintenu en l'état |-).\n",
"\n",
"def GrovDiffOp(n):\n",
" # \"Construit l'opérateur de diffusion de Grover, H^{x n} Z_OR H^{x n} sur n qubits\n",
" # on garde néanmoins le n+1ème qubit qui restera à \\ket{-} tout le long du circuit\n",
" # On utilise donc directement le circuit U_OR et pas Z_OR\n",
"\n",
" pass"
]
},
{
"cell_type": "markdown",
"id": "00d44f7d",
"metadata": {},
"source": [
"Tester “visuellement” votre opérateur de Grover."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ff91b6ea",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "b80b9ff6",
"metadata": {},
"source": [
"## 3. Le circuit de Grover complet (toujours dans le cas “simple”)\n",
"\n",
"Construire ici le circuit de Grover complet. Votre fonction prendra en entrée la taille $n$ des vecteurs considérés en entrée, et le circuit de la fonction “mistère” $f$. \n",
"\n",
"Nous somes toujours sous l'hypothèse où $f$ a une seule entrée $x_1$ telle que $f(x_1)=1$.\n",
"\n",
"Pour mémoire, le circuit de Grover \n",
"- Sera formé de $n+1$ qubits, le dernier étant gardé en permanence à $|-\\rangle$, et de $n$ bits\n",
"- Sur les $n$ premiers qubits, on applique d'abord un mur de portes $H$,\n",
"- Ensuite on répète $t = \\lfloor \\frac{\\pi \\sqrt{2^n}}{4}\\rfloor$ fois l'enchaînement du circuit de $f$ et de l'opérateur de diffusion de Grover,\n",
"- ... et à la fin on mesure les $n$ premiers qubits."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9c3d39f5",
"metadata": {},
"outputs": [],
"source": [
"### Construire le circuit de Grover prenant en entrée n et le circuit cf de la fonction f\n",
"### Vous pouvez mettre en paramètre le nombre t d'itérations\n",
"\n",
"def GrovAlgo(n, cf, t=None):\n",
" # Le circuit de Grover\n",
" if t is None:\n",
" pass "
]
},
{
"cell_type": "markdown",
"id": "c9e1128b",
"metadata": {},
"source": [
"Afficher le circuit de Grover pour plusieurs dimensions $n$ possibles et plusieurs fonctions $f$ possibles."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "10f9b11f",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "1f6514fb",
"metadata": {},
"source": [
"## 4. Test du circuit de Grover et compilation source à source\n",
"\n",
"Vous pouvez déjà tester le circuit de Grover... si l'entrée est de dimension $n \\leq 2$. En revanche, pour $n \\geq 3$, le “simulateur basique” (sim, obtenu avec la fonction BasicProvider) ne fonctionnera pas sur des portes mcx ayant 3 qubits de contrôle ou plus. \n",
"\n",
"Vous devrez “transpiler” votre circuit, ce qui revient à le transformer en un circuit équivalent avec des portes plus simples, et c'est ce dernier qui pourra être simulé."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d9ad24fc",
"metadata": {},
"outputs": [],
"source": [
"# Warning : le simulateur \"basique\" n'accepte pas les portes \"complexes\" \n",
"# telles que les portes mcx avec trois qubits de contrôle\n",
"# du coup, on procède à une \"compilation source à source\" (en anglais : transpile)\n",
"# qui va transformer le circuit en un circuit similaire, mais avec des portes plus basiques,\n",
"# à l'instar du travail du 1er TP\n",
"\n",
"\n",
"# Exemple de \"transpile\" d'un circuit cGrov en circuit simplifié cGrov_t\n",
"#### cGrov_t = transpile(cGrov, backend = sim)\n",
"\n",
"# c'est donc sur ce dernier circuit \"simplifié\" que l'on fait la simulation\n",
"#### res = sim.run(cGrov_t, shots=1000).result()\n",
"\n",
"# pour analyser les réponses les réponses et récupérer la sortie la plus fréquents\n",
"#### counts = res.get_counts(cGrov_t)\n",
"#### most_frequent_result = max(counts, key=counts.get)"
]
},
{
"cell_type": "markdown",
"id": "64c2719a",
"metadata": {},
"source": [
"Vérifier empiriquement l'affirmation faite en cours : l'algorithme de Grover pour les fonctions “simples” (c-à-d avec une seule entrée qui donne 1, toutes les autres donnant 0) trouve la bonne réponse avec une probabilité d'au moins $1-\\frac{1}{2^n}$. "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "94d20081",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "c538e074",
"metadata": {},
"source": [
"## 5. Algorithme de Grover : cas général\n",
"\n",
"Jusqu'à présent, nous avons vu l'algorithme de Grover dans le cas particulier où la fonction $f$ en entrée avait exactement une solution. Cependant, l'algorithme de Grover peut être généralisé pour fonctionner avec une fonction quelconque $f : \\{0,1\\}^n \\to \\{0,1\\}$. \n",
"\n",
"Le nouvel objectif est de traiter maintenant ce cas général. Cette fois, nous allons faire un vrai mélange de code “classique” et ”quantique”, assez représentatif de ce qui se passe dans la pratique : des algorithmes classiques font un certain nombre d'appels à du calcul quantique. "
]
},
{
"cell_type": "markdown",
"id": "1a3f3f86",
"metadata": {},
"source": [
"Rappelons brièvement l'algorithme de Grover dans le cas général.\n",
"\n",
"**A. Description d'une étape**\n",
"\n",
"- choisir le nombre $t$ d'itérations uniformément au hasard parmi $\\{1,2,\\dots, \\lfloor \\pi \\sqrt{2^n} /4 \\rfloor\\}$.\n",
"- construire le circuit de Grover à partir du circuit de la fonction $f$, en faisant $t$ répétitions de l'opérateur $Z_f H^{\\otimes n} Z_{\\operatorname{OR}} H^{\\otimes n}$.\n",
"\n",
"**B. L'algorithme, avec les mains**\n",
"\n",
"Rappelons que, si la fonction $f$ a au moins une entrée $x_1$ telle que $f(x_1) = 1$, le circuit du point A trouvera un tel $x_1$ avec une probabilité d'au moins 40%. En faisant le nombre suffisant d'étapes et le traitement adéquat, adaptez l'algorithme afin d'obtenir le bon résultat avec probabilité au moins $1 - \\frac{1}{2^n}$.\n",
"\n",
"Ce procédé s'appelle **amplification**, il est très utilisé pour les algorithmes probabilistes. \n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "10fc7527",
"metadata": {},
"outputs": [],
"source": [
"### Proposer une fonction qui prend en entrée un circuit circ se terminant par une mesure \n",
"### et qui rend la mesure la plus fréquente, après simulation, sous la forme d'un tableau de 0/1.\n",
"### En plus du circuit, on ajoute deux paramètres optionnels, nb_shots avec le nombre de runs et le booléen \n",
"### transp indiquant si l'on procède à un \"transpile\" du circuit d'origine\n",
"\n",
"def mesure_majoritaire(circ,nb_shots=1,transp=True):\n",
" pass\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fa008b2e",
"metadata": {},
"outputs": [],
"source": [
"### Algorithme de Grover prenant en entrée la dimension n le circuit cf d'une fonction f quelconque à n bits \n",
"### en entrée et 1 bit en sortie\n",
"### C'est un algorithme \"mixte\", faisant appel à un circuit quantique, mais également un post-traitement\n",
"\n",
"def Grov(n,cf): \n",
" # répéter suffisament de fois ; bien choisir le nombre de répétitions\n",
" \n",
" pass"
]
},
{
"cell_type": "markdown",
"id": "466793cd",
"metadata": {},
"source": [
"Testez votre algorithme sur différentes fonctions $f$. Ne pas oublier d'inclure la fonction $f_0:\\{0,1\\}^n \\to \\{0,1\\}$, définie par $f_0(x)=0$, pour toute entrée $x$."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f24941ee",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "bf0b9249",
"metadata": {},
"source": [
"## 6. BONUS : boucler la boucle\n",
"\n",
"Au fait, pourquoi utiliser la fonction *transpile* de *qiskit*, alors que vous avez déjà travaillé sur les portes *mcx* lors du premier TP ?\n",
"\n",
"Adapter la construction du circuit de Grover de sorte à **ne pas utiliser** de porte *mcx* avec plus de 2 qubits de contrôle. Pour ce faire, utilisez les exercices du premier notebook. Vérifiez que tous vos tests antérieurs continuent à fonctionner avec le nouveau circuit, sans utiliser la fonction *transpile*. "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "10e86247",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.10.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
@font-face {
font-family: "AlegreyaSC";
src: url("fonts/AlegreyaSC-Regular.ttf") format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: "AlegreyaSC";
src: url("fonts/AlegreyaSC-Bold.ttf") format('truetype');
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: "AlegreyaSC";
src: url("fonts/AlegreyaSC-BoldItalic.ttf") format('truetype');
font-weight: bold;
font-style: italic;
}
@font-face {
font-family: "AlegreyaSC";
src: url("fonts/AlegreyaSC-Italic.ttf") format('truetype');
font-weight: normal;
font-style: italic;
}
@font-face {
font-family: "Alegreya";
src: url("fonts/Alegreya-Regular.ttf") format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: "Alegreya";
src: url("fonts/Alegreya-SemiBold.ttf") format('truetype');
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: "Alegreya";
src: url("fonts/Alegreya-SemiBoldItalic.ttf") format('truetype');
font-weight: bold;
font-style: italic;
}
@font-face {
font-family: "Alegreya";
src: url("fonts/Alegreya-Italic.ttf") format('truetype');
font-weight: normal;
font-style: italic;
}
.reveal {
font-family: Alegreya;
font-weight: normal;
font-style: normal;
}
.reveal h1 {
font-family: AlegreyaSC;
font-weight: bold;
color: #e07;
text-transform: none;
}
.cool {
color: #e07;
}
.reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 {
font-family: AlegreyaSC;
font-weight: bold;
text-transform: none;
}
.reveal p {
font-weight: normal;
}
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment