{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "62eb0b66-e94e-4370-8c2c-cdf78aff67ee",
   "metadata": {},
   "source": [
    "# Fun with Oracles: The Bernstein-Vazirani Algorithm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "aea3cb24-6b7f-4f3a-83f6-bd54782d0cd2",
   "metadata": {},
   "outputs": [],
   "source": [
    "from qiskit import *\n",
    "%matplotlib inline\n",
    "from qiskit.visualization import plot_histogram\n",
    "from qiskit_aer import AerSimulator\n",
    "\n",
    "# import image module\n",
    "from IPython.display import Image\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8781dcaa-068c-4a70-b72e-df35b0ac2869",
   "metadata": {},
   "source": [
    "In this exercise, we will construct an oracle that lets us guess a secret number (the bitstring $a$) in a single query to a quantum computer. We begin by posing the problem classically. Suppose we have access to a function $f_a(x)$ that takes a bitstring $a$ as input and returns $a\\cdot x$, modulo 2:\n",
    "\n",
    "$$ f_a(x)=a\\cdot x\\quad (\\mathrm{mod}\\; 2).$$\n",
    "\n",
    "As an example, if $a=001$ and $x=101$, then $f_a(x)=0\\times 1 + 0\\times 0 + 1\\times 1=1$. As a classical circuit, we can represent the Bernstein-Vazirani oracle like this (image stolen from Pennylane):\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "6765ec1d-0134-486e-973f-2325058323de",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<img src=\"classical.png\" width=\"400\" height=\"400\"/>"
      ],
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Image(url=\"classical.png\", width=400, height=400)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8cd8eada-56cc-45d5-ad65-ec602044dc35",
   "metadata": {},
   "source": [
    "More generally, we can write the action of this oracle as \n",
    "\n",
    "$$\n",
    "\\begin{equation}\n",
    "U_f\\vert x\\rangle\\vert s\\rangle=\\vert x\\rangle \\vert s\\oplus f_a(x)\\rangle,\\quad s=0,1,\n",
    "\\end{equation}\n",
    "$$\n",
    "\n",
    "where $\\oplus$ denotes addition modulo 2.\n",
    "\n",
    "To find $a$ classically, we would have to query this oracle $N=\\mathrm{len}(a)$ times with inputs $x_0=0\\dots 01$, $x_1=0\\dots 10$,..., $x_{N-1}=10\\dots0$. Given the oracle $U_f$, the Bernstein-Vazirani algorithm allows us to find $a$ in a single shot using the following circuit:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "95991dbc-3881-4ea4-9e04-c9e1073b7b7c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<img src=\"quantum.png\" width=\"400\" height=\"400\"/>"
      ],
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Image(url=\"quantum.png\", width=400, height=400)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "95ca75e1-eba3-474b-8c52-7667cd78f988",
   "metadata": {},
   "source": [
    "The steps of the algorithm are as follows:\n",
    "\n",
    "1. Initialize the register qubits to the $|0\\rangle^{\\otimes n}$ state, and the ancilla qubit to $|{-}\\rangle$\n",
    "2. Apply Hadamard gates to the input register\n",
    "3. Query the oracle\n",
    "4. Apply Hadamard gates to the input register\n",
    "5. Measure\n",
    "\n",
    "\n",
    "Let's walk through the steps one-by-one. If we start with all register qubits initialized in $\\vert a\\rangle$ and apply a Hadamard to each one, then we have prepared the state\n",
    "\n",
    "$$\\vert a \\rangle\\stackrel{H^{\\otimes N}}{\\longrightarrow} \\frac{1}{\\sqrt{2^N}}\\sum_{x=0}^{2^N-1}(-1)^{a\\cdot x}\\vert x\\rangle.$$\n",
    "\n",
    "\n",
    "Since the register qubits are all initialized in $\\vert 0\\rangle$, the input state on which the oracle acts is therefore given by\n",
    "\n",
    "$$\\vert \\psi_0\\rangle =\\frac{1}{\\sqrt{2^{N+1}}}\\sum_{x=0}^{2^N-1}\\vert x\\rangle(\\vert 0\\rangle-\\vert 1\\rangle).$$\n",
    "\n",
    "Let's start by preparing this state. First, we choose a secret number:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "c85ed1b3-5b84-4199-960e-f9b4c9e5ba68",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "Enter a binary number: 101\n"
     ]
    }
   ],
   "source": [
    "secretnumber = input('Enter a binary number:')\n",
    "l = len(secretnumber)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8c533af7-633b-41f1-b8d6-43fae31f81d4",
   "metadata": {},
   "source": [
    "The next code cell is for you to complete. It should realize Steps #1 and #2 in the list above:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2a2eec4d-2d1b-42c4-b966-4b52d9d20831",
   "metadata": {},
   "outputs": [],
   "source": [
    "qc = QuantumCircuit(l+1, l)\n",
    "\n",
    "### YOUR CODE GOES HERE ###\n",
    "\n",
    "qc.barrier()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5ec9584b-98ae-44bf-9d60-4e8a4bf65470",
   "metadata": {},
   "source": [
    "Next, let's see what happens when we apply the oracle $U_f$ to $\\vert \\psi_0\\rangle$. Using the equation given above, we have\n",
    "\n",
    "$$\n",
    "   \\begin{aligned}\n",
    "   U_f\\lvert \\psi_0 \\rangle  \n",
    "   & = \\frac{1}{\\sqrt{2^{N+1}}}\\sum_{x=0}^{2^N-1} \\vert x\\rangle (\\vert f_a(x)\\rangle - \\vert 1 \\oplus f_a(x)\\rangle)  \\\\&= \\frac{1}{\\sqrt{2^{N+1}}}\\sum_{x=0}^{2^N-1}(-1)^{f_a(x)}|x\\rangle ( |0\\rangle - |1\\rangle ) .\n",
    "   \\end{aligned}\n",
    "   $$\n",
    "\n",
    "Neglecting the ancilla qubit, the action of Steps #1-3 above can therefore be summarized as\n",
    "\n",
    "$$|00\\dots 0\\rangle \\xrightarrow{H^{\\otimes N}} \\frac{1}{\\sqrt{2^N}} \\sum_{x} |x\\rangle \\xrightarrow{U_f} \\frac{1}{\\sqrt{2^N}} \\sum_{x} (-1)^{a\\cdot x}|x\\rangle.$$\n",
    "\n",
    "Since the Hadamard is its own inverse, the secret number $a$ can be recovered by once again applying Hadamards to all register qubits:\n",
    "\n",
    "$$ \\frac{1}{\\sqrt{2^N}}\\sum_{x=0}^{2^N-1}(-1)^{a\\cdot x}\\vert x\\rangle\\stackrel{H^{\\otimes N}}{\\longrightarrow}\\vert a \\rangle.$$\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6353c87e-7806-4482-84d6-7b5e4862de86",
   "metadata": {},
   "source": [
    "We will now construct the oracle by applying CNOTs between the ancilla (in position ```l```) and every bit of the secret number equal to 1. (The target qubit should be ```l```.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2c702a6a-83e5-43a6-9ff3-f529fd1babc6",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "#Building the oracle\n",
    "for i, digit in enumerate(reversed(secretnumber)):\n",
    "    if digit == '1':\n",
    "        ### YOUR CODE GOES HERE ###\n",
    "        \n",
    "qc.barrier()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15420105-f5b8-492d-a5ad-442ae9d1a944",
   "metadata": {},
   "source": [
    "Finally, we apply Hadamards and measure all register qubits in the $Z$ basis. You can visualized your circuit using ```qc.draw()```."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e3b4e4f6-02a3-4024-83ab-bba3a5e61e6d",
   "metadata": {},
   "outputs": [],
   "source": [
    "qc.h(range(l))\n",
    "qc.measure(range(l), range(l))\n",
    "\n",
    "qc.draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b8807b89-399b-40fe-b361-7b8f68093cd0",
   "metadata": {},
   "source": [
    "We can finally run our circuit. Only a single shot is required since the outcome is deterministic in the absence of other errors. If you've done everything correctly, the following code block should return the secret number you specified at the beginning of this notebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "c5f6feff-88bf-41bd-b9dd-5d532c30c9c1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'101': 1}\n"
     ]
    }
   ],
   "source": [
    "backend = AerSimulator()\n",
    "result = backend.run(qc, shots=1).result()\n",
    "counts = result.get_counts()\n",
    "print(counts)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "83f46f4a-0b46-4a0b-8e31-785d384a7bf2",
   "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.13.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
