use crate::error::{QuantumError, QuantumResult};
use crate::gates::hadamard::gate_h;
use crate::statevec::apply_1q::apply_1q_inplace;
use crate::statevec::state::StateVector;
#[derive(Debug, Clone)]
pub struct GroverResult {
pub measured_index: usize,
pub probability: f32,
pub iterations: usize,
}
#[must_use]
pub fn optimal_iterations(dim: usize, n_marked: usize) -> usize {
if n_marked == 0 || dim == 0 {
return 0;
}
let ratio = dim as f64 / n_marked as f64;
let theta = (n_marked as f64 / dim as f64).sqrt().asin();
if theta <= 0.0 {
return 0;
}
let raw = (std::f64::consts::FRAC_PI_2 / theta - 1.0) * 0.5;
let rounded = raw.round();
if rounded <= 0.0 {
if ratio >= 2.0 { 1 } else { 0 }
} else {
rounded as usize
}
}
fn validate(oracle_marks: &[usize], n_qubits: usize) -> QuantumResult<usize> {
if n_qubits == 0 || n_qubits > 30 {
return Err(QuantumError::InvalidQubitCount { n: n_qubits });
}
let dim = 1usize << n_qubits;
for &m in oracle_marks {
if m >= dim {
return Err(QuantumError::InvalidParameter {
name: format!("marked index {m} out of range for dim {dim}"),
});
}
}
Ok(dim)
}
fn apply_oracle(sv: &mut StateVector, marks: &[bool]) {
for (a, &is_marked) in sv.amps.iter_mut().zip(marks.iter()) {
if is_marked {
*a = -*a;
}
}
}
fn apply_diffusion(sv: &mut StateVector) {
let dim = sv.amps.len();
if dim == 0 {
return;
}
let inv_dim = 1.0 / dim as f32;
let mut mean = num_complex::Complex::<f32>::new(0.0, 0.0);
for a in &sv.amps {
mean += *a;
}
mean *= inv_dim;
let two_mean = mean * 2.0;
for a in &mut sv.amps {
*a = two_mean - *a;
}
}
pub fn grover_search(oracle_marks: &[usize], n_qubits: usize) -> QuantumResult<GroverResult> {
let dim = validate(oracle_marks, n_qubits)?;
let mut marks = vec![false; dim];
for &m in oracle_marks {
marks[m] = true;
}
let n_marked = marks.iter().filter(|&&b| b).count();
let mut sv = StateVector::new_zero_state(n_qubits)?;
for q in 0..n_qubits {
apply_1q_inplace(&mut sv, q, &gate_h())?;
}
let iterations = optimal_iterations(dim, n_marked);
for _ in 0..iterations {
apply_oracle(&mut sv, &marks);
apply_diffusion(&mut sv);
}
let mut measured_index = 0usize;
let mut best = f32::NEG_INFINITY;
for (i, a) in sv.amps.iter().enumerate() {
let p = a.norm_sqr();
if p > best {
best = p;
measured_index = i;
}
}
Ok(GroverResult {
measured_index,
probability: best,
iterations,
})
}
pub fn marked_probability(oracle_marks: &[usize], n_qubits: usize) -> QuantumResult<f32> {
let dim = validate(oracle_marks, n_qubits)?;
let mut marks = vec![false; dim];
for &m in oracle_marks {
marks[m] = true;
}
let n_marked = marks.iter().filter(|&&b| b).count();
let mut sv = StateVector::new_zero_state(n_qubits)?;
for q in 0..n_qubits {
apply_1q_inplace(&mut sv, q, &gate_h())?;
}
let iterations = optimal_iterations(dim, n_marked);
for _ in 0..iterations {
apply_oracle(&mut sv, &marks);
apply_diffusion(&mut sv);
}
let mass = sv
.amps
.iter()
.zip(marks.iter())
.filter(|&(_, &m)| m)
.map(|(a, _)| a.norm_sqr())
.sum::<f32>();
Ok(mass)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn finds_marked_single_solution() {
let res = grover_search(&[5], 3).expect("valid 3-qubit grover search for mark 5");
assert_eq!(res.measured_index, 5, "got {}", res.measured_index);
assert!(res.probability > 0.9, "prob={}", res.probability);
}
#[test]
fn amplitude_amplified_above_uniform() {
let n = 4;
let uniform = 1.0 / (1u32 << n) as f32;
let mass =
marked_probability(&[9], n).expect("valid marked probability for 4-qubit single mark");
assert!(
mass > uniform * 4.0,
"mass={mass} not amplified over uniform {uniform}"
);
assert!(mass > 0.9, "mass={mass}");
}
#[test]
fn optimal_iterations_matches_known_values() {
assert_eq!(optimal_iterations(4, 1), 1);
assert_eq!(optimal_iterations(8, 1), 2);
assert_eq!(optimal_iterations(16, 1), 3);
}
#[test]
fn multiple_marked_items_found() {
let marks = [2usize, 6];
let res = grover_search(&marks, 3).expect("valid 3-qubit grover search for marks [2, 6]");
assert!(
marks.contains(&res.measured_index),
"got {}",
res.measured_index
);
let mass = marked_probability(&marks, 3)
.expect("valid 3-qubit marked probability for marks [2, 6]");
assert!(mass > 0.9, "mass={mass}");
}
#[test]
fn no_marked_items_zero_iterations() {
let res = grover_search(&[], 3).expect("valid 3-qubit grover search with empty oracle");
assert_eq!(res.iterations, 0);
assert!(
(res.probability - 1.0 / 8.0).abs() < 1e-5,
"p={}",
res.probability
);
}
#[test]
fn n_qubits_zero_errors() {
assert!(grover_search(&[0], 0).is_err());
}
#[test]
fn probability_concentrates_with_more_qubits() {
for n in 2..=6 {
let target = (1usize << n) - 1; let mass = marked_probability(&[target], n)
.expect("valid marked probability for last-index target");
assert!(mass > 0.8, "n={n} mass={mass}");
}
}
#[test]
fn deterministic_runs() {
let a = grover_search(&[3], 4).expect("valid 4-qubit grover search for mark 3 (run a)");
let b = grover_search(&[3], 4).expect("valid 4-qubit grover search for mark 3 (run b)");
assert_eq!(a.measured_index, b.measured_index);
assert!((a.probability - b.probability).abs() < 1e-7);
assert_eq!(a.iterations, b.iterations);
}
#[test]
fn single_item_space() {
let res = grover_search(&[2], 2).expect("valid 2-qubit grover search for mark 2");
assert_eq!(res.measured_index, 2, "got {}", res.measured_index);
assert!(res.probability > 0.99, "prob={}", res.probability);
}
#[test]
fn n_eq_one_is_degenerate() {
let mass =
marked_probability(&[1], 1).expect("valid 1-qubit marked probability for single mark");
assert!((mass - 0.5).abs() < 1e-4, "mass={mass}");
}
#[test]
fn out_of_range_mark_errors() {
assert!(grover_search(&[8], 3).is_err());
}
#[test]
fn preserves_norm_through_iterations() {
let n = 4;
let marks = [1usize, 7, 11];
let marked = marked_probability(&marks, n)
.expect("valid 4-qubit marked probability for marks [1, 7, 11]");
assert!(marked <= 1.0 + 1e-5, "marked mass {marked} exceeds 1");
let mut all = vec![false; 1 << n];
for &m in &marks {
all[m] = true;
}
let n_marked = marks.len();
let mut sv =
StateVector::new_zero_state(n).expect("valid 4-qubit zero state for norm check");
for q in 0..n {
apply_1q_inplace(&mut sv, q, &gate_h()).expect("valid qubit index for Hadamard gate");
}
for _ in 0..optimal_iterations(1 << n, n_marked) {
apply_oracle(&mut sv, &all);
apply_diffusion(&mut sv);
}
assert!((sv.norm_sq() - 1.0).abs() < 1e-4, "norm={}", sv.norm_sq());
}
}