use clap::{arg, Parser};
use ndarray::Array2;
use num::traits::Pow;
use quaru::{
math::real_arr_to_complex,
operation::{hadamard, Operation},
register::Register,
};
use std::{
f64::consts::PI,
fmt::Display,
time::{Duration, Instant},
};
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
#[arg(short, long)]
target: Option<usize>,
#[arg(short, long)]
statistics: bool,
}
fn main() {
let args = Args::parse();
if !args.statistics && args.target.is_none() {
println!("Please provide a target integer with --target, or run in statistics mode with --statistics");
} else if !args.statistics {
let target = args.target.unwrap();
let regsize = ((target + 1) as f64).log2().ceil() as usize;
let regsize = if regsize < 2 { 2 } else { regsize }; println!("Regsize: {regsize}");
let result = grovers_algorithm(target, regsize, iterations_exact);
println!("{result}");
} else {
statistics();
}
}
fn statistics() {
let iterations_fns = [
iterations_ceil,
iterations_floor,
iterations_exact,
iterations_ms,
];
println!("Testing accuracy of Grover's algorithm for targets 1..100 with different iteration functions");
println!("Order of functions: ceil, floor, exact, ms");
for iteration_fn in iterations_fns {
let mut correct: usize = 0;
for i in 0..100 {
let regsize = ((i + 1) as f64).log2().ceil() as usize;
let regsize = if regsize < 2 { 2 } else { regsize }; let result = grovers_algorithm(i, regsize, iteration_fn);
if to_decimal(&result.result) == i {
correct += 1;
}
}
println!("Correctness {}", correct as f64 / 100.0);
}
}
struct GroversResult {
target: usize,
result: Vec<bool>,
iterations: usize,
time: Duration,
}
impl Display for GroversResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Grover's Algorithm executed with the following results:
Target: {}
Result: {} ({:?})
Iterations of oracle + diffuser: {}
Time elapsed: {} ms",
self.target,
to_decimal(&self.result),
self.result,
self.iterations,
self.time.as_millis()
)
}
}
fn grovers_algorithm(
winner: usize,
regsize: usize,
iterations_fn: fn(usize) -> usize,
) -> GroversResult {
let mut reg = Register::new(&(0..regsize).map(|_| false).collect::<Vec<bool>>());
let start = Instant::now();
reg.apply_all(&hadamard(0));
let oracle = oracle_operation(reg.size(), winner);
let n: usize = iterations_fn(reg.size());
for _ in 0..n {
reg.apply(&oracle);
diffuser(&mut reg);
}
let measured_state: Vec<bool> = (0..reg.size()).rev().map(|i| reg.measure(i)).collect();
let time_elapsed = start.elapsed();
GroversResult {
target: winner,
result: measured_state,
iterations: n,
time: time_elapsed,
}
}
fn diffuser(reg: &mut Register) {
reg.apply_all(&hadamard(0));
reg.apply(&diffusion_operation(reg.size()));
reg.apply_all(&hadamard(0));
}
pub fn oracle_operation(regsize: usize, winner: usize) -> Operation {
let n: usize = 2_usize.pow(regsize as u32);
let mut matrix: Array2<f64> = Array2::<f64>::zeros((n, n));
for i in 0..n {
matrix.row_mut(i)[i] = if i == winner { -1.0 } else { 1.0 };
}
let op = Operation::new(real_arr_to_complex(matrix), (0..regsize).collect())
.expect("Could not create oracle operation");
op
}
pub fn diffusion_operation(regsize: usize) -> Operation {
let n: usize = 2_usize.pow(regsize as u32);
let mut matrix: Array2<f64> = Array2::<f64>::zeros((n, n));
matrix.row_mut(0)[0] = 1.0;
for i in 1..n {
matrix.row_mut(i)[i] = -1.0;
}
let op = Operation::new(real_arr_to_complex(matrix), (0..regsize).collect())
.expect("Could not create diffuser operation");
op
}
fn iterations_floor(regsize: usize) -> usize {
(PI / 4.0 * (2.0_f64.pow(regsize as f64)).sqrt()).floor() as usize
}
fn iterations_ceil(regsize: usize) -> usize {
(PI / 4.0 * (2.0_f64.pow(regsize as f64)).sqrt()).ceil() as usize
}
fn iterations_ms(regsize: usize) -> usize {
(PI / 4.0 * (2.0_f64.pow(regsize as f64)).sqrt() - 0.5 as f64).floor() as usize
}
fn iterations_exact(regsize: usize) -> usize {
let theta = theta(regsize);
let n = 2.0_f64.pow(regsize as f64);
(f64::acos((1.0 / n).sqrt()) / theta).round() as usize
}
fn theta(regsize: usize) -> f64 {
let n = 2.0_f64.pow(regsize as f64);
f64::asin((2.0 * (n - 1.0).sqrt()) / n)
}
fn to_decimal(arr: &[bool]) -> usize {
let mut dec = 0;
for (i, n) in arr.iter().rev().enumerate() {
dec += if *n { 2_usize.pow(i as u32) } else { 0 };
}
dec
}
#[cfg(test)]
mod tests {
use quaru::{operation::hadamard, register::Register};
#[test]
fn test_oracle_operation() {
for target in 0..20 {
let regsize = ((target + 1) as f64).log2().ceil() as usize;
let regsize = if regsize < 2 { 2 } else { regsize }; let mut reg = Register::new(&(0..regsize).map(|_| false).collect::<Vec<bool>>());
let oracle = super::oracle_operation(reg.size(), target);
reg.apply_all(&hadamard(0));
reg.apply(&oracle);
let target_state = reg.state.get((target, 0)).unwrap();
assert!(target_state.re.is_sign_negative());
}
}
#[test]
fn test_diffuser_operation() {
for target in 0..20 {
let regsize = ((target + 1) as f64).log2().ceil() as usize;
let regsize = if regsize < 2 { 2 } else { regsize }; let mut reg = Register::new(&(0..regsize).map(|_| false).collect::<Vec<bool>>());
let oracle = super::oracle_operation(reg.size(), target);
reg.apply_all(&hadamard(0));
reg.apply(&oracle);
crate::diffuser(&mut reg);
reg.print_state();
let target_state = reg.state.get((target, 0)).unwrap().norm();
for i in 0..reg.size() {
if i != target {
assert!(reg.state.get((i, 0)).unwrap().norm() < target_state);
}
}
}
}
}