{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "e5e44fb3",
   "metadata": {},
   "source": [
    "## Ejemplo 10.1: Escondiendo información en un cúbit"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "71cd75b7",
   "metadata": {},
   "source": [
    "$\\newcommand{\\ket}[1]{|{#1}\\rangle}\\newcommand{\\bra}[1]{\\langle{#1}|}$Alice almacena cierta información secreta en un estado cuántico utilizando una clave binaria que solo conoce ella misma. Por simplicidad, vamos a asumir que la infromación a almacenar es un único bit $s\\in \\{0,1\\}$ y Alice utiliza una clave $c \\in \\{0,1\\}$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "e51496e6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "s = 0 \n",
      "c = 1\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "s = np.random.randint(2) # secreto\n",
    "c = np.random.randint(2) # clave\n",
    "print('s =',s,'\\nc =',c)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7625c52a",
   "metadata": {},
   "source": [
    "Para proteger esta información, Alice considera los estados cuánticos puros\n",
    "\\begin{align*}\n",
    "\\Pi_{0}=\\begin{bmatrix}1&0\\\\0&0\\end{bmatrix},\\quad\n",
    "\\Pi_{1}=\\begin{bmatrix}0&0\\\\0&1\\end{bmatrix},\\quad\n",
    "\\Pi_{+}=\\frac{1}{2}\\begin{bmatrix}+1&+1\\\\+1&+1\\end{bmatrix},\\quad\n",
    "\\Pi_{-}=\\frac{1}{2}\\begin{bmatrix}+1&-1\\\\-1&+1\\end{bmatrix}.\n",
    "\\end{align*}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "746f0b09",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Pi0 =\n",
      " [[1 0]\n",
      " [0 0]] \n",
      "Pi1 =\n",
      " [[0 0]\n",
      " [0 1]] \n",
      "Pip =\n",
      " [[0.5 0.5]\n",
      " [0.5 0.5]] \n",
      "Pim =\n",
      " [[ 0.5 -0.5]\n",
      " [-0.5  0.5]]\n"
     ]
    }
   ],
   "source": [
    "Pi0 = np.array([[1,0],[0,0]])\n",
    "Pi1 = np.array([[0,0],[0,1]])\n",
    "Pip = np.array([[+1,+1],[+1,+1]])/2\n",
    "Pim = np.array([[+1,-1],[-1,+1]])/2\n",
    "print('Pi0 =\\n',Pi0,'\\nPi1 =\\n',Pi1,'\\nPip =\\n',Pip,'\\nPim =\\n',Pim)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "94c1b5e6",
   "metadata": {},
   "source": [
    "Y aplica el siguiente esquema de codificación:\n",
    "\n",
    "- Si la clave es $c=0$, Alice prepara el estado\n",
    "\\begin{align*}\n",
    "\\rho = \\begin{cases}\n",
    "           \\Pi_{0}, & s=0,\\\\\n",
    "           \\Pi_{1}, & s=1.\n",
    "       \\end{cases}\n",
    "\\end{align*}\n",
    "\n",
    "- Si la clave es $c=1$, Alice prepara el estado\n",
    "\\begin{align*}\n",
    "\\rho = \\begin{cases}\n",
    "           \\Pi_{+}, & s=0,\\\\\n",
    "           \\Pi_{-}, & s=1.\n",
    "       \\end{cases}\n",
    "\\end{align*}\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "e935943a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "rho =\n",
      " [[0.5 0.5]\n",
      " [0.5 0.5]]\n"
     ]
    }
   ],
   "source": [
    "if c == 0:\n",
    "    if s == 0:\n",
    "        rho = Pi0\n",
    "    else:\n",
    "        rho = Pi1\n",
    "elif c == 1:\n",
    "    if s == 0:\n",
    "        rho = Pip\n",
    "    else:\n",
    "        rho = Pim\n",
    "        \n",
    "print('rho =\\n', rho)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca7f37b0",
   "metadata": {},
   "source": [
    "Como Alice conoce la clave $c$, puede recuperar la información almacenada a través de una medida en la base correspondiente,\n",
    "con el POVM  $\\bigl\\{\\Pi_{0}, \\Pi_{1}\\bigr\\}$ para $c=0$, o usando el POVM $\\bigl\\{\\Pi_{+}, \\Pi_{-}\\bigr\\}$ cuando $c=1$.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "326df4af",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Aplicamos una medida: las probabilidades de medir s=0 y s=1 son\n",
      "Pr_s0 = 1.0 \n",
      "Pr_s1 = 0.0\n",
      "respectivamente, y el valor que queríamos recuperar\n",
      "s = 0\n"
     ]
    }
   ],
   "source": [
    "print('Aplicamos una medida: las probabilidades de medir s=0 y s=1 son')\n",
    "if c == 0:\n",
    "    # Aplica el POVM Pi0, Pi1\n",
    "    Pr_s0 = np.trace(Pi0 @ rho)\n",
    "    Pr_s1 = np.trace(Pi1 @ rho)\n",
    "elif c == 1:\n",
    "    # Aplica el POVM Pip, Pim\n",
    "    Pr_s0 = np.trace(Pip @ rho)\n",
    "    Pr_s1 = np.trace(Pim @ rho)\n",
    "print('Pr_s0 =', Pr_s0, '\\nPr_s1 =', Pr_s1)\n",
    "\n",
    "print('respectivamente, y el valor que queríamos recuperar')\n",
    "print('s =',s)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e4e14833",
   "metadata": {},
   "source": [
    "Sin embargo, un atacante, aunque tenga acceso al estado $\\rho$\n",
    "no puede acceder a la información contenida en él, al no conocer la base de medida correspondiente. Si escoge una de las dos bases al azar, existe un 50\\% de posibilidades de que escoja la incorrecta, en cuyo caso destruiría la información contenida en $\\rho$, debido al colapso de la función de onda. \n",
    "En el proceso de codificación, Alice podría considerar incluso más pares de estados, de forma que la probabilidad de que el atacante acierte la base de medida se vería reduciría todavía más, a costa de aumentar la longitud de la clave."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "76b0cc26",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "El atacante escoge una base al azar:\n",
      "c_hat = 0\n",
      "y aplicamos una medida en base a la misma\n",
      "Pr_s0_hat = 0.5 \n",
      "Pr_s1_hat = 0.5\n",
      "Recordemos la clave original y el valor secreto almacenado:\n",
      "c = 1 \n",
      "s = 0\n"
     ]
    }
   ],
   "source": [
    "print('El atacante escoge una base al azar:')\n",
    "c_hat = np.random.randint(2)\n",
    "print('c_hat =', c_hat)\n",
    "\n",
    "print('y aplicamos una medida en base a la misma')\n",
    "if c_hat == 0:\n",
    "    # Aplica el POVM Pi0, Pi1\n",
    "    Pr_s0_hat = np.trace(Pi0 @ rho)\n",
    "    Pr_s1_hat = np.trace(Pi1 @ rho)\n",
    "elif c_hat == 1:\n",
    "    # Aplica el POVM Pip, Pim\n",
    "    Pr_s0_hat = np.trace(Pip @ rho)\n",
    "    Pr_s1_hat = np.trace(Pim @ rho)\n",
    "\n",
    "print('Pr_s0_hat =', Pr_s0_hat, '\\nPr_s1_hat =', Pr_s1_hat)\n",
    "\n",
    "print('Recordemos la clave original y el valor secreto almacenado:')\n",
    "print('c =', c, '\\ns =', s)"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Raw Cell Format",
  "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.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
