"## 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."
"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",
"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 ! Tester 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."
"**Your turn!** We studied the Bernstein–Vazirani problem in detail during the lecture. In this lab, you are expected to do more than just provide code: explain, comment, and implement.\n"
"The circuit may use ancillary qubits initialised to 0, as long as it returns them to 0 after use.\n",
"\n",
"Show by simulation, with a single run, that the circuit you build is able to recover the vector.\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "a58eb407",
"metadata": {},
"outputs": [],
"source": [
"### TODO write 2 implementations of the inner-product computation:\n",
"### 1. the direct version that computes by chaining CNOTs on the x_i where s_i=1\n",
"### 2. a more convoluted version that uses ancilla bits and mixes the computation a bit\n",
"### Your functions must take as parameter the vector s (as an array) \n",
"### and return a QuantumCircuit where the first n bits expect the input, the (n+1)-th produces\n",
"### the output, and the remaining bits are ancillas.\n"
]
},
{
"cell_type": "markdown",
"id": "ffdc1763",
"metadata": {},
"source": [
"Remember to check with a few tests that your functions indeed compute the expected inner product. You might for example pick a few vectors at random and compare the qiskit simulation to a direct inner product computation.\n",
"\n",
"Take the time to make something neat!\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "8eedb3c5",
"metadata": {},
"outputs": [],
"source": [
"### TODO run convincing tests :)\n"
]
},
{
"cell_type": "markdown",
"id": "d821e90d",
"metadata": {},
"source": [
"## 2. The Bernstein–Vazirani algorithm\n",
"\n",
"Explain how the algorithm works in a few sentences, then implement it!\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "bcb4daba",
"metadata": {},
"outputs": [],
"source": [
"### TODO write a function that takes as parameter the size n of the vector, \n",
"### a black-box circuit where the first n bits expect the input and \n",
"### the (n+1)-th returns the output (the other bits are ancillas)\n",
"### This function must return the full circuit for Bernstein–Vazirani\n",
"### With barriers before and after the black-box circuit.\n"
]
},
{
"cell_type": "markdown",
"id": "13e2beff",
"metadata": {},
"source": [
"Show by simulation, with a single run, that the constructed circuit can recover the vector.\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "4d8e3932",
"metadata": {},
"outputs": [],
"source": [
"### TODO proof by simulation! Test your function on your two implementations!\n"
]
},
{
"cell_type": "markdown",
"id": "7708214b",
"metadata": {},
"source": [
"## 3. Proof\n",
"\n",
"The combinatorial proof relies on two identities between circuits. Reproduce these identities by building the corresponding circuits and showing with `Operator` that they are indeed equal.\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "72872895",
"metadata": {},
"outputs": [],
"source": [
"### TODO the two circuit identities at the heart of the proof\n"
]
},
{
"cell_type": "markdown",
"id": "9af745b6",
"metadata": {},
"source": [
"Explain the proof by illustrating the step-by-step circuit transformation on an example with 4 or 5 input qubits.\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "82721b76",
"metadata": {},
"outputs": [],
"source": [
"### TODO combinatorial proof by circuit transformation\n"
]
},
{
"cell_type": "markdown",
"id": "663a2a1e",
"metadata": {},
"source": [
"## 4. Remote execution (BONUS)\n",
"\n",
"Use `qiskit_ibm_runtime` to run the algorithm remotely on a real quantum processor.\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 __12 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."
"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 “mystère” $f$. \n",
"\n",
"Nous sommes 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",
"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",
"### 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*. "
"The goal is to propose an implementation of Grover's algorithm and to study its behaviour experimentally. Explain, comment, and justify your design choices and your observations. Feel free to rely on the lecture notes.\n"
"Recall that Grover's algorithm takes as input a Boolean function (an oracle) $f : \\{0,1\\}^n \\to \\{0,1\\}$ and aims to find a vector $x_1 \\in \\{0,1\\}^n$ such that $f(x_1)=1$, if such a vector exists.\n",
"\n",
"We start with the “simple” case where this vector $x_1$ is unique.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "94b3f95b",
"metadata": {},
"outputs": [],
"source": [
"### Build the circuit of the function f as described above.\n",
"### Your function takes as input a Boolean vector x1 of size n and returns \n",
"### the corresponding circuit U_f. Remember that f(x1)=1, and f(x)=0 for all x != x1.\n",
"\n",
"def fGrov(x1):\n",
" \"Builds a circuit on n+1 qubits that simulates a function f equal to 0 everywhere except at x1\"\n",
" pass\n"
]
},
{
"cell_type": "markdown",
"id": "26914209",
"metadata": {},
"source": [
"Remember to test your function!\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1e90d267",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "ab6c61a1",
"metadata": {},
"source": [
"## 2. Grover's diffusion operator\n",
"\n",
"Recall that Grover's diffusion operator, on $n$ qubits, corresponds to $H^{\\oplus n} Z_{\\operatorname{OR}} H^{\\oplus n}$, where $\\operatorname{OR}$ is the Boolean OR function on $n$ bits.\n",
"\n",
"However, during implementation, a circuit on $n+1$ qubits will be used, and the last qubit will be left permanently in the state $|-\\rangle = \\frac{|0\\rangle - |1\\rangle}{\\sqrt{2}}$."
]
},
{
"cell_type": "markdown",
"id": "a8b28beb",
"metadata": {},
"source": [
"Let us begin with the circuit for the $\\operatorname{OR}$ function on $n$ bits. It already appeared in the first lab!\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "69230b37",
"metadata": {},
"outputs": [],
"source": [
"def nor_gate(n):\n",
" \"Builds a circuit on n+1 qubits that simulates n-OR using C^nX gates\"\n",
" pass\n"
]
},
{
"cell_type": "markdown",
"id": "d531c542",
"metadata": {},
"source": [
"Construct Grover's diffusion operator.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "203c34d1",
"metadata": {},
"outputs": [],
"source": [
"### Build Grover's diffusion operator on n qubits,\n",
"### using an ancilla qubit that will be kept in the state |-).\n",
"\n",
"def GrovDiffOp(n):\n",
" # Builds the Grover diffusion operator $H^{\\otimes n} Z_{\\mathrm{OR}} H^{\\otimes n}$ on n qubits\n",
" # we still keep the (n+1)-th qubit, which remains at \\ket{-} throughout the circuit\n",
" # We therefore use the U_OR circuit directly and not Z_OR\n",
"\n",
" pass\n"
]
},
{
"cell_type": "markdown",
"id": "00d44f7d",
"metadata": {},
"source": [
"“Visually” test your Grover operator.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ff91b6ea",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "b80b9ff6",
"metadata": {},
"source": [
"## 3. The complete Grover circuit (still in the “simple” case)\n",
"\n",
"Build the complete Grover circuit here. Your function will take as input the size $n$ of the vectors considered as input, and the circuit of the “mystery” function $f$. \n",
"\n",
"We are still assuming that $f$ has a single input $x_1$ such that $f(x_1)=1$.\n",
"\n",
"For the record, Grover's circuit\n",
"- Will consist of $n+1$ qubits, the last one being kept permanently at $|-\\rangle$, and $n$ bits\n",
"- On the first $n$ qubits, we first apply a wall of gates $H$,\n",
"- Then we repeat $t = \\lfloor \\frac{\\pi \\sqrt{2^n}}{4}\\rfloor$ times the sequence of the circuit of $f$ and Grover's diffusion operator,\n",
"- ... and at the end we measure the first $n$ qubits."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9c3d39f5",
"metadata": {},
"outputs": [],
"source": [
"### Build the Grover circuit taking as input n and the circuit cf of the function f\n",
"### You may include as a parameter the number t of iterations\n",
"\n",
"def GrovAlgo(n, cf, t=None):\n",
" # The Grover circuit\n",
" if t is None:\n",
" pass \n"
]
},
{
"cell_type": "markdown",
"id": "c9e1128b",
"metadata": {},
"source": [
"Display the Grover circuit for several values of $n$ and several functions $f$.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "10f9b11f",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "1f6514fb",
"metadata": {},
"source": [
"## 4. Testing Grover's circuit and source-to-source compilation\n",
"\n",
"You can already test Grover's circuit... if the input is of dimension $n \\leq 2$. However, for $n \\geq 3$, the “basic simulator” (sim, obtained with the BasicProvider function) will not work on mcx gates with 3 or more control qubits. \n",
"\n",
"You will need to “transpile” your circuit, which means transforming it into an equivalent circuit with simpler gates, and it is this latter circuit that can be simulated."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d9ad24fc",
"metadata": {},
"outputs": [],
"source": [
"# Warning: the \"basic\" simulator does not accept \"complex\" gates \n",
"# such as mcx gates with three control qubits\n",
"# as a result, we perform a \"source-to-source compilation\" (aka transpiling)\n",
"# which will transform the circuit into a similar one but with more basic gates,\n",
"# similar to the work done in the first lab\n",
"\n",
"\n",
"# Example of a \"transpile\" of a circuit cGrov into a simplified circuit cGrov_t\n",
"Empirically verify the statement made in class: Grover's algorithm for “simple” functions (i.e., with a single input that returns 1, all others returning 0) finds the correct answer with a probability of at least $1-\\frac{1}{2^n}$."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "94d20081",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "c538e074",
"metadata": {},
"source": [
"## 5. Grover's algorithm: general case\n",
"\n",
"Up to now, we studied Grover's algorithm in the case where there is a single marked input $x_1$. We now wish to handle the general case, where a function $f : \\{0,1\\}^n \\to \\{0,1\\}$ may admit an arbitrary number $M\\ge 1$ of solutions.\n",
"\n",
"The new objective is to treat this general case. This will lead us to a “hybrid” algorithm in which classical steps make a certain number of calls to quantum computation.\n"
]
},
{
"cell_type": "markdown",
"id": "1a3f3f86",
"metadata": {},
"source": [
"Let us briefly recall Grover's algorithm in the general case.\n",
"\n",
"**A. Description of a step**\n",
"\n",
"- Choose the number $t$ of iterations uniformly at random from $\\{1,2,\\dots, \\lfloor \\pi \\sqrt{2^n} /4 \\rfloor\\}$.\n",
"- Construct Grover's circuit from the circuit of the function $f$, performing $t$ repetitions of the operator $Z_f H^{\\otimes n} Z_{\\operatorname{OR}} H^{\\otimes n}$.\n",
"\n",
"**B. The algorithm, by hands**\n",
"\n",
"Recall that if the function $f$ has at least one input $x_1$ such that $f(x_1) = 1$, the circuit in point A will find such an $x_1$ with a probability of at least 40%. By performing a sufficient number of steps and the appropriate processing, adapt the algorithm to obtain the correct result with a probability of at least $1 - \\frac{1}{2^n}$.\n",
"\n",
"This process is called **amplification**, and is widely used in probabilistic algorithms.\n",
" "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "10fc7527",
"metadata": {},
"outputs": [],
"source": [
"### Provide a function that takes as input a circuit circ ending with a measurement \n",
"### and that returns the most frequent measurement, after simulation, as a 0/1 array.\n",
"### In addition to the circuit, add two optional parameters: nb_shots (number of runs) and the boolean \n",
"### transp indicating whether to perform a \"transpile\" of the original circuit\n",
"### Grover algorithm that takes as input the dimension n and the circuit cf of an arbitrary n-bit function f \n",
"### as input and 1 bit as output\n",
"### This is a \"hybrid\" algorithm: it uses a quantum circuit and classical post-processing\n",
"\n",
"def Grov(n,cf): \n",
" # repeat enough times; choose the number of repetitions appropriately\n",
" \n",
" pass\n"
]
},
{
"cell_type": "markdown",
"id": "466793cd",
"metadata": {},
"source": [
"Test your algorithm on different functions $f$. Do not forget the zero function $f_0 : \\{0,1\\}^n \\to \\{0,1\\}$ defined by $f_0(x)=0$ for every input $x$.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f24941ee",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "bf0b9249",
"metadata": {},
"source": [
"## 6. BONUS: close the loop\n",
"\n",
"By the way, why rely on Qiskit's `transpile`? You have already studied the `mcx` gates in the first lab…\n",
"\n",
"Adapt the construction of Grover's circuit so as **not** to rely on `transpile`: replace each `mcx` (with three or more controls) by an explicit decomposition you implement yourself, for example using the ideas from the first notebook. Check that all your previous tests still pass with the new circuit, without using `transpile`.\n"