Pular para o conteúdo principal
Este projeto está em fase beta. Encontrou um bug? Abra uma issue no GitHub. Tem uma pergunta ou ideia? Pergunte no Discussions — adoraríamos receber seu feedback!
Como está a qualidade da tradução nesta página?

Bits quânticos, portas e circuitos

nota

Kifumi Numata (19 abr 2024)

Clique aqui para baixar o PDF da aula original. Alguns trechos de código podem estar desatualizados, pois são imagens estáticas.

Tempo aproximado de QPU para rodar esse experimento é de 5 segundos.

1. Introdução​

Bits, portas e circuitos são os blocos fundamentais da computação quântica. Você vai aprender computação quântica com o modelo de circuitos usando bits e portas quânticas, além de revisar superposição, medição e emaranhamento.

Nesta aula você vai aprender:

  • Portas de qubit único
  • Esfera de Bloch
  • Superposição
  • Medição
  • Portas de dois qubits e estado emaranhado

Ao final desta aula, você vai aprender sobre profundidade de circuito, que é essencial para a computação quântica em escala de utilidade.

2. Computação como diagrama​

Quando trabalhamos com qubits ou bits, precisamos manipulá-los para transformar as entradas que temos nas saídas que precisamos. Para os programas mais simples, com poucos bits, é útil representar esse processo num diagrama chamado de diagrama de circuito.

A figura inferior esquerda é um exemplo de circuito clássico, e a figura inferior direita é um exemplo de circuito quântico. Em ambos os casos, as entradas estão à esquerda e as saídas à direita, enquanto as operações são representadas por símbolos. Os símbolos usados para as operações são chamados de "portas" (gates), principalmente por razões históricas.

"circuito lógico clássico e circuito quântico"

3. Porta quântica de qubit único​

3.1 Estado quântico e esfera de Bloch​

O estado de um qubit é representado como uma superposição de ∣0⟩|0\rangle e ∣1⟩|1\rangle. Um estado quântico arbitrário é representado como

∣ψ⟩=α∣0⟩+β∣1⟩|\psi\rangle =\alpha|0\rangle+ \beta|1\rangle

onde α\alpha e β\beta são números complexos tais que ∣α∣2+∣β∣2=1|\alpha|^2+|\beta|^2=1.

∣0⟩|0\rangle e ∣1⟩|1\rangle são vetores no espaço vetorial complexo bidimensional:

∣0⟩=(10),∣1⟩=(01)|0\rangle = \begin{pmatrix} 1 \\0 \end{pmatrix}, |1\rangle = \begin{pmatrix} 0\\1 \end{pmatrix}

Portanto, um estado quântico arbitrário também é representado como

∣ψ⟩=α(10)+β(01)=(αβ)|\psi\rangle = \alpha\begin{pmatrix} 1 \\ 0 \end{pmatrix} + \beta\begin{pmatrix}0\\ 1 \end{pmatrix} = \begin{pmatrix} \alpha \\ \beta \end{pmatrix}

A partir disso, podemos ver que o estado de um bit quântico é um vetor unitário em um espaço de produto interno complexo bidimensional com uma base ortonormal de ∣0⟩|0\rangle e ∣1⟩|1\rangle. Ele é normalizado para 1.

⟨ψ∣ψ⟩=(α∗β∗)(αβ)=1\langle\psi|\psi\rangle = \begin{pmatrix} \alpha^* & \beta^* \end{pmatrix} \begin{pmatrix} \alpha \\ \beta \end{pmatrix} = 1

∣ψ⟩=(αβ) |\psi\rangle =\begin{pmatrix} \alpha \\ \beta \end{pmatrix} também é chamado de vetor de estado.

Um estado quântico de um único qubit também é representado como

∣ψ⟩=cos⁡θ2∣0⟩+eiφsin⁡θ2∣1⟩=((cos⁡θ2eiφsin⁡θ2))|\psi\rangle =\cos\frac{\theta}{2}|0\rangle+e^{i\varphi}\sin\frac{\theta}{2}|1\rangle =\left( \begin{pmatrix} \cos\frac{\theta}{2}\\ e^{i\varphi}\sin\frac{\theta}{2} \end{pmatrix}\right)

onde θ\theta e φ\varphi são os ângulos da esfera de Bloch na figura a seguir.

Bloch sphere In the next few code cells, we will build up basic calculations from constituent pieces in Qiskit. We'll construct an empty circuit and then add quantum operations, discussing the gates and visualizing their effects as we go. You can run the cell by "Shift" + "Enter". Import the libraries first.

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"qiskit": "qiskit", "qiskit_aer": "qiskit-aer", "qiskit_ibm_runtime": "qiskit-ibm-runtime"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
# Import the qiskit library
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
from qiskit.quantum_info import Statevector
from qiskit.visualization import plot_bloch_multivector
from qiskit_ibm_runtime import Sampler
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.visualization import plot_histogram

Preparando o circuito quântico​

Vamos criar e desenhar um circuito de qubit único.

# Create the single-qubit quantum circuit
qc = QuantumCircuit(1)

# Draw the circuit
qc.draw("mpl")

Saída da célula de código anterior

Porta X​

A porta X é uma rotação de π\pi em torno do eixo xx da esfera de Bloch. Aplicar a porta X a ∣0⟩|0\rangle resulta em ∣1⟩|1\rangle, e aplicar a porta X a ∣1⟩|1\rangle resulta em ∣0⟩|0\rangle; portanto, é uma operação similar à porta NOT clássica, também conhecida como inversão de bit. A representação matricial da porta X está abaixo.

X=(0110)X = \begin{pmatrix} 0 & 1 \\ 1 & 0 \\ \end{pmatrix}
qc = QuantumCircuit(1) # Prepare the single-qubit quantum circuit

# Apply a X gate to qubit 0
qc.x(0)

# Draw the circuit
qc.draw("mpl")

Saída da célula de código anterior

No IBM Quantum®, o estado inicial é definido como ∣0⟩|0\rangle, então o circuito quântico acima em representação matricial é

X∣0⟩=(0110)(10)=(01)=∣1⟩X|0\rangle= \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix} \begin{pmatrix} 1 \\ 0 \end{pmatrix} =\begin{pmatrix} 0 \\ 1 \end{pmatrix} = |1\rangle

A seguir, vamos rodar esse circuito usando um simulador de vetor de estado.

# See the statevector
out_vector = Statevector(qc)
print(out_vector)

# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([0.+0.j, 1.+0.j],
dims=(2,))

Saída da célula de código anterior

O vetor coluna é exibido como vetor linha, com números complexos (a parte imaginária é indexada por jj).

Porta H​

A porta Hadamard é uma rotação de π\pi em torno de um eixo a meio caminho entre os eixos xx e zz na esfera de Bloch. Aplicar a porta H a ∣0⟩|0\rangle cria um estado de superposição como ∣0⟩+∣1⟩2\frac{|0\rangle + |1\rangle}{\sqrt{2}}. A representação matricial da porta H está abaixo.

H=12(111−1)H = \frac{1}{\sqrt{2}}\begin{pmatrix} 1 & 1 \\ 1 & -1 \\ \end{pmatrix}
qc = QuantumCircuit(1) # Create the single-qubit quantum circuit

# Apply an Hadamard gate to qubit 0
qc.h(0)

# Draw the circuit
qc.draw(output="mpl")

Saída da célula de código anterior

# See the statevector
out_vector = Statevector(qc)
print(out_vector)

# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([0.70710678+0.j, 0.70710678+0.j],
dims=(2,))

Saída da célula de código anterior

Isso é

H∣0⟩=12(111−1)(10)=12(11)=(0.7070.707)=12(∣0⟩+∣1⟩)H|0\rangle= \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} \begin{pmatrix} 1 \\0 \end{pmatrix} =\frac{1}{\sqrt{2}}\begin{pmatrix} 1 \\ 1 \end{pmatrix} =\begin{pmatrix} 0.707 \\ 0.707 \end{pmatrix} =\frac{1}{\sqrt{2}}(|0\rangle+|1\rangle)

Esse estado de superposição é tão comum e importante que recebe seu próprio símbolo:

∣+⟩≡12(∣0⟩+∣1⟩).|+\rangle \equiv \frac{1}{\sqrt{2}}(|0\rangle+|1\rangle).

Ao aplicar a porta HH em ∣0⟩|0\rangle, criamos uma superposição de ∣0⟩|0\rangle e ∣1⟩|1\rangle onde a medição na base computacional (ao longo de z na esfera de Bloch) daria cada estado com probabilidades iguais.

Estado ∣−⟩|-\rangle​

Você provavelmente já imaginou que existe um estado ∣−⟩|-\rangle correspondente:

∣−⟩≡∣0⟩−∣1⟩2.|-\rangle \equiv \frac{|0\rangle -|1\rangle}{\sqrt{2}}.

Para criar esse estado, aplique primeiro uma porta X para obter ∣1⟩|1\rangle, e depois aplique uma porta H.

qc = QuantumCircuit(1) # Create the single-qubit quantum circuit

# Apply a X gate to qubit 0
qc.x(0)

# Apply an Hadamard gate to qubit 0
qc.h(0)

# draw the circuit
qc.draw(output="mpl")

Saída da célula de código anterior

# See the statevector
out_vector = Statevector(qc)
print(out_vector)

# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([ 0.70710678+0.j, -0.70710678+0.j],
dims=(2,))

Saída da célula de código anterior

Isso é

H∣1⟩=12(11 1−1)(0 1)=12(1 −1)=(0.707 −0.707)=12(∣0⟩−∣1⟩)=∣−⟩H|1\rangle= \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 1 \\\ 1 & -1 \end{pmatrix} \begin{pmatrix} 0 \\\ 1 \end{pmatrix} =\frac{1}{\sqrt{2}}\begin{pmatrix} 1 \\\ -1 \end{pmatrix} =\begin{pmatrix} 0.707 \\\ -0.707 \end{pmatrix} =\frac{1}{\sqrt{2}}(|0\rangle-|1\rangle) = |-\rangle

Aplicar a porta HH em ∣1⟩|1\rangle resulta numa superposição igual de ∣0⟩|0\rangle e ∣1⟩|1\rangle, mas o sinal de ∣1⟩|1\rangle é negativo.

3.2 Estado quântico de qubit único e evolução unitária​

As ações de todas as portas que vimos até agora são unitárias, o que significa que podem ser representadas por um operador unitário. Em outras palavras, o estado de saída pode ser obtido atuando no estado inicial com uma matriz unitária:

∣ψ′⟩=U∣ψ⟩|\psi^{'}\rangle = U|\psi\rangle

Uma matriz unitária é uma matriz que satisfaz

U†U=UU†=I.U^{\dagger}U =U U^{\dagger} = I.

Em termos de operação de computador quântico, diríamos que aplicar uma porta quântica ao qubit faz o estado quântico evoluir. As portas de qubit único mais comuns incluem as seguintes.

Portas de Pauli:

X=(0110)=∣0⟩⟨1∣+∣1⟩⟨0∣X = \begin{pmatrix} 0 & 1 \\ 1 & 0 \\ \end{pmatrix} = |0\rangle \langle 1|+|1\rangle \langle 0| Y=(0−ii0)=−i∣0⟩⟨1∣+i∣1⟩⟨0∣Y = \begin{pmatrix} 0 & -i \\ i & 0 \\ \end{pmatrix} = -i|0\rangle \langle 1|+i|1\rangle \langle 0| Z=(100−1)=∣0⟩⟨0∣−∣1⟩⟨1∣Z = \begin{pmatrix} 1 & 0 \\ 0 & -1 \\ \end{pmatrix} = |0\rangle \langle 0|-|1\rangle \langle 1|

onde o produto externo foi calculado da seguinte forma:

∣0⟩⟨0∣=[10][10]=[1000],∣1⟩⟨0∣=[01][10]=[0010],|0\rangle \langle 0|= \begin{bmatrix} 1 \\ 0 \end{bmatrix} \begin{bmatrix} 1 & 0 \end{bmatrix} =\begin{bmatrix} 1 & 0 \\ 0 & 0 \\ \end{bmatrix}, \quad |1\rangle \langle 0|= \begin{bmatrix} 0 \\ 1 \end{bmatrix} \begin{bmatrix} 1 & 0 \end{bmatrix} =\begin{bmatrix} 0 & 0 \\ 1 & 0 \\ \end{bmatrix}, \quad ∣0⟩⟨1∣=[10][01]=[0100],∣1⟩⟨1∣=[01][01]=[0001],|0\rangle \langle 1|= \begin{bmatrix} 1 \\ 0 \end{bmatrix} \begin{bmatrix} 0 & 1 \end{bmatrix} =\begin{bmatrix} 0 & 1 \\ 0 & 0 \\ \end{bmatrix}, \quad |1\rangle \langle 1|= \begin{bmatrix} 0 \\ 1 \end{bmatrix} \begin{bmatrix} 0 & 1 \end{bmatrix} =\begin{bmatrix} 0 & 0 \\ 0 & 1 \\ \end{bmatrix}, \quad

Outras portas típicas de qubit único:

H=12[111−1],S=[100i],T=[100exp(iπ/4)]H= \frac{1}{\sqrt{2}}\begin{bmatrix} 1 & 1 \\ 1 & -1 \\ \end{bmatrix},\quad S = \begin{bmatrix} 1 & 0 \\ 0 & i \\ \end{bmatrix}, \quad T = \begin{bmatrix} 1 & 0 \\ 0 & exp(i\pi/4) \\ \end{bmatrix} Rx(θ)=e−iθX/2=cosθ2I−isinθ2X=[cosθ2−isinθ2−isinθ2cosθ2]R_x(\theta) = e^{-i\theta X/2} = cos\frac{\theta}{2}I - i sin \frac{\theta}{2}X = \begin{bmatrix} cos\frac{\theta}{2} & -i sin \frac{\theta}{2} \\ -i sin \frac{\theta}{2} & cos\frac{\theta}{2} \\ \end{bmatrix} Ry(θ)=e−iθY/2=cosθ2I−isinθ2Y=[cosθ2−sinθ2sinθ2cosθ2]R_y(\theta) = e^{-i\theta Y/2} = cos\frac{\theta}{2}I - i sin \frac{\theta}{2}Y = \begin{bmatrix} cos\frac{\theta}{2} & - sin \frac{\theta}{2} \\ sin \frac{\theta}{2} & cos\frac{\theta}{2} \\ \end{bmatrix} Rz(θ)=e−iθZ/2=cosθ2I−isinθ2Z=[e−iθ/200eiθ/2]R_z(\theta) = e^{-i\theta Z/2} = cos\frac{\theta}{2}I - i sin \frac{\theta}{2}Z = \begin{bmatrix} e^{-i\theta /2} & 0 \\ 0 & e^{i\theta /2} \\ \end{bmatrix}

O significado e o uso dessas portas são descritos em mais detalhes no curso Basics of Quantum Information.

Exercício 1​

Use o Qiskit para criar circuitos quânticos que preparem os estados descritos abaixo. Em seguida, rode cada circuito usando o simulador de vetor de estado e exiba o estado resultante na esfera de Bloch. Como bônus, veja se você consegue antecipar qual deve ser o estado final com base na intuição sobre as portas e rotações na esfera de Bloch.

(1) XX∣0⟩XX|0\rangle

(2) HH∣0⟩HH|0\rangle

(3) HZH∣0⟩HZH|0\rangle

Dica: a porta Z pode ser usada com

qc.z(0)

Solução:

### (1) XX|0> ###

# Create the single-qubit quantum circuit
qc = QuantumCircuit(1) ##your code goes here##

# Add a X gate to qubit 0
qc.x(0) ##your code goes here##

# Add a X gate to qubit 0
qc.x(0) ##your code goes here##

# Draw a circuit
qc.draw(output="mpl")

Saída da célula de código anterior

# See the statevector
out_vector = Statevector(qc)
print(out_vector)

# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([1.+0.j, 0.+0.j],
dims=(2,))

Saída da célula de código anterior

### (2) HH|0> ###
##your code goes here##
qc = QuantumCircuit(1)
qc.h(0)
qc.h(0)
qc.draw("mpl")

Saída da célula de código anterior

# See the statevector
out_vector = Statevector(qc)
print(out_vector)

# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([1.+0.j, 0.+0.j],
dims=(2,))

Saída da célula de código anterior

### (3) HZH|0> ###
##your code goes here##
qc = QuantumCircuit(1)
qc.h(0)
qc.z(0)
qc.h(0)
qc.draw("mpl")

Saída da célula de código anterior

# See the statevector
out_vector = Statevector(qc)
print(out_vector)

# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([0.+0.j, 1.+0.j],
dims=(2,))

Saída da célula de código anterior

3.3 Medição​

A medição é teoricamente um tema bastante complexo. Mas em termos práticos, realizar uma medição ao longo de zz (como todos os computadores quânticos IBM® fazem) simplesmente força o estado do qubit α∣0⟩+β∣1⟩(s.t.∣α∣2+∣β∣2=1)\alpha|0\rangle+\beta|1\rangle \quad (s.t.|\alpha|^2+|\beta|^2=1) a colapsar para ∣0⟩|0\rangle ou ∣1⟩|1\rangle, e observamos o resultado.

  • ∣α∣2|\alpha|^2 é a probabilidade de obtermos ∣0⟩|0\rangle ao medir.
  • ∣β∣2|\beta|^2 é a probabilidade de obtermos ∣1⟩|1\rangle ao medir.

Portanto, α\alpha e β\beta são chamados de amplitudes de probabilidade. (veja a "Regra de Born")

Por exemplo, 22∣0⟩+22∣1⟩\frac{\sqrt{2}}{2}|0\rangle+\frac{\sqrt{2}}{2}|1\rangle tem probabilidade igual de se tornar ∣0⟩|0\rangle ou ∣1⟩|1\rangle após a medição. 32∣0⟩−12i∣1⟩\frac{\sqrt{3}}{2}|0\rangle-\frac{1}{2}i|1\rangle tem 75% de chance de se tornar ∣0⟩|0\rangle.

Simulador Qiskit Aer​

A seguir, vamos medir um circuito que prepara a superposição de probabilidade igual acima. Precisamos adicionar as portas de medição, pois o simulador Qiskit Aer simula um hardware quântico ideal (sem ruído) por padrão. Nota: O simulador Aer também pode aplicar um modelo de ruído baseado em um computador quântico real. Voltaremos aos modelos de ruído mais adiante.

# Create a new circuit with one qubits (first argument) and one classical bits (second argument)
qc = QuantumCircuit(1, 1)
qc.h(0)
qc.measure(0, 0) # Add the measurement gate

qc.draw(output="mpl")

Saída da célula de código anterior

Agora estamos prontos para rodar nosso circuito no simulador Aer. Neste exemplo, vamos usar o padrão shots=1024, o que significa que vamos medir 1024 vezes. Em seguida, vamos plotar essas contagens em um histograma.

# Run the circuit on a simulator to get the results
# Define backend
backend = AerSimulator()

# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)

# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc])
result = job.result()

# Print the results
counts = result[0].data.c.get_counts()
print(counts)

# Plot the counts in a histogram
plot_histogram(counts)
{'0': 521, '1': 503}

Saída da célula de código anterior

Vemos que 0s e 1s foram medidos com uma probabilidade de quase 50% cada. Embora o ruído não tenha sido simulado aqui, os estados ainda são probabilísticos. Portanto, embora esperemos uma distribuição de aproximadamente 50-50, raramente encontraremos exatamente isso. Assim como 100 lançamentos de uma moeda raramente resultariam em exatamente 50 ocorrências de cada lado.

4. Portas quânticas de múltiplos qubits e emaranhamento​

4.1 Circuito quântico de múltiplos qubits​

Podemos criar um circuito quântico de dois qubits com o seguinte código. Vamos aplicar uma porta H a cada qubit.

# Create the two qubits quantum circuit
qc = QuantumCircuit(2)

# Apply an H gate to qubit 0
qc.h(0)

# Apply an H gate to qubit 1
qc.h(1)

# Draw the circuit
qc.draw(output="mpl")

Saída da célula de código anterior

# See the statevector
out_vector = Statevector(qc)
print(out_vector)
Statevector([0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j],
dims=(2, 2))

Observação: ordenação de bits no Qiskit​

O Qiskit usa a notação Little Endian para ordenar qubits e bits, o que significa que o qubit 0 é o bit mais à direita nas cadeias de bits. Exemplo: ∣01⟩|01\rangle significa que q0 está em ∣1⟩|1\rangle e q1 está em ∣0⟩|0\rangle. Atenção: parte da literatura em computação quântica usa a notação Big Endian (qubit 0 é o bit mais à esquerda), assim como grande parte da literatura de mecânica quântica.

Outro detalhe importante: ao representar um circuito quântico, ∣q0⟩|q_0\rangle é sempre colocado no topo do circuito. Com isso em mente, o estado quântico do circuito acima pode ser escrito como um produto tensorial de estados quânticos de um único qubit.

∣q1⟩⊗∣q0⟩=(a∣0⟩+b∣1⟩)⊗(c∣0⟩+d∣1⟩)|q1\rangle \otimes|q0\rangle = (a|0\rangle+b|1\rangle) \otimes (c|0\rangle+d|1\rangle)

=ac∣0⟩∣0⟩+ad∣0⟩∣1⟩+bc∣1⟩∣0⟩+bd∣1⟩∣1⟩= ac|0\rangle|0\rangle+ad|0\rangle|1\rangle+bc|1\rangle|0\rangle+bd|1\rangle|1\rangle

=ac∣00⟩+ad∣01⟩+bc∣10⟩+bd∣11⟩= ac|00\rangle+ad|01\rangle+bc|10\rangle+bd|11\rangle

( ∣ac∣2+∣ad∣2+∣bc∣2+∣bd∣2=1|ac|^2+ |ad|^2+ |bc|^2+ |bd|^2=1 )

O estado inicial do Qiskit é ∣0⟩∣0⟩=∣00⟩|0\rangle|0\rangle=|00\rangle, portanto ao aplicar HH a cada qubit, o estado muda para uma superposição de igual probabilidade.

H∣0⟩⊗H∣0⟩=12(∣0⟩+∣1⟩)⊗12(∣0⟩+∣1⟩)=12(∣00⟩+∣01⟩+∣10⟩+∣11⟩)H|0\rangle \otimes H|0\rangle=\frac{1}{\sqrt{2}}(|0\rangle+|1\rangle) \otimes \frac{1}{\sqrt{2}}(|0\rangle+|1\rangle) = \frac{1}{2}(|00\rangle+|01\rangle+|10\rangle+|11\rangle)

=12((11)⊗(11))=12(1111)=12((1000)+(0100)+(0010)+(0001))=\frac{1}{2}\left( \begin{pmatrix} 1 \\ 1 \end{pmatrix} \otimes \begin{pmatrix} 1 \\ 1 \end{pmatrix}\right) = \frac{1}{2}\begin{pmatrix} 1 \\ 1 \\ 1 \\ 1 \end{pmatrix}=\frac{1}{2}\left(\begin{pmatrix} 1 \\ 0 \\ 0 \\ 0 \end{pmatrix}+\begin{pmatrix} 0 \\ 1 \\ 0 \\ 0 \end{pmatrix}+\begin{pmatrix} 0 \\ 0 \\ 1 \\ 0 \end{pmatrix}+\begin{pmatrix} 0 \\ 0 \\ 0 \\ 1 \end{pmatrix}\right)

A regra de medição é a mesma que no caso de um único qubit: a probabilidade de medir ∣00⟩|00\rangle é ∣ac∣2|ac|^2.

# Draw a Bloch sphere
plot_bloch_multivector(out_vector)

Saída da célula de código anterior

Agora, vamos medir esse circuito.

# Create a new circuit with two qubits (first argument) and two classical bits (second argument)
qc = QuantumCircuit(2, 2)

# Apply the gates
qc.h(0)
qc.h(1)

# Add the measurement gates
qc.measure(0, 0) # Measure qubit 0 and save the result in bit 0
qc.measure(1, 1) # Measure qubit 1 and save the result in bit 1

# Draw the circuit
qc.draw(output="mpl")

Saída da célula de código anterior

Agora vamos usar o simulador Aer novamente para verificar experimentalmente que as probabilidades relativas de todos os estados de saída possíveis são aproximadamente iguais.

# Run the circuit on a simulator to get the results
# Define backend
backend = AerSimulator()

# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)

# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc])
result = job.result()

# Print the results
counts = result[0].data.c.get_counts()
print(counts)

# Plot the counts in a histogram
plot_histogram(counts)
{'10': 262, '01': 246, '00': 265, '11': 251}

Saída da célula de código anterior

Como esperado, os estados ∣00⟩|00\rangle, ∣01⟩|01\rangle, ∣10⟩|10\rangle, ∣11⟩|11\rangle foram medidos com aproximadamente 25% cada.

4.2 Portas quânticas de múltiplos qubits​

Porta CNOT​

A porta CNOT ("NOT controlado" ou CX) é uma porta de dois qubits, ou seja, sua ação envolve dois qubits ao mesmo tempo: o qubit de controle e o qubit alvo. A CNOT inverte o qubit alvo somente quando o qubit de controle está em ∣1⟩|1\rangle.

Entrada (alvo, controle)Saída (alvo, controle)
0000
0111
1010
1101

Vamos primeiro simular a ação dessa porta de dois qubits quando q0 e q1 são ambos ∣0⟩|0\rangle, e obter o vetor de estado de saída. A sintaxe do Qiskit usada é qc.cx(control qubit, target qubit).

# Create a circuit with two quantum registers and two classical registers
qc = QuantumCircuit(2, 2)

# Apply the CNOT (cx) gate to a |00> state.
qc.cx(0, 1) # Here the control is set to q0 and the target is set to q1.

# Draw the circuit
qc.draw(output="mpl")

Saída da célula de código anterior

# See the statevector
out_vector = Statevector(qc)
print(out_vector)
Statevector([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
dims=(2, 2))

Como esperado, aplicar uma porta CNOT em ∣00⟩|00\rangle não alterou o estado, pois o qubit de controle estava no estado ∣0⟩|0\rangle. Vamos voltar à nossa operação CNOT. Desta vez, vamos aplicar uma porta CNOT em ∣01⟩|01\rangle e ver o que acontece.

qc = QuantumCircuit(2, 2)

# q0=1, q1=0
qc.x(0) # Apply a X gate to initialize q0 to 1
qc.cx(0, 1) # Set the control bit to q0 and the target bit to q1.

# Draw the circuit
qc.draw(output="mpl")

Saída da célula de código anterior

# See the statevector
out_vector = Statevector(qc)
print(out_vector)
Statevector([0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],
dims=(2, 2))

Ao aplicar uma porta CNOT, o estado ∣01⟩|01\rangle se tornou ∣11⟩|11\rangle.

Vamos verificar esses resultados executando o circuito em um simulador.

# Add measurements
qc.measure(0, 0)
qc.measure(1, 1)

# Draw the circuit
qc.draw(output="mpl")

Saída da célula de código anterior

# Run the circuit on a simulator to get the results
# Define backend
backend = AerSimulator()

# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)

# Run the job
sampler = Sampler(backend)
job = sampler.run([isa_qc])
result = job.result()

# Print the results
counts = result[0].data.c.get_counts()
print(counts)

# Plot the counts in a histogram
plot_histogram(counts)
{'11': 1024}

Saída da célula de código anterior

Os resultados mostram que ∣11⟩|11\rangle foi medido com 100% de probabilidade.

4.3 Emaranhamento quântico e execução em um dispositivo quântico real​

Vamos começar apresentando um estado emaranhado específico que é particularmente importante na computação quântica, e depois definir o termo "emaranhado":

12∣00⟩+12∣11⟩\frac{1}{\sqrt{2}}|00\rangle + \frac{1}{\sqrt{2}}|11\rangle

e esse estado é chamado de estado de Bell.

Um estado emaranhado é um estado ∣ψAB⟩|\psi_{AB}\rangle composto por estados quânticos ∣ψA⟩|\psi_A\rangle e ∣ψB⟩|\psi_B\rangle que não pode ser representado como um produto tensorial de estados quânticos individuais.

Se ∣ψAB⟩|\psi_{AB}\rangle abaixo tem dois estados ∣ψ⟩A|\psi\rangle_A e ∣ψ⟩B|\psi\rangle_B;

∣ψAB⟩=12(∣00⟩+∣11⟩)=12(∣0⟩A∣0⟩B+∣1⟩A∣1⟩B)|\psi_{AB}\rangle = \frac{1}{\sqrt{2}}(|00\rangle +|11\rangle) = \frac{1}{\sqrt{2}}(|0\rangle_A|0\rangle_B +|1\rangle_A|1\rangle_B) ∣ψ⟩A=a0∣0⟩+a1∣1⟩|\psi\rangle_A = a_0|0\rangle+a_1|1\rangle ∣ψ⟩B=b0∣0⟩+b1∣1⟩|\psi\rangle_B = b_0|0\rangle+b_1|1\rangle

o produto tensorial desses dois estados é o seguinte

∣ψ⟩A⊗∣ψ⟩B=a0b0∣00⟩+a0b1∣01⟩+a1b0∣10⟩+a1b1∣11⟩|\psi\rangle _A\otimes |\psi\rangle _B = a_0 b_0|00\rangle+a_0 b_1|01\rangle+a_1 b_0|10\rangle+a_1 b_1|11\rangle

mas não existem coeficientes a0a_0, a1a_1, b0b_0, e b1b_1 que satisfaçam essas duas equações. Portanto, ∣ψAB⟩|\psi_{AB}\rangle não pode ser representado como um produto tensorial de estados quânticos individuais, ∣ψ⟩A|\psi\rangle_A e ∣ψ⟩B|\psi\rangle_B, o que significa que ∣ψAB⟩=12(∣00⟩+∣11⟩)|\psi_{AB}\rangle = \frac{1}{\sqrt{2}}(|00\rangle +|11\rangle) é um estado emaranhado.

Vamos criar o estado de Bell e executá-lo em um computador quântico real. Para isso, vamos seguir os quatro passos para escrever um programa quântico, chamados de Qiskit patterns (padrões Qiskit):

  1. Mapear o problema para circuitos e operadores quânticos

  2. Otimizar para o hardware alvo

  3. Executar no hardware alvo

  4. Pós-processar os resultados

Passo 1. Mapear o problema para circuitos e operadores quânticos​

Em um programa quântico, os circuitos quânticos são o formato nativo para representar instruções quânticas. Ao criar um circuito, você normalmente criará um novo objeto QuantumCircuit, depois adicionará instruções a ele em sequência.

A célula de código a seguir cria um circuito que prepara um estado de Bell, o estado emaranhado de dois qubits que vimos acima.

qc = QuantumCircuit(2, 2)

qc.h(0)
qc.cx(0, 1)

qc.measure(0, 0)
qc.measure(1, 1)

qc.draw("mpl")

Saída da célula de código anterior

Passo 2. Otimizar para o hardware alvo​

O Qiskit converte circuitos abstratos em circuitos QISA (Quantum Instruction Set Architecture) que respeitam as restrições do hardware alvo e otimiza o desempenho do circuito. Antes da otimização, precisamos especificar o hardware alvo. Se você não tem o qiskit-ibm-runtime, precisa instalá-lo primeiro. Para mais informações, consulte a referência da API.

# Install
# !pip install qiskit-ibm-runtime

Vamos especificar o hardware alvo.

from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
service.backends()
# You can specify the device
# backend = service.backend('ibm_kingston')
# You can also identify the least busy device
backend = service.least_busy(operational=True)
print("The least busy device is ", backend)

A transpilação do circuito é um processo bastante complexo. De forma simplificada, ela reescreve o circuito em um equivalente lógico usando "portas nativas" (portas que um computador quântico específico consegue implementar) e mapeia os qubits do seu circuito para os qubits reais mais adequados no computador quântico alvo. Para mais detalhes sobre transpilação, veja esta documentação.

# Transpile the circuit into basis gates executable on the hardware
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
target_circuit = pm.run(qc)

target_circuit.draw("mpl", idle_wires=False)

Você pode ver que na transpilação o circuito foi reescrito usando novas portas. Para mais informações, consulte a documentação do ECRGate.

Passo 3. Executar o circuito alvo​

Agora vamos executar o circuito alvo no dispositivo real.

sampler = Sampler(backend)
job_real = sampler.run([target_circuit])

job_id = job_real.job_id()
print("job id:", job_id)

A execução no dispositivo real pode exigir espera em uma fila, já que computadores quânticos são recursos valiosos e muito procurados. O job_id é usado para verificar posteriormente o status de execução e os resultados do job.

# Check the job status (replace the job id below with your own)
job_real.status(job_id)

Você também pode verificar o status do job pelo seu painel IBM Quantum: https://quantum.cloud.ibm.com/workloads

# If the Notebook session got disconnected you can also check your job status
# by running the following code
from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
job_real = service.job(job_id) # Input your job-id between the quotations
job_real.status()
# Execute after job has successfully run
result_real = job_real.result()
print(result_real[0].data.c.get_counts())

Passo 4. Pós-processar os resultados​

Por fim, precisamos pós-processar os resultados para criar saídas no formato esperado, como valores ou gráficos.

plot_histogram(result_real[0].data.c.get_counts())

Como você pode ver, ∣00⟩|00\rangle e ∣11⟩|11\rangle são os estados observados com maior frequência. Há alguns resultados além dos esperados, e eles se devem ao ruído e à decoerência dos qubits. Aprenderemos mais sobre erros e ruído em computadores quânticos nas lições seguintes deste curso.

4.4 Estado GHZ​

O conceito de emaranhamento pode ser estendido a sistemas com mais de dois qubits. O estado GHZ (estado de Greenberger-Horne-Zeilinger) é um estado maximamente emaranhado de três ou mais qubits. O estado GHZ para três qubits é definido como

12(∣000⟩+∣111⟩)\frac{1}{\sqrt 2}(|000\rangle + |111\rangle)

Ele pode ser criado com o seguinte circuito quântico.

qc = QuantumCircuit(3, 3)

qc.h(0)
qc.cx(0, 1)
qc.cx(1, 2)

qc.measure(0, 0)
qc.measure(1, 1)
qc.measure(2, 2)

qc.draw("mpl")

Saída da célula de código anterior

A "profundidade" de um circuito quântico é uma métrica útil e comum para descrever circuitos quânticos. Trace um caminho pelo circuito quântico, movendo da esquerda para a direita, trocando de qubit apenas quando eles estiverem conectados por uma porta de múltiplos qubits. Conte o número de portas ao longo desse caminho. O número máximo de portas em qualquer caminho pelo circuito é a profundidade. Em computadores quânticos modernos com ruído, circuitos de baixa profundidade têm menos erros e tendem a retornar bons resultados. Circuitos muito profundos, não.

Usando QuantumCircuit.depth(), podemos verificar a profundidade do nosso circuito quântico. A profundidade do circuito acima é 4. O qubit do topo tem apenas três portas, incluindo a medição. Mas existe um caminho que vai do qubit do topo até o qubit 1 ou o qubit 2, passando por mais uma porta CNOT.

qc.depth()
4

Exercício 2​

O estado GHZ de um sistema de 8 qubits é

12(∣00000000⟩+∣11111111⟩)\frac{1}{\sqrt 2}(|00000000\rangle + |11111111\rangle)

Escreva um código para preparar esse estado com o circuito mais raso possível. A profundidade do circuito quântico mais raso é 5, incluindo as portas de medição.

Solução:

# Step 1
qc = QuantumCircuit(8, 8)

##your code goes here##
qc.h(0)
qc.cx(0, 4)
qc.cx(4, 6)
qc.cx(6, 7)

qc.cx(4, 5)

qc.cx(0, 2)
qc.cx(2, 3)

qc.cx(0, 1)
qc.barrier() # for visual separation

# measure
for i in range(8):
qc.measure(i, i)

qc.draw("mpl")
# print(qc.depth())

Saída da célula de código anterior

print(qc.depth())
5
from qiskit.visualization import plot_histogram
# Step 2
# For this exercise, the circuit and operators are simple, so no optimizations are needed.

# Step 3
# Run the circuit on a simulator to get the results
backend = AerSimulator()

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)

sampler = Sampler(mode=backend)
job = sampler.run([isa_qc], shots=1024)
result = job.result()

counts = result[0].data.c.get_counts()
print(counts)

# Step 4
# Plot the counts in a histogram

plot_histogram(counts)
{'11111111': 535, '00000000': 489}

Saída da célula de código anterior

5. Resumo​

Você aprendeu computação quântica com o modelo de circuitos usando bits e portas quânticas, e revisou superposição, medição e emaranhamento. Também aprendeu como executar o circuito quântico em um dispositivo quântico real.

No exercício final para criar um circuito GHZ, você tentou reduzir a profundidade do circuito, que é um fator importante para obter uma solução em escala de utilidade em um computador quântico com ruído. Nas lições seguintes deste curso, você aprenderá sobre ruído e sobre métodos de mitigação de erros em detalhes. Nesta lição, como introdução, consideramos a redução da profundidade do circuito em um dispositivo ideal, mas na realidade precisamos levar em conta as restrições do dispositivo real, como a conectividade entre qubits. Você aprenderá mais sobre isso nas lições subsequentes deste curso.

# See the version of Qiskit
import qiskit

qiskit.__version__
'2.0.2'