May 15, 2024 | 7 min read
El arte de medir I#
\( \newcommand{\bra}[1]{\langle #1|} \) \( \newcommand{\ket}[1]{|#1\rangle} \) \( \newcommand{\braket}[2]{\langle #1|#2\rangle} \) \( \newcommand{\i}{{\color{blue} i}} \) \( \newcommand{\tr}{{\rm tr}} \) \( \newcommand{\Hil}{{\mathcal H}} \) \( \newcommand{\boldn}{{\bf n}} \) \( \newcommand{\bn}{{\bf n}} \) \( \newcommand{\boldsig}{\boldsymbol{\sigma}} \) \( \newcommand{\bsig}{\boldsymbol{\sigma}} \)
Show code cell source
%run ../../macro_tQ.py
import sys
sys.path.append('../../')
import macro_tQ as tQ
import numpy as np
from IPython.display import display,Markdown,Latex
from qiskit.visualization import array_to_latex
Medidas de 1 cúbit#
Medidas en la base computacional#
El circuito de medida estándar se representa en la forma siguiente
Por defecto, se trata de una medida proyectiva asociada al operador observable \(Z\). Es decir, proyecta a la base \(\ket{a} = \ket{0},\ket{1}\) de autoestados de \(Z\)
Podemos abreviar de forma conjunta, con \(~a=0,1\)
Probabilidades de medida#
Una repetición de medidas del estado \(\ket{\psi} = c_0\ket{0} + c_1\ket{1}\) da acceso estadístico a las amplitudes de probabilidad
Este procedimiento de reconstrucción es la base de la tomografía cuántica.
Definamos un vector aleatorio
from qiskit.quantum_info import Statevector
from qiskit.visualization import plot_bloch_multivector, array_to_latex
theta = 1.9
phi = 0.8
psi_ket = np.array([np.cos(theta/2),(np.exp(1j*phi))*np.sin(theta/2)]).reshape(2,1)
array_to_latex(psi_ket,prefix= '\ket{\psi} = ')
display(Statevector(psi_ket).draw('latex'))
display(Statevector(psi_ket).draw('Bloch'))
Las probabilidades de obtener ‘0’ y ‘1’ son los cuadrados de las amplitudes en valor absoluto
p0 = np.abs(psi_ket[0])**2
p1 = np.abs(psi_ket[1])**2
print('p(0)=', np.round(p0,5),', p(1)=', np.round(p1,5))
p(0)= [0.33836] , p(1)= [0.66164]
Vamos a recuperarlas experimentalmente corriendo un circuito. Primero obtenemos el diccionario de cuentas obtenidas después de ejecutar el circuito un número de veces
nshots = 10000
from qibo.models.circuit import Circuit, gates
'generamos un circuito en el que inicializamos el estado psi'
qc_psi_Qibo = Circuit(1)
qc_psi_Qibo.add(gates.U3(0, theta, phi, 0, trainable=True))
qc_psi_Qibo.add(gates.M(0))
display(tQ.draw_qibo_circuit_mpl(qc_psi_Qibo))
'ejecutamos un número de veces'
counts_psi = qc_psi_Qibo(nshots=nshots).frequencies()
'visualizamos los resultados como un diccionario y mediante un histograma'
from qiskit.visualization import plot_histogram
print('counts_psi = ',counts_psi)
plot_histogram(counts_psi) # Mostramos un histograma de resultados
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
'generamos un circuito en el que inicializamos el estado psi'
qc_psi_Qiskit = QuantumCircuit(1,1)
qc_psi_Qiskit.u(theta,phi,0,0)
qc_psi_Qiskit.measure(0,0)
display(qc_psi_Qiskit.draw('mpl'))
'ejecutamos un número de veces'
counts_psi = AerSimulator().run(qc_psi_Qiskit,shots=nshots).result().get_counts()
'visualizamos los resultados como un diccionario y mediante un histograma'
from qiskit.visualization import plot_histogram
print('counts_psi = ',counts_psi)
plot_histogram(counts_psi) # Mostramos un histograma de resultados
/opt/anaconda3/envs/TalentQ/lib/python3.11/site-packages/qiskit/visualization/circuit/matplotlib.py:266: FutureWarning: The default matplotlib drawer scheme will be changed to "iqp" in a following release. To silence this warning, specify the current default explicitly as style="clifford", or the new default as style="iqp".
self._style, def_font_ratio = load_style(self._style)
[Qibo 0.1.12.dev0|INFO|2024-03-11 21:40:15]: Using tensorflow backend on /device:CPU:0
counts_psi = Counter({'1': <tf.Tensor: shape=(), dtype=int64, numpy=6618>, '0': <tf.Tensor: shape=(), dtype=int64, numpy=3382>})
Ahora podemos calcular las frecuencias relativas \(n_i/N\) y compararlas con las probabilidades \(p(i)\)
p0_experiment = counts_psi['0']/nshots
p1_experiment = counts_psi['1']/nshots
print('p(0)_experiment =', p0_experiment,' p(1)_experiment = ',p1_experiment)
p(0)_experiment = tf.Tensor(0.3382, shape=(), dtype=float64) p(1)_experiment = tf.Tensor(0.6618, shape=(), dtype=float64)
Interferencia cuántica y medidas#
El resultado de tirar una moneda al aire es una variable aleatoria binaria equiprobable: X = (cara,cruz). Es irrelevante si analizamos el resultado cada tirada o cada dos, o tres tiradas. Las frecuencias relativas de caras y cruces, siempre serán próximas a \(1/2\).
Podemos imaginar un experimento similar con un cúbit, donde cara \(\to \ket{0}\) y cruz \(\to\ket{1}\) son los estados posibles de la medida en la base \(Z\)
tirar la moneda clásica es ponerla en un estado incierto que puede acabar en cara o cruz al caer sobre la mesa (medir)
tirar la moneda cuántica es aplicar el operador \(H\) para poner cualquiera de los dos resultados en una superposición \(H\ket{0} = \frac{1}{\sqrt{2}}(\ket{0} + \ket{1})\) y \(H\ket{1} = \frac{1}{\sqrt{2}}(\ket{0} - \ket{1})\)
El resultado clásico y cuántico, para una sola tirada, coinciden en una distribución equiprobable
La sorpresa está en observar el resultado ¡después de dos tiradas! Para una moneda clásica, la distribución volvería a ser equiprobable.
Cuánticamente observamos que se recupera el estado inicial, cara, ¡con un 100% de probabilidad!
Es interesante ver este caso como ejemplo de interferencia cuántica, una herramienta que explotaremos intensivamente para fabricar algoritmos. En efecto el proceso de hacer dos tiradas sucesivas (sin medir entre ellas) genera la siguiente sucesión estados
El resultado final es cierto y arrojará \(0\) con 100% de probabilidad. Vemos que hay dos caminos posible para alcanzar cualquier resultado final, \(\ket{0}\) ó \(\ket{1}\). Los que intervienen para conseguir \(\ket{0} \to \ket{0}\) contribuyen de manera constructiva, mientras que los dos caminos \(\ket{0}\to \ket{1}\) interfieren de forma destructiva. El signo menos en el segundo estado \(\ket{1}\) es una amplitud de probabilidad negativa, algo imposible clásicamente.
Ejercicio
repite el razonamiento anterior
comenzando con el estado cruz, es decir \(\ket{1}\)
intercalando una medida entre ambas tiradas
El resultado del ejercicio anterior ilustra que la introducción de una medida intermedia destruye la interferencia. La interferencia es una propiedad ligada a la coherencia cuántica, la cual se pierde través de la interacción del cúbit con el ambiente. Las medidas son interacciones de ese tipo.
Ejercicio
construye un circuito y verifica los resultados que predice la mecánica cuántica. Estudia la introducción de más tiradas cuántica, \(H^n\). Intercala una medida en distintas partes del circuito y mira qué resultado obtienes.
Medidas en bases generales#
La elección del observable \(Z\) para el medidor en la base computacional es arbitrario. Deberíamos ser capaces de modificar la base de medida a voluntad. Vamos a estudiar primero cómo usar las bases \(X\) e \(Y\) y después analizaremos el caso arbitrario.
Bases X e Y#
Los autovectores del operador \(X\) se denominan convencionalmente \(\ket{+}, \ket{-}\). Desearíamos disponer de un circuito asociado a este observable, de forma que, a la detección de un autovector \(\ket{+} \) registrase un \(0\) y de \(\ket{-} \) un \(1\).
Es fácil construir un circuito así, como se ve en la figura a continuación. Efectivamente la acción de \(H\ket{+} = \ket{0}\) y \(H\ket{-} = \ket{1}\) manda cada autoestado de \(X\) a un autoestado de \(Z\). A continuación un medidor estándar registrará el valor de \(a =0,1\) según sea el estado medido. Una aplicación posterior de \(H\) nos devolverá el estado original \(H\ket{0} = H^2 \ket{+} = \ket{+}\) e igualmente \(H\ket{1} = H^2 \ket{-} = \ket{-}\)
Igualmente denominemos \(\ket{+ i}\) y \(\ket{- i}\) los autoestados del operador \(Y\). Podemos construir un circuito que registra \(a = 0,1\) cuando el estado incidente colapsa a \(\ket{+ i}\) y \(\ket{- i}\) respectivamente. Basta observar que \(\ket{\pm i} = SH\ket{a}\). Inversamente \(\ket{a} = HS^\dagger \ket{\pm i}\) y proceder igual que antes.
En resumen: podemos construir aparatos de medida efectivos que actúan sobre las bases \(X\) e \(Y\) igual que el medidor estándar lo hace sobre \(Z\)
Detrás de estas equivalencias de circuitos, ahora con medidores, se esconde la conjugación de puertas siguiente
Nota
recuerda que el orden de la composición de operadores se invierte al plasmarlos en un circuito
Ejercicio
Comprueba que estas ecuaciones de conjugación son correctas
Formalismo general#
Vamos a ver los dos casos anteriores como caso particulares de un aparato de medida asociado a una dirección arbitraria en la esfera de Bloch. Dado un vector unitario \(~\hat\boldn~\)
el observable asociado será \( \hat{\boldn}\cdot \boldsymbol{\sigma}\) cuyos autoestados forman una base \(\{\ket{0}_{\hat{\bf n}}, \ket{1}_{\hat{\bf n}}\}\) con autovalores \(\pm 1\)
dados por
Los casos particulares de la sección anterior se recuperan haciendo
Usando operadores de rotación podemos convertir el medidor estándar asociado a \(Z\) con un medidor efectivo asociado al observable general \(\hat {\bf n} \cdot \bsig\). Veamos cómo:
sea \(~\hat{\bf z}\cdot\bsig ~\) el aparato de medida estandar orientado a lo largo del eje \(~\hat{\bf z}\)
y \(~\hat\bn\cdot \bsig~\) el aparato de medida orientado a lo largo del eje \(\hat\bn\)
las bases de autovectores asociadas \(\{\ket{a}_{\hat{\bf z}}\}\) y \(\{\ket{a}_{\hat{\bf n}}\}\) ser relacionan mediante una simple rotación unitaria
El operador en cuestión es
como es fácil de comprobar
Si las componentes de los vectores rotan en la forma \(\ket{a}_{\hat{\bf n}} = U\ket{a}_{\hat{\bf z}}\) los observables asociados se relacionan algebraicamente como lo hace cualquier operador
Verificamos la consistencia
Ejercicio
comprueba operando la ecuación anterior
El circuito siguiente simula un aparato de medición en la base \(\{\ket{a}_{\hat{\bf n}}\}\) obtenido mediante la rotación del aparato de medida en la base \(\{\ket{a}_{\hat{\bf n}}\}\) de autoestados de \(Z\)
Notar que
los casos particulares de bases \(X\) e \(Y\) se recuperan con \(U = H\) en el primer caso y \(U = SH\) en el segundo.
si el estado se destruye en la medida o no es conservado posteriormente, podemos simplificar el circuito de medida para que sólo informe del valor medido
Valores esperados de un observable#
Conociento la probabilidades de aparición de los posible autovalores \(\lambda_i\) de un operador \(A\) tenemos acceso al valor esperado
Como sólo disponemos de un observable, el medidor estándar en la dirección \(Z\), tendremos que ser creativos para poder obtener este valor esperado. La clave está en que sólo necesitaremos los medidores efectivos en las bases \(X, Y \) y \(Z\).
Descomposición en la base de Pauli#
La clave reside en observar que cualquier observable sobre un cúbit \(A = A^\dagger\) puede desarrollarse en la base \(\sigma_i = \{I,X,Y,Z\}, \, i=0,1,2,3\)
con coeficientes
Esta expresión se obtiene haciendo uso de la relación \(\to \frac{1}{2} \tr\, (\sigma_i \sigma_j) = \delta_{ij}\).
Comprobación
Entonces, podremos obtener el valor esperado de \(A\) si somos capaces de medir los de \(X,\) \(Y\) y \(Z\).
\(\langle Z \rangle_{\psi}\)
Los estados de la base computacional son autoestados del operador \(Z\) con autovalor \(\pm 1\)
Dado un estado \(\ket{\psi} = c^Z_0\ket{0} + c^Z_1\ket{1}\), la medida repetida arroja de forma aleatoria los valores propios de \(Z \to \pm 1\) con frecuencias relativas
El valor medio de dicha variable es,
Para medir autovalores de \(\langle Z\rangle_\psi\) el circuito es el más simple
\(\langle X \rangle_{\psi}\)
En la base \(X\), el mismo estado de entrada se escribirá con otras componentes \(\ket{\psi} = c_0^X\ket{+} + c_1^X\ket{-}\).
Si ahora medimos \(\ket{\psi}\) en la base \(X\), la repetición arrojará una muestra aleatoria de autovalores de \(X\to \pm 1\) con frecuencias
El valor esperado de \(X\) se obtiene del promedio de frecuencias relativas
Ya hemos visto cuál es el circuito necesario para medir en la base \(X\)
\(\langle Y \rangle_{\psi}\)
Igualmente, si medimos el estado \(\ket{\psi} = c_0^Y\ket{+i} + c_1^Y\ket{-i}\) con un medidor asociado al operador \(Y = SHZHS^\dagger\), la repetición arrojará una muestra aleatoria de valores propios de \(Y\to \pm 1\) con frecuencias relativas \(+1\to n^Y_0\) y \(-1\to n^Y_1\). Entonces
Circuito necesario para medir en la base \( Y\)
from qibo.models.circuit import Circuit, gates
qc_psi_Qibo = Circuit(1)
qc_psi_Qibo.add(gates.U3(0, theta, phi, 0, trainable=True))
'añadimos el medidor efectivo segun queramos medir sea <X>, <Y> o <Z>'
P = 'Y'
if P =='X':
qc_psi_Qibo.add(gates.h(0))
elif P =='Y':
qc_psi_Qibo.add(gates.SDG(0))
qc_psi_Qibo.add(gates.H(0))
qc_psi_Qibo.add(gates.M(0))
display(tQ.draw_qibo_circuit_mpl(qc_psi_Qibo))
'ejecutamos'
nshots = 10000
counts_psi = qc_psi_Qibo(nshots=nshots).frequencies()
print(counts_psi)
'Ahora,con las frecuencias relativas de los autovalores de Z, podemos calcular los valores medios'
mean_P = 0
for bits, counts in counts_psi.items():
mean_P += (-1)**(int(bits))* (counts/nshots)
print('<',P,'>','=',np.round(mean_P,5))
qc_psi_Qiskit = QuantumCircuit(1,1)
qc_psi_Qiskit.u(theta,phi,0,0)
'añadimos el medidor efectivo segun queramos medir sea <X>, <Y> o <Z>'
P = 'Y'
if P =='X':
qc_psi_Qiskit.h(0)
elif P =='Y':
qc_psi_Qiskit.sdg(0)
qc_psi_Qiskit.h(0)
qc_psi_Qiskit.measure(0,0)
display(qc_psi_Qiskit.draw('mpl'))
nshots = 100000
from qiskit_aer import AerSimulator
counts_psi = AerSimulator().run(qc_psi_Qiskit,shots = nshots).result().get_counts()
print(counts_psi)
'Ahora,con las frecuencias relativas de los autovalores de Z, podemos calcular los valores medios'
mean_P = 0
for bits, counts in counts_psi.items():
mean_P += (-1)**(int(bits))* (counts/nshots)
print('<',P,'>','=',np.round(mean_P,5))
/opt/anaconda3/envs/TalentQ/lib/python3.11/site-packages/qiskit/visualization/circuit/matplotlib.py:266: FutureWarning: The default matplotlib drawer scheme will be changed to "iqp" in a following release. To silence this warning, specify the current default explicitly as style="clifford", or the new default as style="iqp".
self._style, def_font_ratio = load_style(self._style)
Counter({'0': <tf.Tensor: shape=(), dtype=int64, numpy=8421>, '1': <tf.Tensor: shape=(), dtype=int64, numpy=1579>})
< Y > = 0.6842
Finalmente ya estamos en condiciones de calcular el valor esperado de un observable arbitrario
Ejercicio
genera un observable arbitrario \(A\). Obtén los coeficientes \(a_i\) y calcula el valor esperado
\(\langle A \rangle\) con un sólo circuito
Podemos escribir la descomposición de \(A\) de la siguiente manera
donde \({\bf a} = (a_1,a_2,a_3)~\) y \(~\hat \bn = {\bf a}/ |{\bf a}|\). De modo que, ahora
Vemos que para calcular \(\langle A \rangle_\psi\) es suficiente con conocer los valores \(a_i = (a_0,{\bf a})\) y un sólo circuito que nos proporcione el valor esperado \(\langle \bn \cdot \bsig \rangle_\psi\).
La clave de este último punto está en usar el medidor efectivo en la dirección \(\hat \bn\)
Con la salida de este circuito, el valor esperado es análogo
Ejercicio
utiliza éste metodo para obtener el mismo valor \(\langle A\rangle_\psi \) que en el ejercicio anterior
Valor esperado de un operador unitario#
En general el estado \(\ket{\psi}\) debe prepararse a partir del estado \(\ket{0}\). No siempre \(U\) es conocido, pero vamos a suponer que es el caso
En este caso, podemos calcular el valor esperado de cualquier operador unitario \(V\) de la siguiente forma
donde hemos definido el nuevo estado $\(\ket{\tilde\psi} \equiv U_\psi^\dagger V U_\psi\ket{0}\)$
que se prepara mediante un circuito sencillo:
Midiendo \(\ket{\tilde \psi}\) en la base \(Z\), la fracción relativa de resultados \(0\to n_0/N\) nos da acceso al módulo del valor esperado de \(V\).
Observable unitario
Si \(V\) además de ser unitario, fuese hermítico, entonces \( \pm \langle V \rangle\) sería una cantidad real. Por este método, al obtener \(|\langle V\rangle |\) sólo perderíamos el signo.
Operadores de 1 cúbit unitarios y hermíticos son, por ejemplo, los operadores \(V = X,Y,Z,H\). Este argumento nos permite calcular de otra manera
Ejercicio
Calcula por este procedimiento los valores de \(\langle X\rangle_\psi\), \(\langle Y\rangle_\psi\) y \(\langle Z\rangle_\psi\) encontrados anteriormente y verifica que obtienes el mismo resultado.
from qibo.models.circuit import Circuit, gates
qc_psi_Qibo = Circuit(1)
qc_psi_Qibo.add(gates.U3(0, theta, phi, 0, trainable=True))
'fijemos el observable a medir'
P = 'Y'
if P =='X':
qc_psi_Qibo.add(gates.X(0))
elif P=='Y':
qc_psi_Qibo.add(gates.Y(0))
elif P=='Z':
qc_psi_Qibo.add(gates.Z(0))
qc_psi_Qibo.add(gates.U3(0, -theta, -phi, 0, trainable=True))
qc_psi_Qibo.add(gates.M(0))
display(tQ.draw_qibo_circuit_mpl(qc_psi_Qibo))
'ejecutamos'
nshots = 10000
counts_psi = qc_psi_Qibo(nshots=nshots).frequencies()
print(counts_psi)
' ahora solo nos interesa la fracción de 0s '
mean_P = np.sqrt(counts['0']/shots)
print('|<',P,'>| =',np.round(mean_P,5))
' el operador unitario que lo genera a partir de |0> es U(theta, phi, 0,0)'
qc_psi_U = QuantumCircuit(1,1)
qc_psi_U.u(theta,phi,0,0)
'fijemos el observable a medir'
P = 'Y'
if P =='X':
qc_psi_U.x(0)
elif P=='Y':
qc_psi_U.y(0)
elif P=='Z':
qc_psi_U.z(0)
qc_psi_U.u(-theta,-phi,0,0)
qc_psi_U.measure(0,0)
display(qc_psi_U.draw('mpl'))
' ejecutamos '
shots= 100000
counts=AerSimulator().run(qc_psi_U,shots=shots).result().get_counts()
' ahora solo nos interesa la fracción de 0s '
mean_P = np.sqrt(counts['0']/shots)
print('|<',P,'>| =',np.round(mean_P,5))
Counter({'1': <tf.Tensor: shape=(), dtype=int64, numpy=8663>, '0': <tf.Tensor: shape=(), dtype=int64, numpy=1337>})
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
Cell In[8], line 28
25 print(counts_psi)
27 ' ahora solo nos interesa la fracción de 0s '
---> 28 mean_P = np.sqrt(counts['0']/shots)
29 print('|<',P,'>| =',np.round(mean_P,5))
File /opt/anaconda3/envs/TalentQ/lib/python3.11/site-packages/tensorflow/python/util/traceback_utils.py:153, in filter_traceback.<locals>.error_handler(*args, **kwargs)
151 except Exception as e:
152 filtered_tb = _process_traceback_frames(e.__traceback__)
--> 153 raise e.with_traceback(filtered_tb) from None
154 finally:
155 del filtered_tb
File /opt/anaconda3/envs/TalentQ/lib/python3.11/site-packages/tensorflow/python/ops/numpy_ops/np_array_ops.py:1721, in _as_index(idx, need_scalar)
1719 np_dtype = data.dtype.as_numpy_dtype
1720 if not np.issubdtype(np_dtype, np.integer):
-> 1721 raise IndexError(_SLICE_ERROR + ', got {!r}'.format(idx))
1722 if data.dtype not in (dtypes.int64, dtypes.int32):
1723 # TF slicing can only handle int32/int64. So we need to cast.
1724 promoted_dtype = np.promote_types(np.int32, np_dtype)
IndexError: only integers, slices (`:`), ellipsis (`...`), numpy.newaxis (`None`) and integer or boolean arrays are valid indices, got '0'
Ejercicio
\(X,Y,Z\) y \(H\) son caso particulares de \(\hat\bn\cdot\bsig\) que es también unitario. Escribe el circuito que calcularía \(\langle \hat\bn\cdot\bsig\rangle\) y verifica que da el mismo resultado que obtuviste en un ejercicio anterior.