Spanish
Idiomas
English
Bengali
French
German
Japanese
Korean
Portuguese
Spanish
Tamil

Nota

Esta página fue generada a partir de tutorials/circuits/3_summary_of_quantum_operations.ipynb.

Resumen de Operaciones Cuánticas

En esta sección presentaremos las diferentes operaciones que están disponibles en Qiskit Terra. Estas son:

  • Compuertas cuánticas de un solo qubit

  • Compuertas cuánticas para varios qubits

  • Mediciones

  • Restablecimiento

  • Condicionales

  • Inicialización del estado

También te mostraremos como usar los tres diferentes simuladores:

  • unitary_simulator

  • qasm_simulator

  • statevector_simulator

[1]:
# Useful additional packages
import matplotlib.pyplot as plt
import numpy as np
from math import pi
[2]:
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister, transpile
from qiskit.tools.visualization import circuit_drawer
from qiskit.quantum_info import state_fidelity
from qiskit import BasicAer

backend = BasicAer.get_backend('unitary_simulator')

Estados cuánticos de un qubit

Un estado cuántico de un qubit se puede escribir como

\[\left|\psi\right\rangle = \alpha\left|0\right\rangle + \beta \left|1\right\rangle\]

donde \(\alpha\) y \(\beta\) son números complejos. En una medición, la probabilidad de que el bit esté en \(\left|0\right\rangle\) es \(|\alpha|^2\) y \(\left|1\right\rangle\) es \(|\beta|^2\). Como vector esto es

\[\begin{split}\left|\psi\right\rangle = \begin{pmatrix} \alpha \\ \beta \end{pmatrix}.\end{split}\]

Note que debido a la conservación de la probabilidad \(|\alpha|^2+ |\beta|^2 = 1\) y dado que la fase global es indetectable \(\left|\psi\right\rangle := e^{i\delta} \left|\psi\right\rangle\) sólo requerimos dos números reales para describir el estado cuántico de un solo qubit.

Una representación conveniente es

\[\left|\psi\right\rangle = \cos(\theta/2)\left|0\right\rangle + \sin(\theta/2)e^{i\phi}\left|1\right\rangle\]

donde \(0\leq \phi < 2\pi\), y \(0\leq \theta \leq \pi\). A partir de esto, es claro que existe una correspondencia de uno a uno entre los estados de los qubits (\(\mathbb{C}^2\)) y los puntos sobre la superficie de una esfera unitaria (\(\mathbb{R}^3\)). Esta se denomina la representación de la esfera de Bloch del estado de un qubit.

Las compuertas u operaciones cuánticas son usualmente representadas como matrices. Una compuerta que actúa sobre un qubit se representa como una matriz unitaria \(U\) de \(2\times 2\). La acción de la compuerta cuántica se encuentra multiplicando la matriz que representa la compuesta con el vector que representa el estado cuántico.

\[\left|\psi'\right\rangle = U\left|\psi\right\rangle\]

Una unitaria general debe poder llevar el \(\left|0\right\rangle\) al estado anterior. Esto es

\[\begin{split}U = \begin{pmatrix} \cos(\theta/2) & a \\ e^{i\phi}\sin(\theta/2) & b \end{pmatrix}\end{split}\]

donde \(a\) y \(b\) son números complejos restringidos, tales que \(U^\dagger U = I\) para todo \(0\leq\theta\leq\pi\) y \(0\leq \phi<2\pi\). Esto da tres restricciones y como tal \(a\rightarrow -e^{i\lambda}\sin(\theta/2)\) y \(b\rightarrow e^{i\lambda+i\phi}\cos(\theta/2)\) donde \(0\leq \lambda<2\pi\) dando por resultado

\[\begin{split}U(\theta, \phi, \lambda) = \begin{pmatrix} \cos\left(\frac{\theta}{2}\right) & -e^{i\lambda}\sin\left(\frac{\theta}{2}\right) \\ e^{i\phi}\sin\left(\frac{\theta}{2}\right) & e^{i(\phi+\lambda)}\cos\left(\frac{\theta}{2}\right) \end{pmatrix}\end{split}\]

Esta es la forma más general de una compuerta unitaria para un solo qubit.

Compuertas de un solo qubit

Las compuertas de un solo qubit disponibles son: - Compuerta U - Compuerta P - Compuerta identidad - Compuertas de Pauli - Compuertas de Clifford - Compuertas \(C3\) - Compuertas de rotación estándar

Hemos provisto un backend: unitary_simulator que te permite calcular las matrices unitarias.

[3]:
q = QuantumRegister(1)

Compuerta U

En Qiskit te damos acceso a la unitaria general usando la compuerta \(u\), la cual tiene la siguiente forma matricial

\[\begin{split}U(\theta, \phi, \lambda) = \begin{pmatrix} \cos\left(\frac{\theta}{2}\right) & -e^{i\lambda}\sin\left(\frac{\theta}{2}\right) \\ e^{i\phi}\sin\left(\frac{\theta}{2}\right) & e^{i(\phi+\lambda)}\cos\left(\frac{\theta}{2}\right) \end{pmatrix}\end{split}\]
[4]:
qc = QuantumCircuit(q)
qc.u(pi/2,pi/4,pi/8,q)
qc.draw()
[4]:
    ┌────────────────┐
q0: ┤ U(π/2,π/4,π/8) ├
    └────────────────┘
[5]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[5]:
array([[ 0.707+0.j   , -0.653-0.271j],
       [ 0.5  +0.5j  ,  0.271+0.653j]])

Nota sobre la obsolescencia de la compuerta U

Los métodos de QuantumCircuit \(u1\), \(u2\) y \(u3\) ahora están obsoletos. En su lugar, se deben utilizar los siguientes reemplazos.

  • \(u1(\lambda) = p(\lambda) = u(0, 0, \lambda)\)

  • \(u2(\phi, \lambda) = u(\frac{\pi}{2}, \phi, \lambda) = p(\frac{\pi}{2} + \phi) \cdot sx \cdot p(\frac{\pi}{2} - \lambda)\)

  • \(u3(\theta, \phi, \lambda) = u(\theta, \phi, \lambda) = p(\phi + \pi) \cdot sx \cdot p(\theta + \pi) \cdot sx \cdot p(\lambda)\)

# qc.u1(lambda) is now:
qc.p(lambda)

# qc.u2(phi, lambda) is now:
qc.u(pi/2, phi, lambda)

# qc.u3(theta, phi, lambda) is now:
qc.u(theta, phi, lambda)

Compuerta P

La compuerta \(p(\lambda)= u(0, 0, \lambda)\) tiene la forma matricial

\[\begin{split}p(\lambda) = \begin{pmatrix} 1 & 0 \\ 0 & e^{i \lambda} \end{pmatrix},\end{split}\]

la cuál es útil ya que nos permite aplicar una fase cuántica.

[13]:
qc = QuantumCircuit(q)
qc.p(pi/2,q)
qc.draw()
[13]:
    ┌────────┐
q0: ┤ P(π/2) ├
    └────────┘
[14]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[14]:
array([[1.+0.j, 0.+0.j],
       [0.+0.j, 0.+1.j]])

Compuerta Identidad

La compuerta identidad es \(Id = p(0)\).

[15]:
qc = QuantumCircuit(q)
qc.id(q)
qc.draw()
[15]:
    ┌───┐
q0: ┤ I ├
    └───┘
[16]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[16]:
array([[1.+0.j, 0.+0.j],
       [0.+0.j, 1.+0.j]])

Compuertas de Pauli

\(X\): compuerta de cambio de bit

La compuerta de cambio de bit \(X\) está definida como:

\[\begin{split}X = \begin{pmatrix} 0 & 1\\ 1 & 0 \end{pmatrix}= u(\pi,0,\pi)\end{split}\]
[17]:
qc = QuantumCircuit(q)
qc.x(q)
qc.draw()
[17]:
    ┌───┐
q0: ┤ X ├
    └───┘
[18]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[18]:
array([[0.+0.j, 1.+0.j],
       [1.+0.j, 0.+0.j]])

\(Y\): compuerta de cambio de bit y de fase

La compuerta \(Y\) está definida como:

\[\begin{split}Y = \begin{pmatrix} 0 & -i\\ i & 0 \end{pmatrix}=u(\pi,\pi/2,\pi/2)\end{split}\]
[19]:
qc = QuantumCircuit(q)
qc.y(q)
qc.draw()
[19]:
    ┌───┐
q0: ┤ Y ├
    └───┘
[20]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[20]:
array([[ 0.+0.j, -0.-1.j],
       [ 0.+1.j,  0.+0.j]])

\(X\): compuerta de cambio de fase

La compuerta de cambio de fase \(Z\) está definida como:

\[\begin{split}Z = \begin{pmatrix} 1 & 0\\ 0 & -1 \end{pmatrix}=p(\pi)\end{split}\]
[21]:
qc = QuantumCircuit(q)
qc.z(q)
qc.draw()
[21]:
    ┌───┐
q0: ┤ Z ├
    └───┘
[23]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[23]:
array([[ 1.+0.j,  0.+0.j],
       [ 0.+0.j, -1.+0.j]])

Compuertas de Clifford

Compuerta de Hadamard

\[\begin{split}H = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 1\\ 1 & -1 \end{pmatrix}= u(\pi/2,0,\pi)\end{split}\]
[22]:
qc = QuantumCircuit(q)
qc.h(q)
qc.draw()
[22]:
    ┌───┐
q0: ┤ H ├
    └───┘
[23]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[23]:
array([[ 0.707+0.j,  0.707-0.j],
       [ 0.707+0.j, -0.707+0.j]])

Compuerta \(S\) (o fase \(\sqrt{Z}\))

\[\begin{split}S = \begin{pmatrix} 1 & 0\\ 0 & i \end{pmatrix}= p(\pi/2)\end{split}\]
[24]:
qc = QuantumCircuit(q)
qc.s(q)
qc.draw()
[24]:
    ┌───┐
q0: ┤ S ├
    └───┘
[29]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[29]:
array([[1.+0.j, 0.+0.j],
       [0.+0.j, 0.+1.j]])

Compuerta \(S^{\dagger}\) (o conjugada de fase \(\sqrt{Z}\))

\[\begin{split}S^{\dagger} = \begin{pmatrix} 1 & 0\\ 0 & -i \end{pmatrix}= p(-\pi/2)\end{split}\]
[25]:
qc = QuantumCircuit(q)
qc.sdg(q)
qc.draw()
[25]:
    ┌─────┐
q0: ┤ Sdg ├
    └─────┘
[26]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[26]:
array([[1.+0.j, 0.+0.j],
       [0.+0.j, 0.-1.j]])

Compuertas \(C3\)

Compuerta \(T\) (o fase \(\sqrt{S}\))

\[\begin{split}T = \begin{pmatrix} 1 & 0\\ 0 & e^{i \pi/4} \end{pmatrix}= p(\pi/4)\end{split}\]
[27]:
qc = QuantumCircuit(q)
qc.t(q)
qc.draw()
[27]:
    ┌───┐
q0: ┤ T ├
    └───┘
[28]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[28]:
array([[1.   +0.j   , 0.   +0.j   ],
       [0.   +0.j   , 0.707+0.707j]])

Compuerta \(T^{\dagger}\) (o conjugada de fase \(\sqrt{S}\))

\[\begin{split}T^{\dagger} = \begin{pmatrix} 1 & 0\\ 0 & e^{-i \pi/4} \end{pmatrix}= p(-\pi/4)\end{split}\]
[29]:
qc = QuantumCircuit(q)
qc.tdg(q)
qc.draw()
[29]:
    ┌─────┐
q0: ┤ Tdg ├
    └─────┘
[30]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[30]:
array([[1.   +0.j   , 0.   +0.j   ],
       [0.   +0.j   , 0.707-0.707j]])

Rotaciones estándar

Las compuertas de rotaciones estándar son aquellas que definen rotaciones alrededor de las Paulis \(P=\{X,Y,Z\}\). Están definidas como

\[R_P(\theta) = \exp(-i \theta P/2) = \cos(\theta/2)I -i \sin(\theta/2)P\]

Rotación alrededor del eje X

\[\begin{split}R_x(\theta) = \begin{pmatrix} \cos(\theta/2) & -i\sin(\theta/2)\\ -i\sin(\theta/2) & \cos(\theta/2) \end{pmatrix} = u(\theta, -\pi/2,\pi/2)\end{split}\]
[31]:
qc = QuantumCircuit(q)
qc.rx(pi/2,q)
qc.draw()
[31]:
    ┌─────────┐
q0: ┤ Rx(π/2) ├
    └─────────┘
[32]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[32]:
array([[ 0.707+0.j   , -0.   -0.707j],
       [ 0.   -0.707j,  0.707+0.j   ]])

Rotación alrededor del eje Y

\[\begin{split}R_y(\theta) = \begin{pmatrix} \cos(\theta/2) & - \sin(\theta/2)\\ \sin(\theta/2) & \cos(\theta/2). \end{pmatrix} =u(\theta,0,0)\end{split}\]
[33]:
qc = QuantumCircuit(q)
qc.ry(pi/2,q)
qc.draw()
[33]:
    ┌─────────┐
q0: ┤ Ry(π/2) ├
    └─────────┘
[34]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[34]:
array([[ 0.707+0.j, -0.707+0.j],
       [ 0.707+0.j,  0.707+0.j]])

Rotación alrededor del eje Z

\[\begin{split}R_z(\phi) = \begin{pmatrix} e^{-i \phi/2} & 0 \\ 0 & e^{i \phi/2} \end{pmatrix}\equiv p(\phi)\end{split}\]

Ten en cuenta que aquí hemos usado un equivalente, ya que es diferente a \(p\) por una fase global \(e^{-i \phi/2}\).

[35]:
qc = QuantumCircuit(q)
qc.rz(pi/2,q)
qc.draw()
[35]:
    ┌─────────┐
q0: ┤ Rz(π/2) ├
    └─────────┘
[36]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[36]:
array([[0.707-0.707j, 0.   +0.j   ],
       [0.   +0.j   , 0.707+0.707j]])

Nota que esto es diferente debido sólo a una fase global.

Compuertas de múltiples qubits

Preliminares Matemáticos

El espacio de una computadora cuántica crece exponencialmente con el número de qubits. Para \(n\) qubits el espacio vectorial complejo tiene dimensión \(d=2^n\). Para describir los estados de un sistema de varios qubits, se utiliza el producto tensorial para «unir» a los operadores y a los vectores base.

Empecemos por considerar un sistema de 2 qubits. Dados dos operadores \(A\) y \(B\) donde cada uno actúa en un qubit, el operador conjunto \(A \otimes B\) que actúa en dos qubits es

\[\begin{split}\begin{equation} A\otimes B = \begin{pmatrix} A_{00} \begin{pmatrix} B_{00} & B_{01} \\ B_{10} & B_{11} \end{pmatrix} & A_{01} \begin{pmatrix} B_{00} & B_{01} \\ B_{10} & B_{11} \end{pmatrix} \\ A_{10} \begin{pmatrix} B_{00} & B_{01} \\ B_{10} & B_{11} \end{pmatrix} & A_{11} \begin{pmatrix} B_{00} & B_{01} \\ B_{10} & B_{11} \end{pmatrix} \end{pmatrix}, \end{equation}\end{split}\]

donde \(A_{jk}\) y \(B_{lm}\) son los elementos de la matriz de \(A\) y \(B\), respectivamente.

Análogamente, los vectores base para el sistema de 2 qubits son formados utilizando el producto tensorial de vectores base para un solo qubit:

\[\begin{split}\begin{equation}\begin{split} \left|{00}\right\rangle &= \begin{pmatrix} 1 \begin{pmatrix} 1 \\ 0 \end{pmatrix} \\ 0 \begin{pmatrix} 1 \\ 0 \end{pmatrix} \end{pmatrix} = \begin{pmatrix} 1 \\ 0 \\ 0 \\0 \end{pmatrix}~~~\left|{01}\right\rangle = \begin{pmatrix} 1 \begin{pmatrix} 0 \\ 1 \end{pmatrix} \\ 0 \begin{pmatrix} 0 \\ 1 \end{pmatrix} \end{pmatrix} = \begin{pmatrix}0 \\ 1 \\ 0 \\ 0 \end{pmatrix}\end{split} \end{equation}\end{split}\]
\[\begin{split}\begin{equation}\begin{split}\left|{10}\right\rangle = \begin{pmatrix} 0\begin{pmatrix} 1 \\ 0 \end{pmatrix} \\ 1\begin{pmatrix} 1 \\ 0 \end{pmatrix} \end{pmatrix} = \begin{pmatrix} 0 \\ 0 \\ 1 \\ 0 \end{pmatrix}~~~ \left|{11}\right\rangle = \begin{pmatrix} 0 \begin{pmatrix} 0 \\ 1 \end{pmatrix} \\ 1\begin{pmatrix} 0 \\ 1 \end{pmatrix} \end{pmatrix} = \begin{pmatrix} 0 \\ 0 \\ 0 \\1 \end{pmatrix}\end{split} \end{equation}.\end{split}\]

Ten en cuenta que hemos introducido una abreviación para el producto tensorial de los vectores base, donde \(\left|0\right\rangle \otimes \left|0\right\rangle\) se escribe como \(\left|00\right\rangle\). El estado de un sistema de \(n\) qubits se puede describir usando el producto tensorial \(n\)-múltiple de vectores base de un solo qubit. Observa que los vectores base para un sistema de 2 qubits son de 4 dimensiones; en general, los vectores base de un sistema de \(n\) qubits son de dimensión \(2^{n}\), como se señaló anteriormente.

Orden de los vectores base en Qiskit

Dentro de la comunidad de física, los qubits de un sistema multi-qubit se ordenan típicamente con el primer qubit en el lado más a la izquierda del producto tensorial y el último qubit en el lado más a la derecha. Por ejemplo, si el primer qubit está en el estado \(\left|0\right\rangle\) y el segundo está en el estado \(\left|1\right\rangle\), su estado conjunto sería \(\left|01\right\rangle\). Qiskit usa un orden ligeramente diferente de los qubits, en el que los qubits se representan desde el bit más significativo (most significant bit, MSB) a la izquierda hasta el bit menos significativo (least significant bit, LSB) a la derecha (little-endian). Esto es similar a la representación de cadenas de bits en computadoras clásicas, y permite una fácil conversión de cadenas de bits a enteros después de realizar las mediciones. Para el ejemplo que se acaba de dar, el estado en conjunto se representa como \(\left|10\right\rangle\). Es importante recordar que, este cambio en la representación de estados multi-qubit afecta la forma en que las compuertas de múltiples qubits son representadas en Qiskit, como se explica a continuación.

La representación utilizada en Qiskit enumera los vectores base en orden creciente a los enteros que representan. Por ejemplo, los vectores base para un sistema de dos qubits serán ordenados como \(\left|00\right\rangle\), \(\left|01\right\rangle\), \(\left|10\right\rangle\), y \(\left|11\right\rangle\). Imaginando los vectores base como cadenas de bits, estos codifican los enteros 0, 1, 2 y 3, respectivamente.

Operaciones controladas en qubits

Una compuerta común de múltiples qubits implica la aplicación de una compuerta a un qubit condicionado al estado de otro qubit. Por ejemplo, podríamos querer cambiar el estado del segundo qubit cuando el primer qubit está en \(\left|0\right\rangle\). Estas compuertas son conocidas como compuertas controladas. Las compuertas estándar de múltiples qubits consisten en compuertas de dos qubits y de tres qubits. Las compuertas de dos qubits son: - compuertas de Pauli controladas - compuerta Hadamard controlada - compuertas de rotación controladas - compuerta de fase controlada - compuerta u3 controlada - compuerta de intercambio

Las compuertas de tres qubits son: - Compuerta de Toffoli - Compuerta de Fredkin

Compuertas de dos qubits

La mayoría de las compuertas de dos qubits son del tipo controlado (la compuerta SWAP es la excepción). En general, una compuerta controlada de dos qubits \(C_{U}\) actúa para aplicar una transformación unitaria \(U\) de un qubit al segundo qubit cuando el estado del primer qubit está en \(\left|1\right\rangle\). Supongamos que \(U\) tiene una representación matricial

\[\begin{split}U = \begin{pmatrix} u_{00} & u_{01} \\ u_{10} & u_{11}\end{pmatrix}.\end{split}\]

Podemos determinar la acción de \(C_{U}\) de la siguiente manera. Recuerda que los vectores base de un sistema de dos qubits son ordenados como \(\left|00\right\rangle, \left|01\right\rangle, \left|10\right\rangle, \left|11\right\rangle\). Supón que el qubit control es qubit 0 (que de acuerdo con las convenciones de Qiskit, es el que está del lado derecho del producto tensorial). Si el qubit control está en \(\left|1\right\rangle\), se debe aplicar \(U\) al objetivo (el qubit 1, del lado izquierdo del producto tensorial). Por lo tanto, bajo la acción de \(C_{U}\), los vectores base son transformados acorde a

\[\begin{split}\begin{align*} C_{U}: \underset{\text{qubit}~1}{\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|0\right\rangle} &\rightarrow \underset{\text{qubit}~1}{\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|0\right\rangle}\\ C_{U}: \underset{\text{qubit}~1}{\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|1\right\rangle} &\rightarrow \underset{\text{qubit}~1}{U\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|1\right\rangle}\\ C_{U}: \underset{\text{qubit}~1}{\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{\left|0\right\rangle} &\rightarrow \underset{\text{qubit}~1}{\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{\left|0\right\rangle}\\ C_{U}: \underset{\text{qubit}~1}{\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{\left|1\right\rangle} &\rightarrow \underset{\text{qubit}~1}{U\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{\left|1\right\rangle}\\ \end{align*}.\end{split}\]

En forma de matriz, la acción de \(C_{U}\) es

\[\begin{split}\begin{equation} C_U = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & u_{00} & 0 & u_{01} \\ 0 & 0 & 1 & 0 \\ 0 & u_{10} &0 & u_{11} \end{pmatrix}. \end{equation}\end{split}\]

Para elaborar estos elementos de la matriz, digamos que

\[C_{(jk), (lm)} = \left(\underset{\text{qubit}~1}{\left\langle j \right|} \otimes \underset{\text{qubit}~0}{\left\langle k \right|}\right) C_{U} \left(\underset{\text{qubit}~1}{\left| l \right\rangle} \otimes \underset{\text{qubit}~0}{\left| m \right\rangle}\right),\]

calcula la acción de \(C_{U}\) (dada anteriormente), y calcula los productos internos.

Como se muestra en los ejemplos siguientes, esta operación es implementada en Qiskit como cU (q[0], q[1]).

Si qubit 1 es el de control y qubit 0 es el objetivo, entonces los vectores base se transforman de acuerdo con

\[\begin{split}\begin{align*} C_{U}: \underset{\text{qubit}~1}{\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|0\right\rangle} &\rightarrow \underset{\text{qubit}~1}{\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|0\right\rangle}\\ C_{U}: \underset{\text{qubit}~1}{\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|1\right\rangle} &\rightarrow \underset{\text{qubit}~1}{\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|1\right\rangle}\\ C_{U}: \underset{\text{qubit}~1}{\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{\left|0\right\rangle} &\rightarrow \underset{\text{qubit}~1}{\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{U\left|0\right\rangle}\\ C_{U}: \underset{\text{qubit}~1}{\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{\left|1\right\rangle} &\rightarrow \underset{\text{qubit}~1}{\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{U\left|1\right\rangle}\\ \end{align*},\end{split}\]

lo que implica que la forma matricial de \(C_{U}\) sea

\[\begin{split}\begin{equation} C_U = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & u_{00} & u_{01} \\ 0 & 0 & u_{10} & u_{11} \end{pmatrix}. \end{equation}\end{split}\]
[37]:
q = QuantumRegister(2)

Compuertas de Pauli Controladas

Compuerta X controlada (o NOT controlado)

La compuerta NOT Controlada cambia el qubit target cuando el qubit de control está en el estado \(\left|1\right\rangle\). Si tomamos el MSB como el qubit de control (por ejemplo, cx(q[1],q[0])), entonces la matriz se vería como

\[\begin{split}C_X = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 1\\ 0 & 0 & 1 & 0 \end{pmatrix}.\end{split}\]

Sin embargo, cuando el LSB es el qubit control, (por ejemplo, cx(q[0],q[1])), esta compuerta es equivalente a la siguiente matriz:

\[\begin{split}C_X = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 0 & 0 & 1\\ 0 & 0 & 1 & 0\\ 0 & 1 & 0 & 0 \end{pmatrix}.\end{split}\]
[38]:
qc = QuantumCircuit(q)
qc.cx(q[0],q[1])
qc.draw()
[38]:
q60_0: ──■──
       ┌─┴─┐
q60_1: ┤ X ├
       └───┘
[39]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[39]:
array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],
       [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],
       [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j]])

Compuerta \(Y\) controlada

Aplicamos la compuerta \(Y\) al qubit objetivo si el qubit de control es el MSB

\[\begin{split}C_Y = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 0 & -i\\ 0 & 0 & i & 0 \end{pmatrix},\end{split}\]

o cuando el LSB es el control

\[\begin{split}C_Y = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 0 & 0 & -i\\ 0 & 0 & 1 & 0\\ 0 & i & 0 & 0 \end{pmatrix}.\end{split}\]
[40]:
qc = QuantumCircuit(q)
qc.cy(q[0],q[1])
qc.draw()
[40]:
q60_0: ──■──
       ┌─┴─┐
q60_1: ┤ Y ├
       └───┘
[41]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[41]:
array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j],
       [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],
       [0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j]])

Compuerta controlada \(Z\) (o, cambio de fase controlada)

Del mismo modo, la compuerta Z controlada invierte la fase del qubit objetivo si el qubit de control es \(\left|1\right\rangle\). La matriz se ve igual independientemente de si el MSB o LSB es el qubit de control:

\[\begin{split}C_Z = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & -1 \end{pmatrix}\end{split}\]
[42]:
qc = QuantumCircuit(q)
qc.cz(q[0],q[1])
qc.draw()
[42]:
q60_0: ─■─
        │
q60_1: ─■─
          
[43]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[43]:
array([[ 1.-0.j,  0.+0.j,  0.+0.j,  0.+0.j],
       [ 0.+0.j,  1.-0.j,  0.+0.j,  0.+0.j],
       [ 0.+0.j,  0.+0.j,  1.-0.j,  0.+0.j],
       [ 0.+0.j,  0.+0.j,  0.+0.j, -1.+0.j]])

Compuerta de Hadamard controlada

Aplica la compuerta \(H\) al qubit objetivo si el qubit de control es \(\left|1\right\rangle\). A continuación, se muestra el caso en el que el control es el qubit LSB.

\[\begin{split}C_H = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & \frac{1}{\sqrt{2}} & 0 & \frac{1}{\sqrt{2}}\\ 0 & 0 & 1 & 0\\ 0 & \frac{1}{\sqrt{2}} & 0& -\frac{1}{\sqrt{2}} \end{pmatrix}\end{split}\]
[44]:
qc = QuantumCircuit(q)
qc.ch(q[0],q[1])
qc.draw()
[44]:
q60_0: ──■──
       ┌─┴─┐
q60_1: ┤ H ├
       └───┘
[45]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[45]:
array([[ 1.   +0.j,  0.   +0.j,  0.   +0.j,  0.   +0.j],
       [ 0.   +0.j,  0.707+0.j,  0.   +0.j,  0.707-0.j],
       [ 0.   +0.j,  0.   +0.j,  1.   -0.j,  0.   +0.j],
       [ 0.   +0.j,  0.707+0.j,  0.   +0.j, -0.707+0.j]])

Compuertas de rotación controladas

Rotación controlada alrededor del eje Z

Realiza la rotación alrededor del eje Z en el qubit de destino si el qubit de control (aquí LSB) es \(\left|1\right\rangle\).

\[\begin{split}C_{Rz}(\lambda) = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & e^{-i\lambda/2} & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & e^{i\lambda/2} \end{pmatrix}\end{split}\]
[46]:
qc = QuantumCircuit(q)
qc.crz(pi/2,q[0],q[1])
qc.draw()
[46]:
q60_0: ─────■─────
       ┌────┴────┐
q60_1: ┤ Rz(π/2) ├
       └─────────┘
[47]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[47]:
array([[1.   +0.j   , 0.   +0.j   , 0.   +0.j   , 0.   +0.j   ],
       [0.   +0.j   , 0.707-0.707j, 0.   +0.j   , 0.   +0.j   ],
       [0.   +0.j   , 0.   +0.j   , 1.   +0.j   , 0.   +0.j   ],
       [0.   +0.j   , 0.   +0.j   , 0.   +0.j   , 0.707+0.707j]])

Rotación de fase controlada

Realiza una rotación de fase si ambos qubits están en el estado \(\left|11\right\rangle\). La matriz tiene el mismo aspecto independientemente de si el MSB o LSB es el qubit de control.

\[\begin{split}C_{p}(\lambda) = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & e^{i\lambda} \end{pmatrix}\end{split}\]
[48]:
qc = QuantumCircuit(q)
qc.cp(pi/2,q[0], q[1])
qc.draw()
[48]:
q60_0: ─■───────
        │P(π/2)
q60_1: ─■───────
                
[49]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[49]:
array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+1.j]])

Rotación \(u\) controlada

Realiza la rotación \(u\) controlada en el qubit objetivo si el qubit de control (aquí LSB) es \(\left|1\right\rangle\).

\[\begin{split}C_{u}(\theta, \phi, \lambda) \equiv \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & e^{-i(\phi+\lambda)/2}\cos(\theta/2) & 0 & -e^{-i(\phi-\lambda)/2}\sin(\theta/2)\\ 0 & 0 & 1 & 0\\ 0 & e^{i(\phi-\lambda)/2}\sin(\theta/2) & 0 & e^{i(\phi+\lambda)/2}\cos(\theta/2) \end{pmatrix}.\end{split}\]
[53]:
qc = QuantumCircuit(q)
qc.cu(pi/2, pi/2, pi/2, 0, q[0], q[1])
qc.draw()
[53]:
q60_0: ─────────■──────────
       ┌────────┴─────────┐
q60_1: ┤ U(π/2,π/2,π/2,0) ├
       └──────────────────┘
[54]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[54]:
array([[ 1.   +0.j   ,  0.   +0.j   ,  0.   +0.j   ,  0.   +0.j   ],
       [ 0.   +0.j   ,  0.707+0.j   ,  0.   +0.j   ,  0.   -0.707j],
       [ 0.   +0.j   ,  0.   +0.j   ,  1.   -0.j   ,  0.   +0.j   ],
       [ 0.   +0.j   ,  0.   +0.707j,  0.   +0.j   , -0.707+0.j   ]])

Compuerta SWAP

La compuerta SWAP intercambia los dos qubits. Transforma los vectores bases como

\[\left|00\right\rangle \rightarrow \left|00\right\rangle~,~\left|01\right\rangle \rightarrow \left|10\right\rangle~,~\left|10\right\rangle \rightarrow \left|01\right\rangle~,~\left|11\right\rangle \rightarrow \left|11\right\rangle,\]

que da una representación matricial de la forma

\[\begin{split}\mathrm{SWAP} = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 1 \end{pmatrix}.\end{split}\]
[55]:
qc = QuantumCircuit(q)
qc.swap(q[0], q[1])
qc.draw()
[55]:
q60_0: ─X─
        │
q60_1: ─X─
          
[56]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[56]:
array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],
       [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j]])

Compuertas de tres qubits

Hay dos compuertas de tres qubit comúnmente usadas. Para tres qubits, los vectores base se ordenan como

\[\left|000\right\rangle, \left|001\right\rangle, \left|010\right\rangle, \left|011\right\rangle, \left|100\right\rangle, \left|101\right\rangle, \left|110\right\rangle, \left|111\right\rangle,\]

que, como cadenas de bits, representan los enteros \(0,1,2,\cdots, 7\). Nuevamente, Qiskit utiliza una representación en la que el primer qubit está en el extremo derecho del producto tensorial y el tercer qubit está en el extremo izquierdo:

\[\left|abc\right\rangle : \underset{\text{qubit 2}}{\left|a\right\rangle}\otimes \underset{\text{qubit 1}}{\left|b\right\rangle}\otimes \underset{\text{qubit 0}}{\left|c\right\rangle}.\]

Compuerta de Toffoli (compuerta \(ccx\))

La compuerta de Toffoli invierte el tercer qubit si los dos primeros qubits (LSB) son ambos \(\left|1\right\rangle\):

\[\left|abc\right\rangle \rightarrow \left|bc\oplus a\right\rangle \otimes \left|b\right\rangle \otimes \left|c\right\rangle.\]

En forma matricial, la compuerta de Toffoli es

\[\begin{split}C_{CX} = \begin{pmatrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1\\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \end{pmatrix}.\end{split}\]
[57]:
q = QuantumRegister(3)
[58]:
qc = QuantumCircuit(q)
qc.ccx(q[0], q[1], q[2])
qc.draw()
[58]:
q105_0: ──■──
          │
q105_1: ──■──
        ┌─┴─┐
q105_2: ┤ X ├
        └───┘
[59]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[59]:
array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]])

Compuerta de intercambio controlada (Compuerta Fredkin)

La compuerta de Fredkin, o la compuerta de intercambio controlada, intercambia el segundo y tercer qubits si el primer qubit (LSB) es \(\left|1\right\rangle\):

\[\left|abc\right\rangle \rightarrow \begin{cases} \left|bac\right\rangle~~\text{if}~c=1 \cr \left|abc\right\rangle~~\text{if}~c=0 \end{cases}.\]

En forma matricial, la compuerta de Fredkin es

\[\begin{split}C_{\mathrm{SWAP}} = \begin{pmatrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0\\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 \end{pmatrix}.\end{split}\]
[60]:
qc = QuantumCircuit(q)
qc.cswap(q[0], q[1], q[2])
qc.draw()
[60]:
q105_0: ─■─
         │
q105_1: ─X─
         │
q105_2: ─X─
           
[61]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[61]:
array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j]])

Operaciones no unitarias

Ahora que hemos pasado por todas las operaciones unitarias en circuitos cuánticos, también tenemos acceso a operaciones no unitarias. Estas incluyen mediciones, restablecimiento de qubits, y operaciones condicionales clásicas.

[62]:
q = QuantumRegister(1)
c = ClassicalRegister(1)

Mediciones

No tenemos acceso a toda la información cuando hacemos una medición en una computadora cuántica. El estado cuántico es proyectado a una de las bases estándar. A continuación, hay dos ejemplos que muestran un circuito preparado en un estado base y la computadora cuántica preparada en un estado de superposición.

[63]:
qc = QuantumCircuit(q, c)
qc.measure(q, c)
qc.draw()
[63]:
      ┌─┐
q124: ┤M├
      └╥┘
c0: 1/═╩═
       0 
[64]:
backend = BasicAer.get_backend('qasm_simulator')
job = backend.run(transpile(qc, backend))
job.result().get_counts(qc)
[64]:
{'0': 1024}

El simulador predice que el 100% de las veces el registro clásico devuelve 0.

[65]:
qc = QuantumCircuit(q, c)
qc.h(q)
qc.measure(q, c)
qc.draw()
[65]:
      ┌───┐┌─┐
q124: ┤ H ├┤M├
      └───┘└╥┘
c0: 1/══════╩═
            0 
[66]:
job = backend.run(transpile(qc, backend))
job.result().get_counts(qc)
[66]:
{'1': 532, '0': 492}

El simulador predice que el 50% de las veces, el registro clásico devuelve 0 o 1.

Restablecimiento

También es posible reset (restablecer) qubits al estado \(\left|0\right\rangle\) en medio del cálculo. Ten en cuenta que reset no es una operación de compuerta, ya que es irreversible.

[67]:
qc = QuantumCircuit(q, c)
qc.reset(q[0])
qc.measure(q, c)
qc.draw()
[67]:
           ┌─┐
q124: ─|0>─┤M├
           └╥┘
c0: 1/══════╩═
            0 
[68]:
job = backend.run(transpile(qc, backend))
job.result().get_counts(qc)
[68]:
{'0': 1024}
[69]:
qc = QuantumCircuit(q, c)
qc.h(q)
qc.reset(q[0])
qc.measure(q, c)
qc.draw()
[69]:
      ┌───┐     ┌─┐
q124: ┤ H ├─|0>─┤M├
      └───┘     └╥┘
c0: 1/═══════════╩═
                 0 
[70]:
job = backend.run(transpile(qc, backend))
job.result().get_counts(qc)
[70]:
{'0': 1024}

Aquí vemos que para ambos de estos circuitos el simulador siempre predice que la salida es del 100 porciento en el estado 0.

Operaciones condicionales

También es posible realizar operaciones condicionadas al estado del registro clásico

[71]:
qc = QuantumCircuit(q, c)
qc.x(q[0]).c_if(c, 0)
qc.measure(q,c)
qc.draw()
[71]:
       ┌───┐ ┌─┐
q124: ─┤ X ├─┤M├
       └─╥─┘ └╥┘
      ┌──╨──┐ ║
c0: 1/╡ 0x0 ╞═╩═
      └─────┘ 0 

Aquí el bit clásico siempre toma el valor 0 para que el estado del qubit sea siempre invertido.

[72]:
job = backend.run(transpile(qc, backend))
job.result().get_counts(qc)
[72]:
{'1': 1024}
[73]:
qc = QuantumCircuit(q, c)
qc.h(q)
qc.measure(q,c)
qc.x(q[0]).c_if(c, 0)
qc.measure(q,c)
qc.draw()
[73]:
      ┌───┐┌─┐ ┌───┐ ┌─┐
q124: ┤ H ├┤M├─┤ X ├─┤M├
      └───┘└╥┘ └─╥─┘ └╥┘
            ║ ┌──╨──┐ ║
c0: 1/══════╩═╡ 0x0 ╞═╩═
            0 └─────┘ 0 
[74]:
job = backend.run(transpile(qc, backend))
job.result().get_counts(qc)
[74]:
{'1': 1024}

Aquí el bit clásico por la primera medición es aleatorio, pero la operación condicional da como resultado que el qubit sea determinísticamente puesto en \(\left|1\right\rangle\).

Inicialización arbitraria

¿Qué pasa si queremos inicializar un registro del qubit a un estado arbitrario? Un estado arbitrario para \(n\) qubits puede ser especificado por un vector de \(2^n\) amplitudes, donde la suma de amplitud-normas al cuadrado es igual a 1. Por ejemplo, el siguiente estrado de 3 qubits puede ser preparado:

\[\left|\psi\right\rangle = \frac{i}{4}\left|000\right\rangle + \frac{1}{\sqrt{8}}\left|001\right\rangle + \frac{1+i}{4}\left|010\right\rangle + \frac{1+2i}{\sqrt{8}}\left|101\right\rangle + \frac{1}{4}\left|110\right\rangle\]
[79]:
# Initializing a three-qubit quantum state
import math
desired_vector = [
    1 / math.sqrt(16) * complex(0, 1),
    1 / math.sqrt(8) * complex(1, 0),
    1 / math.sqrt(16) * complex(1, 1),
    0,
    0,
    1 / math.sqrt(8) * complex(1, 2),
    1 / math.sqrt(16) * complex(1, 0),
    0]


q = QuantumRegister(3)

qc = QuantumCircuit(q)

qc.initialize(desired_vector, [q[0],q[1],q[2]])
qc.draw()
[79]:
        ┌───────────────────────────────────────────────────────────────────┐
q233_0: ┤0                                                                  ├
        │                                                                   │
q233_1: ┤1 Initialize(0.25j,0.35355,0.25+0.25j,0,0,0.35355+0.70711j,0.25,0) ├
        │                                                                   │
q233_2: ┤2                                                                  ├
        └───────────────────────────────────────────────────────────────────┘
[80]:
backend = BasicAer.get_backend('statevector_simulator')
job = backend.run(transpile(qc, backend))
qc_state = job.result().get_statevector(qc)
qc_state
[80]:
array([1.52655666e-16+2.50000000e-01j, 3.53553391e-01+5.55111512e-17j,
       2.50000000e-01+2.50000000e-01j, 0.00000000e+00+0.00000000e+00j,
       0.00000000e+00+0.00000000e+00j, 3.53553391e-01+7.07106781e-01j,
       2.50000000e-01-9.02056208e-17j, 0.00000000e+00+0.00000000e+00j])

Fidelidad es útil para comprobar si dos estados son iguales o no. Para estados cuánticos (puros) \(\left|\psi_1\right\rangle\) y \(\left|\psi_2\right\rangle\), la fidelidad es

\[F\left(\left|\psi_1\right\rangle,\left|\psi_2\right\rangle\right) = \left|\left\langle\psi_1\middle|\psi_2\right\rangle\right|^2.\]

La fidelidad es igual a \(1\) si y sólo si dos estados son iguales.

[81]:
state_fidelity(desired_vector,qc_state)
[81]:
1.0000000000000004

Más detalles:

¿Cómo se genera el estado deseado detrás de las escenas? Hay varios métodos para hacer esto. Qiskit usa un método propuesto por Shende et al. Aquí, la idea es asumir que el registro cuántico ha comenzado desde nuestro estado deseado, y construir un circuito que lo lleve al estado \(\left|00..0\right\rangle\). El circuito de inicialización es entonces el reverso de tal circuito.

Para llevar un estado cuántico arbitrario al estado cero en la base computacional, realizamos un procedimiento iterativo que desentrelaza los qubits del registro uno por uno. Sabemos que cualquier estado arbitrario de un solo qubit \(\left|\rho\right\rangle\) puede ser llevado al estado \(\left|0\right\rangle\) utilizando una rotación de \(\phi\) grados sobre el eje Z seguido de una rotación de \(\theta\) grados sobre el eje Y:

\[R_y(-\theta)R_z(-\phi)\left|\rho\right\rangle = re^{it}\left|0\right\rangle\]

Puesto que ahora estamos tratando con \(n\) qubits en lugar de sólo 1, debemos factorizar el vector del estado para separar el bit menos significativo (LSB):

\[\begin{split}\begin{align*} \left|\psi\right\rangle =& \alpha_{0_0}\left|00..00\right\rangle + \alpha_{0_1}\left|00..01\right\rangle + \alpha_{1_0}\left|00..10\right\rangle + \alpha_{1_1}\left|00..11\right\rangle + ... \\&+ \alpha_{(2^{n-1}-1)_0}\left|11..10\right\rangle + \alpha_{(2^{n-1}-1)_1}\left|11..11\right\rangle \\ =& \left|00..0\right\rangle (\alpha_{0_0}\left|0\right\rangle + \alpha_{0_1}\left|1\right\rangle) + \left|00..1\right\rangle (\alpha_{1_0}\left|0\right\rangle + \alpha_{1_1}\left|1\right\rangle) + ... \\&+ \left|11..1\right\rangle (\alpha_{(2^{n-1}-1)_0}(\left|0\right\rangle + \alpha_{(2^{n-1}-1)_1}\left|1\right\rangle) \\ =& \left|00..0\right\rangle\left|\rho_0\right\rangle + \left|00..1\right\rangle\left|\rho_1\right\rangle + ... + \left|11..1\right\rangle\left|\rho_{2^{n-1}-1}\right\rangle \end{align*}\end{split}\]

Ahora, cada uno de los estados de un solo qubit \(\left|\rho_0\right\rangle, ..., \left|\rho_{2^{n-1}-1}\right\rangle\) se puede llevar a \(\left|0\right\rangle\) mediante la búsqueda de los ángulos \(\phi\) y \(\theta\) apropiados según la ecuación anterior. Hacer esto simultáneamente en todos los estados equivale a la siguiente unitaria, que desentrelaza el LSB:

\[\begin{split}U = \begin{pmatrix} R_{y}(-\theta_0)R_{z}(-\phi_0) & & & &\\ & R_{y}(-\theta_1)R_{z}(-\phi_1) & & &\\ & . & & &\\ & & . & &\\ & & & & R_y(-\theta_{2^{n-1}-1})R_z(-\phi_{2^{n-1}-1}) \end{pmatrix}\end{split}\]

Por lo tanto,

\[\begin{split}U\left|\psi\right\rangle = \begin{pmatrix} r_0e^{it_0}\\ r_1e^{it_1}\\ . \\ . \\ r_{2^{n-1}-1}e^{it_{2^{n-1}-1}} \end{pmatrix}\otimes\left|0\right\rangle\end{split}\]

U se puede implementar como una compuerta de «multiplexor cuántico», ya que es una matriz diagonal de bloque. En el formalismo del multiplexor cuántico, una matriz diagonal de bloque de tamaño \(2^n \times 2^n\), y que consiste de \(2^s\) bloques, es equivalente a un multiplexor con \(s\) qubits seleccionados y \(n-s\) qubits de datos. Dependiendo del estado de los qubits seleccionados, los bloques correspondientes se aplican a los qubits de datos. Un multiplexor de este tipo puede ser implementado después de una descomposición recursiva a las compuertas primitivas de cx, rz y ry.

[82]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright

Version Information

Qiskit SoftwareVersion
qiskit-terra0.20.2
qiskit-aer0.10.4
qiskit-ignis0.7.1
qiskit-ibmq-provider0.19.1
qiskit0.36.2
qiskit-optimization0.3.2
qiskit-machine-learning0.4.0
System information
Python version3.10.4
Python compilerMSC v.1916 64 bit (AMD64)
Python buildmain, Mar 30 2022 08:38:02
OSWindows
CPUs6
Memory (Gb)15.869640350341797
Sat May 28 10:03:32 2022 Eastern Daylight Time

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.

[ ]: