Note
Cette page a été générée à partir de tutorials/circuits_advanced/1_advanced_circuits.ipynb.
Exécuter en mode interactif dans le IBM Quantum lab <.
Circuits avancés¶
[1]:
import numpy as np
from qiskit import *
Portes opaques¶
[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 ├ └──────────┘
Portes composites¶
[4]:
# 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.u3(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()
[4]:
┌───┐ q_0: ┤ H ├──■──────────────────── └───┘┌─┴─┐ ┌───────────┐ q_1: ─────┤ X ├──■──┤0 ├ └───┘┌─┴─┐│ sub_circ │ q_2: ──────────┤ X ├┤1 ├ └───┘└───────────┘
Les circuits ne sont pas immédiatement décomposés lors de la conversion ` ` to_instruction ` ` pour permettre la conception de circuits à des niveaux d’abstraction plus élevés. Lorsque vous le souhaitez, ou avant la compilation, les sous-circuits seront décomposés via la méthode ` ` decompose ` `.
[5]:
decomposed_circ = circ.decompose() # Does not modify original circuit
decomposed_circ.draw()
[5]:
┌──────────┐ q_0: ┤ U2(0,pi) ├──■────────────────────────────────────── └──────────┘┌─┴─┐ ┌───┐ ░ ┌────────────┐ q_1: ────────────┤ X ├──■──┤ H ├────■─────░─┤ U3(1,2,-2) ├ └───┘┌─┴─┐└───┘┌───┴───┐ ░ └───┬───┬────┘ q_2: ─────────────────┤ X ├─────┤ RZ(1) ├─░─────┤ I ├───── └───┘ └───────┘ ░ └───┘
Circuits paramétrés¶
[6]:
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')
[6]:

Nous pouvons inspecter les paramètres du circuit
[7]:
print(qc.parameters)
{Parameter(θ)}
Lier les paramètres aux valeurs¶
Tous les paramètres du circuit doivent être liés avant d’envoyer le circuit à un système d’execution. Cela peut se faire de l’une des deux manières suivantes: - La méthode ` ` bind_parameters ` ` accepte un mappage de ` ` Parameter ` s taux valeurs, et retourne un nouveau circuit ou chaque paramètre est remplacé par sa valeur correspondante. Une liaison -bind- partielle est prise en charge, auquel cas le circuit retourné aura comme paramètre(s) les ` ` paramètre ` s pour lesquels une valuer n’a pas été fournie.
[8]:
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()
[8]:
┌───┐ ░ ┌─────────┐ ░ ┌───┐┌─┐ q_0: ┤ H ├──■──────────────────░─┤ RZ(2pi) ├─░──────────────────■──┤ H ├┤M├ └───┘┌─┴─┐ ░ ├─────────┤ ░ ┌─┴─┐└───┘└╥┘ q_1: ─────┤ X ├──■─────────────░─┤ RZ(2pi) ├─░─────────────■──┤ X ├──────╫─ └───┘┌─┴─┐ ░ ├─────────┤ ░ ┌─┴─┐└───┘ ║ q_2: ──────────┤ X ├──■────────░─┤ RZ(2pi) ├─░────────■──┤ X ├───────────╫─ └───┘┌─┴─┐ ░ ├─────────┤ ░ ┌─┴─┐└───┘ ║ q_3: ───────────────┤ X ├──■───░─┤ RZ(2pi) ├─░───■──┤ X ├────────────────╫─ └───┘┌─┴─┐ ░ ├─────────┤ ░ ┌─┴─┐└───┘ ║ q_4: ────────────────────┤ X ├─░─┤ RZ(2pi) ├─░─┤ X ├─────────────────────╫─ └───┘ ░ └─────────┘ ░ └───┘ ║ c_0: ════════════════════════════════════════════════════════════════════╩═
` `qiskit.execute ` ` accepte l’argument ` ` parameter_bind ` ` qui, lorsqu’il est spécifié comme une liste de corrrespondance de ` ` Parameter ` s vers des valeurs, va lier -binder- et exécuter un circuit sur le backend pour chaque dictionnaire de mappage -correspondance- de la liste.
[9]:
job = execute(qc,
backend=BasicAer.get_backend('qasm_simulator'),
parameter_binds=[{theta: theta_val} for theta_val in theta_range])
counts = job.result().get_counts()
Dans cet exemple de circuit, nous appliquons une rotation globale :math:` R_z (theta) sur un état à cinq qubits intriqués -entangled-, et nous nous attendons a voir une oscillation du qubit-0 de :math:` 5theta `.
[10]:
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)
[10]:
<matplotlib.legend.Legend at 0x11dce80a0>

Réduction du coût de compilation¶
La compilation sur un circuit paramétré avant la liaison -binding- peut, dans certains cas, réduire considérablement le temps de compilation par rapport à la compilation sur un ensemble de circuits liés.
[11]:
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: 8.771650075912476
[12]:
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: 0.8169369697570801
Composition¶
Les circuits paramétrés peuvent être composés comme des ` ` QuantumCircuit ` ` s standard. En général, lors de la composition de deux circuits paramétrés, le circuit résultant sera paramétré par l’union des paramètres des circuits d’entrée.
Cependant, les noms de paramètre doivent être uniques dans un circuit donné. Lors de la tentative d’ajout d’un paramètre dont le nom est déjà présent dans le circuit cible: - si la source et la cible partagent la même instance ` ` Parameter ` `, les paramètres sont supposés être les mêmes et combinés - si la source et la cible ont des instances ` ` Parameter ` différentes, une erreur sera générée
[13]:
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 ├──────────────
└────────────┘
Pour insérer un sous-circuit avec une paramétrisation différente, la méthode ` ` to_instruction ` ` accepte un argument optionnel (` ` parameter_map ` `) qui, lorsqu’il est présent, va générer des instructions ou le paramètre source sera remplacé par un nouveau paramètre.
[14]:
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())
┌────────────────┐
q1_0: ┤0 ├
│ │
q1_1: ┤1 oracle(theta) ├
│ │
q1_2: ┤2 ├
└┬──────────────┬┘
q1_3: ─┤0 ├─
│ │
q1_4: ─┤1 oracle(phi) ├─
│ │
q1_5: ─┤2 ├─
┌┴──────────────┴┐
q1_6: ┤0 ├
│ │
q1_7: ┤1 oracle(gamma) ├
│ │
q1_8: ┤2 ├
└────────────────┘
┌───────────┐
q1_0: ┤ RZ(theta) ├──■─────────────────────────────────
└───────────┘┌─┴─┐┌───────────┐
q1_1: ─────────────┤ X ├┤ RZ(theta) ├──■───────────────
└───┘└───────────┘┌─┴─┐┌───────────┐
q1_2: ───────────────────────────────┤ X ├┤ RZ(theta) ├
┌─────────┐ └───┘└───────────┘
q1_3: ─┤ RZ(phi) ├───■─────────────────────────────────
└─────────┘ ┌─┴─┐ ┌─────────┐
q1_4: ─────────────┤ X ├─┤ RZ(phi) ├───■───────────────
└───┘ └─────────┘ ┌─┴─┐ ┌─────────┐
q1_5: ───────────────────────────────┤ X ├─┤ RZ(phi) ├─
┌───────────┐ └───┘ └─────────┘
q1_6: ┤ RZ(gamma) ├──■─────────────────────────────────
└───────────┘┌─┴─┐┌───────────┐
q1_7: ─────────────┤ X ├┤ RZ(gamma) ├──■───────────────
└───┘└───────────┘┌─┴─┐┌───────────┐
q1_8: ───────────────────────────────┤ X ├┤ RZ(gamma) ├
└───┘└───────────┘
[15]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
Version Information
Qiskit Software | Version |
---|---|
Qiskit | None |
Terra | 0.15.0 |
Aer | 0.5.1 |
Ignis | None |
Aqua | None |
IBM Q Provider | 0.7.0 |
System information | |
Python | 3.8.2 (default, Mar 26 2020, 10:43:30) [Clang 4.0.1 (tags/RELEASE_401/final)] |
OS | Darwin |
CPUs | 4 |
Memory (Gb) | 16.0 |
Fri May 08 08:42:21 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.
[ ]: