Expert Chapitre 43-44 / 23

Qiskit en profondeur & Compilation et synthèse de circuits

Révision Expert : transpilation Qiskit (basis gates, Sampler/Estimator, niveaux d'optimisation) et synthèse de circuits (décomposition KAK, théorème de Solovay–Kitaev, routage) — sans prérequis mathématiques, avec du code Q# et Qiskit.

Qiskit en profondeur

Tu écris un circuit abstrait avec des portes pratiques (H, T, Toffoli…). Mais une vraie machine ne sait exécuter qu’une petite poignée de portes natives. Le pont entre les deux s’appelle la transpilation.

Analogie .NET. La transpilation est au quantique ce que la compilation C# → IL → JIT est au classique : ton code de haut niveau est réécrit dans le « jeu d’instructions » réel du processeur cible. Les basis gates sont ce jeu d’instructions.

Ce que fait la transpilation

  • Décomposition : réécrit chaque porte dans les basis gates du backend (ex. {rz, sx, x, cx}).
  • Placement (layout) : associe tes qubits logiques à des qubits physiques réels.
  • Routage (routing) : insère des SWAP quand deux qubits qui doivent interagir ne sont pas voisins sur la puce (la coupling map).
  • Optimisation : annule les portes redondantes, fusionne les rotations.

Le paramètre optimization_level va de 0 (juste rendre le circuit exécutable) à 3 (optimisation la plus agressive : annulations, commutations, resynthèse KAK des blocs 2-qubits). Plus haut = compilation plus lente mais moins de portes.

Les primitives : Sampler vs Estimator

  • Sampler : exécute un circuit qui contient des mesures et renvoie une distribution de bitstrings (les comptages des résultats).
  • Estimator : prend un circuit sans mesure + un observable, et renvoie une valeur moyenne ⟨ψ|H|ψ⟩ (utile pour VQE, chimie).
# Transpilation : Toffoli (CCX) n'est PAS native → réécrite
from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator

qc = QuantumCircuit(3)
qc.h(0)
qc.ccx(0, 1, 2)        # Toffoli : porte non native
qc.measure_all()

backend = AerSimulator()
tqc = transpile(qc, backend, optimization_level=3)
print(tqc.count_ops())  # ex. {'cx': 6, 'rz': 7, 'sx': 4, ...}
# Sampler (distribution) vs Estimator (valeur moyenne)
from qiskit.primitives import StatevectorSampler, StatevectorEstimator
from qiskit.quantum_info import SparsePauliOp

# Estimator : circuit SANS measure, on demande ⟨ZZ⟩
H = SparsePauliOp("ZZ")
ev = StatevectorEstimator().run([(bell, H)]).result()[0].data.evs

# Sampler : circuit AVEC measure, on récupère les comptages
counts = StatevectorSampler().run([bell_mesure]).result()[0].data.meas.get_counts()
PrimitiveEntréeSortie
Samplercircuit avec mesuresdistribution de bitstrings
Estimatorcircuit sans mesure + observablevaleur moyenne ⟨H⟩

Piège fréquent. Un circuit qui « a l’air » de 2 portes peut exploser après transpilation : un CCX (Toffoli) se décompose en ~6 CNOT + plusieurs portes mono-qubit. Et ne mets jamais de measure() dans un circuit destiné à l’Estimator : il calcule ⟨H⟩ directement, la mesure n’a pas de sens là.


Compilation et synthèse de circuits

La transpilation s’appuie sur des résultats de synthèse : comment réécrire n’importe quelle opération en portes élémentaires, et avec le moins de portes possible.

Synthèse mono-qubit et 2-qubits

  • Toute porte mono-qubit = 3 rotations (décomposition d’Euler, ex. Rz·Ry·Rz). Une « forme normale » d’un seul qubit.
  • Décomposition KAK : toute porte 2-qubits se réalise avec au plus 3 CNOT + des portes mono-qubits. C’est une borne serrée (certaines en demandent moins : 0, 1 ou 2).

Solovay–Kitaev : approcher l’inatteignable

Un jeu de portes discret (ex. Clifford+T) ne peut pas produire exactement une rotation d’angle arbitraire — il y a une infinité d’angles mais un alphabet fini.

  • Théorème de Solovay–Kitaev : on peut approcher toute porte mono-qubit à une précision ε en utilisant seulement O(log^c(1/ε)) portes du jeu discret (c ≈ 2–4). Le coût grandit lentement (polylog), pas exponentiellement.

Analogie. Comme représenter n’importe quel nombre réel avec un alphabet fini de chiffres : on n’est jamais exact, mais on s’approche autant qu’on veut, et le « nombre de chiffres » nécessaire croît doucement quand on exige plus de précision.

Routage sous contrainte de connectivité

Sur une vraie puce, tous les qubits ne sont pas câblés entre eux (coupling map). Un CNOT entre deux qubits non voisins exige d’insérer des SWAP pour les rapprocher. Le placement+routage optimal est NP-difficile → on utilise des heuristiques (ex. SABRE).

# Routage : un CNOT entre qubits non voisins force des SWAP
from qiskit import QuantumCircuit, transpile
from qiskit.transpiler import CouplingMap

cmap = CouplingMap([[0, 1], [1, 2]])   # ligne 0–1–2 (PAS de lien 0–2)
qc = QuantumCircuit(3)
qc.cx(0, 2)                            # 0 et 2 ne sont pas voisins !

tqc = transpile(qc, coupling_map=cmap, basis_gates=['cx', 'u'],
                routing_method='sabre')
print(tqc.count_ops())  # un/des SWAP ont été ajoutés (→ 3 CNOT chacun)
// Q# : une rotation d'angle arbitraire n'est PAS réalisable
// exactement dans un jeu Clifford+T → le compilateur tolérant aux
// fautes la remplace par une séquence H/T/S via Solovay–Kitaev.
operation RotationArbitraire(q : Qubit) : Unit {
    Rz(0.123 * PI(), q);   // synthétisée en H/T/S à epsilon près
}
CibleRésultat de synthèseExact ?
Unitaire mono-qubit (continu)3 rotations d’Euleroui
Unitaire 2-qubits≤ 3 CNOT + mono-qubits (KAK)oui
Rotation arbitraire en Clifford+TO(log^c(1/ε)) portes (Solovay–Kitaev)approché (ε)

Piège. Sur le matériel NISQ, les SWAP de routage peuvent dominer le nombre total de portes — un circuit « court » devient profond une fois mappé sur une connectivité pauvre. Et optimization_level=3 ne garantit pas le minimum global de portes : c’est une heuristique, pas un optimum prouvé.


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.