Construire des circuits
Versions des packages
Le code de cette page a été développé avec les dépendances suivantes. Nous recommandons d'utiliser ces versions ou des versions plus récentes.
qiskit[all]~=2.3.0
Cette page examine de plus près la classe QuantumCircuit du SDK Qiskit, notamment des méthodes plus avancées pour créer des circuits quantiques.
Qu'est-ce qu'un circuit quantique ?
Un circuit quantique simple est un ensemble de qubits et une liste d'instructions qui s'appliquent à ces qubits. Pour illustrer cela, la cellule suivante crée un nouveau circuit avec deux nouveaux qubits, puis affiche l'attribut qubits du circuit, qui est une liste de Qubits ordonnés du bit le moins significatif au bit le plus significatif .
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit
from qiskit import QuantumCircuit
qc = QuantumCircuit(2)
qc.qubits
[<Qubit register=(2, "q"), index=0>, <Qubit register=(2, "q"), index=1>]
Plusieurs objets QuantumRegister et ClassicalRegister peuvent être combinés pour créer un circuit. Chaque QuantumRegister et ClassicalRegister peut également être nommé.
from qiskit.circuit import QuantumRegister, ClassicalRegister
qr1 = QuantumRegister(2, "qreg1") # Create a QuantumRegister with 2 qubits
qr2 = QuantumRegister(1, "qreg2") # Create a QuantumRegister with 1 qubit
cr1 = ClassicalRegister(3, "creg1") # Create a ClassicalRegister with 3 cbits
combined_circ = QuantumCircuit(
qr1, qr2, cr1
) # Create a quantum circuit with 2 QuantumRegisters and 1 ClassicalRegister
combined_circ.qubits
[<Qubit register=(2, "qreg1"), index=0>,
<Qubit register=(2, "qreg1"), index=1>,
<Qubit register=(1, "qreg2"), index=0>]
Tu peux retrouver l'index et le registre d'un qubit en utilisant la méthode find_bit du circuit et ses attributs.
desired_qubit = qr2[0] # Qubit 0 of register 'qreg2'
print("Index:", combined_circ.find_bit(desired_qubit).index)
print("Register:", combined_circ.find_bit(desired_qubit).registers)
Index: 2
Register: [(QuantumRegister(1, 'qreg2'), 0)]
Ajouter une instruction au circuit ajoute cette instruction à l'attribut data du circuit. La sortie de la cellule suivante montre que data est une liste d'objets CircuitInstruction, chacun possédant un attribut operation et un attribut qubits.
qc.x(0) # Add X-gate to qubit 0
qc.data
[CircuitInstruction(operation=Instruction(name='x', num_qubits=1, num_clbits=0, params=[]), qubits=(<Qubit register=(2, "q"), index=0>,), clbits=())]
La façon la plus simple de visualiser ces informations est d'utiliser la méthode draw, qui retourne une représentation visuelle du circuit. Consulte la page Visualiser des circuits pour découvrir les différentes façons d'afficher des circuits quantiques.
qc.draw("mpl")
Les objets d'instruction de circuit peuvent contenir des circuits de « définition » qui décrivent l'instruction en termes d'instructions plus fondamentales. Par exemple, la porte X est définie comme un cas particulier de la porte U3, une porte à qubit unique plus générale.
# Draw definition circuit of 0th instruction in `qc`
qc.data[0].operation.definition.draw("mpl")
Les instructions et les circuits se ressemblent en ce sens qu'ils décrivent tous deux des opérations sur des bits et des qubits, mais ils ont des objectifs différents :
- Les instructions sont traitées comme fixes, et leurs méthodes retournent généralement de nouvelles instructions (sans modifier l'objet d'origine).
- Les circuits sont conçus pour être construits sur de nombreuses lignes de code, et les méthodes de
QuantumCircuitmodifient souvent l'objet existant.
Qu'est-ce que la profondeur d'un circuit ?
La profondeur (depth()) d'un circuit quantique mesure le nombre de « couches » de portes quantiques, exécutées en parallèle, nécessaires pour accomplir le calcul défini par le circuit. Étant donné que les portes quantiques prennent du temps à s'exécuter, la profondeur d'un circuit correspond approximativement au temps que met l'ordinateur quantique pour l'exécuter. La profondeur d'un circuit est donc une quantité importante pour déterminer si un circuit quantique peut être exécuté sur un appareil donné.
Le reste de cette page illustre comment manipuler des circuits quantiques.
Construire des circuits
Des méthodes comme QuantumCircuit.h et QuantumCircuit.cx ajoutent des instructions spécifiques aux circuits. Pour ajouter des instructions de façon plus générale, utilise la méthode append. Elle prend une instruction et une liste de qubits auxquels appliquer cette instruction. Consulte la documentation de l'API Circuit Library pour la liste des instructions prises en charge.
from qiskit.circuit.library import HGate
qc = QuantumCircuit(1)
qc.append(
HGate(), # New HGate instruction
[0], # Apply to qubit 0
)
qc.draw("mpl")
Pour combiner deux circuits, utilise la méthode compose. Elle accepte un autre QuantumCircuit et une liste optionnelle de correspondances de qubits.
qc_a = QuantumCircuit(4)
qc_a.x(0)
qc_b = QuantumCircuit(2, name="qc_b")
qc_b.y(0)
qc_b.z(1)
# compose qubits (0, 1) of qc_a to qubits (1, 3) of qc_b respectively
combined = qc_a.compose(qc_b, qubits=[1, 3])
combined.draw("mpl")
Tu pourrais également vouloir compiler des circuits en instructions pour mieux organiser tes circuits. Tu peux convertir un circuit en instruction avec la méthode to_instruction, puis l'ajouter à un autre circuit comme n'importe quelle autre instruction. Le circuit dessiné dans la cellule suivante est fonctionnellement équivalent au circuit dessiné dans la cellule précédente.
inst = qc_b.to_instruction()
qc_a.append(inst, [1, 3])
qc_a.draw("mpl")
Si ton circuit est unitaire, tu peux le convertir en Gate à l'aide de la méthode to_gate. Les objets Gate sont des types spécifiques d'instructions avec quelques fonctionnalités supplémentaires, comme la méthode control, qui ajoute un contrôle quantique.
gate = qc_b.to_gate().control()
qc_a.append(gate, [0, 1, 3])
qc_a.draw("mpl")
Pour voir ce qui se passe, tu peux utiliser la méthode decompose pour développer chaque instruction en sa définition.
La méthode decompose retourne un nouveau circuit et ne modifie pas le circuit sur lequel elle opère.
qc_a.decompose().draw("mpl")
Mesurer des qubits
Les mesures servent à échantillonner les états de qubits individuels et à transférer les résultats dans un registre classique. Note que si tu soumets des circuits à une primitive Sampler, des mesures sont obligatoires. En revanche, les circuits soumis à une primitive Estimator ne doivent pas contenir de mesures.
Les qubits peuvent être mesurés à l'aide de trois méthodes : measure, measure_all et measure_active. Pour apprendre à visualiser les résultats de mesure, consulte la page Visualiser les résultats.
-
QuantumCircuit.measure: mesure chaque qubit du premier argument sur le bit classique donné en second argument. Cette méthode offre un contrôle complet sur l'emplacement de stockage du résultat de mesure. -
QuantumCircuit.measure_all: ne prend aucun argument et peut être utilisée pour des circuits quantiques sans bits classiques prédéfinis. Elle crée des fils classiques et stocke les résultats de mesure dans l'ordre. Par exemple, la mesure du qubit est stockée dans le cbit . Elle ajoute également une barrière avant la mesure. -
QuantumCircuit.measure_active: similaire àmeasure_all, mais ne mesure que les qubits qui ont des opérations.
qc1 = QuantumCircuit(2, 2)
qc1.measure(0, 1)
qc1.draw("mpl", cregbundle=False)
qc2 = QuantumCircuit(2)
qc2.measure_all()
qc2.draw("mpl", cregbundle=False)
qc3 = QuantumCircuit(2)
qc3.x(1)
qc3.measure_active()
qc3.draw("mpl", cregbundle=False)
Circuits paramétrés
De nombreux algorithmes quantiques à court terme impliquent l'exécution de nombreuses variantes d'un circuit quantique. Étant donné que la construction et l'optimisation de grands circuits peuvent être coûteuses en calcul, Qiskit prend en charge les circuits paramétrés. Ces circuits ont des paramètres non définis dont les valeurs n'ont pas besoin d'être fixées jusqu'au moment de l'exécution. Cela te permet de sortir la construction et l'optimisation du circuit de la boucle principale du programme. La cellule suivante crée et affiche un circuit paramétré.
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.circuit import Parameter
angle = Parameter("angle") # undefined number
# Create and optimize circuit once
qc = QuantumCircuit(1)
qc.rx(angle, 0)
qc = generate_preset_pass_manager(
optimization_level=3, basis_gates=["u", "cx"]
).run(qc)
qc.draw("mpl")
La cellule suivante crée de nombreuses variantes de ce circuit et en affiche une.
circuits = []
for value in range(100):
circuits.append(qc.assign_parameters({angle: value}))
circuits[0].draw("mpl")
Tu peux trouver la liste des paramètres non définis d'un circuit dans son attribut parameters.
qc.parameters
ParameterView([Parameter(angle)])
Changer le nom d'un paramètre
Par défaut, les noms de paramètres d'un circuit paramétré sont préfixés par x — par exemple, x[0]. Tu peux modifier ces noms après leur définition, comme montré dans l'exemple suivant.
from qiskit.circuit.library import z_feature_map
from qiskit.circuit import ParameterVector
# Define a parameterized circuit with default names
# For example, x[0]
circuit = z_feature_map(2)
# Set new parameter names
# They will now be prefixed by `hi` instead
# For example, hi[0]
training_params = ParameterVector("hi", 2)
# Assign parameter names to the quantum circuit
circuit = circuit.assign_parameters(parameters=training_params)
Étapes suivantes
- Pour apprendre les algorithmes quantiques à court terme, suis le cours Variational algorithm design.
- Consulte un exemple de circuits utilisés dans le tutoriel Algorithme de Grover.
- Travaille avec des circuits simples en utilisant IBM Quantum Composer.