Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions pyqpanda-algorithm/example/QKD/cmp_QKD.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Compare the five QKD protocols side by side.

Runs BB84, B92, E91, BBM92 and SARG04 over a clean channel with a low/high-intensity
intercept-resend eavesdropper, then reports sift efficiency and the quantum
bit-error rate (QBER) for each. In every case the QBER stays at 0 without an
eavesdropper and rises well above the ~11 % security threshold once Eve is on the
line -- the signature that lets the parties detect her.

Outputs (written to ./output):
cmp_QKD.png -- sift-efficiency and QBER bar charts

Run:
python cmp_QKD.py
"""

import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from pyqpanda_alg.QKD import QKD, BB84, B92, E91, BBM92, SARG04

import matplotlib ; matplotlib.use('Agg')
import matplotlib.pyplot as plt

BASE_PATH = Path(__file__).resolve().parent
OUT_DIR = BASE_PATH / 'output'

PROTOCOLS: list[QKD] = [BB84, B92, E91, BBM92, SARG04]
N_RAW = 30000
THRESHOLD = 0.11
EVE_LOW_PROB = 0.2
EVE_HIGH_PROB = 0.5


if __name__ == '__main__':
names, sift, qber_low, qber_high = [], [], [], []
print(f'{"protocol":8s} {"sift":>7s} {"QBER(Low)":>12s} {"QBER(High)":>10s} secure(Low/High)')
print('-' * 60)
for P in PROTOCOLS:
eve_low = P(eve_prob=EVE_LOW_PROB, seed=1).distribute(N_RAW)
eve_high = P(eve_prob=EVE_HIGH_PROB, seed=1).distribute(N_RAW)
names.append(P.__name__)
sift.append(eve_low.sift_rate)
qber_low.append(eve_low.qber)
qber_high.append(eve_high.qber)
chsh = ''
if 'chsh_S' in eve_low.extra:
chsh = f" CHSH S: Low={eve_low.extra['chsh_S']:+.2f} High={eve_high.extra['chsh_S']:+.2f}"
print(f'{P.__name__:8s} {eve_low.sift_rate:6.1%} {eve_low.qber:12.3f} {eve_high.qber:10.3f} {eve_low.secure}/{eve_high.secure}{chsh}')

x = range(len(names))
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(11, 4))
ax1.bar(x, sift, color='#4c72b0')
ax1.set_xticks(list(x)) ; ax1.set_xticklabels(names, rotation=20)
ax1.set_ylabel('sift efficiency')
ax1.set_title('Sift efficiency (kept fraction)')
ax1.set_ylim(0, 0.6)
for i, v in zip(x, sift):
ax1.text(i, v + 0.01, f'{v:.0%}', ha='center', va='bottom', fontsize=9)
w = 0.38
ax2.bar([i - w / 2 for i in x], qber_low, width=w, label=f'Low intercepts {EVE_LOW_PROB:.0%}', color='#55a868')
ax2.bar([i + w / 2 for i in x], qber_high, width=w, label=f'High intercepts {EVE_HIGH_PROB:.0%}', color='#c44e52')
ax2.axhline(THRESHOLD, ls='--', color='k', lw=1, label=f'security threshold {THRESHOLD:.0%}')
ax2.set_xticks(list(x)) ; ax2.set_xticklabels(names, rotation=20)
ax2.set_ylabel('QBER')
ax2.set_title('QBER: eavesdropped low vs high intensity')
ax2.legend(fontsize=8)
fig.tight_layout()
OUT_DIR.mkdir(exist_ok=True)
path = OUT_DIR / 'cmp_QKD.png'
fig.savefig(path, dpi=120, bbox_inches='tight')
print(f'\nSaved comparison figure to {path}')
50 changes: 50 additions & 0 deletions pyqpanda-algorithm/example/QKD/demo_B92.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Demo: B92 quantum key distribution.

B92 needs only two non-orthogonal states: Alice sends |0> for bit 0 and |+> for
bit 1. Bob measures in a random basis and keeps only the *conclusive* outcomes
(about 25 % of rounds) that unambiguously identify Alice's bit. As with BB84 an
eavesdropper is exposed by the resulting error rate.

Run:
python demo_B92.py
"""

import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from pyqpanda_alg.QKD import B92, QKDInsecureError


def report(title: str, qkd: B92, n_raw: int):
r = qkd.distribute(n_raw)
print(f'\n[{title}]')
print(f' raw qubits : {r.n_raw}')
print(f' conclusive (sifted) : {len(r.alice_key)} ({r.sift_rate:.1%})')
print(f' QBER : {r.qber:.3f} (secure={r.secure})')
print(f' Alice sifted key : {r.alice_key[:48]}...')
print(f' Bob sifted key : {r.bob_key[:48]}...')
print(f' keys identical : {r.alice_key == r.bob_key}')
print(f' timecost : {r.timecost:.3f}s')


if __name__ == '__main__':
print('=' * 64)
print('B92 -- prepare-and-measure QKD (2 non-orthogonal states)')
print('=' * 64)

report('clean channel', B92(seed=42), 8192)
report('Eve intercepts every round', B92(eve_prob=1.0, seed=42), 8192)

print('\n[keygen over a clean channel]')
key = B92(seed=7).keygen(128)
print(f' 128-bit privacy-amplified key: {key}')

print('\n[keygen with full interception -> aborts]')
try:
B92(eve_prob=1.0, seed=7).keygen(128)
except QKDInsecureError as e:
print(f' {e}')
51 changes: 51 additions & 0 deletions pyqpanda-algorithm/example/QKD/demo_BB84.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Demo: BB84 quantum key distribution.

Alice sends random bits in random bases (Z/X); Bob measures in his own random
bases; they sift the rounds where the bases matched. The demo runs the protocol
over a clean channel (Alice and Bob obtain an identical key with QBER 0) and then
with an intercept-resend eavesdropper (QBER jumps to ~25 % and the key is
rejected).

Run:
python demo_BB84.py
"""

import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from pyqpanda_alg.QKD import BB84, QKDInsecureError


def report(title: str, qkd: BB84, n_raw: int):
r = qkd.distribute(n_raw)
print(f'\n[{title}]')
print(f' raw qubits : {r.n_raw}')
print(f' sifted (bases match) : {len(r.alice_key)} ({r.sift_rate:.1%})')
print(f' QBER : {r.qber:.3f} (secure={r.secure})')
print(f' Alice sifted key : {r.alice_key[:48]}...')
print(f' Bob sifted key : {r.bob_key[:48]}...')
print(f' keys identical : {r.alice_key == r.bob_key}')
print(f' timecost : {r.timecost:.3f}s')


if __name__ == '__main__':
print('=' * 64)
print('BB84 -- prepare-and-measure QKD (4 states, 2 bases)')
print('=' * 64)

report('clean channel', BB84(seed=42), 8192)
report('Eve intercepts every round', BB84(eve_prob=1.0, seed=42), 8192)

print('\n[keygen over a clean channel]')
key = BB84(seed=7).keygen(128)
print(f' 128-bit privacy-amplified key: {key}')

print('\n[keygen with full interception -> aborts]')
try:
BB84(eve_prob=1.0, seed=7).keygen(128)
except QKDInsecureError as e:
print(f' {e}')
50 changes: 50 additions & 0 deletions pyqpanda-algorithm/example/QKD/demo_BBM92.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Demo: BBM92 entanglement-based quantum key distribution.

BBM92 is the EPR version of BB84: instead of Alice preparing states, a source
emits Bell pairs |Phi+> and both parties measure in a random Z/X basis. Because
|Phi+> is perfectly correlated in both bases, matching-basis rounds yield the key.
Intercepting one arm breaks the correlation and shows up as QBER, just like BB84.

Run:
python demo_BBM92.py
"""

import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from pyqpanda_alg.QKD import BBM92, QKDInsecureError


def report(title: str, qkd: BBM92, n_raw: int):
r = qkd.distribute(n_raw)
print(f'\n[{title}]')
print(f' entangled pairs : {r.n_raw}')
print(f' sifted (bases match) : {len(r.alice_key)} ({r.sift_rate:.1%})')
print(f' QBER : {r.qber:.3f} (secure={r.secure})')
print(f' Alice sifted key : {r.alice_key[:48]}...')
print(f' Bob sifted key : {r.bob_key[:48]}...')
print(f' keys identical : {r.alice_key == r.bob_key}')
print(f' timecost : {r.timecost:.3f}s')


if __name__ == '__main__':
print('=' * 64)
print('BBM92 -- entanglement-based twin of BB84 (EPR pairs)')
print('=' * 64)

report('clean channel', BBM92(seed=42), 8192)
report('Eve intercepts every round', BBM92(eve_prob=1.0, seed=42), 8192)

print('\n[keygen over a clean channel]')
key = BBM92(seed=7).keygen(128)
print(f' 128-bit privacy-amplified key: {key}')

print('\n[keygen with full interception -> aborts]')
try:
BBM92(eve_prob=1.0, seed=7).keygen(128)
except QKDInsecureError as e:
print(f' {e}')
51 changes: 51 additions & 0 deletions pyqpanda-algorithm/example/QKD/demo_E91.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Demo: E91 (Ekert) entanglement-based quantum key distribution.

A source shares Bell pairs. Alice and Bob each measure along random settings;
rounds with the same physical angle give perfectly correlated bits (the key),
while the mismatched-angle rounds are spent on the CHSH test. Genuine
entanglement yields S ~ 2*sqrt(2) = 2.83; an eavesdropper collapses the
entanglement, pushing S back toward the classical bound of 2 and raising the QBER.

Run:
python demo_E91.py
"""

import sys
import math
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from pyqpanda_alg.QKD import E91, QKDInsecureError


def report(title: str, qkd: E91, n_raw: int):
r = qkd.distribute(n_raw)
print(f'\n[{title}]')
print(f' entangled pairs : {r.n_raw}')
print(f' key rounds (sifted) : {len(r.alice_key)} ({r.sift_rate:.1%})')
print(f' QBER : {r.qber:.3f} (secure={r.secure})')
print(f" CHSH S : {r.extra['chsh_S']:+.3f} (|S|>2 => entangled; ideal {2*math.sqrt(2):.3f})")
print(f' keys identical : {r.alice_key == r.bob_key}')
print(f' timecost : {r.timecost:.3f}s')


if __name__ == '__main__':
print('=' * 64)
print('E91 -- entanglement-based QKD with a CHSH Bell test')
print('=' * 64)

report('clean channel', E91(seed=42), 8192)
report('Eve intercepts every round', E91(eve_prob=1.0, seed=42), 8192)

print('\n[keygen over a clean channel]')
key = E91(seed=7).keygen(128)
print(f' 128-bit privacy-amplified key: {key}')

print('\n[keygen with full interception -> aborts]')
try:
E91(eve_prob=1.0, seed=7).keygen(128)
except QKDInsecureError as e:
print(f' {e}')
51 changes: 51 additions & 0 deletions pyqpanda-algorithm/example/QKD/demo_SARG04.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Demo: SARG04 quantum key distribution.

SARG04 uses the same four states as BB84 but encodes the key in the *basis* and
sifts differently: Alice announces the state she sent together with a decoy from
the conjugate basis, and Bob keeps a round only when his measurement excludes one
of the two. This makes it more robust against photon-number-splitting attacks.
An intercept-resend eavesdropper still drives up the QBER.

Run:
python demo_SARG04.py
"""

import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from pyqpanda_alg.QKD import SARG04, QKDInsecureError


def report(title: str, qkd: SARG04, n_raw: int):
r = qkd.distribute(n_raw)
print(f'\n[{title}]')
print(f' raw qubits : {r.n_raw}')
print(f' conclusive (sifted) : {len(r.alice_key)} ({r.sift_rate:.1%})')
print(f' QBER : {r.qber:.3f} (secure={r.secure})')
print(f' Alice sifted key : {r.alice_key[:48]}...')
print(f' Bob sifted key : {r.bob_key[:48]}...')
print(f' keys identical : {r.alice_key == r.bob_key}')
print(f' timecost : {r.timecost:.3f}s')


if __name__ == '__main__':
print('=' * 64)
print('SARG04 -- BB84 states, basis-encoded key, exclusion sifting')
print('=' * 64)

report('clean channel', SARG04(seed=42), 8192)
report('Eve intercepts every round', SARG04(eve_prob=1.0, seed=42), 8192)

print('\n[keygen over a clean channel]')
key = SARG04(seed=7).keygen(128)
print(f' 128-bit privacy-amplified key: {key}')

print('\n[keygen with full interception -> aborts]')
try:
SARG04(eve_prob=1.0, seed=7).keygen(128)
except QKDInsecureError as e:
print(f' {e}')
Binary file added pyqpanda-algorithm/example/QKD/output/cmp_QKD.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading