i want to know fullly and detailed abuut quantum key distribution including practical ways implementation with pennylane
Hi @krish_18 , welcome to the Forum!
There’s a lot of content online on QKD, especially BB84 which is the most famous protocol for this.
If you are still getting comfortable with PennyLane itself, the PennyLane Codebook is the best place to start since it teaches the framework interactively from the ground up: Codebook — PennyLane
QKD is built almost entirely on state preparation, changing measurement bases, and entanglement. Our Quantum Teleportation demo covers exactly those building blocks, including the no-cloning theorem, which is the physical fact that makes QKD secure in the first place. Reading that first will make BB84 feel natural: Quantum Teleportation | PennyLane Demos
Quantum key distribution lets two people, usually called Alice and Bob, agree on a shared secret string of bits over a channel that anyone might be listening to. The security does not come from a hard math problem like it does with classical cryptography. It comes from physics. An eavesdropper cannot copy an unknown quantum state, and any attempt to measure it leaves a statistical fingerprint. QKD only produces the key. You still encrypt your actual message with that key afterwards.
BB84 encodes each bit using one of two bases, chosen at random for every qubit.
In the Z basis, bit 0 is the state |0\rangle and bit 1 is the state |1\rangle. In the X basis, bit 0 is the |+\rangle state and bit 1 is the |-\rangle state.
The protocol runs like this. Alice picks a random bit and a random basis for each qubit, prepares the qubit accordingly, and sends it to Bob. Bob does not know which basis Alice used, so he picks his own basis at random and measures. Afterwards, over a normal public channel, the two of them announce only which basis they used for each qubit, never the bit values. They throw away every position where their bases did not match and keep the rest. This step is called sifting and it keeps roughly half the qubits. On the qubits that survive, and assuming nobody interfered, their bits agree perfectly.
To check for a listener, they sacrifice a random handful of the surviving bits and compare them openly. If those bits agree, the channel is clean and they use the rest as their key. If too many disagree, someone was measuring the qubits in transit and they throw the whole thing away.
Say an eavesdropper, Eve, intercepts each qubit, measures it, and sends on whatever she found. She does not know Alice’s basis, so half the time she measures in the wrong one. When she guesses wrong, the qubit she forwards to Bob is randomized, so even when Bob’s basis matches Alice’s he now gets the wrong bit a quarter of the time. That error rate of about 25 percent is the giveaway. In a real system the threshold for aborting is lower, although not zero, because real channels are already a bit noisy and you have to stay conservative.
This simulates the whole thing on a single qubit, runs it clean, then runs it again with an intercept and resend eavesdropper so you can watch the error rate jump. I ran it on PennyLane 0.45.1 and the numbers come out as expected: about half the qubits survive sifting, zero errors with no eavesdropper, and close to 25 percent errors when Eve is present.
I’m sharing a full code example below. Please note though that this is an idealized example. In practice you would need an actual quantum channel which will have some inherent loss.
import pennylane as qp
import numpy as np
rng = np.random.default_rng(42)
N = 2000
# 0 = Z basis, 1 = X basis
alice_bits = rng.integers(0, 2, N)
alice_bases = rng.integers(0, 2, N)
bob_bases = rng.integers(0, 2, N)
dev = qp.device("default.qubit", wires=1)
@qp.set_shots(1)
@qp.qnode(dev)
def bb84_qubit(bit, a_basis, b_basis):
# Alice prepares her qubit
if bit == 1:
qp.PauliX(0)
if a_basis == 1: # X basis: apply Hadamard
qp.Hadamard(0)
# Bob measures in his chosen basis
if b_basis == 1:
qp.Hadamard(0)
return qp.sample(qp.PauliZ(0)) # +1 -> bit 0, -1 -> bit 1
bob_bits = np.empty(N, dtype=int)
for i in range(N):
s = bb84_qubit(int(alice_bits[i]), int(alice_bases[i]), int(bob_bases[i]))
bob_bits[i] = 0 if s == 1 else 1
# Sifting: keep only the positions where the bases agreed
match = alice_bases == bob_bases
sift_alice = alice_bits[match]
sift_bob = bob_bits[match]
error_rate = np.mean(sift_alice != sift_bob)
print(f"raw qubits sent : {N}")
print(f"sifted key length : {match.sum()} (~{match.mean():.2%})")
print(f"Error rate (no eavesdrop) : {error_rate:.4f}")
# Now with an eavesdropper (Eve)
eve_bases = rng.integers(0, 2, N)
@qp.set_shots(1)
@qp.qnode(dev)
def with_eve(bit, a_basis, e_basis, b_basis):
if bit == 1:
qp.PauliX(0)
if a_basis == 1:
qp.Hadamard(0)
# Eve measures and resends. A mid-circuit measurement collapses the
# state to the measured eigenstate, and that collapsed state is the resend.
if e_basis == 1:
qp.Hadamard(0)
qp.measure(0) # collapse in Eve's basis
if e_basis == 1:
qp.Hadamard(0) # Eve re-encodes in her basis before forwarding
if b_basis == 1:
qp.Hadamard(0)
return qp.sample(qp.PauliZ(0))
bob_bits_e = np.empty(N, dtype=int)
for i in range(N):
s = with_eve(int(alice_bits[i]), int(alice_bases[i]), int(eve_bases[i]), int(bob_bases[i]))
bob_bits_e[i] = 0 if s == 1 else 1
error_rate_e = np.mean(alice_bits[match] != bob_bits_e[match])
print(f"Error rate (with Eve) : {error_rate_e:.4f} (theory ~0.25)")
I hope this helps!