Avancé Chapitre 26-27 / 16

Quantum Machine Learning — bases & Algorithme HHL

Découvrir les fondamentaux du machine learning quantique (encodages, PQC, QNN, kernels) et l'algorithme HHL pour résoudre des systèmes linéaires — sans prérequis mathématiques, avec du code Q# et Qiskit.

Quantum Machine Learning — bases

C’est quoi le QML ?

En machine learning classique, tes données sont des vecteurs (des listes de nombres) et ton modèle fait des multiplications de matrices. En QML, on exploite le fait qu’un état quantique est déjà un vecteur dans un espace gigantesque. Avec n qubits, tu travailles dans un espace de dimension 2ⁿ — comme si tu avais un vecteur de taille 1024 avec seulement 10 qubits.

L’idée centrale : utiliser cette capacité naturelle pour accélérer ou enrichir les algorithmes de ML.

Encodages de données

Avant de faire du ML quantique, il faut charger tes données classiques dans un circuit quantique. Trois méthodes principales :

EncodagePrincipeAnalogieCoût en qubits
AmplitudeLes valeurs de ton vecteur deviennent les amplitudes de l’état quantique. Un vecteur de 2ⁿ valeurs tient dans n qubits.Compresser un fichier CSV entier dans quelques qubits — très compact mais la préparation est coûteuse.log₂(N) qubits pour N données
AngleChaque valeur est encodée comme l’angle de rotation d’un qubit (Ry(θ) ou Rx(θ)).Mapper chaque feature sur le cadran d’une horloge — un qubit par feature.1 qubit par feature
BasisLes données binaires (0 et 1) sont mappées sur les états de base des qubits.Écrire un nombre en binaire sur les qubits — le bit 1 met le qubit à |1⟩, le bit 0 le laisse à |0⟩.1 qubit par bit

Point clé : L’amplitude encoding est le plus compact (logarithmique en qubits), mais préparer l’état quantique correspondant peut nécessiter un circuit de profondeur exponentielle. En pratique, l’angle encoding est souvent le plus utilisé car il est simple à implémenter.

Circuits paramétrés (PQC)

Un PQC (Parameterized Quantum Circuit), c’est l’équivalent quantique d’un réseau de neurones. Imagine un circuit avec des portes de rotation (Ry, Rz, Rx) dont les angles sont des paramètres ajustables. Comme les poids d’un réseau de neurones, ces angles sont optimisés pendant l’entraînement.

Le circuit suit généralement ce schéma : encodage des données → couche(s) de portes paramétrées + intrication (CNOT) → mesure. La sortie de la mesure donne la prédiction du modèle.

Le circuit EST le modèle. Pas de matrice de poids séparée — les poids sont les angles des portes quantiques.

# Créer un circuit paramétré avec Qiskit
from qiskit.circuit import QuantumCircuit, ParameterVector

# 2 qubits, 4 paramètres ajustables
params = ParameterVector('θ', 4)
qc = QuantumCircuit(2)

# Couche d'encodage (angle encoding)
qc.ry(params[0], 0)  # Encode la feature 0 sur le qubit 0
qc.ry(params[1], 1)  # Encode la feature 1 sur le qubit 1

# Couche d'intrication
qc.cx(0, 1)          # CNOT pour créer des corrélations

# Couche paramétrée
qc.ry(params[2], 0)  # Paramètre entraînable
qc.ry(params[3], 1)  # Paramètre entraînable

# Mesure → la distribution des résultats est la prédiction
qc.measure_all()

QNN — Quantum Neural Network

Un QNN, c’est simplement un PQC + un optimiseur classique. La boucle d’entraînement est identique au ML classique :

  1. Forward pass — exécuter le circuit quantique avec les données d’entrée, mesurer la sortie.
  2. Calculer la loss — comparer la sortie à la vérité terrain (classiquement).
  3. Backward pass — estimer les gradients (via parameter-shift rule ou finite differences).
  4. Mettre à jour les paramètres — l’optimiseur classique (Adam, COBYLA…) ajuste les angles.

Le circuit quantique fait le travail du « forward pass », tout le reste se passe sur ton CPU/GPU classique.

// Q# — Opération paramétrée simple (ansatz)
operation Ansatz(thetas : Double[], qubits : Qubit[]) : Unit is Adj + Ctl {
    // Couche de rotations paramétrées
    Ry(thetas[0], qubits[0]);
    Ry(thetas[1], qubits[1]);
    // Intrication
    CNOT(qubits[0], qubits[1]);
    // Deuxième couche paramétrée
    Ry(thetas[2], qubits[0]);
    Ry(thetas[3], qubits[1]);
}

Kernels quantiques

Une autre approche du QML ne passe pas par des PQC mais par des kernels — exactement comme les SVM classiques. L’idée : utiliser un circuit quantique pour mapper les données dans l’espace de Hilbert (un espace de très grande dimension), puis calculer le produit scalaire entre les états transformés. Ce produit scalaire joue le rôle de fonction noyau (kernel).

En pratique, on mesure le chevauchement (overlap) entre deux états pour obtenir k(xᵢ, xⱼ) = |⟨φ(xᵢ)|φ(xⱼ)⟩|². Plus l’overlap est grand, plus les deux points de données sont « similaires » dans l’espace quantique.

Attention — Barren plateaus : Plus tu ajoutes de qubits à un PQC, plus les gradients tendent à devenir exponentiellement petits (ils « s’aplatissent »). C’est le problème des barren plateaus. Concrètement, ton optimiseur ne sait plus dans quelle direction bouger les paramètres. C’est un frein majeur au passage à l’échelle des QNN.


Algorithme HHL

Le problème : résoudre Ax = b

Résoudre un système linéaire Ax = b, c’est le pain quotidien de l’ingénierie : simulations physiques, régressions, optimisation. Classiquement, pour une matrice N×N, les meilleurs algorithmes tournent en O(N) à O(N² log N) selon la structure.

L’algorithme HHL (Harrow, Hassidim, Lloyd, 2009) promet de résoudre ce problème en temps O(log(N) · κ² · poly(1/ε)), où κ est le nombre de condition de la matrice et ε la précision souhaitée. C’est un speedup exponentiel en N.

Analogie

Imagine que tu as une recette de cuisine (la matrice A) et un plat que tu veux obtenir (le vecteur b). HHL trouve les ingrédients (le vecteur x) exponentiellement plus vite qu’un chef classique — mais seulement si la recette est « simple » (matrice creuse et bien conditionnée). Si la recette est un fouillis incompréhensible (matrice dense, mal conditionnée), l’avantage disparaît.

Les 4 étapes de HHL

  1. Préparer |b⟩ — encoder le vecteur b comme état quantique (amplitude encoding).
  2. QPE (Estimation de Phase) — décomposer A en ses valeurs propres λⱼ. On utilise QPE sur l’opérateur unitaire e^(iAt) pour extraire les λⱼ dans un registre auxiliaire.
  3. Rotation conditionnelle — appliquer une rotation sur un qubit ancilla, d’angle proportionnel à C/λⱼ. C’est l’étape qui « inverse » les valeurs propres.
  4. Uncompute + mesure — défaire la QPE, mesurer l’ancilla. Si on obtient |1⟩ sur l’ancilla, le registre principal contient |x⟩.

Conditions sur la matrice A

ConditionCe que ça veut direPourquoi c’est nécessaire
Creuse (sparse)Peu d’éléments non nuls par ligne (s éléments)Pour simuler e^(iAt) efficacement
Bien conditionnéeLe ratio entre la plus grande et la plus petite valeur propre (κ) est raisonnableLa complexité dépend de κ² — si κ est énorme, l’avantage fond
HermitienneA = A† (la matrice est égale à sa conjuguée transposée)QPE requiert un opérateur unitaire. Si A n’est pas hermitienne, on peut la « hermitianiser » avec une astuce standard
# Qiskit — Esquisse d'un circuit HHL simplifié (2x2)
from qiskit.circuit import QuantumCircuit
import numpy as np

# Système 2x2 : A|x⟩ = |b⟩
qc = QuantumCircuit(4, 1)  # 1 qubit b, 2 qubits QPE, 1 ancilla

# Étape 1 : Préparer |b⟩ sur le qubit 0
qc.ry(np.pi / 3, 0)  # Encode b = [cos(π/6), sin(π/6)]

# Étape 2 : QPE pour extraire les valeurs propres de A
# (circuit simplifié — en réalité, dépend de e^(iAt))
qc.h(1)
qc.h(2)
qc.cp(np.pi / 2, 1, 0)   # Rotation contrôlée simulant e^(iAt)
qc.cp(np.pi, 2, 0)
# QFT inverse sur registre QPE (qubits 1-2)
qc.swap(1, 2)
qc.h(1)
qc.cp(-np.pi / 2, 2, 1)
qc.h(2)

# Étape 3 : Rotation conditionnelle sur l'ancilla (qubit 3)
qc.cry(np.pi / 4, 1, 3)   # Angle ~ C/λ₁
qc.cry(np.pi / 8, 2, 3)   # Angle ~ C/λ₂

# Étape 4 : Mesurer l'ancilla
qc.measure(3, 0)
# Si résultat = 1, le qubit 0 contient |x⟩
// Q# — Rotation conditionnelle (cœur de HHL)
operation RotationConditionnelle(
    registre : Qubit[],
    ancilla : Qubit,
    c : Double
) : Unit is Adj + Ctl {
    // Pour chaque qubit du registre QPE,
    // appliquer une rotation proportionnelle à C/λ
    for k in 0 .. Length(registre) - 1 {
        let angle = c / IntAsDouble(1 <<< (k + 1));
        Controlled Ry([registre[k]], (angle, ancilla));
    }
    // Après cette étape, l'amplitude de |1⟩ sur l'ancilla
    // est proportionnelle à C/λⱼ pour chaque composante propre
}

Attention — Le speedup a du « fine print » : HHL est exponentiellement plus rapide… en théorie. En pratique, trois goulots d’étranglement peuvent annuler l’avantage : (1) préparer |b⟩ coûte O(N) si on n’a pas de structure exploitable ; (2) lire toutes les composantes de x coûte O(N) mesures ; (3) le nombre de condition κ peut exploser. Le speedup est réel uniquement si tu as besoin d’une propriété globale de x (comme ⟨x|M|x⟩), pas de toutes ses composantes individuelles.


Quiz — teste tes connaissances
Avancé 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.