Le QCNN, architecture NISQ prometteuse & Synthèse et ouverture
Entraîner un QCNN complet sur un jeu de données jouet puis synthétiser les 13 chapitres du livre — avec du code Qiskit exécutable sur simulateur.
Le QCNN, architecture NISQ prometteuse
L’idée en une phrase
Le QCNN réunit les ingrédients étudiés dans ce livre — encodage, ansatz structuré, mesure locale, optimisation classique — en une architecture dont les propriétés théoriques (gradients polynomiaux, profondeur logarithmique, partage de poids) et les résultats expérimentaux (classification de phases quantiques, reconnaissance de codes correcteurs) en font le candidat le plus crédible pour un avantage quantique en régime NISQ. Dans la boucle variationnelle hybride, le QCNN délègue au QPU une hiérarchie de corrélations locales → globales en O(log n) étages, tandis que l’optimiseur classique ajuste un nombre constant de paramètres par étage.
Analogie : Un entonnoir industriel de tri postal. Chaque étage de la machine compare les colis voisins (convolution), éjecte la moitié (pooling) et transmet les plus pertinents à l’étage suivant. Le contremaître (optimiseur classique) ne règle qu’un jeu de vis par étage (partage de poids) — il peut ajuster la machine en quelques essais, là où un opérateur devant régler chaque vis individuellement serait submergé. C’est la raison pour laquelle le QCNN reste entraînable quand d’autres circuits variationnels sombrent dans les plateaus de Barren.
Points clés
- Pipeline complet du QCNN supervisé : (1) encoder les données classiques
xdans un registre denqubits par angle encoding ou re-uploading (chapitres 9-10, 21-22), (2) appliquerO(log n)étages convolution + pooling avec partage de poids (chapitres 21-22, 23-24), (3) mesurer le qubit final pour obtenirP(classe 1), (4) calculer le coût (entropie croisée ou MSE), (5) estimer le gradient par parameter shift (chapitre 13-14) et mettre à jourθpar un optimiseur classique (COBYLA, SPSA, Adam). - Garantie de trainabilité (Pesah et al., 2021) : pour un QCNN à
nqubits avec partage de poids, la variance du gradient satisfaitVar[∂C/∂θ_k] ∈ Ω(1/poly(n)). C’est un résultat rigoureux — pas un comportement empirique — qui distingue le QCNN de la plupart des ansatz variationnels, dont les gradients décroissent enO(1/2ⁿ)(chapitre 23-24). - Application phare : classification de phases quantiques (Cong, Choi, Lukin, 2019). Le QCNN identifie la phase topologique (code de surface, chaîne SPT) d’un état préparé sur le QPU. L’avantage est structurel : un algorithme classique a besoin de
O(n)mesures locales suivies d’un post-traitement coûteux, tandis que le QCNN produit la réponse en une seule mesure du qubit final. - Limites connues : le QCNN est adapté aux tâches présentant une localité spatiale (corrélations entre qubits voisins). Pour des données sans structure spatiale naturelle, l’encodage initial doit placer les features corrélées sur des qubits voisins — un pré-traitement classique qui conditionne la performance. De plus, le partage de poids impose une invariance par translation qui n’est pas toujours souhaitable.
- Bruit matériel : la profondeur
O(log n)est un avantage décisif face au bruit. Pourn = 64qubits, un QCNN a une profondeur de ~6 étages (quelques dizaines de portes à 2 qubits), compatible avec les temps de cohérence des processeurs supraconducteurs actuels (~100 µs, soit ~1 000 portes). Un ansatz dense de profondeurO(n)atteint rapidement la limite de fidélité.
Exemple concret
Entraînement d’un QCNN à 4 qubits sur un jeu de données jouet. Les données sont des vecteurs (x₁, x₂, x₃, x₄) avec le label y = 1 si x₁ + x₂ > x₃ + x₄, sinon y = 0. Le circuit encode les 4 features par angle encoding (Ry(xᵢ)), applique un étage convolutif (paires (0,1) et (2,3), puis (1,2) en motif brique, 4 paramètres partagés), un pooling (qubits 0 et 2 tracés), un second étage convolutif sur (1,3), un second pooling (qubit 1 tracé), et mesure le qubit 3. Avec COBYLA sur 8 paramètres et 20 échantillons d’entraînement, le modèle converge en ~50 itérations vers une accuracy de 90 %. L’optimiseur classique n’a que 8 paramètres à ajuster — un paysage navigable.
Architectures QML — comparaison sur l’axe expressivité ↔ entraînabilité
| Architecture | Paramètres | Var gradient | Profondeur | Forces | Limites |
|---|---|---|---|---|---|
| Ansatz dense (HEA) | O(n·L) | O(1/2ⁿ) | O(L) | Expressivité maximale | Plateaus de Barren |
| VQC structuré | O(n) | O(1/poly(n)) | O(1) | Simple, peu profond | Expressivité limitée |
| Data re-uploading | O(L) | Dépend de L | O(L) | Universalité (1 qubit) | Profondeur croissante |
| QCNN | O(log n) | Ω(1/poly(n)) | O(log n) | Trainabilité + structure | Localité requise |
| QSVM (noyau) | 0 (kernel) | N/A | O(1) par noyau | Pas d’optimisation circuit | Coût quadratique en données |
Code Python — entraînement complet d’un QCNN à 4 qubits
# Pipeline complet : encoder → QCNN (conv + pool) → mesure → optimisation COBYLA.
# Jeu de données jouet : y = 1 si x1 + x2 > x3 + x4, sinon y = 0.
import numpy as np
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
def build_qcnn(data, params):
"""QCNN à 4 qubits : encodage + 2 étages conv/pool."""
qc = QuantumCircuit(4)
# Encodage par angle encoding
for i, x in enumerate(data):
qc.ry(x, i)
# Étage 1 — convolution (partage de poids)
for q0, q1 in [(0, 1), (2, 3), (1, 2)]:
qc.rz(params[0], q0)
qc.rz(params[1], q1)
qc.cx(q0, q1)
qc.ry(params[2], q0)
qc.ry(params[3], q1)
# Pooling 1 : CRz + traçage logique de 0 et 2
qc.crz(params[4], 0, 1)
qc.crz(params[4], 2, 3)
# Étage 2 — convolution sur qubits actifs (1, 3)
qc.rz(params[5], 1)
qc.rz(params[6], 3)
qc.cx(1, 3)
qc.ry(params[5], 1)
qc.ry(params[6], 3)
# Pooling 2 : CRz + traçage logique de 1
qc.crz(params[7], 1, 3)
return qc
def predict_proba(data, params):
"""P(qubit 3 = |1⟩) — la probabilité de la classe 1."""
qc = build_qcnn(data, params)
sv = Statevector(qc)
probs = sv.probabilities()
# Qubit 3 = bit de poids 2³ (little-endian Qiskit)
return sum(probs[8:])
# Jeu de données jouet (10 échantillons)
np.random.seed(42)
X = np.random.uniform(0, np.pi, (10, 4))
y = (X[:, 0] + X[:, 1] > X[:, 2] + X[:, 3]).astype(float)
# Coût : erreur quadratique moyenne
def cost(params):
return np.mean([(predict_proba(X[i], params) - y[i])**2
for i in range(len(X))])
# Optimisation par COBYLA (sans gradient explicite)
from scipy.optimize import minimize
init_params = np.random.uniform(0, np.pi, 8)
result = minimize(cost, init_params, method='COBYLA',
options={'maxiter': 80, 'rhobeg': 0.5})
# Évaluation
preds = [1 if predict_proba(X[i], result.x) > 0.5 else 0
for i in range(len(X))]
accuracy = np.mean([p == yi for p, yi in zip(preds, y)])
print(f"Coût final : {result.fun:.4f}")
print(f"Accuracy : {accuracy:.0%}")
print(f"Paramètres optimisés : {len(result.x)}")
Piège courant : « Le QCNN surpasse les méthodes classiques sur tout jeu de données » est une affirmation sans fondement. Les résultats théoriques de Cong et al. portent sur des tâches intrinsèquement quantiques (classification de phases). Pour des données classiques tabulaires, un gradient boosting ou un réseau de neurones classique est généralement plus performant, plus rapide et plus simple. L’intérêt du QCNN pour les données classiques est exploratoire : il structure l’ansatz et maintient l’entraînabilité, mais l’avantage quantique reste à démontrer dans ce régime.
Synthèse et ouverture
L’idée en une phrase
Ce livre a parcouru la boucle variationnelle hybride sous tous ses angles — de l’approximation de fonction classique (chapitre 1-2) à l’architecture QCNN (chapitres 21-22, 23-24, 25-26) — en établissant que le QML NISQ est un compromis permanent entre expressivité et entraînabilité, où chaque choix de conception (encodage, ansatz, mesure, coût) déplace le curseur sur cet axe. L’hybridation quantique-classique n’est pas un détail d’implémentation : c’est le principe fondateur du QML en régime NISQ.
Analogie : Un architecte qui conçoit un pont doit arbitrer entre portée (expressivité) et stabilité (entraînabilité). Un pont suspendu franchit de grandes distances mais demande des câbles et des fondations massives. Un pont en arc est intrinsèquement stable mais couvre une portée limitée. Le QML NISQ est dans la même situation : chaque architecture quantique est un compromis entre la classe de fonctions atteignable et la capacité à trouver les bons paramètres. Le QCNN est un pont en arc — modeste en portée, solide en stabilité.
Points clés
- La boucle variationnelle hybride est le motif unificateur du livre : (1) encoder
xdans un état quantique via un feature mapS(x), (2) appliquer un ansatz paramétréU(θ), (3) mesurer pour obtenir un scalaire ou un vecteur classique, (4) calculer le coût et mettre à jourθpar un optimiseur classique. Chaque chapitre a approfondi un ou plusieurs de ces blocs. - Expressivité ↔ entraînabilité : les encodages riches (amplitude, re-uploading) et les ansatz profonds augmentent l’expressivité mais risquent les plateaus de Barren. Les circuits structurés (QCNN, VQC peu profond) préservent l’entraînabilité mais réduisent la classe de fonctions. Le praticien QML navigue ce compromis à chaque décision de conception.
- L’avantage quantique en QML reste une question ouverte. Les résultats théoriques (séparations exponentielles pour des problèmes construits ad hoc, noyaux quantiques sur certaines distributions) ne se sont pas encore traduits en avantage pratique sur des jeux de données réels. La communauté distingue trois niveaux : avantage computationnel (vitesse), avantage en généralisation (meilleure performance sur données non vues), avantage en expressivité (fonctions inaccessibles au classique).
- Au-delà du NISQ : la correction d’erreur quantique ouvrira la voie à des circuits profonds sans bruit, éliminant les plateaus induits par la décohérence. Les algorithmes de QML tolérants aux pannes (quantum principal component analysis, quantum singular value transformation) exploitent des speedups polynomiaux ou exponentiels, mais nécessitent des millions de qubits physiques — un horizon à moyen terme.
- Directions émergentes : le QML sur données quantiques (quantum sensing + classification QCNN), les modèles génératifs quantiques (QGAN, quantum Boltzmann machines), l’apprentissage par renforcement quantique, et l’intégration de modèles quantiques comme sous-routines dans des pipelines classiques (quantum-enhanced features).
Exemple concret
Récapitulatif du parcours en 13 chapitres. Les chapitres 1-6 (Débutant) ont posé les fondations classiques (approximation, gradient, biais-variance) et introduit le cadre NISQ. Les chapitres 7-14 (Intermédiaire) ont détaillé les briques de la boucle variationnelle : encodages (basis, amplitude, angle), ansatz, primitives Sampler/Estimator, et entraînement. Les chapitres 15-20 (Avancé) ont exploré les noyaux quantiques (QSVM) et la non-linéarité. Les chapitres 21-26 (Expert) ont abouti à l’architecture QCNN — le point de convergence du livre, où encodage structuré, ansatz à poids partagés, coût local et profondeur logarithmique résolvent simultanément les défis identifiés dans les chapitres précédents. La boucle variationnelle hybride est le fil qui relie chaque chapitre au suivant.
Maturité du QML — état des lieux en 2025
| Domaine | Maturité | Horizon | Référence clé |
|---|---|---|---|
| Classification de phases quantiques | Démontrée (simulateur + matériel) | Disponible | Cong et al., 2019 |
| Noyaux quantiques (QSVM) | Résultats théoriques + petite échelle | Court terme | Havlicek et al., 2019 |
| QCNN sur données classiques | Exploratoire | Moyen terme | Hur et al., 2022 |
| Modèles génératifs quantiques | Preuve de concept | Moyen terme | Zoufal et al., 2019 |
| QML tolérant aux pannes | Théorique | Long terme | Gilyen et al., 2019 |
Code Python — récapitulatif de la boucle variationnelle hybride
# Synthèse : la boucle variationnelle hybride en 30 lignes.
# Chaque ligne correspond à un bloc étudié dans le livre.
import numpy as np
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector, SparsePauliOp
# 1. ENCODAGE — chapitre 9-10 (angle encoding)
def encode(qc, x):
for i, xi in enumerate(x):
qc.ry(xi, i)
# 2. ANSATZ — chapitre 13-14 (circuit paramétré)
def ansatz(qc, params):
for q in range(qc.num_qubits):
qc.ry(params[q], q)
for q in range(qc.num_qubits - 1):
qc.cx(q, q + 1)
# 3. MESURE — chapitre 13-14 (valeur d'attente)
def measure(qc, obs):
return Statevector(qc).expectation_value(obs).real
# 4. BOUCLE HYBRIDE — chapitres 3-4, 13-14
def variational_loop(x, obs, n_steps=30, lr=0.3):
n = len(x)
params = np.random.uniform(0, np.pi, n)
for step in range(n_steps):
qc = QuantumCircuit(n)
encode(qc, x)
ansatz(qc, params)
cost = measure(qc, obs)
# Parameter shift (chapitre 13-14)
grads = np.zeros(n)
for k in range(n):
p_plus = params.copy(); p_plus[k] += np.pi / 2
p_minus = params.copy(); p_minus[k] -= np.pi / 2
qc_p = QuantumCircuit(n); encode(qc_p, x); ansatz(qc_p, p_plus)
qc_m = QuantumCircuit(n); encode(qc_m, x); ansatz(qc_m, p_minus)
grads[k] = (measure(qc_p, obs) - measure(qc_m, obs)) / 2
params -= lr * grads # Mise à jour classique
return cost, params
# Exécution : minimiser ⟨Z⊗Z⟩ (trouver l'état antiparallèle)
obs = SparsePauliOp('ZZ')
cost, params = variational_loop([0.0, 0.0], obs)
print(f"Coût final ⟨ZZ⟩ : {cost:.4f}")
print(f"(Minimum théorique : -1, atteint pour |01⟩ ou |10⟩)")
Piège courant : « Le QML va remplacer le ML classique à court terme » est une prédiction sans appui empirique. Aucun résultat publié ne démontre un avantage quantique pratique en QML sur des données classiques à l’échelle industrielle. Le QML NISQ est un domaine de recherche actif dont la valeur réside dans l’exploration de nouvelles classes de modèles. Le praticien avisé utilise le QML comme un outil complémentaire pour des tâches spécifiques (données quantiques, noyaux sur distributions structurées), pas comme un remplacement universel.
Fil rouge — la frontière quantique/classique
Ce dernier chapitre boucle la boucle. Le QCNN concentre les leçons du livre : l’encodage structure l’information classique pour le QPU, l’ansatz à poids partagés contrôle l’expressivité, la mesure locale garantit des gradients exploitables, et l’optimiseur classique ajuste un nombre logarithmique de paramètres. Sur l’axe expressivité ↔ entraînabilité, le QCNN occupe un point d’équilibre rare — suffisamment expressif pour résoudre des problèmes non triviaux (classification de phases), suffisamment structuré pour rester entraînable à grande échelle. La frontière quantique/classique n’est pas un mur mais un curseur que le concepteur déplace à chaque choix d’architecture. Le QML NISQ, c’est l’art de placer ce curseur au bon endroit.
Kata Qiskit — pipeline QCNN complet
Objectif : implémenter un pipeline QCNN complet (encodage → convolution → pooling → classification) et vérifier qu’il produit des prédictions valides sur un mini-jeu de données.
Squelette :
# kata_day_25_26.py
import numpy as np
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
def build_qcnn(data, params):
"""Construire un QCNN à 4 qubits.
Architecture :
1. Angle encoding : Ry(data[i]) sur chaque qubit i
2. Convolution étage 1 : même bloc sur paires (0,1) et (2,3)
Bloc = Rz(p0, q0) + Rz(p1, q1) + CX(q0,q1) + Ry(p2, q0) + Ry(p3, q1)
3. Pooling étage 1 : CRz(p4, source=0, target=1) et CRz(p4, source=2, target=3)
4. Convolution étage 2 : même bloc sur paire (1,3)
Bloc = Rz(p5, 1) + Rz(p6, 3) + CX(1,3) + Ry(p5, 1) + Ry(p6, 3)
5. Pooling étage 2 : CRz(p7, source=1, target=3)
Args:
data: array de 4 floats (features)
params: array de 8 floats (paramètres du circuit)
Returns:
QuantumCircuit à 4 qubits
"""
qc = QuantumCircuit(4)
# TODO 1 : encoder les données par angle encoding (Ry)
# TODO 2 : convolution étage 1 sur paires (0,1) et (2,3)
# TODO 3 : pooling étage 1 — CRz(params[4]) de 0→1 et 2→3
# TODO 4 : convolution étage 2 sur paire (1,3)
# TODO 5 : pooling étage 2 — CRz(params[7]) de 1→3
return qc
def predict(data, params):
"""Renvoyer P(qubit 3 = |1⟩) pour le QCNN.
Le qubit 3 est le qubit de classification.
En convention little-endian de Qiskit, qubit 3 correspond
au bit de poids 2³ = 8 : P(classe 1) = sum(probs[8:]).
Args:
data: array de 4 floats
params: array de 8 floats
Returns:
float : probabilité de la classe 1
"""
# TODO 6 : construire le circuit, obtenir le Statevector,
# renvoyer la somme des probabilités pour les indices >= 8
return 0.0
def accuracy(X, y, params):
"""Calculer l'accuracy du QCNN sur un jeu de données.
Prédiction : classe 1 si predict > 0.5, sinon classe 0.
Args:
X: array (n_samples, 4)
y: array (n_samples,) de labels 0 ou 1
params: array de 8 floats
Returns:
float : fraction de prédictions correctes
"""
# TODO 7 : calculer le taux de bonnes prédictions
return 0.0
Auto-correction :
# test_kata_day_25_26.py
import numpy as np
from kata_day_25_26 import build_qcnn, predict, accuracy
# Test 1 : le circuit a 4 qubits
params = np.array([0.3, 0.5, 0.7, 0.1, 0.4, 0.2, 0.8, 0.6])
data = np.array([0.5, 1.0, 1.5, 2.0])
qc = build_qcnn(data, params)
assert qc.num_qubits == 4, f"Le circuit doit avoir 4 qubits, trouvé {qc.num_qubits}"
# Test 2 : le circuit contient des portes Ry, Rz, CX, CRz
ops = [instr.operation.name for instr in qc.data]
assert 'ry' in ops, "Le circuit doit contenir des portes Ry (encodage + ansatz)"
assert 'rz' in ops, "Le circuit doit contenir des portes Rz (convolution)"
assert 'cx' in ops, "Le circuit doit contenir des portes CNOT (convolution)"
assert 'crz' in ops, "Le circuit doit contenir des portes CRz (pooling)"
# Test 3 : predict renvoie une probabilité dans [0, 1]
p = predict(data, params)
assert 0 - 1e-9 <= p <= 1 + 1e-9, f"P(classe 1) hors de [0,1] : {p}"
# Test 4 : predict dépend des données (entrées différentes → sorties différentes)
p1 = predict(np.array([0.5, 1.0, 1.5, 2.0]), params)
p2 = predict(np.array([2.0, 0.3, 0.1, 0.8]), params)
assert abs(p1 - p2) > 1e-6, "Le modèle doit produire des sorties différentes pour des entrées différentes"
# Test 5 : predict dépend des paramètres
p3 = predict(data, np.zeros(8))
p4 = predict(data, np.ones(8))
assert abs(p3 - p4) > 1e-6, "Le modèle doit produire des sorties différentes pour des paramètres différents"
# Test 6 : accuracy fonctionne correctement
X = np.array([[0.5, 1.0, 1.5, 2.0], [2.0, 1.5, 0.5, 0.3]])
y = np.array([0, 1])
acc = accuracy(X, y, params)
assert 0 <= acc <= 1, f"L'accuracy doit être dans [0, 1], trouvé {acc}"
assert isinstance(acc, (float, np.floating)), f"L'accuracy doit être un float, trouvé {type(acc)}"
print("Kata validé !")
Solution et explication
import numpy as np
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
def build_qcnn(data, params):
qc = QuantumCircuit(4)
# TODO 1 : encodage par angle
for i, x in enumerate(data):
qc.ry(x, i)
# TODO 2 : convolution étage 1 (partage de poids sur paires 0,1 et 2,3)
for q0, q1 in [(0, 1), (2, 3)]:
qc.rz(params[0], q0)
qc.rz(params[1], q1)
qc.cx(q0, q1)
qc.ry(params[2], q0)
qc.ry(params[3], q1)
# TODO 3 : pooling étage 1
qc.crz(params[4], 0, 1)
qc.crz(params[4], 2, 3)
# TODO 4 : convolution étage 2 sur qubits actifs (1, 3)
qc.rz(params[5], 1)
qc.rz(params[6], 3)
qc.cx(1, 3)
qc.ry(params[5], 1)
qc.ry(params[6], 3)
# TODO 5 : pooling étage 2
qc.crz(params[7], 1, 3)
return qc
def predict(data, params):
qc = build_qcnn(data, params) # TODO 6
sv = Statevector(qc)
probs = sv.probabilities()
return float(sum(probs[8:])) # qubit 3 = bit de poids 2³
def accuracy(X, y, params):
correct = 0 # TODO 7
for i in range(len(X)):
pred = 1 if predict(X[i], params) > 0.5 else 0
if pred == y[i]:
correct += 1
return float(correct / len(X))Pourquoi : ce kata rassemble toutes les briques du livre. L’encodage par angle (chapitre 9-10) injecte les données classiques dans le registre quantique. La convolution à poids partagés (chapitres 21-22, 23-24) structure l’ansatz en limitant les paramètres. Le pooling par CRz (chapitre 21-22) réduit le registre de 4 à 2 puis 1 qubit actif. La mesure du qubit final (chapitre 13-14) fournit la probabilité de classification. L’accuracy évalue le modèle comme en ML classique (chapitre 3-4). C’est la boucle variationnelle hybride complète, du Ry(x) initial au label prédit.