Expert Chapitre 55-56 / 29

Qiskit : circuits, registres et portes personnalisées & Compilation et synthèse de circuits

Construire des circuits modulaires en Qiskit (registres, portes personnalisées, inverse, control) et comprendre la compilation quantique (KAK, Solovay-Kitaev, routing) — sans prérequis mathématiques, avec du code Qiskit.

Qiskit : circuits, registres et portes personnalisées

Qiskit organise un circuit quantique autour de registres nommés et de portes réutilisables. Si vous venez du monde .NET/Python, pensez aux registres comme des variables typées et aux portes personnalisées comme des méthodes extraites — on structure le code pour le rendre lisible, testable et composable.

Registres : organiser ses qubits et bits classiques

  • QuantumRegister(n, 'nom') regroupe n qubits sous un nom logique. Un circuit peut contenir plusieurs registres : qr_data pour les données, qr_ctrl pour les contrôles. Analogie C# : c’est comme déclarer des variables nommées plutôt que d’utiliser un tableau anonyme — on sait tout de suite quel qubit fait quoi.
  • ClassicalRegister(n, 'nom') stocke les résultats de mesure dans n bits classiques nommés. Indispensable pour le contrôle conditionnel (c_if) ou simplement pour savoir quel bit correspond à quelle mesure.
  • AncillaRegister(n, 'nom') est un registre spécial pour les qubits ancillaires (de travail temporaire). Le transpileur Qiskit sait qu’il peut réutiliser ou optimiser ces qubits, contrairement à un QuantumRegister ordinaire. Tous les qubits démarrent à |0>.

Portes personnalisées : to_gate(), compose(), append()

  • sub_circuit.to_gate(label="...") convertit un sous-circuit en une porte réutilisable. Analogie OOP : c’est exactement le refactoring “Extract Method” — on prend un bloc de code, on en fait une fonction nommée qu’on appelle partout.
  • qc.append(gate, qubits) ajoute une porte (standard ou personnalisée) à un circuit. C’est l’ajout ponctuel — on place la porte sur les qubits voulus.
  • qc.compose(other, qubits=[...]) colle un circuit entier à la suite. C’est la composition modulaire — chaque couche peut être un sous-circuit indépendant qu’on assemble. Contrairement à append, compose fusionne le circuit inline plutôt que de l’encapsuler dans une boîte.

Transformations : inverse() et control(n)

  • gate.inverse() renvoie la porte inverse (son “annuler”). Appliquer U puis U.inverse() = identité. Indispensable pour le pattern compute—uncompute (Grover, QPE, nettoyage d’ancillas).
  • gate.control(n) ajoute n qubits de contrôle : l’opération ne s’applique que si les n contrôles valent tous |1>. Exemple : sur une porte 2-qubits, .control(2) produit une porte à 4 qubits (2 contrôles + 2 cibles).

Simulation locale avec AerSimulator

  • AerSimulator (paquet qiskit-aer) permet de simuler un circuit localement — vecteur d’état exact, simulation QASM avec shots, ou modèle de bruit réaliste. Pas besoin d’accès à un backend IBM pour tester son code.
Méthode / ClasseRôleAnalogie dev
QuantumRegisterGroupe nommé de qubitsVariable typée
ClassicalRegisterBits classiques pour mesuresVariable de retour
AncillaRegisterQubits temporaires optimisablesVariable locale jetable
.to_gate()Sous-circuit vers porte réutilisableExtract Method
.append()Ajoute une porte au circuitAppel de méthode
.compose()Fusionne un circuit entierInlining de méthode
.inverse()Porte inverse (“annuler”)Undo / rollback
.control(n)Ajoute n qubits de contrôleGarde conditionnelle (if)
# Qiskit — registres nommés et construction de circuit
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister, AncillaRegister

# Registres nommés : on sait immédiatement quel qubit fait quoi
qr_data = QuantumRegister(2, "data")       # 2 qubits de données
qr_anc  = AncillaRegister(1, "ancilla")    # 1 qubit de travail temporaire
cr      = ClassicalRegister(2, "result")    # 2 bits classiques pour les mesures

qc = QuantumCircuit(qr_data, qr_anc, cr)

# Construction du circuit
qc.h(qr_data[0])                           # Hadamard sur data[0]
qc.cx(qr_data[0], qr_data[1])              # CNOT : intrication data[0] → data[1]
qc.cx(qr_data[0], qr_anc[0])               # ancilla utilisée comme travail temporaire
qc.measure(qr_data, cr)                     # mesure data → result

print(qc.draw())
# Les registres nommés rendent le circuit lisible comme du code bien structuré
# Qiskit — to_gate(), control(), inverse() et le pattern compute-uncompute
from qiskit.circuit import QuantumCircuit, QuantumRegister, AncillaRegister

# 1. Créer une sous-routine réutilisable
sub = QuantumCircuit(2, name="encode")
sub.h(0)
sub.cx(0, 1)
encode_gate = sub.to_gate()                  # « Extract Method » → porte réutilisable

# 2. Version contrôlée : 1 qubit de contrôle + 2 cibles = 3 qubits
c_encode = encode_gate.control(1)            # applique encode seulement si ctrl = |1⟩

# 3. Version inverse pour « uncompute »
decode_gate = encode_gate.inverse()          # annule encode → retour à l'état initial

# 4. Assemblage dans un circuit avec registres
qr = QuantumRegister(2, "q")
anc = AncillaRegister(1, "ctrl")
qc = QuantumCircuit(qr, anc)

qc.h(anc[0])                                # superposition du qubit de contrôle
qc.append(c_encode, [anc[0], qr[0], qr[1]]) # encode contrôlé par l'ancilla
qc.append(decode_gate, [qr[0], qr[1]])      # uncompute : nettoie les données

print(qc.draw())
# .control(2) sur encode_gate donnerait une porte à 4 qubits (2 ctrl + 2 cibles)

Attention. .inverse() échoue si le circuit contient une mesure ou un reset : ces opérations sont irréversibles. Retirez les mesures avant d’appeler .inverse(). De même, .to_gate() ne fonctionne que sur un circuit sans bits classiques (pas de mesure dans le sous-circuit).


Compilation et synthèse de circuits

Un circuit logique (Toffoli, portes multi-qubits, rotations quelconques) ne peut pas s’exécuter tel quel sur un processeur quantique réel. Le compilateur quantique (transpileur) le transforme en instructions natives du matériel. C’est la même idée qu’un compilateur C# vers IL vers code machine : chaque couche réduit l’abstraction vers ce que le matériel sait réellement faire.

Décomposition KAK

  • Toute porte 2-qubits peut être décomposée en au maximum 3 portes CNOT plus des rotations locales (portes 1-qubit). C’est le résultat de la décomposition KAK (Khaneja—Glaser). En pratique, beaucoup de portes 2-qubits se décomposent en seulement 0, 1 ou 2 CNOT.
  • Analogie : c’est comme réduire n’importe quelle opération matricielle complexe en une séquence de quelques opérations élémentaires — on ne perd rien, on reformule pour la machine.

Synthèse de Solovay—Kitaev

  • Le théorème de Solovay—Kitaev garantit qu’on peut approximer n’importe quelle porte 1-qubit à précision epsilon à partir d’un ensemble fini de portes (ex. H, T), avec seulement O(log^c(1/epsilon)) portes (c environ 3,97). La complexité est poly-logarithmique en 1/epsilon — on n’a pas besoin de beaucoup de portes même pour une très grande précision.
  • Analogie : avec seulement deux briques LEGO de base, on peut reconstruire n’importe quelle forme à une précision arbitraire — il suffit de savoir comment les empiler.

Optimisation par patterns

  • Le transpileur reconnaît des identités algébriques et simplifie : H*H = I (deux Hadamard consécutifs s’annulent), CNOT*CNOT = I, fusion de rotations Rz(alpha)*Rz(beta) = Rz(alpha+beta), commutation de portes indépendantes pour créer des annulations.
  • Analogie : c’est le peephole optimizer d’un compilateur classique — il repère les séquences inutiles et les élimine.

Qubit routing et insertion de SWAP

  • Sur un vrai processeur, les qubits ne sont pas tous connectés entre eux : un CNOT ne peut s’appliquer qu’entre qubits physiquement voisins. Si un CNOT logique cible deux qubits éloignés, le transpileur insère des portes SWAP pour rapprocher les données — chaque SWAP coûte 3 portes CNOT.
  • Analogie : c’est exactement un allocateur de registres dans un compilateur CPU. Quand deux variables doivent interagir mais sont dans des registres éloignés, on insère des MOV (ici des SWAP) pour les rapprocher. Le coût est réel — chaque SWAP ajoute du bruit.

Niveaux de transpilation dans Qiskit (0—3)

NiveauOptimisationsTemps
optimization_level=0Aucune optimisation, juste mapping et décomposition en portes nativesLe plus rapide
optimization_level=1Réduction de portes, fusion de rotations simplesRapide
optimization_level=2Commutation de portes, annulations, meilleur routingModéré
optimization_level=3Toutes les passes, synthèse unitaire, essais multiples de layoutLe plus lent
# Qiskit — transpilation d'un Toffoli (CCX) vers un backend réel
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_aer import AerSimulator

# Circuit logique avec un Toffoli (3-qubit : 2 contrôles + 1 cible)
qc = QuantumCircuit(3)
qc.h(2)
qc.ccx(0, 1, 2)     # Porte Toffoli — n'existe pas nativement sur la plupart des backends
qc.measure_all()

print("Circuit logique :")
print(qc.draw())
print(f"  Portes : {qc.count_ops()}")

# Transpilation niveau 0 vs niveau 3
backend = AerSimulator()
for level in [0, 3]:
    pm = generate_preset_pass_manager(optimization_level=level, backend=backend)
    qc_t = pm.run(qc)
    ops = qc_t.count_ops()
    cx_count = ops.get('cx', 0)
    print(f"\nNiveau {level} : {sum(ops.values())} portes, dont {cx_count} CX")
    # Niveau 0 : ~15 portes, ~6 CX (décomposition brute du Toffoli)
    # Niveau 3 : souvent moins de CX grâce aux optimisations

Le coût caché des SWAP. Chaque porte SWAP insérée par le routing coûte 3 portes CNOT. Sur un processeur à connectivité limitée (topologie en ligne ou en grille), un circuit de 10 CNOT logiques peut facilement devenir 30+ CNOT physiques. C’est la principale source de bruit sur le matériel actuel. Un optimization_level plus élevé ne garantit pas strictement moins de portes — il essaie davantage de stratégies mais le résultat dépend du circuit et de la topologie.


Quiz — teste tes connaissances
Expert 7 questions Objectif : 5/7 minimum
0/7
bonnes reponses
Objectif non atteint (minimum 5/7 requis).
Remonte relire la fiche memo ci-dessus en pretant attention aux points rates, puis clique sur « Recommencer » pour retenter.