Nota
Esta página foi gerada a partir do tutorials/circuits_advanced/01_advanced_circuits.ipynb.
Circuitos Avançados¶
[1]:
import numpy as np
from qiskit import *
Gates opacos¶
[2]:
from qiskit.circuit import Gate
my_gate = Gate(name='my_gate', num_qubits=2, params=[])
[3]:
qr = QuantumRegister(3, 'q')
circ = QuantumCircuit(qr)
circ.append(my_gate, [qr[0], qr[1]])
circ.append(my_gate, [qr[1], qr[2]])
circ.draw()
[3]:
┌──────────┐ q_0: ┤0 ├──────────── │ my_gate │┌──────────┐ q_1: ┤1 ├┤0 ├ └──────────┘│ my_gate │ q_2: ────────────┤1 ├ └──────────┘
Portões compostos¶
[5]:
# Build a sub-circuit
sub_q = QuantumRegister(2)
sub_circ = QuantumCircuit(sub_q, name='sub_circ')
sub_circ.h(sub_q[0])
sub_circ.crz(1, sub_q[0], sub_q[1])
sub_circ.barrier()
sub_circ.id(sub_q[1])
sub_circ.u(1, 2, -2, sub_q[0])
# Convert to a gate and stick it into an arbitrary place in the bigger circuit
sub_inst = sub_circ.to_instruction()
qr = QuantumRegister(3, 'q')
circ = QuantumCircuit(qr)
circ.h(qr[0])
circ.cx(qr[0], qr[1])
circ.cx(qr[1], qr[2])
circ.append(sub_inst, [qr[1], qr[2]])
circ.draw()
[5]:
┌───┐ q_0: ┤ H ├──■──────────────────── └───┘┌─┴─┐ ┌───────────┐ q_1: ─────┤ X ├──■──┤0 ├ └───┘┌─┴─┐│ sub_circ │ q_2: ──────────┤ X ├┤1 ├ └───┘└───────────┘
Os circuitos não são imediatamente decompostos mediante a conversão to_instruction
, para permitir o design do circuito em níveis mais elevados de abstração. Quando desejado, ou antes da compilação, os sub-circuitos serão decompostos através do método decompose
.
[6]:
decomposed_circ = circ.decompose() # Does not modify original circuit
decomposed_circ.draw()
[6]:
┌─────────┐ q_0: ┤ U2(0,π) ├──■───────────────────────────────────── └─────────┘┌─┴─┐ ┌───┐ ░ ┌───────────┐ q_1: ───────────┤ X ├──■──┤ H ├────■─────░─┤ U(1,2,-2) ├ └───┘┌─┴─┐└───┘┌───┴───┐ ░ └───┬───┬───┘ q_2: ────────────────┤ X ├─────┤ Rz(1) ├─░─────┤ I ├──── └───┘ └───────┘ ░ └───┘
Circuitos parametrizados¶
[7]:
from qiskit.circuit import Parameter
theta = Parameter('θ')
n = 5
qc = QuantumCircuit(5, 1)
qc.h(0)
for i in range(n-1):
qc.cx(i, i+1)
qc.barrier()
qc.rz(theta, range(5))
qc.barrier()
for i in reversed(range(n-1)):
qc.cx(i, i+1)
qc.h(0)
qc.measure(0, 0)
qc.draw('mpl')
[7]:

We can inspect the circuit’s parameters
[8]:
print(qc.parameters)
ParameterView([Parameter(θ)])
Vinculando parâmetros aos valores¶
Todos os parâmetros de circuito devem ser ligados antes de enviar o circuito para um backend. Isto pode ser feito de duas maneiras: - O método bind_parameters
aceita um dicionário mapeando Parameter
s para valores, e retorna um novo circuito com cada parâmetro substituído pelo seu valor correspondente. A ligação parcial é suportada, e nesse caso o circuito retornado será parametrizado por qualquer `Parameter
s que não foram mapeados para um valor.
[9]:
import numpy as np
theta_range = np.linspace(0, 2 * np.pi, 128)
circuits = [qc.bind_parameters({theta: theta_val})
for theta_val in theta_range]
circuits[-1].draw()
[9]:
┌───┐ ░ ┌────────┐ ░ ┌───┐┌─┐ q_0: ┤ H ├──■──────────────────░─┤ Rz(2π) ├─░──────────────────■──┤ H ├┤M├ └───┘┌─┴─┐ ░ ├────────┤ ░ ┌─┴─┐└───┘└╥┘ q_1: ─────┤ X ├──■─────────────░─┤ Rz(2π) ├─░─────────────■──┤ X ├──────╫─ └───┘┌─┴─┐ ░ ├────────┤ ░ ┌─┴─┐└───┘ ║ q_2: ──────────┤ X ├──■────────░─┤ Rz(2π) ├─░────────■──┤ X ├───────────╫─ └───┘┌─┴─┐ ░ ├────────┤ ░ ┌─┴─┐└───┘ ║ q_3: ───────────────┤ X ├──■───░─┤ Rz(2π) ├─░───■──┤ X ├────────────────╫─ └───┘┌─┴─┐ ░ ├────────┤ ░ ┌─┴─┐└───┘ ║ q_4: ────────────────────┤ X ├─░─┤ Rz(2π) ├─░─┤ X ├─────────────────────╫─ └───┘ ░ └────────┘ ░ └───┘ ║ c: 1/═══════════════════════════════════════════════════════════════════╩═ 0
[12]:
backend = BasicAer.get_backend('qasm_simulator')
job = backend.run(transpile(circuits, backend))
counts = job.result().get_counts()
No circuito de exemplo, aplicamos uma rotação global \(R_z(\theta)\) em um estado enredado de cinco qubit, e então espere ver a oscilação em qubit-0 em \(5\theta\).
[13]:
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8,6))
ax = fig.add_subplot(111)
ax.plot(theta_range, list(map(lambda c: c.get('0', 0), counts)), '.-', label='0')
ax.plot(theta_range, list(map(lambda c: c.get('1', 0), counts)), '.-', label='1')
ax.set_xticks([i * np.pi / 2 for i in range(5)])
ax.set_xticklabels(['0', r'$\frac{\pi}{2}$', r'$\pi$', r'$\frac{3\pi}{2}$', r'$2\pi$'], fontsize=14)
ax.set_xlabel('θ', fontsize=14)
ax.set_ylabel('Counts', fontsize=14)
ax.legend(fontsize=14)
[13]:
<matplotlib.legend.Legend at 0x7f8fcb307710>

Reduzindo o custo de compilação¶
Compilando um circuito parametrizado antes da ligação pode, em alguns casos, reduzir significativamente o tempo de compilação em comparação com a compilação de um conjunto de circuitos conectados.
[14]:
import time
from itertools import combinations
from qiskit.compiler import assemble
from qiskit.test.mock import FakeVigo
start = time.time()
qcs = []
theta_range = np.linspace(0, 2*np.pi, 32)
for n in theta_range:
qc = QuantumCircuit(5)
for k in range(8):
for i,j in combinations(range(5), 2):
qc.cx(i,j)
qc.rz(n, range(5))
for i,j in combinations(range(5), 2):
qc.cx(i,j)
qcs.append(qc)
compiled_circuits = transpile(qcs, backend=FakeVigo())
qobj = assemble(compiled_circuits, backend=FakeVigo())
end = time.time()
print('Time compiling over set of bound circuits: ', end-start)
Time compiling over set of bound circuits: 2.4267938137054443
[16]:
start = time.time()
qc = QuantumCircuit(5)
theta = Parameter('theta')
for k in range(8):
for i,j in combinations(range(5), 2):
qc.cx(i,j)
qc.rz(theta, range(5))
for i,j in combinations(range(5), 2):
qc.cx(i,j)
transpiled_qc = transpile(qc, backend=FakeVigo())
qobj = assemble([transpiled_qc.bind_parameters({theta: n})
for n in theta_range], backend=FakeVigo())
end = time.time()
print('Time compiling over parameterized circuit, then binding: ', end-start)
Time compiling over parameterized circuit, then binding: 1.4472951889038086
Composição¶
Circuitos parametrizados podem ser compostos como o padrão QuantumCircuit
s. Geralmente, ao compor dois circuitos parametrizados, o circuito resultante será parametrificado pela união dos parâmetros dos circuitos de entrada.
No entanto, nomes de parâmetros devem ser únicos dentro de um determinado circuito. Quando se tenta adicionar um parâmetro cujo nome já está presente no circuito alvo: - se a fonte e o alvo compartilham a mesma instância Parameter
, os parâmetros serão assumidos como iguais e serão combinados - se a fonte e o alvo tiverem diferentes instâncias de Parameter
, um erro será gerado
[17]:
phi = Parameter('phi')
sub_circ1 = QuantumCircuit(2, name='sc_1')
sub_circ1.rz(phi, 0)
sub_circ1.rx(phi, 1)
sub_circ2 = QuantumCircuit(2, name='sc_2')
sub_circ2.rx(phi, 0)
sub_circ2.rz(phi, 1)
qc = QuantumCircuit(4)
qr = qc.qregs[0]
qc.append(sub_circ1.to_instruction(), [qr[0], qr[1]])
qc.append(sub_circ2.to_instruction(), [qr[0], qr[1]])
qc.append(sub_circ2.to_instruction(), [qr[2], qr[3]])
print(qc.draw())
# The following raises an error: "QiskitError: 'Name conflict on adding parameter: phi'"
# phi2 = Parameter('phi')
# qc.u3(0.1, phi2, 0.3, 0)
┌────────────┐┌────────────┐
q_0: ┤0 ├┤0 ├
│ sc_1(phi) ││ sc_2(phi) │
q_1: ┤1 ├┤1 ├
├────────────┤└────────────┘
q_2: ┤0 ├──────────────
│ sc_2(phi) │
q_3: ┤1 ├──────────────
└────────────┘
Para inserir um subcircuito sob uma parametrização diferente, o método to_instruction
aceita um argumento opcional (parameter_map
) que, quando presente, irá gerar instruções com o parâmetro de origem substituído por um novo parâmetro.
[18]:
p = Parameter('p')
qc = QuantumCircuit(3, name='oracle')
qc.rz(p, 0)
qc.cx(0, 1)
qc.rz(p, 1)
qc.cx(1, 2)
qc.rz(p, 2)
theta = Parameter('theta')
phi = Parameter('phi')
gamma = Parameter('gamma')
qr = QuantumRegister(9)
larger_qc = QuantumCircuit(qr)
larger_qc.append(qc.to_instruction({p: theta}), qr[0:3])
larger_qc.append(qc.to_instruction({p: phi}), qr[3:6])
larger_qc.append(qc.to_instruction({p: gamma}), qr[6:9])
print(larger_qc.draw())
print(larger_qc.decompose().draw())
┌────────────────┐
q24_0: ┤0 ├
│ │
q24_1: ┤1 oracle(theta) ├
│ │
q24_2: ┤2 ├
└┬──────────────┬┘
q24_3: ─┤0 ├─
│ │
q24_4: ─┤1 oracle(phi) ├─
│ │
q24_5: ─┤2 ├─
┌┴──────────────┴┐
q24_6: ┤0 ├
│ │
q24_7: ┤1 oracle(gamma) ├
│ │
q24_8: ┤2 ├
└────────────────┘
┌───────────┐
q24_0: ┤ Rz(theta) ├──■─────────────────────────────────
└───────────┘┌─┴─┐┌───────────┐
q24_1: ─────────────┤ X ├┤ Rz(theta) ├──■───────────────
└───┘└───────────┘┌─┴─┐┌───────────┐
q24_2: ───────────────────────────────┤ X ├┤ Rz(theta) ├
┌─────────┐ └───┘└───────────┘
q24_3: ─┤ Rz(phi) ├───■─────────────────────────────────
└─────────┘ ┌─┴─┐ ┌─────────┐
q24_4: ─────────────┤ X ├─┤ Rz(phi) ├───■───────────────
└───┘ └─────────┘ ┌─┴─┐ ┌─────────┐
q24_5: ───────────────────────────────┤ X ├─┤ Rz(phi) ├─
┌───────────┐ └───┘ └─────────┘
q24_6: ┤ Rz(gamma) ├──■─────────────────────────────────
└───────────┘┌─┴─┐┌───────────┐
q24_7: ─────────────┤ X ├┤ Rz(gamma) ├──■───────────────
└───┘└───────────┘┌─┴─┐┌───────────┐
q24_8: ───────────────────────────────┤ X ├┤ Rz(gamma) ├
└───┘└───────────┘
[19]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
Version Information
Qiskit Software | Version |
---|---|
qiskit-terra | 0.19.0.dev0+be0ed5f |
qiskit-aer | 0.8.2 |
qiskit-ignis | 0.6.0 |
qiskit-ibmq-provider | 0.16.0.dev0+4e20edc |
System information | |
Python | 3.7.7 (default, May 6 2020, 04:59:01) [Clang 4.0.1 (tags/RELEASE_401/final)] |
OS | Darwin |
CPUs | 6 |
Memory (Gb) | 32.0 |
Sat Jul 31 01:02:16 2021 EDT |
This code is a part of Qiskit
© Copyright IBM 2017, 2021.
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.
[ ]: