Portuguese, Brazilian
Idiomas
English
Japanese
German
Korean
Portuguese, Brazilian
French
Shortcuts

Nota

Esta página foi gerada, a partir do tutoriais/circuitos/3_resumo_de_operações_quantum.ipynb.

Execute interativamente no IBM Quantum lab.

Resumo das Operações Quânticas

Nesta seção entraremos nas diferentes operações que estão disponíveis no Qiskit Terra. Estas são:

  • Portas quânticas de um único qubit

  • Portas quânticas de múltiplos qubits

  • Medições

  • Redefinir

  • Condicionais

  • Inicialização de estado

Mostraremos também como usar os três simuladores distintos:

  • unitary_simulator

  • qasm_simulator

  • statevector_simulator

[1]:
# Useful additional packages
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
from math import pi
[2]:
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister, execute
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 quânticos de um único qubit

O estado quântico de um qubit pode ser escrito como

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

onde \(\alpha\) e \(\beta\) são números complexos. Em uma medição, a probabilidade do bit estar em \(\left|0\right\rangle\) é \(|\alpha|^2\) e em \(\left|1\right\rangle\) é \(|\beta|^2\). Representando como um vetor isso é

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

Note que, devido à conservação da probabilidade \(|\alpha|^2+ |\beta|^2 = 1\) e, como a fase global é indetectável \(\left|\psi\right\rangle := e^{i\delta}, \left|\psi\right\rangle\) necessitamos de apenas dois números reais, para descrever o estado quântico de um único qubit.

Uma representação conveniente é

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

onde \(0\leq \phi < 2\pi\), and \(0\leq \theta \leq \pi\). A partir disto, fica claro que existe uma correspondência um-pra-um (bijeção) entre os estados do qubit (\(\mathbb{C}^2\)) e os pontos na superfície de uma esfera unitária (\(\mathbb{R}^3\)). Isto é chamado de representação da esfera de Bloch do estado de um qubit.

Portas/Operações quânticas são geralmente representadas como matrizes. Uma porta que atua em um qubit é representada por uma matriz unitária \(2\times 2\) \(U\). A ação da porta quântica é obtida multiplicando a matriz, que representa a porta, pelo vetor, que representa o estado quântico.

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

Uma unitária geral deve ser capaz de levar \(\left|0\right\rangle\) para o estado acima. Isto é

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

onde \(a\) e \(b\) são números complexos condicionados, tais que \(U^\dagger U = I\) para todo \(0\leq\theta\leq\pi\) e \(0\leq \phi<2\pi\). Isto fornece três condições, de modo que \(a\rightarrow -e^{i\lambda}\sin(\theta/2)\) e \(b\rightarrow e^{i\lambda+i\phi}\cos(\theta/2)\), onde \(0\leq \lambda<2\pi\) resultando em

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

Esta é a forma mais geral da unitária de um único qubit.

Portas de um qubit

As portas de um qubit disponíveis são: - u gates - Identity gate - Pauli gates - Clifford gates - \(C3\) gates - Standard rotation gates

Fornecemos um backend: unitary_simulator para permitir calcular as matrizes unitárias.

[3]:
q = QuantumRegister(1)

Portas u

No Qiskit nós lhe damos acesso à unitária geral usando a porta \(u3\)

\[u3(\theta, \phi, \lambda) = U(\theta, \phi, \lambda)\]
[4]:
qc = QuantumCircuit(q)
qc.u3(pi/2,pi/2,pi/2,q)
qc.draw()
[4]:
      ┌────────────────────┐
q0_0: ┤ U3(pi/2,pi/2,pi/2) ├
      └────────────────────┘
[5]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[5]:
array([[ 0.707+0.j   , -0.   -0.707j],
       [ 0.   +0.707j, -0.707+0.j   ]])

A porta \(u2(\phi, \lambda) =u3(\pi/2, \phi, \lambda)\) tem a forma matricial

\[\begin{split}u2(\phi, \lambda) = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & -e^{i\lambda} \\ e^{i\phi} & e^{i(\phi + \lambda)} \end{pmatrix}.\end{split}\]

Esta é uma porta útil, já que nos permite criar superposições.

[6]:
qc = QuantumCircuit(q)
qc.u2(pi/2,pi/2,q)
qc.draw()
[6]:
      ┌───────────────┐
q0_0: ┤ U2(pi/2,pi/2) ├
      └───────────────┘
[7]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[7]:
array([[ 0.707+0.j   , -0.   -0.707j],
       [ 0.   +0.707j, -0.707+0.j   ]])

A porta \(u1(\lambda)= u3(0, 0, \lambda)\) tem a forma matricial

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

que é útil, uma vez que nos permite aplicar uma fase quântica.

[8]:
qc = QuantumCircuit(q)
qc.u1(pi/2,q)
qc.draw()
[8]:
      ┌──────────┐
q0_0: ┤ U1(pi/2) ├
      └──────────┘
[9]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[9]:
array([[1.+0.j, 0.+0.j],
       [0.+0.j, 0.+1.j]])

Porta identidade

A porta identidade é \(Id = u0(1)\).

[13]:
qc = QuantumCircuit(q)
qc.id(q)
qc.draw()
[13]:
      ┌───┐
q0_0: ┤ I ├
      └───┘
[14]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[14]:
array([[1.+0.j, 0.+0.j],
       [0.+0.j, 1.+0.j]])

Portas Pauli

\(X\): porta inversora (bit-flip)

A porta inversora \(X\) é definida como:

\[\begin{split}X = \begin{pmatrix} 0 & 1\\ 1 & 0 \end{pmatrix}= u3(\pi,0,\pi)\end{split}\]
[15]:
qc = QuantumCircuit(q)
qc.x(q)
qc.draw()
[15]:
      ┌───┐
q0_0: ┤ X ├
      └───┘
[16]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[16]:
array([[0.+0.j, 1.-0.j],
       [1.+0.j, 0.+0.j]])

\(Y\): porta inversora de bit e fase (bit- and phase-flip)

A porta \(Y\) é definida como:

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

\(Z\): porta inversora de fase (phase-flip)

A porta \(Z\) é definida como:

\[\begin{split}Z = \begin{pmatrix} 1 & 0\\ 0 & -1 \end{pmatrix}=u1(\pi)\end{split}\]
[19]:
qc = QuantumCircuit(q)
qc.z(q)
qc.draw()
[19]:
      ┌───┐
q0_0: ┤ Z ├
      └───┘
[20]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[20]:
array([[ 1.+0.j,  0.+0.j],
       [ 0.+0.j, -1.+0.j]])

Portas Clifford

Porta Hadamard

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

Porta \(S\) (ou, fase \(\sqrt{Z}\))

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

Porta \(S^{\dagger}\) (ou, conjugada da fase \(\sqrt{Z}\))

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

Portas \(C3\)

Porta \(T\) (ou, fase \(\sqrt{S}\))

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

Porta \(T^{\dagger}\) (ou, conjugada da fase \(\sqrt{S}\))

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

Rotações Padrão

As portas de rotação padrão são aquelas que definem rotações em torno de \(P=\{X,Y,Z\}\) de Pauli. Elas são definidas como

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

Rotação em torno do eixo X

\[\begin{split}R_x(\theta) = \begin{pmatrix} \cos(\theta/2) & -i\sin(\theta/2)\\ -i\sin(\theta/2) & \cos(\theta/2) \end{pmatrix} = u3(\theta, -\pi/2,\pi/2)\end{split}\]
[31]:
qc = QuantumCircuit(q)
qc.rx(pi/2,q)
qc.draw()
[31]:
      ┌──────────┐
q0_0: ┤ RX(pi/2) ├
      └──────────┘
[32]:
job = execute(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   ]])

Rotação em torno do eixo Y

\[\begin{split}R_y(\theta) = \begin{pmatrix} \cos(\theta/2) & - \sin(\theta/2)\\ \sin(\theta/2) & \cos(\theta/2). \end{pmatrix} =u3(\theta,0,0)\end{split}\]
[33]:
qc = QuantumCircuit(q)
qc.ry(pi/2,q)
qc.draw()
[33]:
      ┌──────────┐
q0_0: ┤ RY(pi/2) ├
      └──────────┘
[34]:
job = execute(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]])

Rotação em torno do eixo Z

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

Perceba que aqui utilizamos um equivalente, já que difere de u1, por uma fase global \(e^{-i \phi/2}\).

[35]:
qc = QuantumCircuit(q)
qc.rz(pi/2,q)
qc.draw()
[35]:
      ┌──────────┐
q0_0: ┤ RZ(pi/2) ├
      └──────────┘
[36]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[36]:
array([[1.+0.j, 0.+0.j],
       [0.+0.j, 0.+1.j]])

Note que isto é diferente, apenas devido a uma fase global.

Portas de Múltiplos Qubits

Preliminares Matemáticos

O espaço de um computador quântico cresce exponencialmente com o número de qubits. Para \(n\) qubits, o espaço vetorial complexo tem dimensão \(d=2^n\). Para descrever estados de um sistema com múltiplos qubits, o produto tensorial é usado para “grudar” operadores e vetores da base.

Vamos começar considerando um sistema de 2 qubits. Dados dois operadores \(A\) e \(B\), tais que cada um age em um qubit, o operador conjunto \(A \otimes B\) atuando em dois qubits é

\[\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}\]

onde \(A_{jk}\) e \(B_{lm}\) são os elementos de \(A\) e \(B\), respectivamente.

Analogamente, os vetores da base para o sistema de 2 qubits são formados usando o produto tensorial dos vetores da base para um único 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}\]

Note we’ve introduced a shorthand for the tensor product of basis vectors, wherein \(\left|0\right\rangle \otimes \left|0\right\rangle\) is written as \(\left|00\right\rangle\). The state of an \(n\)-qubit system can be described using the \(n\)-fold tensor product of single-qubit basis vectors. Notice that the basis vectors for a 2-qubit system are 4-dimensional; in general, the basis vectors of an \(n\)-qubit system are \(2^{n}\)-dimensional, as noted earlier.

Ordem do vetor da base no Qiskit

Dentro da comunidade de física, os qubits de sistemas com múltiplos qubits são, normalmente, ordenados com o primeiro qubit do lado esquerdo do produto tensorial e o último qubit do lado direito. Por exemplo, se o primeiro qubit estiver em um estado \(\left|0\right\rangle\) e o segundo estiver no estado \(\left|1\right\rangle\), o seu estado conjunto seria \(\left|01\right\rangle\). O Qiskit usa uma ordem ligeiramente diferente para os qubits, em que eles são representados do bit mais significativo (MSB) à esquerda para o bit menos significativo (LSB) à direita (big-endian). Isto é semelhante à representação de bitstrings em computadores clássicos e permite fácil conversão de bitstrings para inteiros, após à realização de medições. Pelo exemplo que acabamos de dar, o estado conjunto seria representado como \(\left|10\right\rangle\). Importante, esta mudança na representação dos estados de múltiplos qubits afeta a forma como as portas de múltiplos qubits são representadas no Qiskit, conforme discutido abaixo.

A representação usada no Qiskit enumera os vetores da base em ordem crescente dos inteiros que eles representam. Por exemplo, os vetores da base para um sistema de 2 qubits seria ordenado como \(\left|00\right\rangle\), \(\left|01\right\rangle\), \(\left|10\right\rangle\), e \(\left|11\right\rangle\). Pensando nos vetores da base como strings de bits, eles codificam os inteiros 0,1,2 e 3, respectivamente.

Operações controladas em qubits

Uma porta de múltiplos qubits comum envolve a aplicação de uma porta de um único qubit, condicionada pelo estado de outro qubit. Por exemplo, podemos desejar inverter o estado do segundo qubit, quando o primeiro estiver em \(\left|0\right\rangle\). Tais portas são conhecidas como portas controladas. A portas padrão de múltiplos qubits consistem de portas de dois qubits e três qubits. As portas de dois qubits são: - portas Pauli controladas - porta Hadamard controlada - portas de rotação controladas - portas de fase controladas - porta u3 controlada - porta swap

As portas de três qubits são: - Porta Toffoli - Porta Fredkin

Portas de dois qubits

A maioria das portas de dois qubits é do tipo controlado (a porta SWAP sendo a exceção). Em geral, uma porta de dois qubits controlada \(C_{U}\) atua aplicando a unitária de um qubit \(U\) ao segundo qubit ,quando o estado do primeiro qubit está em \(\left|1\right\rangle\). Suponha que \(U\) tenha uma representação matricial

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

Podemos encontrar a ação de \(C_{U}\) da seguinte forma. Lembre que os vetores da base para um sistema de dois qubits são ordenado como \(\left|00\right\rangle, \left|01\right\rangle, \left|10\right\rangle, \left|11\right\rangle\). Suponha que o qubit de controle é o qubit 0 (o qual, de acordo com a convenção do Qiskit, fica no lado mais à direita do produto tensorial). Se o qubit de controle estiver em \(\left|1\right\rangle\), \(U\) deve ser aplicada ao alvo (o qubit 1, no lado mais à esquerda do produto tensorial). Portanto, sob a ação de \(C_{U}\), os vetores da base são transformados conforme

\[\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}\]

Em sua forma matricial, a ação de \(C_{U}\) é

\[\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 encontrar estes elementos da matriz, considere

\[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| k \right\rangle}\right),\]

calcule a ação de \(C_{U}\) (dado acima), e calcule os produtos internos.

Como mostrado nos exemplos abaixo, esta operação é implementada no Qiskit como cU(q[0],q[1]).

Se o qubit 1 é o controle e o qubit 0 é o alvo, então os vetores da base são transformados, de acordo com

\[\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}\]

o que implica que a forma matricial de \(C_{U}\) é

\[\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)

Portas Pauli Controladas

Porta X controlada (or, Não controlada)

A porta controlada NOT inverte o qubit alvo ,quando o qubit de controle estiver no estado \(\left|1\right\rangle\). Se tomarmos o MSB como qubit de controle (ex.: cx(q[1],q[0])), então a matriz pareceria com

\[\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}\]

No entanto, quando o qubit de controle é o LSB, (por exemplo, cx(q[0],q[1])), esta porta é equivalente à seguinte 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]:
q1_0: ──■──
      ┌─┴─┐
q1_1: ┤ X ├
      └───┘
[39]:
job = execute(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]])

Porta \(Y\) controlada

Aplica a porta \(Y\) no qubit alvo, se o qubit de controle for o 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}\]

ou quando o LSB é o controle

\[\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]:
q1_0: ──■──
      ┌─┴─┐
q1_1: ┤ Y ├
      └───┘
[41]:
job = execute(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]])

Porta \(Z\) controlada (ou, Fase controlada)

Da mesma forma, a porta Z controlada inverte a fase do qubit alvo, se o qubit de controle for \(\left|1\right\rangle\). A matriz parece a mesma, independentemente, de o MSB ou LSB ser o qubit de controle:

\[\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]:
q1_0: ─■─
       │
q1_1: ─■─
         
[43]:
job = execute(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]])

Porta controlada Hadamard

Aplica a porta \(H\) no qubit alvo, se o qubit de controle for \(\left|1\right\rangle\). Abaixo, está o caso em que o controle é o 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]:
q1_0: ──■──
      ┌─┴─┐
q1_1: ┤ H ├
      └───┘
[45]:
job = execute(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]])

Portas de rotação controladas

Rotação controlada em torno do eixo Z

Executa uma rotação em torno do eixo Z no qubit alvo, se o qubit de controle (LSB aqui) for \(\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]:
q1_0: ─────■──────
      ┌────┴─────┐
q1_1: ┤ RZ(pi/2) ├
      └──────────┘
[47]:
job = execute(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]])

Rotação de fase controlada

Realiza uma rotação da fase, se ambos os qubits estiverem no estado \(\left|11\right\rangle\). A matriz parece a mesma, independentemente, do MSB ou do LSB ser o qubit de controle.

\[\begin{split}C_{u1}(\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.cu1(pi/2,q[0], q[1])
qc.draw()
[48]:
q1_0: ─■─────
       │pi/2
q1_1: ─■─────
             
[49]:
job = execute(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]])

Rotação \(u3\) controlada

Perform controlled-\(u3\) rotation on the target qubit if the control qubit (here LSB) is \(\left|1\right\rangle\).

\[\begin{split}C_{u3}(\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}\]
[50]:
qc = QuantumCircuit(q)
qc.cu3(pi/2, pi/2, pi/2, q[0], q[1])
qc.draw()
[50]:
q1_0: ──────────■───────────
      ┌─────────┴──────────┐
q1_1: ┤ U3(pi/2,pi/2,pi/2) ├
      └────────────────────┘
[51]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[51]:
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   ]])

Porta SWAP

A porta SWAP troca os dois qubits. Ela transforma os vetores da base da seguinte maneira

\[\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,\]

o que fornece uma representação matricial da 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}\]
[52]:
qc = QuantumCircuit(q)
qc.swap(q[0], q[1])
qc.draw()
[52]:
q1_0: ─X─
       │
q1_1: ─X─
         
[53]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[53]:
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]])

Portas de três qubits

Existem duas portas de três qubits, comumente, usadas. Para três qubits, os vetores da base são ordenados 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 bitstrings, representam os inteiros \(0,1,2,\cdots, 7\). Novamente, o Qiskit usa uma representação, em que o primeiro qubit está do lado direito do produto tensorial e o terceiro qubit está do lado mais à esquerda:

\[\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}.\]

Porta Toffoli (porta \(ccx\))

A porta Toffoli inverte o terceiro qubit, se os dois primeiros qubits (LSB) forem 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.\]

Em sua forma matricial, a porta Toffoli é

\[\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}\]
[54]:
q = QuantumRegister(3)
[55]:
qc = QuantumCircuit(q)
qc.ccx(q[0], q[1], q[2])
qc.draw()
[55]:
q2_0: ──■──
        │
q2_1: ──■──
      ┌─┴─┐
q2_2: ┤ X ├
      └───┘
[56]:
job = execute(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, 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]])

Porta controlada de troca (Porta Fredkin)

A porta Fredkin, ou porta controlada de troca, troca o segundo e o terceiro qubits, se o primeiro qubit (LSB) for \(\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}.\]

Em sua forma matricial, a porta Fredkin é

\[\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}\]
[57]:
qc = QuantumCircuit(q)
qc.cswap(q[0], q[1], q[2])
qc.draw()
[57]:
q2_0: ─■─
       │
q2_1: ─X─
       │
q2_2: ─X─
         
[58]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[58]:
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]])

Operações não unitárias

Agora que passamos por todas as operações unitárias em circuitos quânticos, também temos acesso a operações não unitárias. Estas incluem medições, reset de qubits e operações condicionais clássicas.

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

Medições

Não temos acesso a todas as informações quando fazemos uma medição em um computador quântico. O estado quântico é projetado sobre a base padrão. Abaixo estão dois exemplos que mostram um circuito que é preparado em um estado da base e o computador quântico preparado em um estado de superposição.

[60]:
qc = QuantumCircuit(q, c)
qc.measure(q, c)
qc.draw()
[60]:
      ┌─┐
q3_0: ┤M├
      └╥┘
c0_0: ═╩═
         
[61]:
backend = BasicAer.get_backend('qasm_simulator')
job = execute(qc, backend, shots=1024)
job.result().get_counts(qc)
[61]:
{'0': 1024}

O simulador prevê que, em 100% do tempo, o registrador clássico retorna 0.

[62]:
qc = QuantumCircuit(q, c)
qc.h(q)
qc.measure(q, c)
qc.draw()
[62]:
      ┌───┐┌─┐
q3_0: ┤ H ├┤M├
      └───┘└╥┘
c0_0: ══════╩═
              
[63]:
job = execute(qc, backend, shots=1024)
job.result().get_counts(qc)
[63]:
{'0': 532, '1': 492}

O simulador prevê que, em 50% do tempo, o registrador clássico retorna 0 ou 1.

Redefinir

Também é possível reset qubits para o estado \(\left|0\right\rangle\) no meio do processamento. Observe que reset não é uma operação de Gate, uma vez que é irreversível.

[64]:
qc = QuantumCircuit(q, c)
qc.reset(q[0])
qc.measure(q, c)
qc.draw()
[64]:
           ┌─┐
q3_0: ─|0>─┤M├
           └╥┘
c0_0: ══════╩═
              
[65]:
job = execute(qc, backend, shots=1024)
job.result().get_counts(qc)
[65]:
{'0': 1024}
[66]:
qc = QuantumCircuit(q, c)
qc.h(q)
qc.reset(q[0])
qc.measure(q, c)
qc.draw()
[66]:
      ┌───┐     ┌─┐
q3_0: ┤ H ├─|0>─┤M├
      └───┘     └╥┘
c0_0: ═══════════╩═
                   
[67]:
job = execute(qc, backend, shots=1024)
job.result().get_counts(qc)
[67]:
{'0': 1024}

Aqui vemos que, para ambos os circuitos, o simulador sempre prevê que a saída é o estado 0, em 100% das vezes.

Operações condicionais

Também é possível fazer operações condicionadas ao estado do registrador clássico

[68]:
qc = QuantumCircuit(q, c)
qc.x(q[0]).c_if(c, 0)
qc.measure(q,c)
qc.draw()
[68]:
       ┌───┐ ┌─┐
q3_0: ─┤ X ├─┤M├
       └─┬─┘ └╥┘
      ┌──┴──┐ ║
c0_0: ╡ = 0 ╞═╩═
      └─────┘   

Aqui o bit clássico sempre recebe o valor 0, então, o estado do qubit é sempre invertido.

[69]:
job = execute(qc, backend, shots=1024)
job.result().get_counts(qc)
[69]:
{'1': 1024}
[70]:
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()
[70]:
      ┌───┐┌─┐ ┌───┐ ┌─┐
q3_0: ┤ H ├┤M├─┤ X ├─┤M├
      └───┘└╥┘ └─┬─┘ └╥┘
            ║ ┌──┴──┐ ║
c0_0: ══════╩═╡ = 0 ╞═╩═
              └─────┘   
[71]:
job = execute(qc, backend, shots=1024)
job.result().get_counts(qc)
[71]:
{'1': 1024}

Aqui o bit clássico pela primeira medição é aleatório, mas a operação condicional resulta no qubit ser,deterministicamente, colocado em \(\left|1\right\rangle\).

Inicialização arbitrária

E se quisermos inicializar um registrador de qubits em um estado arbitrário? Um estado arbitrário para \(n\) qubits, pode ser especificado por um vetor de \(2^n\) amplitudes, em que a soma das normas, ao quadrado das amplitudes, é igual a 1. Por exemplo, o seguinte estado de três qubit pode 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\]
[77]:
# 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()
[77]:
       ┌───────────────────────────────────────────────────────────────────┐
q28_0: ┤0                                                                  ├
       │                                                                   │
q28_1: ┤1 initialize(0.25j,0.35355,0.25+0.25j,0,0,0.35355+0.70711j,0.25,0) ├
       │                                                                   │
q28_2: ┤2                                                                  ├
       └───────────────────────────────────────────────────────────────────┘
[74]:
backend = BasicAer.get_backend('statevector_simulator')
job = execute(qc, backend)
qc_state = job.result().get_statevector(qc)
qc_state
[74]:
array([2.50000000e-01+0.j        , 5.55111512e-17-0.35355339j,
       2.50000000e-01-0.25j      , 0.00000000e+00+0.j        ,
       0.00000000e+00+0.j        , 7.07106781e-01-0.35355339j,
       8.67361738e-17-0.25j      , 0.00000000e+00+0.j        ])

Fidelidade é útil para verificar se dois estados são o mesmo ou não. Para estados quânticos (puros) \(\left|\psi_1\right\rangle\) e \(\left|\psi_2\right\rangle\), a fidelidade é

\[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.\]

A fidelidade é igual a \(1\) se e, somente se, dois estados são iguais.

[75]:
state_fidelity(desired_vector,qc_state)
[75]:
1.0

Mais detalhes:

Como o estado desejado é gerado nos bastidores? Existem vários métodos para fazer isto. O Qiskit usa um método proposto por Shende et al. Aqui, a ideia é assumir que o registrador quântico iniciou, a partir do nosso estado desejado, e construir um circuito que o leva para o estado \(\left|00..0\right\rangle\). O circuito de inicialização é, então, o inverso de tal circuito.

Para levar um estado quântico arbitrário para o estado zero na base computacional, realizamos um procedimento iterativo que desentrelaça qubits do registrador um a um. Sabemos que qualquer estado arbitrário de um único qubit \(\left|\rho\right\rangle\) pode ser levado para o estado \(\left|0\right\rangle\) utilizando uma rotação de \(\phi\) graus sobre o eixo Z seguido de uma rotação de \(\theta\) graus sobre o eixo Y:

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

Como agora estamos lidando com \(n\) qubits, ao invés de apenas 1, precisamos fatorar o vetor de estado, para separar o 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}\]

Agora, cada um dos estados de um único qubit \(\left|\rho_0\right\rangle, ..., \left|\rho_{2^{n-1}-1}\right\rangle\) pode ser levado a \(\left|0\right\rangle\) encontrando os ângulos \(\phi\) e \(\theta\) apropriados, pela equação acima. Fazer isto, simultaneamente, em todos os estados, equivale a seguinte unitária, que desentrelaça o 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}\]

Consequentemente,

\[\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 pode ser implementado como uma porta “multiplexadora quântica”, uma vez que se trata de uma matriz diagonal em blocos. No formalismo de multiplexores quânticos, uma matriz diagonal em blocos de tamanho \(2^n \times 2^n\), e constituída por blocos de \(2^s\), é equivalente a um multiplexador com \(s\) qubits de seleção e \(n-s\) qubits de dados. Dependendo do estado dos qubits de seleção, os blocos correspondentes são aplicados nos qubits de dados. Um multiplexador deste tipo pode ser implementado, após à decomposição recursiva para as portas primitivas de cx, rz e ry.

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

Version Information

Qiskit SoftwareVersion
QiskitNone
Terra0.14.0
Aer0.6.0
Ignis0.3.0
AquaNone
IBM Q Provider0.6.1
System information
Python3.7.7 (default, Mar 26 2020, 10:32:53) [Clang 4.0.1 (tags/RELEASE_401/final)]
OSDarwin
CPUs4
Memory (Gb)16.0
Tue Apr 28 22:09:33 2020 EDT

This code is a part of Qiskit

© Copyright IBM 2017, 2020.

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.

[ ]: