Encodage Amplitude & Encodage Angle et Dense Angle
Encoder des données continues dans des amplitudes ou des angles de rotation — deux stratégies complémentaires pour alimenter la boucle variationnelle avec Qiskit.
Encodage Amplitude
L’idée en une phrase
L’encodage amplitude stocke un vecteur classique normalisé de N = 2ⁿ valeurs directement dans les amplitudes de n qubits, offrant une compression exponentielle — mais au prix d’un circuit de préparation potentiellement profond, ce qui place cet encodage du côté « expressivité maximale » dans le compromis expressivité ↔ entraînabilité de la boucle variationnelle hybride.
Analogie : L’encodage amplitude est comparable à un hologramme : une plaque de quelques centimètres carrés stocke une scène 3D complète — une quantité d’information bien supérieure à sa surface physique. De la même façon, 3 qubits suffisent à encoder un vecteur de 8 valeurs réelles. Mais produire l’hologramme exige un laser et un montage optique précis — tout comme préparer l’état quantique exige un circuit de profondeur potentiellement élevée.
Points clés
- L’encodage amplitude mappe un vecteur classique
x = [x₀, x₁, …, x_{N-1}]vers l’état|ψ⟩ = Σᵢ xᵢ |i⟩, où|i⟩est le i-ème état de la base computationnelle. Le vecteur doit être normalisé :Σᵢ |xᵢ|² = 1. - Le gain en qubits est exponentiel : n qubits pour 2ⁿ valeurs. Comme vu au chapitre 4, l’encodage basis nécessite N qubits pour N features — l’encodage amplitude divise ce coût par un facteur exponentiel.
- La préparation d’état générique nécessite O(2ⁿ) portes dans le pire cas, ce qui peut annuler l’avantage en qubits sur un processeur NISQ où la profondeur de circuit est limitée par le bruit.
- En Qiskit, la méthode
initialize()prépare un état arbitraire. Le transpileur décompose l’initialisation en portes élémentaires — la profondeur résultante dépend du vecteur et du nombre de qubits. - Sur l’axe expressivité ↔ entraînabilité, l’encodage amplitude maximise l’information injectée dans le QPU mais peut rendre le circuit total (feature map + ansatz) trop profond pour l’ère NISQ.
Exemple concret
On dispose d’un vecteur de 4 features normalisé x = [0.5, 0.5, 0.5, 0.5] (chaque composante vaut 1/2, la norme L2 vaut 1). L’encodage amplitude le mappe sur 2 qubits dans l’état |ψ⟩ = 0.5|00⟩ + 0.5|01⟩ + 0.5|10⟩ + 0.5|11⟩. La mesure donne chaque état de base avec probabilité 0.25. Avec l’encodage basis (chapitre 4), il faudrait 4 qubits et les données devraient être binaires. Avec l’encodage angle (section suivante), il faudrait 4 qubits pour 4 features continues — ici, 2 suffisent.
Coût en qubits selon l’encodage
| Encodage | Qubits pour N features | Type de données | Profondeur du circuit |
|---|---|---|---|
| Basis (ch. 4) | N | Binaires | O(N) — portes X |
| Amplitude | log₂(N) | Continues (normalisées) | O(2ⁿ) pire cas |
| Angle (ci-dessous) | N | Continues | O(1) par qubit |
| Dense Angle (ci-dessous) | N/2 | Continues | O(1) par qubit |
Code Python — encoder un vecteur dans les amplitudes
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
import numpy as np
def amplitude_encode(data: list[float]) -> QuantumCircuit:
"""Encoder un vecteur normalisé dans les amplitudes de n qubits.
Le vecteur doit avoir 2^n éléments et une norme L2 égale à 1.
"""
n_qubits = int(np.log2(len(data)))
qc = QuantumCircuit(n_qubits)
qc.initialize(data, range(n_qubits))
return qc
# Vecteur de 4 éléments → 2 qubits
x = [0.5, 0.5, 0.5, 0.5] # norme = sqrt(4 * 0.25) = 1
qc = amplitude_encode(x)
sv = Statevector.from_instruction(qc)
print("Probabilités :", sv.probabilities_dict())
# {'00': 0.25, '01': 0.25, '10': 0.25, '11': 0.25}
# Vérifier la profondeur après décomposition
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
pm = generate_preset_pass_manager(optimization_level=1)
transpiled = pm.run(qc)
print(f"Profondeur après transpilation : {transpiled.depth()}")
Piège courant : « L’encodage amplitude est toujours supérieur car il utilise moins de qubits » est une simplification. Le circuit de préparation peut être très profond — sur un processeur NISQ bruité, un circuit profond accumule des erreurs qui dégradent la fidélité de l’état préparé. Un encodage angle avec plus de qubits mais un circuit de profondeur 1 peut donner de meilleurs résultats en pratique.
Encodage Angle et Dense Angle
L’idée en une phrase
L’encodage angle transforme chaque feature continue xᵢ en rotation Ry(xᵢ) sur le qubit i — un circuit de profondeur 1 idéal pour l’ère NISQ — tandis que l’encodage dense angle double la densité en ajoutant une rotation Rz(x_{2i+1}) par qubit, encodant 2 features par qubit au lieu d’une, toujours dans le cadre de la boucle variationnelle hybride.
Analogie : L’encodage angle est comparable à un cadran solaire : chaque feature contrôle l’inclinaison d’un gnomon — une valeur continue, un cadran. L’encodage dense angle ajoute une boussole à chaque cadran : le gnomon encode simultanément l’altitude du soleil (Ry) et l’azimut (Rz) — deux informations sur un seul instrument, au prix d’une lecture plus subtile.
Points clés
- L’encodage angle applique
Ry(xᵢ)au qubit i. Le qubit passe de|0⟩àcos(xᵢ/2)|0⟩ + sin(xᵢ/2)|1⟩. La probabilité de mesurer|1⟩estsin²(xᵢ/2), qui dépend de façon non linéaire de xᵢ — une propriété utile pour la classification. - Le coût est linéaire : n qubits pour n features, comme l’encodage basis (chapitre 4). Mais ici, les données sont continues et le circuit n’a qu’une profondeur de 1 porte par qubit.
- L’encodage dense angle applique
Ry(x_{2i})puisRz(x_{2i+1})au qubit i. On encode ainsi 2 features par qubit, soit n features dans⌈n/2⌉qubits. La rotation Rz modifie la phase de l’état sans changer les probabilités de mesure dans la base Z — l’ansatz ou des mesures dans d’autres bases doivent exploiter cette information. - Les états encodés par angle ne sont pas orthogonaux en général. Deux données proches produisent des états à fort recouvrement, ce qui peut compliquer la séparation des classes — contrairement à l’encodage basis (chapitre 4) où l’orthogonalité est garantie.
- Sur l’axe expressivité ↔ entraînabilité, l’encodage angle privilégie l’entraînabilité : circuit peu profond, compatible NISQ, mais expressivité modérée que l’ansatz doit compenser.
Exemple concret
On encode 2 features normalisées entre 0 et π : x = [π/4, π/3]. L’encodage angle sur 2 qubits applique Ry(π/4) au qubit 0 et Ry(π/3) au qubit 1. Le qubit 0 passe à l’état cos(π/8)|0⟩ + sin(π/8)|1⟩, avec P(q₀ = 1) = sin²(π/8) ≈ 0.146. Le qubit 1 passe à cos(π/6)|0⟩ + sin(π/6)|1⟩, avec P(q₁ = 1) = sin²(π/6) = 0.25. Les qubits ne sont pas encore corrélés — l’intrication viendra de l’ansatz. L’encodage dense angle de x = [π/4, π/3] sur 1 seul qubit applique Ry(π/4) puis Rz(π/3) : les probabilités de mesure en base Z restent identiques (P(|1⟩) ≈ 0.146), mais la phase relative porte l’information de la seconde feature.
Encodage Angle vs Dense Angle
| Critère | Angle | Dense Angle |
|---|---|---|
| Portes par qubit | 1 (Ry) | 2 (Ry + Rz) |
| Features par qubit | 1 | 2 |
| Qubits pour n features | n | ⌈n/2⌉ |
| Profondeur | 1 | 2 |
| Information en phase | Non utilisée | Oui (via Rz) |
| Lecture de la phase | Directe en base Z | Nécessite ansatz ou mesure en base X/Y |
Code Python — encodage angle et dense angle
from qiskit import QuantumCircuit
from qiskit.circuit import ParameterVector
from qiskit.quantum_info import Statevector
import numpy as np
def angle_encode(data: list[float]) -> QuantumCircuit:
"""Encodage angle : Ry(xᵢ) sur le qubit i."""
n = len(data)
qc = QuantumCircuit(n)
for i, val in enumerate(data):
qc.ry(val, i)
return qc
def dense_angle_encode(data: list[float]) -> QuantumCircuit:
"""Encodage dense angle : Ry(x_{2i}) + Rz(x_{2i+1}) sur le qubit i.
Encode 2 features par qubit. Si len(data) est impair, le dernier
élément est encodé seul avec Ry.
"""
n_qubits = (len(data) + 1) // 2
qc = QuantumCircuit(n_qubits)
for i in range(n_qubits):
qc.ry(data[2 * i], i)
if 2 * i + 1 < len(data):
qc.rz(data[2 * i + 1], i)
return qc
# Encodage angle de [π/4, π/3] → 2 qubits
qc_angle = angle_encode([np.pi / 4, np.pi / 3])
sv = Statevector.from_instruction(qc_angle)
print("Angle — probabilités :", {k: round(v, 4) for k, v in sv.probabilities_dict().items()})
# {'00': 0.6402, '01': 0.1098, '10': 0.2134, '11': 0.0366}
# Encodage dense angle de [π/4, π/3] → 1 qubit
qc_dense = dense_angle_encode([np.pi / 4, np.pi / 3])
sv_dense = Statevector.from_instruction(qc_dense)
print("Dense — probabilités :", {k: round(v, 4) for k, v in sv_dense.probabilities_dict().items()})
# {'0': 0.8536, '1': 0.1464} — la phase de Rz n'affecte pas P(Z)
# Recouvrement entre deux encodages angle proches
sv1 = Statevector.from_instruction(angle_encode([0.1, 0.2]))
sv2 = Statevector.from_instruction(angle_encode([0.15, 0.25]))
overlap = abs(sv1.inner(sv2)) ** 2
print(f"Recouvrement angle (données proches) : {overlap:.4f}")
# Proche de 1 — les états ne sont PAS orthogonaux
Piège courant : « L’encodage dense angle est toujours préférable à l’encodage angle car il utilise moins de qubits » est inexact. La seconde feature est encodée dans la phase via Rz, or une mesure dans la base computationnelle (Z) est insensible à la phase. Pour exploiter cette information, l’ansatz doit inclure des portes qui mélangent les bases (Hadamard, Ry) ou la mesure doit se faire dans une base complémentaire — sinon la seconde feature est perdue.
Fil rouge — la frontière quantique/classique
L’encodage amplitude et l’encodage angle délèguent au QPU des tâches de nature différente. L’encodage amplitude confie au processeur quantique la préparation d’un état riche — le circuit de feature map est profond et utilise une fraction significative du budget de portes NISQ. L’encodage angle, au contraire, consomme une seule couche de rotations, laissant l’essentiel de la profondeur à l’ansatz. Le dense angle se situe entre les deux. Le compromis expressivité ↔ entraînabilité se joue donc dès le choix du feature map : un encodage expressif (amplitude) peut réduire la charge de l’ansatz mais aggraver la sensibilité au bruit ; un encodage frugal (angle) préserve le budget circuit pour l’ansatz au prix d’une représentation moins compacte des données.
Kata Qiskit — comparer les encodages continus
Objectif : implémenter les encodages angle et dense angle, puis mesurer le recouvrement entre deux états encodés pour vérifier que ces encodages ne garantissent pas l’orthogonalité.
Squelette :
# kata_day_9_10.py
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
import numpy as np
def angle_encode(data: list[float]) -> QuantumCircuit:
"""Encodage angle : appliquer Ry(xᵢ) sur le qubit i."""
n = len(data)
qc = QuantumCircuit(n)
# TODO 1 : pour chaque feature xᵢ, appliquer une rotation Ry(xᵢ) sur le qubit i
return qc
def dense_angle_encode(data: list[float]) -> QuantumCircuit:
"""Encodage dense angle : Ry(x_{2i}) + Rz(x_{2i+1}) sur le qubit i.
Si le nombre de features est impair, le dernier élément est encodé seul avec Ry.
"""
n_qubits = (len(data) + 1) // 2
qc = QuantumCircuit(n_qubits)
# TODO 2 : pour chaque qubit i, appliquer Ry(data[2*i])
# TODO 3 : si data[2*i+1] existe, appliquer Rz(data[2*i+1]) sur le même qubit
return qc
def overlap(qc1: QuantumCircuit, qc2: QuantumCircuit) -> float:
"""Calculer le recouvrement |⟨ψ₁|ψ₂⟩|² entre deux circuits."""
# TODO 4 : obtenir les Statevector des deux circuits
# TODO 5 : calculer et retourner |inner product|²
return 0.0
Auto-correction :
# test_kata_day_9_10.py
import numpy as np
from kata_day_9_10 import angle_encode, dense_angle_encode, overlap
from qiskit.quantum_info import Statevector
# --- Tests angle_encode ---
# Ry(0) ne fait rien : l'état reste |00⟩
sv = Statevector.from_instruction(angle_encode([0.0, 0.0]))
probs = sv.probabilities_dict()
assert '00' in probs and abs(probs['00'] - 1.0) < 1e-8, \
"angle_encode([0, 0]) doit produire |00⟩"
# Ry(π) sur qubit 0 → P(q₀=1) = 1
sv = Statevector.from_instruction(angle_encode([np.pi, 0.0]))
probs = sv.probabilities()
p_q0_1 = sum(probs[j] for j in range(len(probs)) if j % 2 == 1)
assert abs(p_q0_1 - 1.0) < 1e-8, "angle_encode([π, 0]) : P(q₀=1) doit valoir 1"
# --- Tests dense_angle_encode ---
# 2 features → 1 qubit, Ry(0) + Rz(0) → |0⟩
sv = Statevector.from_instruction(dense_angle_encode([0.0, 0.0]))
assert sv.num_qubits == 1, "dense_angle_encode([0, 0]) doit utiliser 1 qubit"
probs = sv.probabilities_dict()
assert '0' in probs and abs(probs['0'] - 1.0) < 1e-8, \
"dense_angle_encode([0, 0]) doit produire |0⟩"
# 4 features → 2 qubits
sv = Statevector.from_instruction(dense_angle_encode([0.0, 0.0, 0.0, 0.0]))
assert sv.num_qubits == 2, "dense_angle_encode avec 4 features doit utiliser 2 qubits"
# 3 features (impair) → 2 qubits
sv = Statevector.from_instruction(dense_angle_encode([np.pi, 0.0, 0.0]))
assert sv.num_qubits == 2, "dense_angle_encode avec 3 features doit utiliser 2 qubits"
# --- Tests overlap ---
# Même circuit → recouvrement = 1
qc = angle_encode([0.5, 0.3])
assert abs(overlap(qc, qc) - 1.0) < 1e-8, "Recouvrement d'un état avec lui-même = 1"
# Circuits orthogonaux → recouvrement = 0
qc1 = angle_encode([0.0, 0.0]) # |00⟩
qc2 = angle_encode([np.pi, np.pi]) # |11⟩
assert abs(overlap(qc1, qc2)) < 1e-8, "Recouvrement |00⟩ vs |11⟩ = 0"
# Circuits proches → recouvrement élevé mais < 1
qc3 = angle_encode([0.1, 0.2])
qc4 = angle_encode([0.15, 0.25])
ov = overlap(qc3, qc4)
assert 0.9 < ov < 1.0, f"Données proches : recouvrement attendu > 0.9, obtenu {ov:.4f}"
print("Kata validé !")
Solution et explication
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
import numpy as np
def angle_encode(data: list[float]) -> QuantumCircuit:
n = len(data)
qc = QuantumCircuit(n)
for i, val in enumerate(data):
qc.ry(val, i)
return qc
def dense_angle_encode(data: list[float]) -> QuantumCircuit:
n_qubits = (len(data) + 1) // 2
qc = QuantumCircuit(n_qubits)
for i in range(n_qubits):
qc.ry(data[2 * i], i)
if 2 * i + 1 < len(data):
qc.rz(data[2 * i + 1], i)
return qc
def overlap(qc1: QuantumCircuit, qc2: QuantumCircuit) -> float:
sv1 = Statevector.from_instruction(qc1)
sv2 = Statevector.from_instruction(qc2)
return float(abs(sv1.inner(sv2)) ** 2)Pourquoi : le kata met en œuvre les deux encodages continus du chapitre et introduit la notion de recouvrement — un concept central pour évaluer la capacité d’un feature map à séparer les données. L’encodage angle applique une seule rotation par qubit (profondeur 1, idéal NISQ). L’encodage dense angle double la densité via Rz, mais l’information de phase n’apparaît pas dans une mesure Z. Le test de recouvrement montre que des données proches produisent des états presque identiques — l’ansatz devra amplifier ces différences pour classifier correctement.