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 BernsteinVaziraniResult {
pub hidden_string: usize,
pub probability: f32,
}
pub fn bernstein_vazirani(
secret: usize,
n_qubits: usize,
) -> QuantumResult<BernsteinVaziraniResult> {
if n_qubits == 0 || n_qubits > 30 {
return Err(QuantumError::InvalidQubitCount { n: n_qubits });
}
let dim = 1usize << n_qubits;
if secret >= dim {
return Err(QuantumError::InvalidParameter {
name: format!("secret {secret} out of range for {n_qubits} qubits"),
});
}
let mut sv = StateVector::new_zero_state(n_qubits)?;
for q in 0..n_qubits {
apply_1q_inplace(&mut sv, q, &gate_h())?;
}
for (x, a) in sv.amps.iter_mut().enumerate() {
if ((secret & x).count_ones() & 1) == 1 {
*a = -*a;
}
}
for q in 0..n_qubits {
apply_1q_inplace(&mut sv, q, &gate_h())?;
}
let mut hidden_string = 0usize;
let mut best = f32::NEG_INFINITY;
for (i, amp) in sv.amps.iter().enumerate() {
let p = amp.norm_sqr();
if p > best {
best = p;
hidden_string = i;
}
}
Ok(BernsteinVaziraniResult {
hidden_string,
probability: best,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn recovers_all_zero_string() {
let res = bernstein_vazirani(0, 4).expect("secret=0 is within range for 4 qubits");
assert_eq!(res.hidden_string, 0);
assert!(res.probability > 0.999, "p={}", res.probability);
}
#[test]
fn recovers_all_one_string() {
let res = bernstein_vazirani(15, 4).expect("secret=15 is within range for 4 qubits");
assert_eq!(res.hidden_string, 15);
assert!(res.probability > 0.999, "p={}", res.probability);
}
#[test]
fn recovers_arbitrary_string() {
let res =
bernstein_vazirani(0b1011, 4).expect("secret=0b1011 is within range for 4 qubits");
assert_eq!(res.hidden_string, 0b1011);
assert!(res.probability > 0.999, "p={}", res.probability);
}
#[test]
fn recovers_every_string_for_n3() {
for s in 0..8usize {
let res = bernstein_vazirani(s, 3).expect("s is in 0..8, within range for 3 qubits");
assert_eq!(res.hidden_string, s, "failed for s={s}");
assert!(res.probability > 0.999, "s={s} p={}", res.probability);
}
}
#[test]
fn single_qubit_recovers_bit() {
let r0 = bernstein_vazirani(0, 1).expect("secret=0 is within range for 1 qubit");
assert_eq!(r0.hidden_string, 0);
let r1 = bernstein_vazirani(1, 1).expect("secret=1 is within range for 1 qubit");
assert_eq!(r1.hidden_string, 1);
}
#[test]
fn probability_concentrates() {
let res =
bernstein_vazirani(0b101010, 6).expect("secret=0b101010 is within range for 6 qubits");
assert_eq!(res.hidden_string, 0b101010);
assert!(res.probability > 0.999, "p={}", res.probability);
}
#[test]
fn n_qubits_zero_errors() {
assert!(bernstein_vazirani(0, 0).is_err());
}
#[test]
fn secret_out_of_range_errors() {
assert!(bernstein_vazirani(8, 3).is_err());
}
#[test]
fn probability_finite_and_in_range() {
let res = bernstein_vazirani(5, 4).expect("secret=5 is within range for 4 qubits");
assert!(res.probability.is_finite());
assert!((0.0..=1.0 + 1e-5).contains(&res.probability));
}
#[test]
fn deterministic() {
let a = bernstein_vazirani(9, 4).expect("secret=9 is within range for 4 qubits");
let b = bernstein_vazirani(9, 4)
.expect("secret=9 is within range for 4 qubits, second call for determinism check");
assert_eq!(a.hidden_string, b.hidden_string);
assert!((a.probability - b.probability).abs() < 1e-7);
}
}