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
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¶
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
[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
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:
[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:
[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:
[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¶
[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}\))¶
[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}\))¶
[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}\))¶
[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}\))¶
[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
Rotação em torno do eixo X¶
[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¶
[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¶
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
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 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
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
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 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
However, when the LSB is the control qubit, (e.g. cx(q[0],q[1])
), this gate is equivalent to the following matrix:
[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
ou quando o LSB é o controle
[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:
[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.
[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\).
[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.
[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\).
[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
o que fornece uma representação matricial da forma
[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
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 é
[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\):
Em sua forma matricial, a porta Fredkin é
[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:
[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 é
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:
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 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 Software | Version |
---|---|
qiskit-terra | 0.20.2 |
qiskit-aer | 0.10.4 |
qiskit-ignis | 0.7.1 |
qiskit-ibmq-provider | 0.19.1 |
qiskit | 0.36.2 |
qiskit-optimization | 0.3.2 |
qiskit-machine-learning | 0.4.0 |
System information | |
Python version | 3.10.4 |
Python compiler | MSC v.1916 64 bit (AMD64) |
Python build | main, Mar 30 2022 08:38:02 |
OS | Windows |
CPUs | 6 |
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.
[ ]: