$\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\}$.
import numpy as np
s = np.random.randint(2) # secreto
c = np.random.randint(2) # clave
print('s =',s,'\nc =',c)
s = 0 c = 1
Para proteger esta información, Alice considera los estados cuánticos puros \begin{align*} \Pi_{0}=\begin{bmatrix}1&0\\0&0\end{bmatrix},\quad \Pi_{1}=\begin{bmatrix}0&0\\0&1\end{bmatrix},\quad \Pi_{+}=\frac{1}{2}\begin{bmatrix}+1&+1\\+1&+1\end{bmatrix},\quad \Pi_{-}=\frac{1}{2}\begin{bmatrix}+1&-1\\-1&+1\end{bmatrix}. \end{align*}
Pi0 = np.array([[1,0],[0,0]])
Pi1 = np.array([[0,0],[0,1]])
Pip = np.array([[+1,+1],[+1,+1]])/2
Pim = np.array([[+1,-1],[-1,+1]])/2
print('Pi0 =\n',Pi0,'\nPi1 =\n',Pi1,'\nPip =\n',Pip,'\nPim =\n',Pim)
Pi0 = [[1 0] [0 0]] Pi1 = [[0 0] [0 1]] Pip = [[0.5 0.5] [0.5 0.5]] Pim = [[ 0.5 -0.5] [-0.5 0.5]]
Y aplica el siguiente esquema de codificación:
Si la clave es $c=0$, Alice prepara el estado \begin{align*} \rho = \begin{cases}
\Pi_{0}, & s=0,\\
\Pi_{1}, & s=1.
\end{cases}
\end{align*}
Si la clave es $c=1$, Alice prepara el estado \begin{align*} \rho = \begin{cases}
\Pi_{+}, & s=0,\\
\Pi_{-}, & s=1.
\end{cases}
\end{align*}
if c == 0:
if s == 0:
rho = Pi0
else:
rho = Pi1
elif c == 1:
if s == 0:
rho = Pip
else:
rho = Pim
print('rho =\n', rho)
rho = [[0.5 0.5] [0.5 0.5]]
Como Alice conoce la clave $c$, puede recuperar la información almacenada a través de una medida en la base correspondiente, con el POVM $\bigl\{\Pi_{0}, \Pi_{1}\bigr\}$ para $c=0$, o usando el POVM $\bigl\{\Pi_{+}, \Pi_{-}\bigr\}$ cuando $c=1$.
print('Aplicamos una medida: las probabilidades de medir s=0 y s=1 son')
if c == 0:
# Aplica el POVM Pi0, Pi1
Pr_s0 = np.trace(Pi0 @ rho)
Pr_s1 = np.trace(Pi1 @ rho)
elif c == 1:
# Aplica el POVM Pip, Pim
Pr_s0 = np.trace(Pip @ rho)
Pr_s1 = np.trace(Pim @ rho)
print('Pr_s0 =', Pr_s0, '\nPr_s1 =', Pr_s1)
print('respectivamente, y el valor que queríamos recuperar')
print('s =',s)
Aplicamos una medida: las probabilidades de medir s=0 y s=1 son Pr_s0 = 1.0 Pr_s1 = 0.0 respectivamente, y el valor que queríamos recuperar s = 0
Sin embargo, un atacante, aunque tenga acceso al estado $\rho$ 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. 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.
print('El atacante escoge una base al azar:')
c_hat = np.random.randint(2)
print('c_hat =', c_hat)
print('y aplicamos una medida en base a la misma')
if c_hat == 0:
# Aplica el POVM Pi0, Pi1
Pr_s0_hat = np.trace(Pi0 @ rho)
Pr_s1_hat = np.trace(Pi1 @ rho)
elif c_hat == 1:
# Aplica el POVM Pip, Pim
Pr_s0_hat = np.trace(Pip @ rho)
Pr_s1_hat = np.trace(Pim @ rho)
print('Pr_s0_hat =', Pr_s0_hat, '\nPr_s1_hat =', Pr_s1_hat)
print('Recordemos la clave original y el valor secreto almacenado:')
print('c =', c, '\ns =', s)
El atacante escoge una base al azar: c_hat = 0 y aplicamos una medida en base a la misma Pr_s0_hat = 0.5 Pr_s1_hat = 0.5 Recordemos la clave original y el valor secreto almacenado: c = 1 s = 0