Codage superdense & Phase kickback
Codage superdense (2 bits via 1 qubit intriqué) et phase kickback — deux protocoles clés expliqués avec du code Q# et Qiskit.
Codage superdense
Le codage superdense permet d’envoyer 2 bits classiques d’information en ne transmettant physiquement qu’un seul qubit — à condition que l’expéditeur et le destinataire partagent au préalable une paire de qubits intriqués (un état de Bell).
Analogie : Imagine que toi et un ami avez chacun un dé magique. Avant de vous séparer, vous les « liez » ensemble (intrication). Plus tard, tu veux transmettre un message codé sur 2 bits (00, 01, 10, 11). Tu manipules uniquement ton dé (ton qubit), puis tu l’envoies à ton ami. En combinant ton dé reçu avec le sien, il peut décoder exactement lequel des 4 messages tu voulais envoyer.
Prérequis : une paire de Bell partagée — typiquement |Φ⁺⟩ = (|00⟩ + |11⟩)/√2, créée par un circuit H + CNOT (comme vu dans les jours 6-7).
Le protocole étape par étape
- Étape 0 — Préparation : Alice et Bob partagent l’état de Bell
|Φ⁺⟩. Alice a le qubit A, Bob a le qubit B. - Étape 1 — Encodage par Alice : Selon les 2 bits qu’elle veut envoyer, Alice applique une porte sur son qubit uniquement :
00→ elle ne fait rien (identité I)01→ elle applique X (bit-flip)10→ elle applique Z (phase-flip)11→ elle applique ZX (d’abord X, puis Z)
- Étape 2 — Transmission : Alice envoie physiquement son qubit A à Bob. Un seul qubit voyage !
- Étape 3 — Décodage par Bob : Bob applique CNOT (A contrôle, B cible) puis H sur A, et mesure les deux qubits. Le résultat de la mesure donne exactement les 2 bits d’Alice.
| Message (2 bits) | Porte d’Alice | État après encodage | État de Bell résultant |
|---|---|---|---|
00 | I (rien) | (|00⟩ + |11⟩)/√2 | |Φ⁺⟩ |
01 | X | (|10⟩ + |01⟩)/√2 | |Ψ⁺⟩ |
10 | Z | (|00⟩ − |11⟩)/√2 | |Φ⁻⟩ |
11 | ZX | (−|10⟩ + |01⟩)/√2 | |Ψ⁻⟩ |
Dualité avec la téléportation : La téléportation envoie 1 qubit en utilisant 2 bits classiques + intrication. Le codage superdense fait l’inverse : il envoie 2 bits classiques en utilisant 1 qubit + intrication. Ce sont des protocoles « miroir ».
Code Q# — Codage superdense
// Codage superdense : Alice envoie 2 bits classiques via 1 qubit
operation SuperdenseCoding(bit1 : Bool, bit2 : Bool) : (Result, Result) {
use (qAlice, qBob) = (Qubit(), Qubit());
// Étape 0 : créer la paire de Bell |Φ⁺⟩
H(qAlice);
CNOT(qAlice, qBob);
// Étape 1 : Alice encode ses 2 bits
if bit2 { X(qAlice); } // bit de droite → X
if bit1 { Z(qAlice); } // bit de gauche → Z
// Étape 2 : Alice envoie qAlice à Bob (implicite)
// Étape 3 : Bob décode
CNOT(qAlice, qBob);
H(qAlice);
// Mesure → retrouve les 2 bits
let r1 = M(qAlice); // = bit1
let r2 = M(qBob); // = bit2
return (r1, r2);
}
Code Qiskit — Codage superdense
# Codage superdense en Qiskit
from qiskit import QuantumCircuit
def superdense_encode(bit1: int, bit2: int) -> QuantumCircuit:
qc = QuantumCircuit(2, 2)
# Étape 0 : paire de Bell
qc.h(0)
qc.cx(0, 1)
qc.barrier()
# Étape 1 : Alice encode (agit sur qubit 0)
if bit2 == 1:
qc.x(0)
if bit1 == 1:
qc.z(0)
qc.barrier()
# Étape 3 : Bob décode
qc.cx(0, 1)
qc.h(0)
qc.measure([0, 1], [0, 1])
return qc
# Envoyer "10" → Z sur qubit d'Alice
circuit = superdense_encode(1, 0)
# La mesure donnera toujours "10"
⚠ Piège fréquent : Le codage superdense ne viole PAS la limite de Shannon. L’intrication a été distribuée à l’avance (coût initial). On ne transmet pas 2 bits dans 1 bit : on transmet 2 bits classiques via 1 qubit + 1 ebit (paire intriquée pré-partagée). Le canal quantique + la ressource d’intrication ensemble permettent cette capacité doublée.
Phase kickback
Le phase kickback est le phénomène par lequel, quand on applique une porte contrôlée sur un qubit cible qui est un vecteur propre de cette porte, la phase propre associée « remonte » (kicks back) sur le qubit de contrôle, sans modifier le qubit cible.
Analogie : Imagine un diapason (qubit cible) qui vibre déjà à sa fréquence naturelle. Tu poses ta main dessus (qubit de contrôle) : le diapason continue de vibrer pareil, mais c’est ta main qui se met à vibrer — la « phase » de la vibration est transférée à toi. Le diapason (cible) est inchangé ; c’est le contrôle qui absorbe l’information.
Les maths sans les maths
Un vecteur propre d’une porte U, c’est un état que U ne change pas (ou presque) : il le multiplie juste par un nombre. Ce nombre s’appelle la valeur propre, et en quantique c’est toujours un nombre de la forme e^(iφ) — un simple « décalage de phase ».
Concrètement : si |v⟩ est vecteur propre de U avec valeur propre e^(iφ), alors U|v⟩ = e^(iφ)|v⟩. L’état ne change pas de direction, il tourne juste son « cadran de phase ».
Le mécanisme : Quand le qubit de contrôle est en superposition |+⟩ = (|0⟩ + |1⟩)/√2 et la cible est |v⟩ (vecteur propre de U), l’opération Controlled-U transforme :
(|0⟩ + |1⟩)/√2 ⊗ |v⟩ → (|0⟩ + e^(iφ)|1⟩)/√2 ⊗ |v⟩
La cible est inchangée. La phase e^(iφ) apparaît sur le contrôle !
Cas concret : oracle de phase avec X et |−⟩
L’exemple le plus simple et le plus important :
- La porte X (NOT) a deux vecteurs propres :
|+⟩(valeur propre +1) et|−⟩(valeur propre −1). - Si la cible est dans l’état
|−⟩ = (|0⟩ − |1⟩)/√2, alors CNOT avec contrôle en|+⟩donne :(|0⟩ + |1⟩)/√2 ⊗ |−⟩→(|0⟩ − |1⟩)/√2 ⊗ |−⟩=|−⟩ ⊗ |−⟩ - Le contrôle passe de
|+⟩à|−⟩: un flip de phase ! La cible reste|−⟩.
Oracle de phase : C’est ce trick qui transforme un « oracle classique » f(x) ∈ 1 en un oracle de phase : au lieu de stocker f(x) dans un registre de sortie, on prépare la cible en
|−⟩et la phase (−1)^f(x) remonte sur le registre d’entrée. C’est la base de Deutsch–Jozsa, Bernstein–Vazirani et Grover.
Lien avec Deutsch–Jozsa
Dans l’algorithme de Deutsch–Jozsa, on veut savoir si une fonction f est constante (toujours 0 ou toujours 1) ou équilibrée (0 pour la moitié des entrées, 1 pour l’autre). Le circuit prépare le registre d’entrée en superposition (H sur chaque qubit) et place un qubit ancilla en |−⟩. L’oracle applique un CNOT conditionné par f(x). Grâce au phase kickback, chaque branche |x⟩ acquiert un facteur (−1)^f(x). Un dernier Hadamard sur le registre d’entrée, puis une mesure, suffit à distinguer les deux cas — en une seule requête.
| Concept | Rôle dans le phase kickback |
|---|---|
| Qubit de contrôle | En superposition ; reçoit la phase |
| Qubit cible | Vecteur propre de U ; reste inchangé |
| Valeur propre e^(iφ) | Apparaît comme phase relative sur le contrôle |
| État |−⟩ comme cible | Vecteur propre de X avec valeur propre −1 → kickback de (−1) |
| Oracle de phase | Transforme f(x) en (−1)^f(x) sur le registre d’entrée |
Code Q# — Phase kickback en action
// Démonstration du phase kickback :
// Le qubit de contrôle passe de |+⟩ à |−⟩
// quand la cible est |−⟩ (vecteur propre de X)
operation PhaseKickbackDemo() : Result {
use (control, target) = (Qubit(), Qubit());
// Préparer control = |+⟩, target = |−⟩
H(control);
X(target);
H(target); // target = |−⟩
// Appliquer CNOT : la phase −1 remonte sur control
CNOT(control, target);
// Remettre target à |0⟩ pour libération
H(target);
X(target);
// Si kickback a fonctionné, control est maintenant |−⟩
// H transforme |−⟩ en |1⟩
H(control);
let result = M(control); // Toujours One !
return result;
}
Code Qiskit — Phase kickback
# Phase kickback : le contrôle absorbe la phase
from qiskit import QuantumCircuit
qc = QuantumCircuit(2, 1)
# Préparer control (q0) = |+⟩
qc.h(0)
# Préparer target (q1) = |−⟩
qc.x(1)
qc.h(1)
# CNOT : phase −1 remonte sur q0
qc.cx(0, 1)
# Vérifier : H ramène |−⟩ → |1⟩
qc.h(0)
qc.measure(0, 0)
# Résultat : toujours "1" → preuve du kickback
# q0 était |+⟩, après kickback c'est |−⟩,
# et H(|−⟩) = |1⟩
⚠ Piège fréquent : On pourrait croire que le CNOT modifie la cible — après tout, c’est une porte « contrôlée ». Mais si la cible est déjà un vecteur propre de X, elle ne bouge pas du tout. Toute « l’action » se produit sur le contrôle. C’est contre-intuitif mais c’est la clé de tous les algorithmes à oracle quantique.