{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pennylane as pl\n",
    "from pennylane import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Exercise 1: Normalise states"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1.+0.j 0.+0.j]\n"
     ]
    }
   ],
   "source": [
    "ket_0 = np.array([1, 0])\n",
    "ket_1 = np.array([0, 1])\n",
    "\n",
    "def normalise(alpha, beta):\n",
    "    \"\"\"Given complex amplitudes alpha and beta for the |0> and |1> states, return a vector (np.array[complex]) of size 2 for the normalised state\"\"\"\n",
    "\n",
    "    # Compute vector psi [a,b] based on alpha and beta such that |a|^2+|b|^2=1 \n",
    "    psi = np.empty(2).astype(complex)\n",
    "    length = np.sqrt(alpha*np.conjugate(alpha) + beta*np.conjugate(beta))\n",
    "    psi[0]=alpha/length \n",
    "    psi[1]=beta/length\n",
    "\n",
    "    return psi\n",
    "\n",
    "print(normalise(2,0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Exercise 2: Inner product"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<0|0> = 1\n",
      "<0|1> = 0\n",
      "<1|0> = 0\n",
      "<1|1> = 1\n"
     ]
    }
   ],
   "source": [
    "def innerproduct(phi, psi):\n",
    "    \"\"\"Compute the (complex) inner product between two normalised states (np.array[complex]) phi and psi\"\"\"\n",
    "\n",
    "    # Compute the inner product of phi and psi\n",
    "    z = np.conjugate(phi[0])*psi[0] + np.conjugate(phi[1])*psi[1]\n",
    "\n",
    "    return z\n",
    "\n",
    "print(f\"<0|0> = {innerproduct(ket_0, ket_0)}\")\n",
    "print(f\"<0|1> = {innerproduct(ket_0, ket_1)}\")\n",
    "print(f\"<1|0> = {innerproduct(ket_1, ket_0)}\")\n",
    "print(f\"<1|1> = {innerproduct(ket_1, ket_1)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Exercise 3: Measurement"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[np.int64(0), np.int64(0), np.int64(1), np.int64(0), np.int64(1), np.int64(0), np.int64(1), np.int64(1), np.int64(0), np.int64(1)]\n"
     ]
    }
   ],
   "source": [
    "def measure(psi, n):\n",
    "    \"\"\"Simulate n quantum measurements of state psi, returning n samples 0 or 1\"\"\"\n",
    "\n",
    "    # Compute the measurement outcome probabilities\n",
    "    # Return a list of sample measurement outcomes\n",
    "    # Hint: use numpy.random.choice\n",
    "    p = np.abs(psi[0])**2\n",
    "    outcomes = [ np.random.choice([0,1], p=[p,1-p]) for i in range(n) ]\n",
    "    return outcomes\n",
    "\n",
    "psi = normalise(1,1j)\n",
    "print(measure(psi, 10))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Exercise 4: Unitary"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0.70710678+0.j 0.70710678+0.j]\n"
     ]
    }
   ],
   "source": [
    "def apply_unitary(U, psi):\n",
    "    \"\"\"Apply a unitary operation U to state psi\"\"\"\n",
    "\n",
    "    # Apply U to psi and return the result\n",
    "    phi = np.empty(2).astype(complex)\n",
    "    phi[0] = U[0,0]*psi[0] + U[1,0]*psi[1]\n",
    "    phi[1] = U[0,1]*psi[0] + U[1,1]*psi[1]\n",
    "    # or simply: phi = np.dot(U, psi)\n",
    "    return phi\n",
    "\n",
    "U = np.array([[1, 1], [1, -1]]) / np.sqrt(2)\n",
    "psi = ket_0\n",
    "print(apply_unitary(U, psi))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Exercise 5: Baby quantum simulator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[np.int64(1), np.int64(0), np.int64(1), np.int64(1), np.int64(1), np.int64(1), np.int64(1), np.int64(0), np.int64(0), np.int64(1), np.int64(0), np.int64(0), np.int64(1), np.int64(0), np.int64(1), np.int64(1), np.int64(0), np.int64(0), np.int64(1), np.int64(0), np.int64(1), np.int64(0), np.int64(1), np.int64(1), np.int64(1), np.int64(0), np.int64(1), np.int64(1), np.int64(1), np.int64(0), np.int64(0), np.int64(1), np.int64(0), np.int64(0), np.int64(1), np.int64(0), np.int64(0), np.int64(1), np.int64(0), np.int64(0), np.int64(0), np.int64(1), np.int64(0), np.int64(1), np.int64(1), np.int64(1), np.int64(1), np.int64(1), np.int64(1), np.int64(0), np.int64(0), np.int64(1), np.int64(0), np.int64(1), np.int64(1), np.int64(1), np.int64(1), np.int64(0), np.int64(1), np.int64(1), np.int64(1), np.int64(0), np.int64(0), np.int64(0), np.int64(1), np.int64(1), np.int64(1), np.int64(0), np.int64(1), np.int64(1), np.int64(0), np.int64(1), np.int64(1), np.int64(0), np.int64(0), np.int64(0), np.int64(1), np.int64(0), np.int64(1), np.int64(1), np.int64(1), np.int64(0), np.int64(1), np.int64(1), np.int64(1), np.int64(0), np.int64(1), np.int64(1), np.int64(0), np.int64(1), np.int64(1), np.int64(1), np.int64(0), np.int64(1), np.int64(1), np.int64(0), np.int64(1), np.int64(1), np.int64(1), np.int64(0)]\n"
     ]
    }
   ],
   "source": [
    "def quantum_simulator(U, psi):\n",
    "    \"\"\"Use previous exercises to sample the result of applying gate U to state psi 100 times\"\"\"\n",
    "    \n",
    "    statistics = measure(apply_unitary(U, psi), 100)\n",
    "    return statistics\n",
    "\n",
    "U = np.array([[1, 1], [1, -1]]) / np.sqrt(2)\n",
    "psi = ket_0\n",
    "print(quantum_simulator(U, psi))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "pennylane (3.14.7)",
   "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.14.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
