Portuguese
Idiomas
English
Bengali
French
German
Japanese
Korean
Portuguese
Spanish
Tamil

Nota

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

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
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 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(\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 é a forma mais geral da unitária de um único qubit.

Portas de um qubit

Os gates de qubit unitário disponíveis são: - Gate U - Gate de identidade - Gates de Pauli - Gates de Clifford - Gates \(C3\) - Gates de rotação padrão

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

[3]:
q = QuantumRegister(1)

Gate U

No Qiskit damos a você acesso ao unitario geral usando a porta \(u\), a qual tem a seguinte 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]])

Note que o gate U está depreciado

Os métodos \(u1\), \(u2\) e \(u3\) de QuantumCircuit estão depreciados. Os seguintes substitutos devem ser utilizados.

  • \(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)

Gate P

O portão \(p(\lambda)= u(0, 0, \lambda)\) tem a forma matricial

\[\begin{split}p(\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.

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

Porta identidade

O gate de identidade é \(Id = u1(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]])

Portas Pauli

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

A porta inversora \(X\) é 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\): 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}=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]])

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

A porta \(Z\) é 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]])

Portas Clifford

Porta 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]])

Porta \(S\) (ou, 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]])

Porta \(S^{\dagger}\) (ou, conjugada da 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]])

Portas \(C3\)

Porta \(T\) (ou, 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]])

Porta \(T^{\dagger}\) (ou, conjugada da 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]])

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} = 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   ]])

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} =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]])

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 p(\phi)\end{split}\]

Perceba que aqui utilizamos um equivalente, já que difere de \(p\), por uma 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]])

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

Portas de Múltiplos Qubits

Preliminares Matemáticos

The space of a quantum computer grows exponentially with the number of qubits. For \(n\) qubits the complex vector space has dimension \(d=2^n\). To describe states of a multi-qubit system, the tensor product is used to «glue together» operators and basis vectors.

Let’s start by considering a 2-qubit system. Given two operators \(A\) and \(B\) that each act on one qubit, the joint operator \(A \otimes B\) acting on two qubits is

\[\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 física, os qubits de um sistema multi-qubit são normalmente ordenados com o primeiro qubit no lado esquerdo do produto tensor e o último qubit no lado direito. Por exemplo, se o primeiro qubit estiver no estado \(\left|0\right\rangle\) e o segundo estiver no estado: \(\left|1\right\rangle\), seu estado conjunto será \(\left|01\right\rangle\). O Qiskit usa uma ordenação ligeiramente diferente dos qubits, na qual os qubits são representados do bit mais significativo (MSB) à esquerda para o bit menos significativo (LSB) à direita (little-endian). Isso é semelhante à representação de bitstring em computadores clássicos e permite a conversão fácil de bitstrings para inteiros após a execução das medições. Para o exemplo dado, o estado conjunto seria representado como \(\left|10\right\rangle\). É importante ressaltar que essa mudança na representação de estados multi-qubit afeta a forma como as portas multi-qubit 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}\]

We can work out the action of \(C_{U}\) as follows. Recall that the basis vectors for a two-qubit system are ordered as \(\left|00\right\rangle, \left|01\right\rangle, \left|10\right\rangle, \left|11\right\rangle\). Suppose the control qubit is qubit 0 (which, according to Qiskit’s convention, is one the right-hand side of the tensor product). If the control qubit is in \(\left|1\right\rangle\), \(U\) should be applied to the target (qubit 1, on the left-hand side of the tensor product). Therefore, under the action of \(C_{U}\), the basis vectors are transformed according to

\[\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| m \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 Controlled-X (ou, Controlled-NOT)

The Controlled-NOT gate flips the target qubit when the control qubit is in the state \(\left|1\right\rangle\). If we take the MSB as the control qubit (e.g. cx(q[1],q[0])), then the matrix would look like

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

However, when the LSB is the control qubit, (e.g. cx(q[0],q[1])), this gate is equivalent to the following matrix:

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

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]:
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]])

Porta Controlled \(Z\) (ou, controlled Phase-Flip)

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]:
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]])

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]:
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]])

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]:
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]])

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_{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]])

Rotação controlada \(u\)

Perform controlled-\(u\) rotation on the target qubit if the control qubit (here LSB) is \(\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   ]])

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

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

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

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.

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

Medições

We don’t have access to all the information when we make a measurement in a quantum computer. The quantum state is projected onto the standard basis. Below are two examples showing a circuit that is prepared in a basis state and the quantum computer prepared in a superposition state.

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

O simulador prevê que, em 100% do tempo, o registrador clássico retorna 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}

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.

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

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

[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 

Aqui o bit clássico sempre recebe o valor 0, então, o estado do qubit é sempre 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}

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

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.

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

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 can be implemented as a «quantum multiplexor» gate, since it is a block diagonal matrix. In the quantum multiplexor formalism, a block diagonal matrix of size \(2^n \times 2^n\), and consisting of \(2^s\) blocks, is equivalent to a multiplexor with \(s\) select qubits and \(n-s\) data qubits. Depending on the state of the select qubits, the corresponding blocks are applied to the data qubits. A multiplexor of this kind can be implemented after recursive decomposition to primitive gates of cx, rz and 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.

[ ]: