Tutorial: Implementación de la transformación de Quantum Fourier en Q#
En este tutorial, se muestra cómo escribir y simular un programa cuántico que funciona en cúbits individuales.
Aunque Q# se creó como un lenguaje de programación genérico para programas cuánticos a gran escala, puede utilizarse también para explorar el nivel inferior de los programas cuánticos, es decir, dirigirse directamente a cúbits específicos. En concreto, en este tutorial vamos a ver más de cerca la transformación cuántica de Fourier (QFT), una subrutina que forma parte de muchos algoritmos cuánticos de mayor tamaño.
En este tutorial, aprenderá a:
- Defina las operaciones cuánticas en Q#.
- Escribir el circuito de transformación Quantum Fourier
- Simulación de una operación cuántica desde la asignación de cúbits a la salida de medición.
- Observe cómo evoluciona la función de onda simulada del sistema cuántico a lo largo de la operación.
Nota:
Esta vista general del procesamiento cuántico de la información suele describirse en términos de circuitos cuánticos, que representan la aplicación secuencial de puertas, u operaciones, a cúbits específicos de un sistema. Así, las operaciones de uno y varios cúbits que aplique secuencialmente pueden representarse fácilmente en un diagrama de circuitos. Por ejemplo, la transformación de Fourier cuántico de tres cúbits completa que se usa en este tutorial tiene la siguiente representación como un circuito:
Sugerencia
Si desea acelerar el recorrido de la computación cuántica, consulte Código con Azure Quantum, una característica única del sitio web de Azure Quantum. Aquí puede ejecutar ejemplos integrados Q# o sus propios Q# programas, generar código nuevo Q# a partir de las indicaciones, abrir y ejecutar el código en VS Code para la Web con un solo clic y hacer preguntas a Copilot sobre la computación cuántica.
Requisitos previos
La última versión de Visual Studio Code o abrir VS Code en la Web.
La versión más reciente de la extensión de AzureQuantum Development Kit. Para obtener más información sobre la instalación, consulte Instalación del QDK en VS Code.
Si desea usar cuadernos de Jupyter Notebook, también debe instalar Python y extensiones de Jupyter y el paquete de Python más reciente
qsharp
. Para ello, abra un terminal y ejecute el siguiente comando:$ pip install --upgrade qsharp
Crear un nuevo Q# archivo
- En VS Code, seleccione Archivo nuevo archivo > de texto.
- Guarde el archivo como QFTcircuit.qs. Este archivo contendrá el Q# código del programa.
- Abra QFTcircuit.qs.
Escribir un circuito QFT en Q#
La primera parte de este tutorial consiste en definir la operación de Q# Main
, que realiza la transformación cuántica de Fourier sobre tres cúbits. La función DumpMachine
se usa para observar cómo evoluciona la función de onda simulada del sistema de tres cúbits a lo largo de la operación. En la segunda parte del tutorial, agregará funcionalidad de medición y comparará los estados previos y posteriores a la medición de los cúbits.
Compilará la operación paso a paso. Copie y pegue el código en las secciones siguientes en el archivo QFTcircuit.qs .
Puede ver el código completo Q# de esta sección como referencia.
Importación de bibliotecas necesarias Q#
Dentro del Q# archivo, importe los espacios de nombres pertinentes Microsoft.Quantum.*
.
import Microsoft.Quantum.Diagnostics.*;
import Microsoft.Quantum.Math.*;
import Microsoft.Quantum.Arrays.*;
// operations go here
Definición de operaciones con argumentos y retornos
A continuación, se define la operación Main
:
operation Main() : Unit {
// do stuff
}
La Main()
operación nunca toma argumentos y, por ahora, devuelve un Unit
objeto , que es análogo a devolver void
en C# o una tupla vacía, Tuple[()]
, en Python.
Después, modificará la operación para que devuelva una matriz de resultados de medida.
Asignación de cúbits
Dentro de la Q# operación, asigne un registro de tres cúbits con la use
palabra clave . Con use
, los cúbits se asignan automáticamente en el estado $\ket{0}$.
use qs = Qubit[3]; // allocate three qubits
Message("Initial state |000>:");
DumpMachine();
Al igual que la computación cuántica real, Q# no permite acceder directamente a los estados de los cúbits. Sin embargo, la DumpMachine
operación imprime el estado actual de la target máquina, por lo que puede proporcionar información valiosa para la depuración y el aprendizaje cuando se usa junto con el simulador de estado completo.
Aplicación de operaciones de un solo cúbit y controladas
A continuación, aplicará las operaciones que componen la Main
propia operación. Q# ya contiene muchas de estas y otras operaciones cuánticas básicas, en el Microsoft.Quantum.Intrinsic
espacio de nombres .
Nota:
Tenga en cuenta que no se importó en el fragmento de código anterior con los otros espacios de nombres, ya que Microsoft.Quantum.Intrinsic
el compilador lo carga automáticamente para todos los Q# programas.
La primera operación aplicada es la operación H
(Hadamard) al primer cúbit:
Para aplicar una operación a un cúbit específico de un registro (por ejemplo, un solo Qubit
de una matriz Qubit[]
) use la notación de índice estándar.
Así, la aplicación de la operación H
al primer cúbit del registro qs
toma la siguiente forma:
H(qs[0]);
Además de aplicar la operación H
(Hadamard) a los cúbits individuales, el circuito de QFT consiste principalmente en rotaciones de R1
controladas. Una R1(θ, <qubit>)
operación en general deja el componente $\ket$ del cúbit sin cambios al aplicar una rotación de $e^{i\theta}$ al componente $\ket{0}{1}$.
Q# permite condicionar fácilmente la ejecución de una operación a uno o varios cúbits de control. En general, se antepone Controlled
a la llamada, y los argumentos de la operación cambian de la siguiente manera:
Op(<normal args>)
$\to$ Controlled Op([<control qubits>], (<normal args>))
Tenga en cuenta que el argumento de los cúbits de control deben proporcionarse como una matriz, aunque se trate de un solo cúbit.
Las operaciones controladas en QFT son las R1
operaciones que actúan en el primer cúbit (y controladas por los cúbits segundo y tercero):
En el archivo de Q#, llame a estas operaciones con estas instrucciones:
Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));
La función PI()
se utiliza para definir las rotaciones en términos de radianes pi.
Aplicar operación SWAP
Después de aplicar las operaciones pertinentes H
y las rotaciones controladas al segundo y tercer cúbits, el circuito tiene el siguiente aspecto:
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
Por último, se aplica una SWAP
operación a los cúbits primero y tercer para completar el circuito. Esto es necesario porque la naturaleza de la transformación cuántica de Fourier hace que los cúbits salgan en orden inverso, por lo que los intercambios permiten una integración perfecta de la subrutina en algoritmos de mayor tamaño.
SWAP(qs[2], qs[0]);
Ahora que ha terminado de escribir las operaciones en el nivel de cúbit de la transformación cuántica de Fourier en nuestra operación de Q#:
Desasignación de cúbits
El último paso es llamar de nuevo a DumpMachine()
para ver el estado después de la operación y desasignar los cúbits. Los cúbits estaban en el estado $\ket{0}$ cuando los asignó y deben restablecerse a su estado inicial mediante la operación ResetAll
.
Requerir que todos los cúbits se restablezcan explícitamente a $\ket{0}$ es una característica básica de Q#, ya que permite que otras operaciones sepan su estado exactamente cuando comienzan a usar esos mismos cúbits (un recurso insuficiente). Esto también asegura que no se entrelazarán con ningún otro cúbit del sistema. Si el restablecimiento no se realiza al final de un bloque de asignación use
, puede producirse un error en tiempo de ejecución.
Agregue las siguientes líneas al archivo de Q#:
Message("After:");
DumpMachine();
ResetAll(qs); // deallocate qubits
La operación de QFT completa
El Q# programa se ha completado. Su archivo QFTcircuit.qs debería tener este aspecto:
import Microsoft.Quantum.Diagnostics.*;
import Microsoft.Quantum.Math.*;
import Microsoft.Quantum.Arrays.*;
operation Main() : Unit {
use qs = Qubit[3]; // allocate three qubits
Message("Initial state |000>:");
DumpMachine();
//QFT:
//first qubit:
H(qs[0]);
Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
SWAP(qs[2], qs[0]);
Message("After:");
DumpMachine();
ResetAll(qs); // deallocate qubits
}
Ejecución del circuito QFT
Por ahora, la Main
operación no devuelve ningún valor: la operación devuelve Unit
el valor . Más adelante, modificará la operación para devolver una matriz de resultados de medida (Result[]
).
- Antes de ejecutar el programa, compruebe en la barra de estado de la parte inferior de VS Code que el target perfil está establecido Q#en : Sin restricciones. Para cambiar el target perfil, seleccione el target perfil en la barra de estado y seleccione Sin restricciones en el menú desplegable. Si el target perfil no está establecido en Sin restricciones, recibirá un error al ejecutar el programa.
- Para ejecutar el programa, seleccione Ejecutar Q# archivo en la lista desplegable icono de reproducción de la parte superior derecha o presione Ctrl+F5. El programa ejecuta la
Main()
operación en el simulador predeterminado. - Las
Message
salidas yDumpMachine
aparecen en la consola de depuración.
Si tiene curiosidad sobre cómo se ven afectados otros estados de entrada, le recomendamos que pruebe a aplicar operaciones de cúbits antes de la transformación.
Adición de medidas al circuito QFT
La presentación de la función DumpMachine
mostró los resultados de la operación; lamentablemente, una piedra angular de la mecánica cuántica indica que un sistema cuántico real no puede tener esta función DumpMachine
.
En su lugar, la información se extrae mediante medidas que, por lo general, no solo no proporcionan información sobre el estado cuántico completo, sino que también pueden modificar drásticamente el propio sistema.
Hay muchos tipos de medidas cuánticas, pero el ejemplo se centra en la más básica: la medida de proyección en cúbits únicos. Tras medir en una base determinada (por ejemplo, la base de cálculo $ { \ket{0}, \ket{1} } $), el estado del cúbit se proyecta en el estado base que se midió, lo que destruye cualquier superposición entre los dos.
Modificación de la operación QFT
Para implementar las medidas dentro de un programa de Q#, se usa la operación M
, que devuelve un tipo Result
.
En primer lugar, modifique la operación Main
para devolver una matriz de resultados de medida, Result[]
, en lugar de Unit
.
operation Main() : Result[] {
Definición e inicialización de la matriz Result[]
Antes de asignar cúbits, declare y enlace una matriz de tres elementos (una Result
para cada cúbit):
mutable resultArray = [Zero, size = 3];
La palabra clave mutable
que precede a resultArray
permite modificar la variable más adelante en el código; por ejemplo, al agregar los resultados de la medida.
Medida en un bucle for
y adición de los resultados a la matriz
Después de las operaciones de transformación de QFT, inserte el código siguiente:
for i in IndexRange(qs) {
set resultArray w/= i <- M(qs[i]);
}
La función IndexRange
a la que se llama en una matriz (por ejemplo, nuestra matriz de cúbits, qs
) devuelve un intervalo de índices de la matriz.
Aquí, se usa en el bucle for
para medir secuencialmente cada cúbit con la instrucción M(qs[i])
.
A continuación, cada tipo de Result
medido (Zero
o One
) se agrega a la posición del índice correspondiente en resultArray
con una instrucción de actualización y reasignación.
Nota:
La sintaxis de esta instrucción es única para Q#, pero corresponde a la reasignación de variables similar resultArray[i] <- M(qs[i])
que se ve en otros lenguajes, como F# y R.
La palabra clave set
siempre se usa para reasignar variables enlazadas mediante mutable
.
Devuelve resultArray
.
Con los tres bits cúbits medidos y los resultados agregados a resultArray
, es seguro restablecer y desasignar los cúbits como antes. Para devolver las medidas, inserte:
return resultArray;
Ejecutar el circuito QFT con las medidas
Ahora, cambie la ubicación de las funciones DumpMachine
para generar el estado antes y después de las medidas.
El código de Q# final tendrá el aspecto siguiente:
import Microsoft.Quantum.Diagnostics.*;
import Microsoft.Quantum.Math.*;
import Microsoft.Quantum.Arrays.*;
operation Main() : Result[] {
mutable resultArray = [Zero, size = 3];
use qs = Qubit[3];
//QFT:
//first qubit:
H(qs[0]);
Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
SWAP(qs[2], qs[0]);
Message("Before measurement: ");
DumpMachine();
for i in IndexRange(qs) {
set resultArray w/= i <- M(qs[i]);
}
Message("After measurement: ");
DumpMachine();
ResetAll(qs);
Message("Post-QFT measurement results [qubit0, qubit1, qubit2]: ");
return resultArray;
}
Sugerencia
Recuerde guardar el archivo cada vez que introduzca un cambio en el código antes de volver a ejecutarlo.
- Antes de ejecutar el programa, compruebe en la barra de estado de la parte inferior de VS Code que el target perfil está establecido Q#en : Sin restricciones. Para cambiar el target perfil, seleccione el target perfil en la barra de estado y seleccione Sin restricciones en el menú desplegable. Si el target perfil no está establecido en Sin restricciones, recibirá un error al ejecutar el programa.
- Para ejecutar el programa, seleccione Ejecutar Q# archivo en la lista desplegable icono de reproducción de la parte superior derecha o presione Ctrl+5. El programa ejecuta la
Main()
operación en el simulador predeterminado. - Las
Message
salidas yDumpMachine
aparecen en la consola de depuración.
La salida se parecerá a esta:
Before measurement:
Basis | Amplitude | Probability | Phase
-----------------------------------------------
|000⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|001⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|010⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|011⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|100⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|101⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|110⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|111⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
After measurement:
Basis | Amplitude | Probability | Phase
-----------------------------------------------
|010⟩ | 1.0000+0.0000𝑖 | 100.0000% | 0.0000
Post-QFT measurement results [qubit0, qubit1, qubit2]:
[Zero, One, Zero]
Esta salida muestra algunas cosas diferentes:
- Al comparar el resultado devuelto con la medida previa
DumpMachine
, no se ilustra claramente la superposición posterior de QFT sobre los estados base. Una medida solo devuelve un estado base único, con una probabilidad determinada por la amplitud de ese estado en la función de onda del sistema. - A partir de la medida posterior
DumpMachine
, se puede ver que la medida cambia el estado en sí y lo proyecta desde la superposición inicial sobre los estados base al estado base único que corresponde al valor medido.
Si repite esta operación muchas veces, las estadísticas de los resultados comienzan a ilustrar la superposición igualmente ponderada del estado posterior a QFT que da lugar a un resultado aleatorio en cada iteración. Sin embargo, además de ser un método ineficaz e imperfecto, solo reproduciría las amplitudes relativas de los estados base, no las fases relativas entre ellos. Esto último no es un problema en este ejemplo, pero vería que aparecen fases relativas si se da una entrada más compleja a QFT que $\ket{000}$.
Uso de las Q# operaciones para simplificar el circuito QFT
Tal y como se mencionó en la introducción, gran parte de la potencia de Q# se debe al hecho de que permite olvidarse de las preocupaciones de tener que tratar con cúbits individuales.
De hecho, si desea desarrollar programas cuánticos aplicables a escala completa, tener que preocuparse de si una operación H
va antes o después de una rotación determinada es un lastre. Azure Quantum proporciona la ApplyQFT
operación, que puede usar y aplicar para cualquier número de cúbits.
Reemplace todo desde la primera
H
operación a laSWAP
operación, inclusiva, por:ApplyQFT(qs);
El código debería tener ahora un aspecto similar al siguiente
import Microsoft.Quantum.Diagnostics.*; import Microsoft.Quantum.Math.*; import Microsoft.Quantum.Arrays.*; operation Main() : Result[] { mutable resultArray = [Zero, size = 3]; use qs = Qubit[3]; //QFT: //first qubit: ApplyQFT(qs); Message("Before measurement: "); DumpMachine(); for i in IndexRange(qs) { set resultArray w/= i <- M(qs[i]); } Message("After measurement: "); DumpMachine(); ResetAll(qs); Message("Post-QFT measurement results [qubit0, qubit1, qubit2]: "); return resultArray; }
Vuelva a ejecutar el Q# programa y observe que la salida es la misma que antes.
Para ver la ventaja real del uso Q# de operaciones, cambie el número de cúbits a algo distinto de
3
:
mutable resultArray = [Zero, size = 4];
use qs = Qubit[4];
//...
Por lo tanto, puede aplicar el QFT adecuado para cualquier número determinado de cúbits, sin tener que preocuparse de agregar nuevas H
operaciones y rotaciones en cada cúbit.
Contenido relacionado
Explore otros tutoriales de Q#:
- El generador de números aleatorios cuánticos muestra cómo escribir un Q# programa que genera números aleatorios fuera de cúbits en superposición.
- El algoritmo de búsqueda de Grover muestra cómo escribir un Q# programa que usa el algoritmo de búsqueda de Grover.
- El entrelazamiento cuántico muestra cómo escribir un Q# programa que manipula y mide cúbits y muestra los efectos de la superposición y el entrelazamiento.
- Quantum Katas son tutoriales autodirigido y ejercicios de programación dirigidos a enseñar los elementos de la computación cuántica y Q# la programación al mismo tiempo.