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
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 é
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 é
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.
Uma unitária geral deve ser capaz de levar \(\left|0\right\rangle\) para o estado acima. Isto é
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
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\)
[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
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
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:
[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:
[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:
[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¶
[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}\))¶
[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}\))¶
[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}\))¶
[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}\))¶
[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
Rotação em torno do eixo X¶
[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¶
[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¶
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 é
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:
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
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
Em sua forma matricial, a ação de \(C_{U}\) é
Para encontrar estes elementos da matriz, considere
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
o que implica que a forma matricial de \(C_{U}\) é
[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
No entanto, quando o qubit de controle é o LSB, (por exemplo, cx(q[0],q[1])
), esta porta é equivalente à seguinte matriz:
[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
ou quando o LSB é o controle
[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:
[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.
[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\).
[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.
[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\).
[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
o que fornece uma representação matricial da forma
[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
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:
Porta Toffoli (porta \(ccx\))¶
A porta Toffoli inverte o terceiro qubit, se os dois primeiros qubits (LSB) forem ambos \(\left|1\right\rangle\):
Em sua forma matricial, a porta Toffoli é
[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\):
Em sua forma matricial, a porta Fredkin é
[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:
[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 é
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:
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):
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:
Consequentemente,
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 Software | Version |
---|---|
Qiskit | None |
Terra | 0.14.0 |
Aer | 0.6.0 |
Ignis | 0.3.0 |
Aqua | None |
IBM Q Provider | 0.6.1 |
System information | |
Python | 3.7.7 (default, Mar 26 2020, 10:32:53) [Clang 4.0.1 (tags/RELEASE_401/final)] |
OS | Darwin |
CPUs | 4 |
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.
[ ]: