use crate::constants::{H, I, X};
use crate::helpers::{rand_float, shuffle_and_split};
use crate::participants::{Receiver, Sender};
use bon::Builder;
use statrs::distribution::{ContinuousCDF, Normal};
use std::time::Instant;
#[derive(Clone, Debug)]
pub struct QExecutionResult {
pub alice_value: bool,
pub alice_basis: usize,
pub bob_value: bool,
pub bob_basis: usize,
pub eve_value: Option<bool>,
pub eve_basis: Option<usize>,
}
impl QExecutionResult {
pub fn new(
alice_value: bool,
alice_basis: usize,
bob_value: bool,
bob_basis: usize,
eve_value: Option<bool>,
eve_basis: Option<usize>,
) -> Self {
QExecutionResult {
alice_value,
alice_basis,
bob_value,
bob_basis,
eve_value,
eve_basis,
}
}
}
#[derive(Debug)]
pub struct QKDResult {
pub elapsed_time: u128,
pub is_considered_secure: bool,
pub key_length: Option<usize>,
pub final_key_qber: Option<f64>,
pub measured_qber: f64,
pub eve_knowledge: f64,
}
#[derive(Debug)]
pub struct PublicDiscussionResult {
pub alice_public_values: Vec<bool>,
pub bob_public_values: Vec<bool>,
pub indexes_to_key: Vec<usize>,
pub results: Vec<QExecutionResult>,
}
#[derive(Builder)]
pub struct QKD {
name: String,
alice: Sender,
bob: Receiver,
#[builder(default = Receiver::builder().posible_basis(vec![I, H]).build())]
eve: Receiver,
#[builder(default = Box::new(default_public_basis_discussion))]
public_basis_discussion: Box<dyn Fn(&Vec<QExecutionResult>) -> PublicDiscussionResult>,
}
impl QKD {
pub fn get_name(&self) -> String {
self.name.clone()
}
pub fn run(
&self,
number_of_qubits: usize,
interception_rate: f64,
noise: f64,
confidence: f64,
) -> QKDResult {
let initial_time = Instant::now();
let results = (0..number_of_qubits)
.map(|_| self.quantum_communication(interception_rate, noise))
.collect::<Vec<QExecutionResult>>();
let discussion_result = (self.public_basis_discussion)(&results);
let results = discussion_result.results;
let (is_considered_secure, measured_qber) = self.check_public_values(
discussion_result.alice_public_values,
discussion_result.bob_public_values,
noise,
confidence,
);
let mut eve_knowledge = 0.0;
let mut final_key_qber = None;
let mut key_length = None;
if is_considered_secure {
let ((alice_secret_values, bob_secret_values), eve_secret_values): (
(Vec<bool>, Vec<bool>),
Vec<Option<bool>>,
) = discussion_result
.indexes_to_key
.iter()
.map(|&i| {
(
(results[i].alice_value, results[i].bob_value),
results[i].eve_value,
)
})
.unzip();
key_length = Some(alice_secret_values.len());
let (mismatched_bits, absolute_eve_knowledge) = alice_secret_values
.into_iter()
.zip(bob_secret_values)
.zip(eve_secret_values)
.fold((0.0, 0.0), |mut acc, ((a, b), e)| {
if a != b {
acc.0 += 1.0;
} else {
acc.1 += if e == Some(a) { 1.0 } else { 0.0 }
}
acc
});
final_key_qber = Some(mismatched_bits / key_length.unwrap() as f64);
eve_knowledge = absolute_eve_knowledge / key_length.unwrap() as f64;
}
let elapsed_time = initial_time.elapsed().as_micros();
QKDResult {
elapsed_time,
is_considered_secure,
key_length,
measured_qber,
final_key_qber,
eve_knowledge,
}
}
fn quantum_communication(&self, interception_rate: f64, noise: f64) -> QExecutionResult {
let (mut qubit, alice_value) = (self.alice.prepare)();
let alice_basis = (self.alice.change_basis)(&mut qubit, &self.alice.posible_basis);
let mut eve_basis = None;
let mut eve_value = None;
if rand_float() < interception_rate {
eve_basis = Some((self.eve.change_basis)(&mut qubit, &self.eve.posible_basis));
eve_value = Some((self.eve.measure)(&mut qubit));
(self.eve.try_to_restore_qubit)(
&mut qubit,
&self.eve.posible_basis[eve_basis.unwrap()],
);
}
if rand_float() < noise {
qubit.apply_transformation(&X);
}
let bob_basis = (self.bob.change_basis)(&mut qubit, &self.bob.posible_basis);
let bob_value = (self.bob.measure)(&mut qubit);
QExecutionResult::new(
alice_value,
alice_basis,
bob_value,
bob_basis,
eve_value,
eve_basis,
)
}
fn check_public_values(
&self,
alice_public_values: Vec<bool>,
bob_public_values: Vec<bool>,
noise: f64,
confidence: f64,
) -> (bool, f64) {
let number_of_qubits = alice_public_values.len() as f64;
let normal = Normal::standard();
let p = (1.0 + confidence) / 2.0;
let z = normal.inverse_cdf(p);
let threshold = noise + z / number_of_qubits.sqrt() * (noise * (1.0 - noise)).sqrt();
let measured_qber = alice_public_values
.into_iter()
.zip(bob_public_values)
.filter(|(a, b)| a != b)
.count() as f64
/ number_of_qubits;
(measured_qber <= threshold, measured_qber)
}
}
fn default_public_basis_discussion(results: &Vec<QExecutionResult>) -> PublicDiscussionResult {
let (alice_basis, bob_basis): (Vec<usize>, Vec<usize>) =
results.iter().map(|x| (x.alice_basis, x.bob_basis)).unzip();
let eq_basis_indexes = alice_basis
.into_iter()
.zip(bob_basis)
.enumerate()
.filter(|(_, (a, b))| a == b)
.map(|(i, _)| i)
.collect::<Vec<usize>>();
let (indexes_to_check, indexes_to_key) = shuffle_and_split(eq_basis_indexes);
let (alice_public_values, bob_public_values) = indexes_to_check
.iter()
.map(|&i| (results[i].alice_value, results[i].bob_value))
.unzip();
PublicDiscussionResult {
alice_public_values,
bob_public_values,
indexes_to_key,
results: results.to_vec(),
}
}