Nota
Esta página fue generada a partir de tutorials/algorithms/09_IQPE.ipynb.
Algoritmo de Estimación de Fase Cuántica Iterativa¶
El objetivo de este tutorial es comprender cómo funciona el algoritmo de Estimación de Fase Iterativa (Iterative Phase Estimation, IPE), por qué usaríamos el algoritmo IPE en lugar del algoritmo QPE (Quantum Phase Estimation, Estimación de Fase Cuántica) y cómo construirlo con Qiskit utilizando el mismo circuito aprovechando la compuerta de reinicio y el método c_if
que permite aplicar compuertas condicionadas por los valores almacenados en un registro clásico, resultantes de mediciones previas.
Referencias
Compuertas condicionadas: el método c_if
¶
Antes de iniciar el algoritmo IPE, vamos a dar un breve tutorial sobre el método condicional de Qiskit, c_if
, a medida que se avanza en la construcción del circuito IPE.
c_if
es una función (en realidad, un método de la clase compuerta) para realizar operaciones condicionadas basadas en el valor almacenado previamente en un registro clásico. Con esta función puedes aplicar compuertas después de una medición en el mismo circuito condicionado por el resultado de la medición.
Por ejemplo, el siguiente código ejecutará la compuerta \(X\) si el valor del registro clásico es \(0\).
[1]:
from qiskit import QuantumCircuit
qc = QuantumCircuit(1, 1)
qc.h(0)
qc.measure(0,0)
qc.x(0).c_if(0, 0)
qc.draw(output='mpl')
[1]:

Resaltamos que el método c_if
espera como primer argumento un registro clásico completo, no un solo bit clásico (o una lista de bits clásicos), y como segundo argumento un valor en representación decimal (un entero no negativo), no el valor de un solo bit, 0, o 1 (o una lista/cadena de dígitos binarios).
Hagamos otro ejemplo. Considera que queremos realizar un cambio de bit en el tercer qubit después de las mediciones en el siguiente circuito, cuando los resultados de la medición de \(q_0\) y \(q_1\) son ambos \(1\).
[2]:
from qiskit import QuantumRegister, ClassicalRegister
q = QuantumRegister(3, 'q')
c = ClassicalRegister(3, 'c')
qc = QuantumCircuit(q, c)
qc.h([0, 1, 2])
qc.barrier()
qc.measure(q, c)
qc.draw('mpl')
[2]:

Queremos aplicar la compuerta \(X\), solo si ambos resultados de la medición de \(q_0\) y \(q_1\) son \(1\). Podemos hacer esto usando el método c_if
, condicionando la aplicación de \(X\) dependiendo del valor pasado como argumento a c_if
.
Tendremos que codificar el valor para pasar al método c_if
de manera que verifique los valores 011 y 111 (en representación binaria), ya que no importa cómo sea medido \(q_2\).
Los 2 valores enteros en representación decimal:
Podemos verificar las soluciones usando el método bin()
en python (el prefijo 0b
indica el formato binario).
[3]:
print(bin(3))
print(bin(7))
0b11
0b111
Entonces tenemos que aplicar \(X\) a \(q_2\) usando c_if
dos veces, una para cada valor correspondiente a 011 y 111.
[4]:
qc.x(2).c_if(c, 3) # for the 011 case
qc.x(2).c_if(c, 7) # for the 111 case
qc.draw(output='mpl')
[4]:

IPE¶
La motivación para usar el algoritmo IPE es que el algoritmo QPE funciona bien para circuitos de poca profundidad, pero cuando el circuito comienza a crecer, no funciona correctamente debido al ruido de la compuerta y los tiempos de decoherencia.
La explicación detallada de cómo funciona el algoritmo se puede encontrar en Algoritmo de Estimación de Fase Iterativa (IPE). Para comprender la QPE en profundidad, puedes consultar también Ch.3.6 Estimación de Fase Cuántica.
Ejemplo de IPE con una compuerta de 1 qubit para \(U\)¶
Queremos aplicar el algoritmo IPE para estimar la fase de un operador \(U\) de 1 qubit. Por ejemplo, aquí usamos la compuerta \(S\).
Apliquemos el algoritmo IPE para estimar la fase de la compuerta \(S\). Su matriz es
Es decir, la compuerta \(S\) agrega una fase de \(\pi/2\) al estado \(|1\rangle\), dejando sin cambios a la fase del estado \(|0\rangle\)
En lo siguiente, utilizaremos la notación y los términos utilizados en la Sección 2 del laboratorio 4.
Consideremos estimar la fase \(\phi=\frac{\pi}{2}\) para el estado propio \(|1\rangle\), deberíamos encontrar \(\varphi=\frac{1}{4}\) (donde \(\phi = 2 \pi \varphi\)). Por lo tanto, para estimar la fase necesitamos exactamente 2 bits de fase, es decir, \(m=2\), ya que \(1/2^2=1/4\). Entonces \(\varphi=0.\varphi_1\varphi_2\).
Recuerda de la teoría que para el algoritmo IPE, \(m\) es también el número de iteraciones, por lo que solo necesitamos \(2\) iteraciones o pasos.
Primero, inicializamos el circuito. IPE trabaja con solo 1 qubit auxiliar, en lugar de \(m\) qubits contados del algoritmo QPE. Por lo tanto, necesitamos 2 qubits, 1 qubit auxiliar y 1 para el estado propio de la compuerta \(U\), y un registro clásico de 2 bits, para los bits de fase \(\varphi_1\), \(\varphi_2\).
[5]:
nq = 2
m = 2
q = QuantumRegister(nq, 'q')
c = ClassicalRegister(m, 'c')
qc_S = QuantumCircuit(q,c)
Primer paso¶
Ahora construimos el circuito cuántico para el primer paso, es decir, la primera iteración del algoritmo, para estimar el bit de fase menos significativo \(\varphi_m\), en este caso \(\varphi_2\). Para el primer paso tenemos 3 subpasos: - inicialización - aplicación de las compuertas \(U\) controladas - medición del qubit auxiliar en la base X
Inicialización¶
La inicialización consiste en la aplicación de la compuerta Hadamard al qubit auxiliar y la preparación del estado propio \(|1\rangle\).
[6]:
qc_S.h(0)
qc_S.x(1)
qc_S.draw('mpl')
[6]:

Aplicación de las compuertas \(U\) controladas¶
Luego tenemos que aplicar \(2^t\) veces los operadores \(U\) controlados (ver también en la documentación Compuertas de dos qubits), que, en este ejemplo, es la compuerta \(S\) controlada (\(CS\) para abreviar).
Para implementar \(CS\) en el circuito, ya que \(S\) es una compuerta de fase, podemos usar la compuerta de fase controlada \(\text{CP}(\theta)\), con \(\theta=\pi/2\).
[7]:
from math import pi
cu_circ = QuantumCircuit(2)
cu_circ.cp(pi/2, 0, 1)
cu_circ.draw('mpl')
[7]:

Apliquemos \(2^t\) veces \(\text{CP}(\pi/2)\). Dado que para el primer paso \(t=m-1\), y \(m=2\), tenemos \(2^t=2\).
[8]:
for _ in range(2 ** (m - 1)):
qc_S.cp(pi/2, 0, 1)
qc_S.draw('mpl')
[8]:

Medición en la base X¶
Finalmente, realizamos la medición del qubit auxiliar en la base X. Así que definiremos una función para realizar la x_measurement
y luego la aplicaremos.
[9]:
def x_measurement(qc, qubit, cbit):
"""Measure 'qubit' in the X-basis, and store the result in 'cbit'"""
qc.h(qubit)
qc.measure(qubit, cbit)
De esta forma obtenemos el bit de fase \(\varphi_2\) y lo almacenamos en el bit clásico \(c_0\).
[10]:
x_measurement(qc_S, q[0], c[0])
qc_S.draw('mpl')
[10]:

Pasos posteriores (segundo paso)¶
Ahora construimos el circuito cuántico para los otros pasos restantes, en este ejemplo, solo el segundo. En estos pasos tenemos 4 subpasos: los 3 subpasos como en el primer paso y, en medio, el paso adicional de la corrección de fase - inicialización con restablecimiento - corrección de fase - aplicación de la compuerta \(U\) controlada - medición del qubit auxiliar en la base X
Inicialización con restablecimiento¶
Como queremos realizar un algoritmo iterativo en el mismo circuito, necesitamos restablecer el qubit auxiliar \(q_0\) después de la compuerta de medición e inicializarlo nuevamente como antes para reciclar el qubit.
[11]:
qc_S.reset(0)
qc_S.h(0)
qc_S.draw('mpl')
[11]:

Corrección de fase (para el paso 2)¶
Como se ve en la teoría, para extraer el bit de fase \(\varphi_{1}\), realizamos una corrección de fase de \(-\pi\varphi_2/2\). Por supuesto, necesitamos aplicar la corrección de fase en el circuito solo si el bit de fase \(\varphi_2=1\), es decir, tenemos que aplicar la corrección de fase de \(-\pi/2\) solo si el bit clásico \(c_0\) es 1.
Entonces, después del restablecimiento, aplicamos la compuerta de fase \(P(\theta)\) con fase \(\theta=-\pi/2\) condicionada por el bit clásico \(c_0\) (\(=\varphi_2\)) usando el método c_if
. Entonces, como vimos en la primera parte de este tutorial, tenemos que usar el método c_if
con un valor de 1, como \(1_{10} = 001_{2}\) (los subíndices \(_{10}\) y \(_2\) indican las representaciones decimal y binaria).
[12]:
qc_S.p(-pi/2, 0).c_if(c, 1)
qc_S.draw('mpl')
[12]:

Aplicación de las compuertas \(U\) controladas y medición en x (para el paso 2)¶
Aplicamos las operaciones \(CU\) como hicimos en el primer paso. Para el segundo paso tenemos \(t=m-2\), por lo tanto \(2^t=1\). Entonces aplicamos \(\text{CP}(\pi/2)\) una vez. Y luego realizamos la medición en X del qubit \(q_0\), almacenando el resultado, el bit de fase \(\varphi_1\), en el bit \(c_1\) del registro clásico.
[13]:
## 2^t c-U operations (with t=m-2)
for _ in range(2 ** (m - 2)):
qc_S.cp(pi/2, 0, 1)
x_measurement(qc_S, q[0], c[1])
Y listo, tenemos nuestro circuito final
[14]:
qc_S.draw('mpl')
[14]:

Probemos el circuito con la primitiva Sampler
de Qiskit Aer, un simulador local sin ruido que se ejecuta localmente.
[15]:
import matplotlib.pyplot as plt
from qiskit.tools.visualization import plot_histogram
from qiskit_aer.primitives import Sampler
sampler = Sampler()
job = sampler.run(qc_S)
result = job.result()
dist0 = result.quasi_dists[0]
key_new = [str(key/2**m) for key in list(dist0.keys())]
dist1 = dict(zip(key_new, dist0.values()))
fig, ax = plt.subplots(1,2)
plot_histogram(dist0, ax=ax[0])
plot_histogram(dist1, ax=ax[1])
plt.tight_layout()

En la imagen tenemos los mismos histogramas, pero a la izquierda tenemos en el eje x la cadena con bits de fase \(\varphi_1\), \(\varphi_2\) y a la derecha la fase real \(\varphi\) en representación decimal.
Como esperábamos, hemos encontrado \(\varphi=\frac{1}{4}=0.25\) con una probabilidad de \(100\%\).
Ejemplo de IPE con una compuerta de 2 qubits¶
Ahora, queremos aplicar el algoritmo IPE para estimar la fase para una compuerta \(U\) de 2 qubits. Para este ejemplo, consideremos la versión controlada de la compuerta \(T\), es decir, la compuerta \(U=\textrm{Controlled-}T\) (que a partir de ahora expresaremos de manera más compacta con \(CT\)). Su matriz es
Es decir, la compuerta \(CT\) agrega una fase de \(\pi/4\) al estado \(|11\rangle\), dejando sin cambios la fase de los otros estados de la base computacional \(|00\rangle\), \(|01\rangle\), \(|10\rangle\).
Consideremos estimar la fase \(\phi=\pi/4\) para el estado propio \(|11\rangle\), deberíamos encontrar \(\varphi=1/8\), ya que \(\phi = 2 \pi \varphi\). Por lo tanto, para estimar la fase necesitamos exactamente 3 bits clásicos, es decir, \(m=3\), ya que \(1/2^3=1/8\). Entonces \(\varphi=0.\varphi_1\varphi_2\varphi_3\).
Como se hizo con el ejemplo para el operador \(U\) de 1 qubit, seguiremos los mismos pasos, pero esta vez tendremos \(3\) pasos ya que \(m=3\), y no repetiremos todas las explicaciones. Entonces, para obtener más detalles, consulta el ejemplo anterior para la compuerta \(U\) de 1 qubit.
Primero, inicializamos el circuito con 3 qubits, 1 para el qubit auxiliar y 2 para la compuerta de 2 qubits, y 3 bits clásicos para almacenar los bits de fase \(\varphi_1\), \(\varphi_2\), \(\varphi_3\).
[16]:
nq = 3 # number of qubits
m = 3 # number of classical bits
q = QuantumRegister(nq,'q')
c = ClassicalRegister(m,'c')
qc = QuantumCircuit(q,c)
Primer paso¶
Ahora construimos el circuito cuántico para el primer paso, para estimar el bit de fase menos significativo \(\varphi_m=\varphi_3\).
Inicialización¶
Inicializamos el qubit auxiliar y los otros qubits con el estado propio \(|11\rangle\).
[17]:
qc.h(0)
qc.x([1, 2])
qc.draw('mpl')
[17]:

Aplicación de las compuertas \(U\) controladas¶
Luego tenemos que aplicar varias veces el operador \(CU\), que, en este ejemplo, es la compuerta \(CT\) controlada (\(CCT\) para abreviar).
Para implementar \(CCT\) en el circuito, ya que \(T\) es una compuerta de fase, podemos usar la compuerta de fase multicontrolada \(\text{MCP}(\theta)\), con \(\theta=\pi/4\).
[18]:
cu_circ = QuantumCircuit(nq)
cu_circ.mcp(pi/4, [0, 1], 2)
cu_circ.draw('mpl')
[18]:

Apliquemos \(2^t\) veces \(\text{MCP}(\pi/4)\). Dado que para el primer paso \(t=m-1\) y \(m=3\), tenemos \(2^t=4\).
[19]:
for _ in range(2 ** (m - 1)):
qc.mcp(pi/4, [0, 1], 2)
qc.draw('mpl')
[19]:

Medición en la base X¶
Finalmente, realizamos la medición del qubit auxiliar en la base X. Podemos usar la función x_measurement
definida anteriormente en el ejemplo para la compuerta de 1 qubit. De esta forma hemos obtenido el bit de fase \(\varphi_3\) y lo almacenamos en el bit clásico \(c_0\).
[20]:
x_measurement(qc, q[0], c[0])
qc.draw('mpl')
[20]:

Pasos posteriores (segundo, tercero)¶
Ahora construimos el circuito cuántico para los otros pasos restantes, el segundo y el tercero. Como se dijo en el primer ejemplo, en estos pasos tenemos el subpaso adicional de la corrección de fase.
Corrección de fase (para el paso 2)¶
Para extraer el bit de fase \(\varphi_{2}\), realizamos una corrección de fase de \(-\pi\varphi_3/2\).
Entonces, después del restablecimiento, aplicamos la compuerta de fase \(P(\theta)\) con la fase \(\theta=-\pi/2\) condicionada por el bit clásico \(c_0\) (\(=\varphi_3\)).
[22]:
qc.p(-pi/2, 0).c_if(c, 1)
qc.draw('mpl')
[22]:

Aplicación de las compuertas \(U\) controladas y medición en x (para el paso 2)¶
Aplicamos las operaciones \(CU\) como hicimos en el primer paso. Para el segundo paso tenemos \(t=m-2\), por lo tanto \(2^t=2\). Entonces aplicamos \(\text{MCP}(\pi/4)\) \(2\) veces. Y luego realizamos la medición en X del qubit \(q_0\), almacenando el bit de fase \(\varphi_2\) en el bit \(c_1\).
[23]:
for _ in range(2 ** (m - 2)):
qc.mcp(pi/4, [0, 1], 2)
x_measurement(qc, q[0], c[1])
qc.draw('mpl')
[23]:

Todos los subpasos del 3er paso¶
Para el tercer y último paso, realizamos el restablecimiento e inicialización del qubit auxiliar como se hizo en el segundo paso.
Luego, en el tercer paso, tenemos que realizar la corrección de fase de \(-2\pi 0.0\varphi_{2}\varphi_{3}= -2\pi \left(\frac{\varphi_2}{4}+\frac{\varphi_3}{8}\right)=-\frac{\varphi_2\pi}{2}-\frac{ \varphi_3\pi}{4}\), por lo que tenemos que aplicar 2 correcciones de fase condicionadas, una condicionada por \(\varphi_3\) (\(=c_0\)) y la otra por \(\varphi_2\)(\(=c_1\)). Para ello tenemos que aplicar lo siguiente: - compuerta \(P(-\pi/4)\) condicionada por \(c_0=1\), es decir, por \(c=001\) (c_if
con valor \(1\)) - compuerta \(P(-\pi/2)\) condicionada por \(c_1=1\), es decir, la compuerta se aplica cuando \(c=010\) (c_if
con valor \(2\)) - compuerta \(P(-3\pi/4)\) condicionada por \(c_1=1\) y \(c_0=1\) es decir, la compuerta se aplica cuando \(c=011\) (c_if
con valor \(3\))
A continuación, las operaciones \(CU\): aplicamos \(2^t\) veces la compuerta \(\text{MCP}(\pi/4)\) y dado que en el 3er paso \(t=m-3=0\), aplicamos la compuerta solo una vez.
[24]:
# initialization of qubit q0
qc.reset(0)
qc.h(0)
# phase correction
qc.p(-pi/4, 0).c_if(c, 1)
qc.p(-pi/2, 0).c_if(c, 2)
qc.p(-3*pi/2, 0).c_if(c, 3)
# c-U operations
for _ in range(2 ** (m - 3)):
qc.mcp(pi/4, [0, 1], 2)
# X measurement
qc.h(0)
qc.measure(0, 2)
qc.draw('mpl')
[24]:

Ahora, ejecutamos el circuito con el simulador sin ruido.
[25]:
result = sampler.run(qc).result()
dist0 = result.quasi_dists[0]
key_new = [str(key/2**m) for key in list(dist0.keys())]
dist1 = dict(zip(key_new, dist0.values()))
fig, ax = plt.subplots(1,2)
plot_histogram(dist0, ax=ax[0])
plot_histogram(dist1, ax=ax[1])
plt.tight_layout()

Hemos obtenido \(100\%\) de probabilidad de encontrar \(\varphi=0.125\), es decir, \(1/8\), como se esperaba.
[26]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
Version Information
Qiskit Software | Version |
---|---|
qiskit-terra | 0.23.0.dev0+1b4fed3 |
qiskit-aer | 0.11.1 |
qiskit-nature | 0.5.0 |
System information | |
Python version | 3.9.13 |
Python compiler | Clang 12.0.0 |
Python build | main, Oct 13 2022 16:12:30 |
OS | Darwin |
CPUs | 4 |
Memory (Gb) | 32.0 |
Fri Dec 09 16:18:07 2022 CET |
This code is a part of Qiskit
© Copyright IBM 2017, 2022.
This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.