Categorías
Quantum

Compuertas y circuitos

Tras presentar el estado cuántico de un qubit, vimos las transformaciones unitarias que actuaban sobre dicho estado. Ahora, que ya hemos presentado el estado de un grupo de qubits, tenemos que ver las transformaciones unitarias que nos permitirán manejar su estado.

Compuertas

Cuando se trata de operaciones sobre qubits individuales, no es mayor problema utilizar directamente las matrices en notación de componentes. El vector de estado de un qubit se representa con dos componentes complejos, por lo que las matriz son «simples» matrices complejas de $2\times2$. En cambio, es más pesado tener que listar los componentes de matrices con más dimensiones.

La alternativa, que todo el mundo sigue, es representar las transformaciones mediante compuertas cuánticas y combinar estas compuertas en circuitos. Por supuesto, detrás de todo circuito hay, simple y llanamente, una matriz probablemente enorme que es la que realiza la transformación necesaria antes de la medición. A mí, personalmente, me resulta muchísimo más fácil entender el funcionamiento de un algoritmo calculando las matrices asociadas pero, eso espero, con el tiempo uno llega a familiarizarse con la notación de circuitos y desarrollar la intuición necesaria para ahorrarse unos cuantos pasos. En cualquier caso, intentaré dar las herramientas para pasar de compuertas a matrices y, en el caso de circuitos correspondientes a algoritmos sencillos, del circuito completo a la matriz resultante.

Para el diseño de circuitos y algoritmos, mi recomendación es utilizar Qiskit, de IBM. Qiskit te permite crear una cuenta gratuita, y te da acceso a dos herramientas muy útiles para crear y evaluar circuitos: mediante software gráfico y a través de Python. No soy un entusiasta de Python, pero para este tipo de tareas no duele mucho usarlo. La siguiente imagen muestra parte de la barra de herramientas del editor gráfico, y un circuito muy sencillo con dos qubits, a los cuales se le está aplicando una inversión (la compuerta que parece una cruz) y una medición (la línea inferior representa la salida de bits clásicos del algoritmo):

El objetivo de esta entrada no es ponernos ya a diseñar circuitos: cuando llegue el momento, es preferible mostrar el código en Python. Ahora nos interesa la parte matemática, sobre cómo definir transformaciones que afecten a más de un qubit. Utilizo Qiskit para no tener que dibujar los circuitos a mano.

Hay alternativas a Qiskit. Las principales que conozco son Q#, una iniciativa de Microsoft, e incluso librerías de Matlab. Q# es interesante, pero en estos momentos es una pequeña tragedia hacer que funcione a la primera en Visual Studio. Y Matlab es de pago.

Cómo combinar matrices

Volvamos al circuito que he mostrado antes:
Este circuito muestra dos qubits, $q_0$ y $q_1$. A cada uno de ellos se le aplica una negación: la matriz $X$ que ya hemos visto, que convierte ceros en unos y viceversa, y que Qiskit representa como un círculo con una cruz dentro. Luego se realizan las mediciones. El resultado es muy tonto: obtenemos un $11$ clásico:

  1. Asumimos que cada qubit se inicializa en el estado $\vert0\rangle$. Es decir, el sistema de dos qubits se inicializa en el estado conjunto $\vert00\rangle$.
  2. Cada inversión convierte su qubit al estado $\vert1\rangle$. El sistema queda en un estado puro $\vert11\rangle$.
  3. Por supuesto, al medir obtendremos ese estado, sin ningún tipo de incertidumbre.

Sabemos que la matriz o compuerta $X$ tiene la siguiente representación en notación de componentes:
$$\pmatrix{0&1\cr1&0}
$$La pregunta nuestra es: ¿cómo representamos la matriz que aplica una negación por separado a cada uno de los qubits? En este caso, la intuición podría ayudarnos a determinar el contenido de la matriz, pero vamos a aprovechar que es un ejercicio sencillo para poner a punto la herramienta matemática que necesitaremos para casos más complicados. Esa herramienta es la multiplicación tensorial de matrices (previsible, ¿no?).

Supongamos que tenemos dos matrices $A$ y $B$, y que queremos calcular el producto tensorial de las dos:
$$\pmatrix{a_{11}&a_{12}\cr a_{21}&a_{22}}\otimes\pmatrix{b_{11}&b_{12}\cr b_{21}&b_{22}}
$$La definición de producto tensorial de matrices es un poco engorrosa, pero no es complicada:
$$\pmatrix{
a_{11}\times\pmatrix{b_{11}&b_{12}\cr b_{21}&b_{22}}&
a_{12}\times\pmatrix{b_{11}&b_{12}\cr b_{21}&b_{22}}\cr
a_{21}\times\pmatrix{b_{11}&b_{12}\cr b_{21}&b_{22}}&
a_{22}\times\pmatrix{b_{11}&b_{12}\cr b_{21}&b_{22}}
}$$Simplificando, nos queda esto:
$$\pmatrix{
a_{11}b_{11}&a_{11}b_{12}&a_{12}b_{11}&a_{12}b_{12}\cr
a_{11}b_{21}&a_{11}b_{22}&a_{12}b_{21}&a_{12}b_{22}\cr
a_{21}b_{11}&a_{21}b_{12}&a_{22}b_{11}&a_{22}b_{12}\cr
a_{21}b_{21}&a_{21}b_{22}&a_{22}b_{21}&a_{22}b_{22}
}$$Es muy importante dominar esta técnica. Asegúrese de que comprende de dónde sale cada celda del resultado antes de seguir adelante.

En nuestro ejemplo, queríamos combinar dos matrices $X$:
$$\pmatrix{0&1\cr1&0}\otimes\pmatrix{0&1\cr1&0}
$$En el paso intermedio, obtenemos esto:
$$\pmatrix{
0\times\pmatrix{0&1\cr1&0}&
1\times\pmatrix{0&1\cr1&0}\cr
1\times\pmatrix{0&1\cr1&0}&
0\times\pmatrix{0&1\cr1&0}
}$$Simplificando:
$$\pmatrix{
0&0&0&1\cr
0&0&1&0\cr
0&1&0&0\cr
1&0&0&0
}$$Intuitivamente, el resultado tiene buen aspecto: la matriz $X$ de dos dimensiones parece una matriz con la diagonal invertida, y esta matriz resultado tiene la misma forma. ¿Cómo comprobamos que es correcta? Muy sencillo. La base para dos qubits tiene cuatro vectores, que son $\vert00\rangle,\vert01\rangle,\vert10\rangle,\vert11\rangle$. Estos cuatro vectores, sin embargo, tienen una representación en componentes que puede resultar confusa si no la domina todavía. Vamos a hacer los cálculos y las representaciones explícitos. Para el primer vector de la base:
$$(X\otimes X)\vert00\rangle = \pmatrix{
0&0&0&1\cr
0&0&1&0\cr
0&1&0&0\cr
1&0&0&0
} \times \pmatrix {1\cr0\cr0\cr0} = \pmatrix {0\cr0\cr0\cr1} = \vert11\rangle
$$Para el segundo vector:
$$(X\otimes X)\vert01\rangle = \pmatrix{
0&0&0&1\cr
0&0&1&0\cr
0&1&0&0\cr
1&0&0&0
} \times \pmatrix {0\cr1\cr0\cr0} = \pmatrix {0\cr0\cr1\cr0} = \vert10\rangle
$$Tercer vector:
$$(X\otimes X)\vert10\rangle = \pmatrix{
0&0&0&1\cr
0&0&1&0\cr
0&1&0&0\cr
1&0&0&0
} \times \pmatrix {0\cr0\cr1\cr0} = \pmatrix {0\cr1\cr0\cr0} = \vert01\rangle
$$Y el cuarto vector:
$$(X\otimes X)\vert11\rangle = \pmatrix{
0&1&0&0\cr
1&0&0&0\cr
0&1&0&0\cr
1&0&0&0
} \times \pmatrix {0\cr0\cr0\cr1} = \pmatrix {1\cr0\cr0\cr0} = \vert00\rangle
$$Perdone que haya sido tan pedante y haya escrito explícitamente todos los detalles. La programación, en general, es el reino de los detalles. En este caso, hemos podido comprobar que la matriz combinada invierte los ceros y unos en el estado cuántico combinado.

El orden de los factores

Sin embargo, me he dejado un detalle importante en el tintero: el orden de los qubits. En un estado como $\vert01\rangle$, ¿cuál es el qubit más significativo? ¿Es el $0$ o el $1$? Todo depende del convenio, pero una vez elegido uno, hay que atenerse estrictamente a él. Nuestro convenio será que en $\vert01\rangle$ el bit más significativo es el de la izquierda, es decir, el $0$, y el menos significativo es el de la derecha, el $1$.

Recuerde, además, que $\vert01\rangle$ es realmente $\vert0\rangle\otimes\vert1\rangle$, esto es, un producto tensorial. Esto es muy importante también para combinar correctamente matrices. Nuestro primer ejemplo de producto tensorial fue muy sencillo porque las dos matrices eran idénticas, y no tuvimos que preocuparnos por su orden relativo. Vamos a suponer que nuestro circuito tiene esta otra forma:

Esta vez queremos invertir solamente el qubit menos significativo. Aparentemente, además, sólo tenemos una matriz, pero eso no es del todo cierto. Lo que queremos calcular ahora es la matriz combinada $I\otimes X$, donde $I$ es la matriz identidad.

Observe el orden: primero va la matriz identidad $I$… ¡porque cuando representamos el sistema de dos qubits, hemos elegido que el más significativo sea el de la izquierda! Y la matriz $X$ es entonces el segundo operando. Queremos entonces calcular esto:

$$\pmatrix{1&0\cr0&1}\otimes\pmatrix{0&1\cr1&0}
$$Expandiendo, según la definición de producto tensorial:
$$\pmatrix{
1\times\pmatrix{0&1\cr1&0}&
0\times\pmatrix{0&1\cr1&0}\cr
0\times\pmatrix{0&1\cr1&0}&
1\times\pmatrix{0&1\cr1&0}
}$$Y si simplificamos:
$$\pmatrix{
0&1&0&0\cr
1&0&0&0\cr
0&0&0&1\cr
0&0&1&0
}$$Esto, así de repente, es chino mandarín, y la única manera sensata de comprobar que no nos hemos equivocado es comprobar directamente las transformaciones.
$$(I\otimes X)\vert00\rangle = \pmatrix{
0&1&0&0\cr
1&0&0&0\cr
0&0&0&1\cr
0&0&1&0
} \times \pmatrix {1\cr0\cr0\cr0} = \pmatrix {0\cr1\cr0\cr0} = \vert01\rangle
$$
$$(I\otimes X)\vert01\rangle = \pmatrix{
0&1&0&0\cr
1&0&0&0\cr
0&0&0&1\cr
0&0&1&0
} \times \pmatrix {0\cr1\cr0\cr0} = \pmatrix {1\cr0\cr0\cr0} = \vert00\rangle
$$
$$(I\otimes X)\vert10\rangle = \pmatrix{
0&1&0&0\cr
1&0&0&0\cr
0&0&0&1\cr
0&0&1&0
} \times \pmatrix {0\cr0\cr1\cr0} = \pmatrix {0\cr0\cr0\cr1} = \vert11\rangle
$$
$$(I\otimes X)\vert11\rangle = \pmatrix{
0&1&0&0\cr
1&0&0&0\cr
0&0&0&1\cr
0&0&1&0
} \times \pmatrix {0\cr0\cr0\cr1} = \pmatrix {0\cr0\cr1\cr0} = \vert10\rangle
$$Nuestra matriz, por lo tanto, transforma $00\rightarrow01$, $01\rightarrow00$, $10\rightarrow11$ y $11\rightarrow11$. Esto es, lo que hace es cambiar el estado del bit menos significativo… que era lo que estábamos buscando. Como ejercicio, puede calcular cuál sería la representación en componentes de la matriz $X\otimes I$, que debe invertir el estado del qubit más significativo. Y si quiere adelantarse a la siguiente entrada, pruebe fortuna con $H\otimes H$, donde $H$ es la transformación de Hadamard que ya hemos visto.

Advertencia

Todas las matrices y estados que hemos visto tienen componentes que son o $0$ o $1$. Esto puede llevar a confusión: ese cero es realmente un cero «complejo», y lo mismo se aplica al uno. No pierda de vista, aunque en estos primeros ejemplos no importe mucho, que en vez de ceros y unos, nuestras matrices y vectores pueden contener, y contendrán, números complejos.