{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "pmm5uV8cQQN6"
   },
   "source": [
    "# Exercise 6\n",
    "\n",
    "\n",
    "## 1\n",
    "\n",
    "Show that the Hadamard gate can be written in the following two forms\n",
    "\n",
    "$$H = \\frac{X+Z}{\\sqrt{2}} \\sim \\exp\\left(i \\frac{\\pi}{2} \\, \\frac{X+Z}{\\sqrt{2}}\\right).$$\n",
    "\n",
    "Here $\\sim$ is used to denote that the equality is valid up to a global phase, and hence that the resulting gates are physically equivalent.\n",
    "\n",
    "Hint: it might even be easiest to prove that $e^{i\\frac{\\pi}{2} M} \\sim M$ for any matrix whose eigenvalues are all $\\pm 1$, and that such matrices uniquely satisfy $M^2=I$."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "CJyxxSDUQQN9"
   },
   "source": [
    "## 2\n",
    "\n",
    "The Hadamard can be constructed from `rx` and `rz` operations as\n",
    "\n",
    "$$ R_x(\\theta) = e^{i\\frac{\\theta}{2} X}, ~~~ R_z(\\theta) = e^{i\\frac{\\theta}{2} Z},\\\\ H \\equiv \\lim_{n\\rightarrow\\infty} \\left( ~R_x\\left(\\frac{\\theta}{n}\\right) ~~R_z \\left(\\frac{\\theta}{n}\\right) ~\\right)^n.$$\n",
    "\n",
    "For some suitably chosen $\\theta$. When implemented for finite $n$, the resulting gate will be an approximation to the Hadamard whose error decreases with $n$.\n",
    "\n",
    "The following shows an example of this implemented with Qiskit with an incorrectly chosen value of $\\theta$ (and with the global phase ignored).\n",
    "\n",
    "* Determine the correct value of $\\theta$.\n",
    "\n",
    "* Show that the error (when using the correct value of $\\theta$) decreases quadratically with $n$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qiskit import *\n",
    "from qiskit.tools.visualization import plot_histogram\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "4yqeQMlZQQN_"
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 17084 characters]\n",
      "text/plain": [
       "<Figure size 504x360 with 1 Axes>"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "q = QuantumRegister(1)\n",
    "c = ClassicalRegister(1)\n",
    "\n",
    "error = {}\n",
    "for n in range(1,11):\n",
    "\n",
    "    # Create a blank circuit\n",
    "    qc = QuantumCircuit(q,c)\n",
    "    \n",
    "    # Implement an approximate Hadamard\n",
    "    theta = np.pi # here we incorrectly choose theta=pi\n",
    "    for j in range(n):\n",
    "        qc.rx(theta/n,q[0])\n",
    "        qc.rz(theta/n,q[0])\n",
    "      \n",
    "    # We need to measure how good the above approximation is. Here's a simple way to do this.\n",
    "    # Step 1: Use a real hadamard to cancel the above approximation.\n",
    "    # For a good approximatuon, the qubit will return to state 0. For a bad one, it will end up as some superposition.\n",
    "    qc.h(q[0])\n",
    "    \n",
    "    # Step 2: Run the circuit, and see how many times we get the outcome 1.\n",
    "    # Since it should return 0 with certainty, the fraction of 1s is a measure of the error.\n",
    "    qc.measure(q,c)\n",
    "    shots = 20000\n",
    "    job = execute(qc, Aer.get_backend('qasm_simulator'),shots=shots)\n",
    "    try:\n",
    "        error[n] = (job.result().get_counts()['1']/shots)\n",
    "    except:\n",
    "        pass\n",
    "        \n",
    "plot_histogram(error)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Yr5vuU_eQQOU"
   },
   "source": [
    "## 3\n",
    "\n",
    "An improved version of the approximation can be found from,\n",
    "\n",
    "$$H \\equiv \\lim_{n\\rightarrow\\infty} \\left( ~ R_z \\left(\\frac{\\theta}{2n}\\right)~~ R_x\\left(\\frac{\\theta}{n}\\right) ~~ R_z \\left(\\frac{\\theta}{2n}\\right) ~\\right)^n.$$\n",
    "\n",
    "Implement this, and investigate the scaling of the error."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4\n",
    "\n",
    "Write a circuit to implement the following unitary with perfect precision, using only `rx`, `rz` and Clifford gates\n",
    "\n",
    "$$U = \\exp\\left[i \\frac{\\theta}{2} \\, \\left( X\\otimes X + Z\\otimes Z \\right) \\right].$$\n",
    "\n",
    "Apply this to the initial state $|10\\rangle$ and determine the final state for the following values of $\\theta$.\n",
    "\n",
    "* (a) $\\theta=\\pi/4 $\n",
    "* (b) $\\theta=\\pi/2 $\n",
    "* (c) $\\theta=\\pi $\n",
    "\n",
    "You can use the following as a starting point."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Statevector([0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n",
      "            dims=(2, 2))\n"
     ]
    }
   ],
   "source": [
    "theta = np.pi/2\n",
    "\n",
    "q = QuantumRegister(2)\n",
    "\n",
    "# Create a blank circuit\n",
    "qc = QuantumCircuit(q)\n",
    "\n",
    "# prepare the |10> state\n",
    "qc.x(1)\n",
    "\n",
    "# do things!\n",
    "\n",
    "# get the final statevector\n",
    "job = Aer.get_backend('statevector_simulator').run(qc)\n",
    "statevector = job.result().get_statevector()\n",
    "print(statevector)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "TeQnxYGDQ2ro"
   },
   "source": [
    "## 5. Effects of garbage in quantum circuits\n",
    "\n",
    "The CX gate performs the mapping \n",
    "$(i,j) \\rightarrow (i,i+j \\mod 2)$ from an input on two bits to an output on two bits.\n",
    "\n",
    "It is interesting to note (though not relevant to the problem) that this mapping is not of the form $(z,0) \\rightarrow (z,f(z))$, as considered last week, since the function $f(i,j) = i+j \\mod 2$ does not require the whole input to be present for reversibility.\n",
    "\n",
    "Here are two circuits with a CX gate.\n",
    "\n",
    "![](circuits.png)\n",
    "\n",
    "* (a) The first circuit is composed of both a quantum part and an irreversible classical part. Determine the final output bit .\n",
    "\n",
    "* (b) Show that the second circuit effectively acts as a CX between the first and third qubits, but with the additional effect of producting a garbage qubit on the second.\n",
    "\n",
    "* (c) Replace the true CX in the first circuit with the garbage producing one from the second. Show that this changes the output bit.\n",
    "\n",
    "* (d) Show how the garbage producing CX can be corrected by uncomputation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "colab": {
   "collapsed_sections": [],
   "name": "Exercise_4.ipynb",
   "provenance": [],
   "version": "0.3.2"
  },
  "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.9.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
