Jun 11, 2024 | 8 min read
Estimación de fase y aplicaciones#
\( \newcommand{\bra}[1]{\langle #1|} \) \( \newcommand{\ket}[1]{|#1\rangle} \) \( \newcommand{\braket}[2]{\langle #1|#2\rangle} \) \( \newcommand{\i}{{\color{blue} i}} \) \( \newcommand{\Hil}{{\cal H}} \) \( \newcommand{\cg}[1]{{\rm C}#1} \) \( \newcommand{\det}{{\rm det}}\)
Show code cell source
%run ../macro_tQ.py
import sys
sys.path.append('../')
import macro_tQ as tQ
Estimación Cuántica de Fase#
Cuando se construya un computador cuántico capaz de realizar una Transformada de Fourier Cuántica, una de las aplicaciones más frecuentes será la Estimación Cuántica de Fase. Veamos cómo funciona.
Sea \(U\) un operador unitario y \(\ket{u}\) un autovector del espacio de Hilbert \(\Hil\) de dimensión \(n\).
Lema:
por ser unitario, su autovalor es una fase compleja
Demostración
Una forma rápida de demostrar esto es analizar el determinante de la matriz \(U\) asociada al operdor. El determinante del producto de dos matrices tiene una propiedad importantísima, y es que factoriza
De esta propiedad se deduce, como ya se vio en el capítulo de operadores, que el determinante de un operador es independiente de la base (lo mismo le pasaba a la traza). También se deduce que el determinante de un operador unitario es una fase. Por un lado tenemos que
Por otro
Es decir, el módulo de \(\det U\) es igual a 1, y por tanto \(\det U = e^{i\varphi}\), una fase. En la base en la que \(U\) es diagonal, esta fase es el producto de los elementos de la diagonal. Por tanto, todos los autovaleres deben ser fases
s>
\(\Rightarrow~\) Promesa: podemos preparar el autoestado \(\ket{u}\) y aplicar el operador \(U\) tantas veces como sea necesario
\(\Rightarrow~\) Problema: calcular la mejor aproximación posible a \(\varphi\).
Notar
Los autovalores \(\lambda = e^{i2\pi \varphi}=e^{i2\pi (\varphi+n)}\) son iguales, para cualquier \(n\in {\mathbb Z}\) entero. Por tanto, será suficiente considerar valores de \(\varphi\) en el intervalo
para generar todos los posibles autovalores \(\lambda\) distintos
El circuito QPE (“Quantum Phase Estimation”) viene representado en la figura, donde el primer registro tiene dimensión \(t\) y la entrada es el estado \(\ket{0}_t\otimes \ket{u}\).
\(t\) es la dimensión del primer registro, o registro de contaje. Su valor controlará la precisión de nuestra aproximación a \(\varphi\).
\(n\) es la dimensión del espacio \(\Hil\) al que pertenece el vector \(\ket{u}\), sobre el que actúa \(U\).
Si el primer registro tiene \(t\) cúbits \(\{\ket{y}\}\) con \(y \in S_t = 0,1,... 2^t-1\) el estado que genera el circuito justo antes de la transformada de Fourier es el siguiente
La clave: por ser \(\ket{u}\) un autoestado de \(U\), se produce un retroceso de fase
Como de costumbre, el segundo registro ya ha realizado su labor y podemos medirlo o ignorarlo (es una ancilla). De modo que nos vamos a concentrar en el registro de contaje, de \(t\) cúbits. Para completar el circuito sólo nos faltaría aplicar la transformada de Fourier inversa. Antes de hacerlo vamos a notar que \(2^t\varphi\) es, en general, un número real. Vamos a escribirlos separando la parte entera \(a\), de la mantisa \(\delta\)
Ejemplo
Supongamos que la fase que queremos hallar sea: \(\varphi = 0.4375 \in [0,1) \). Entonces
\( 2^2 \varphi = 1.75 \Rightarrow a=1~,~\delta = 0.75\)
\( 2^3 \varphi = 3.5~ \Rightarrow a=3~,~\delta = 0.5\)
\( 2^4 \varphi = 7 ~~~ \Rightarrow a=7~,~\delta = 0\)
Cuando \(t=4\) obtenemos un número entero. En realidad ya lo habíamos cocinado así \(0.4375 = 7 / 2^{4}\). Pero si \(\varphi\) es irracional no existirá ningún \(t\) para el cuál \(2^t\varphi\) no tenga mantisa.
Ahora aplicamos la TFC inversa, que es igual a la adjunta, al primer registro
\(\ket{\Phi}\) es el estado final en el registro de contaje. Vamos a analizar su composición, separando dos casos posibles para \(\varphi\).
Supongamos que \(\underline{\delta = 0}\). Es decir, que \(2^t\varphi\) sea un entero \(a\in S_t = \{0, \ldots, 2^t-1\}\).
Entonces el resultado sería exactamente \(\ket{\Phi} = \ket{a} = \ket{2^t\varphi}\)
Midiendo el primer registro obtendríamos, con probabilidad 1, el registro binario del número \(a\in[0, 2^t-1]\)
Escribiendo \(a\) en base decimal, y normalizando por \(2^t\), recuperaríamos la fase buscada de forma exacta
Supongamos ahora que \(\underline{\delta \neq 0}.~\) Es decir, \(2^t\varphi\) no es un entero.
En este caso la transformada de Fourier inversa no produce un único estado, sino una superposición
Las amplitudes \(f(x)\) están evaluadas con \(x\in S_t\) números enteros. Pero la expresión es valida para cualquier número real \(x\in {\mathbb R}\).
Una medida dará el registro binario de un número entero \(x \in [0,2^t-1)\) con distribución de probabilidad \(p(x) = |f(x)|^2\) centrada en \(a=[2^t\varphi]\).
Por tanto, con una sucesión de tiradas podríamos reconstruir la distribución de probabilidad, y su media sería el valor buscado de \(2^t\delta\).
Distribución de probabilidad#
Para una mantisa fija, \(\delta \Rightarrow 2^t\varphi = a+\delta\) las distintas medidas del estado resultante \(\ket{x}\) se distribuirán en el torno a \(\ket{a} \).
Escribiendo \(\ket{x}=\ket{a+l}\) podemos obtener la distribución de probabilidad \(p(x) = p(\delta, l)\) de medir \(\ket{x}=\ket{a+l}\)
donde en la última línea hemos sumado una serie geométrica. Vamos a graficar esta función para distintos valores de \(\delta\)
t=4
deltalist = np.array([0.1,0.3,0.5,0.7])
shift=-2**(t-1)+1
xlist = np.arange(shift,2**(t)-1+shift)
for delta in deltalist:
plist = [(1/2**(2*t))*(np.sin(np.pi*(delta-l))/np.sin(np.pi*(delta-l)/2**t))**2 for l in xlist]
plt.plot(xlist,plist, label=f'delta = {delta}')
plt.xlabel(r'$x$')
plt.ylabel(r'$p(\delta, t)$')
plt.legend()
plt.show()
El registo de contaje sólo puede dar \(2^t\) valores distintos que hemos escogido en el intervalo \(l \in [\hbox{shift},\, 2^{t}-1+\hbox{shift})\). Puedes mover shift.
Cuando \(\delta\ll 1\) la distribución está muy picada en torno a \(l=0\), es decir \(x=a\).
A medida que \(\delta\) se acerca a \(0.5\) el valor máximo de \(p(\delta,l)\) diminuye, pero nunca es menor que \(0.405\).
Cuando \(\delta >0.5\) la distribución empieza a mostrar que el valor más aproximado para \(2^t\varphi\) es el siguiente, con \(l=1\). Es decir \(x=a+1\).
Demostración
Que la probabilidad nunca es menor de 0.405 en su máximo se puede probar analíticamente
donde hemos usado que \(\pi x\geq \sin(\pi x) \geq 2 x\) para \(x\in [ 0,\pi/2 ]\).
Si varías \(t\) comprobaras que: la anchura \(t\) del circuito de contaje determina la dispersion de la curva de probabilidad en torno al valor medio. A mayor valor de \(t\), la distribución se vuelve más estrecha.
Si queremos acotar por debajo la probabilidad de encontrar un valor de \(x\) que aproxime el valor correcto de \(2^t\varphi\) dentro de una cierta tolerancia, debemos aumentar \(t\) los suficiente. El siguiente teorema aclara definitivamente cuánto hay que hacerlo
Teorema:
El algoritmo QPE (Quantum Phase Estimation) es capaz de producir
una estimación \(x\) de orden \(k\) para la fase \(\varphi\) (en el sentido de que \(|\varphi- x/2^t |<2^{-k}\))
con una probabilidad \(1-\epsilon\)
tomando una anchura del registro de contaje, \(t\), suficientemente alta
Demostración
ver Nielsen Chuang p. 224).
Ejemplo#
Vamos a estudiar esta distribución analizando el operador unitario de fase
que tiene por autoestados \(\ket{0}\) y \(\ket{1}\) con autovalores \(e^{2\pi\phi_0}\) y \(e^{2\pi \phi_1}\) respectivamente.
Para programar el oráculo que implementa este operador notamos que
El circuito requiere la versión controlada \(\cg U^k\) de potencias de este operador
def c_Uop(phi0,phi1,power):
U = QuantumCircuit(1)
for _ in range(power):
U.rz(-phi0*4*np.pi,0)
U.p((phi1+phi0)*2*np.pi,0)
U = U.to_gate()
U.name = "c_U(%f ,%f)^%i" % (np.round(phi0,5), np.round(phi1,5),power)
U = U.control()
return U
Para poner a prueba el algoritmo vamos a por seleccionar unos valores de \(\phi_i = (\phi_0,\phi_1)\) de la forma
t_seed=3
a0 = 2.
delta0 = 0.2
phi0 = (a0+delta0)*2.**(-t_seed)
a1 = 3.
delta1 = 0.
phi1 =(a1+ delta1)*2.**(-t_seed)
print('phi0=', phi0, ' -> 2^t*phi0 = a0 + delta0 = ', 2**t_seed*phi0 , ' , a0 =' ,int(2**t_seed*phi0), '=>', format(int(2**t_seed*phi0),'b').zfill(t_seed) )
print('phi1=', phi1, ' -> 2^t*phi1 = a1 + delta1 = ', 2**t_seed*phi1 , ' , a1 =' ,int(2**t_seed*phi1), '=>', format(int(2**t_seed*phi1),'b').zfill(t_seed) )
phi0= 0.275 -> 2^t*phi0 = a0 + delta0 = 2.2 , a0 = 2 => 010
phi1= 0.375 -> 2^t*phi1 = a1 + delta1 = 3.0 , a1 = 3 => 011
Construimos el circuito QPE
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
t = 6 # la dimensión del registro de evaluación
n = 1 # la dimensión del espacio de representación del opeador U
qrt=QuantumRegister(t,name="t")
qra=QuantumRegister(n,name="qr_U") # aquí 1 es la dimensión del espacio en el que opera U
crt=ClassicalRegister(t)
qpe = QuantumCircuit(qrt,qra,crt)
##########################################################################
#inicializamos la ancilla en un autoestado |u> =|0> o |u> = |1>
#qpe.x(qra)
##########################################################################
qpe.h(range(t))
qpe.barrier()
# aplicamos los operadores controlados c-U^j
for j in range(t):
qpe.append(c_Uop(phi0,phi1,2**j),[qrt[j]]+qra[:])
qpe.barrier()
# aplicamos la QFT^-1 que está definida en macro.py
qpe.append(TFC_adj(t),qrt)
qpe.measure(qrt,crt)
qpe.draw(output="mpl")
/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)
'ejecutamos el circuito'
from qiskit.primitives import Sampler
from qiskit.visualization import plot_histogram, plot_bloch_multivector
nshots = 1000
dist = Sampler().run(qpe,shots=nshots).result().quasi_dists[0]
counts = tQ.dist2counts(dist,t,nshots)
plot_histogram(counts)
De aquí extraemos la aproximación que hemos obtenido.
max(counts, key=counts.get)
'010010'
a_resultante = int(max(counts, key=counts.get),2)
# Seleccionamos el autovalor asociado a |u>
phi = phi0 # si |0>
#phi = phi1 # si |1>
print('el valor de phi original era =', phi)
print('la mejor aproximación al valor verdadero es a/2^t =', a_resultante*2**(-t))
print('el error relativo es delta/(2^t phi) =', (phi -a_resultante*2**(-t))/phi)
el valor de phi original era = 0.275
la mejor aproximación al valor verdadero es a/2^t = 0.28125
el error relativo es delta/(2^t phi) = -0.022727272727272645
Ejercicio
vuelve a ejecutar el circuito cambiando el valor de \(\varphi_0\).
¿y si no podemos preparar un autoestado?#
Una de las premisas usadas en la sección anterior es la posibilidad de preparar el autoestado \(\ket{u}\) de \(U\)
En caso contrario, ¿qué resultado obtenemos después de usar el circuito de estimación cuántica de fase?. Vamos a aprovechar el hecho de que el conjunto \(\{\ket{u_i}\}\) de los autoestados de \(U\)
forman una base del espacio de Hilbert \(\Hil\). Apliquemos el circuito anterior a un vector arbitrario \(\ket{b}\). Genéricamente dicho vector admitirá una expansión de la forma
Debido a la linealidad del circuito QPE, a la salida del mismo encontraremos una combinación lineal de estados de la forma
Midiendo en el primer registro obtendremos un número entero \( x \) asociado a un vector \(\ket{x}\) con probabilidad
En resumen: la probabilidad \(p(x)\) es una suma ponderada de distribuciones \(p(\delta_i,l_i) = |\braket{x}{\Phi_i}|^2\) con \(x = a_i + l_i\) picadas en torno \(l_i = 0\).
Si \(t\) se escoge como en el teorema descrito anteriormente, el resultado será una aproximación \(x\) a alguno de los valores \(2^t\varphi_i\) con una probabilidad de acierto de, al menos, \(|c_i|^2 (1-\epsilon)\).
Ejemplo:
Vamos a correr el circuito QPE inicializando para una combinaciones \(\ket{b} = \cos \theta/2 \ket{0} + \sin \theta/2 \ket{1}= R_y(\theta)\ket{0}\) de autoestados de \(P(\varphi)\).
t_seed = 3
a0 = 1.
delta0 = 0.1
phi0 = (a0+ delta0)*2.**(-t_seed)
a1 = 3.
delta1 = 0.1
phi1 =(a1+ delta1)*2.**(-t_seed)
print('phi0=', phi0, ' -> 2^t*phi0 = a0 + delta0 = ', 2**t_seed*phi0 , ' , a0 =' ,int(2**t_seed*phi0), '=>', format(int(2**t_seed*phi0),'b').zfill(t_seed) )
print('phi1=', phi1, ' -> 2^t*phi1 = a1 + delta1 = ', 2**t_seed*phi1 , ' , a1 =' ,int(2**t_seed*phi0), '=>', format(int(2**t_seed*phi1),'b').zfill(t_seed) )
phi0= 0.1375 -> 2^t*phi0 = a0 + delta0 = 1.1 , a0 = 1 => 001
phi1= 0.3875 -> 2^t*phi1 = a1 + delta1 = 3.1 , a1 = 1 => 011
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
t = 6 # la dimensión del registro de evaluación
n = 1 # la dimensiónd el espacio de representación del opeador U
qrt=QuantumRegister(t,name="t")
qra=QuantumRegister(n,name="qr_U") # aquí 1 es la dimensión del espacio en el que opera U
crt=ClassicalRegister(t)
qpe = QuantumCircuit(qrt,qra,crt)
##########################################################################
#inicializamos la ancilla en una combinación cos(th/2)|0> + sin(th/2)|1>
theta = 2*np.pi*0.15
qpe.ry(theta,qra)
##########################################################################
qpe.h(range(t))
qpe.barrier()
# aplicamos los operadores controlados c-U^j
for j in range(t):
qpe.append(c_Uop(phi0,phi1,2**j),[qrt[j]]+qra[:])
# qpe.cp(2*np.pi*phi1*(2**j), qrt[j], qra); # This is C-U
qpe.barrier()
# aplicamos la QFT^-1 que está definida en macro.py
qpe.append(TFC_adj(t),qrt)
# podemos hacerlo usando la librería de qiskit
#from qiskit.circuit.library import QFT
#qpe.append(QFT(len(qr),do_swaps=True).inverse(),qr)
qpe.measure(qrt,crt)
qpe.draw(output="mpl")
/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)
'ejecutamos el circuito'
from qiskit.primitives import Sampler
from qiskit.visualization import plot_histogram, plot_bloch_multivector
nshots = 1000
dist = Sampler().run(qpe,shots=nshots).result().quasi_dists[0]
counts = tQ.dist2counts(dist,t,nshots)
plot_histogram(counts)
Orden modular#
A continuación vamos a estudiar un importante problema matemático que está debajo del algoritmo de Shor, que puede formularse como un problema de estimación de fase.
Definición: Orden modular
Sean \(m,N \in {\mathbb N}\) dos enteros \(m<N\) coprimos (sin divisores comunes). Definimos el orden de \(m\) módulo \(N\) como el menor entero positivo, \(r\), tal que \(m^r = 1\) mod\(N\).
Promesa: \(U\) es el oráculo de una función modular \((m,N)\) de orden \(r\).
Problema: hallar \(r\) con el número mínimo de consultas.
'Ejemplo'
{2,4,7,8,11,13} # comprimos con 15
m=7
N=15
[m**i%N for i in range(17)]
[1, 7, 4, 13, 1, 7, 4, 13, 1, 7, 4, 13, 1, 7, 4, 13, 1]
Nota
En general, si \(N=2^n\), clásicamente no existen algoritmos capaces de resolver este problema en un número de pasos que sea polinómico en \(n\). Por el contrario, verificar si un \(r\) es solución es fácil. Nos hallamos ante un problema de tipo NP.
Como \(m^r = 1 = m^0\), a partir de ese momento la serie de valores \(\{m^0,m^1,\ldots m^{r-1}\}\) se repite. Por tanto, hallar el orden modular de \(m\) módulo \(N\) es lo mismo que hallar la periodicidad de la función \(f(x) = m^x\, \)mod\(N\).
Si tratamos de fabricar un oráculo para el operador unitario \(U_f:\ket{x}\ket{y} \to \ket{x}\ket{m^x y \,{\rm mod} N}\) nos encontraremos con una complejidad exponencial. En este caso, sin embargo, la estructura del problema permite una simplificación.
Supongamos que sabemos implementar un oráculo \(U ; \Hil_n \to \Hil_n \) que actúa en la forma siguiente sobre la base \(\{\ket{y}\}\) donde \(y \in S_n = {0,1,...,N-1}\)
La composición \(U^x = \underbrace{U\cdot U \cdots U}_{x}\) genera todos los vectores que buscamos
El orden de \(m\) mod\(N\) es claramente el número \(r\) tal que
Con \(U\) podríamos implementar la función \(f(x) = m^x{\rm mod} N\) en la forma
y buscar la periodicidad de \(f(x+r) = f(x)\) siguiento el método de la lección anterior.
La acción de \(U_f\) parece mucho a la de un operador controlado \(\cg{U}\ket{x}\ket{y} = \ket{x}U^x\ket{y}\). Sin embargo ahora \(x = 0,1,...,2^t-1\) es un entero.
Este operador controlado es exactamente el que se utiliza en el circuito de QPE. Con esta observación vamos a aplicar el método de Estimación Cuántica de Fase (QPE) al operador \(U\)
Recordemos que dicho método nos pide evaluar el operador \(U\) sobre sus autovectores \(U\ket{u} = e^{i\varphi}\ket{u}\). Pero… ¿conocemos estos autovectores? Resulta que, si conociésemos \(r\) podríamos construir el siguiente conjunto de \(r\) vectores \(\ket{u_s}\) con \(s=0,...,r-1\)
Nótese el papel importante que juega ahora el estado de la base \(\ket{1} = \ket{0...01}\)
Teorema:
Los vectores \(\{\ket{u_s}\}\) forman un conjunto de autovectores de \(U\) con autovalores \(e^{2\pi i s/r}~\). Es decir
con $\( \varphi_s = s/r~~~~~~ s=0,...,r-1 \)$
Demostración
El problema es que \(r\) es, precisamente, ¡el dato que queremos encontrar! Una observación importante que nos permite soslayar esta dificultad es la siguiente: vamos a tomar una combinación homogénea de los autoestados \(\ket{u_s}\)
¡El resultado es asombrosamente simple!: si inicializamos el circuito de estimación de fase con el estado \(\ket{0}_t\ket{1}_n\) a la salida obtendremos una superposición uniforme
donde \(x_s = 2^t s/r\) siempre que \(t\) se haya elegido de forma que todos los número \(~2^t s/r \in {\mathbb Z}\) sean enteros
¡A programar!#
Necesitamos un oráculo que implemente la operación
El siguiente código implementa este oráculo para \(N=15\) y \(m\in \{2,4,7,8,11,13\}\) coprimos, y lo convierte en una puerta controlada
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
def c_amod15(m, power):
"""Controlled multiplication by a mod 15"""
if m not in [2,4,7,8,11,13]:
raise ValueError("'a' must be 2,4,7,8,11 or 13")
U = QuantumCircuit(4)
for iteration in range(power):
if m in [2,13]:
U.swap(2,3)
U.swap(1,2)
U.swap(0,1)
if m in [7,8]:
U.swap(0,1)
U.swap(1,2)
U.swap(2,3)
if m in [4, 11]:
U.swap(1,3)
U.swap(0,2)
if m in [7,11,13]:
for q in range(4):
U.x(q)
# print(U)
U = U.to_gate()
U.name = "%i^%i mod 15" % (m, power)
c_U = U.control()
return c_U
c_amod15(4,3 ).definition.draw('mpl')
/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)
Nota
Función definida en Qiskit Textbook
No hay una prescripción general para valores arbitrarios de \(m\) y \(N\) que no pase por escribir toda la tabla de verdad. No queremos eso porque sabemos que no es eficiente.
Ejercicio
Construye un circuito con esta puerta y verifica que reproduce la lista \(m^x\) mod 15 con \(x=0,1,2,...\)
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
t = 4
n = 4
m = 7
qr=QuantumRegister(t,name="t")
ar=QuantumRegister(4,name="qr_U") # aquí 1 es la dimensión del espacio en el que opera U
cr=ClassicalRegister(t)
qc = QuantumCircuit(qr,ar,cr)
qc.x(ar[0]) # inicializamos la ancilla al autoestado de P(phi) -> |u> = |1>
#qpe.h(ar[0]) #
qc.h(range(n))
qc.barrier()
# aplicamos los operadores controlados c-U^j
for q in range(t):
qc.append(c_amod15(m, 2**q), [qr[q]] + ar[:])
qc.barrier()
# aplicamos la QFT^-1 que está definida en macro.py
qc.append(TFC_adj(t),qr)
qc.measure(qr,cr)
qc.draw(output="mpl")
Ejecutamos el circuito y listamos los suceso más probables
'ejecutamos el circuito'
from qiskit.primitives import Sampler
from qiskit.visualization import plot_histogram, plot_bloch_multivector
nshots = 1000
dist = Sampler().run(qc,shots=nshots).result().quasi_dists[0]
counts = tQ.dist2counts(dist,t,nshots)
plot_histogram(counts)
de aquí obtenemos la lista de valores enteros \(2^t s/r\)
Ns_list = sorted([int(keys,2) for keys in counts])[1:]
print('[2^ts/r] ~ ', Ns_list)
# dividiendo por 2**t
phis_list = [Ns_list[i]/2**t for i in range(len(Ns_list))]
print('phi_s', phis_list)
[2^ts/r] ~ [4, 8, 12]
phi_s [0.25, 0.5, 0.75]
Ahora podemos usar, como antes, dos estrategias para extraer el valor del período \(r\):
analizando las diferencias: \(r = \displaystyle \left(\frac{s+1}{r} - \frac{s}{r}\right)^{-1}\)
differences = [int((phis_list[s+1]-phis_list[s])**(-1)) for s in range(len(phis_list)-1)]
print(differences)
print('el valor entero del promedio =', int(sum(differences)/len(differences)))
[4, 4]
el valor entero del promedio = 4
usando la representación en fracciones continuas: \(\, \displaystyle \varphi_s = \frac{s}{r}\)
from fractions import Fraction
print('[s/r]=',[Fraction(phis_list[i]).limit_denominator(20) for i in range(int(len(phis_list)))])
[s/r]= [Fraction(1, 4), Fraction(1, 2), Fraction(3, 4)]
Notar
En el caso más general \(\varphi_s = 2^t s/r \notin {\mathbb Z}\) no será un entero
Entonces el resultado de la medida \(m_s\) seguirá una distribución de probabilidad picada en torno a la parte entera \(a_s = [2^t s/r]\)
Haciendo un número alto de medidas y seleccionando los eventos \(m_s = a_s\) más probables podemos recuperar \(r\) por los mismos métodos anteriores.