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, Copy, PartialEq, Eq)]
pub enum FunctionKind {
Constant,
Balanced,
}
#[derive(Debug, Clone)]
pub struct DeutschJozsaResult {
pub kind: FunctionKind,
pub all_zero_probability: f32,
}
pub fn deutsch_jozsa<F>(oracle: F, n_qubits: usize) -> QuantumResult<DeutschJozsaResult>
where
F: Fn(usize) -> bool,
{
if n_qubits == 0 || n_qubits > 30 {
return Err(QuantumError::InvalidQubitCount { n: n_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 oracle(x) {
*a = -*a;
}
}
for q in 0..n_qubits {
apply_1q_inplace(&mut sv, q, &gate_h())?;
}
let all_zero_probability = sv.amps[0].norm_sqr();
let kind = if all_zero_probability > 0.5 {
FunctionKind::Constant
} else {
FunctionKind::Balanced
};
Ok(DeutschJozsaResult {
kind,
all_zero_probability,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn constant_zero_is_constant() {
let res = deutsch_jozsa(|_| false, 3)
.expect("n=3 is within [1, 30] and constant-zero oracle is well-defined");
assert_eq!(res.kind, FunctionKind::Constant);
assert!(
(res.all_zero_probability - 1.0).abs() < 1e-5,
"p={}",
res.all_zero_probability
);
}
#[test]
fn constant_one_is_constant() {
let res = deutsch_jozsa(|_| true, 4)
.expect("n=4 is within [1, 30] and constant-one oracle is well-defined");
assert_eq!(res.kind, FunctionKind::Constant);
assert!(
(res.all_zero_probability - 1.0).abs() < 1e-5,
"p={}",
res.all_zero_probability
);
}
#[test]
fn parity_function_is_balanced() {
let res = deutsch_jozsa(|x| (x.count_ones() & 1) == 1, 3)
.expect("n=3 is within [1, 30] and parity is a well-defined boolean function");
assert_eq!(res.kind, FunctionKind::Balanced);
assert!(
res.all_zero_probability < 1e-5,
"p={}",
res.all_zero_probability
);
}
#[test]
fn single_bit_balanced() {
let res = deutsch_jozsa(|x| (x & 1) == 1, 3)
.expect("n=3 is within [1, 30] and the LSB oracle is well-defined");
assert_eq!(res.kind, FunctionKind::Balanced);
assert!(
res.all_zero_probability < 1e-5,
"p={}",
res.all_zero_probability
);
}
#[test]
fn balanced_half_outputs_one() {
let res = deutsch_jozsa(|x| x >= 4, 3)
.expect("n=3 is within [1, 30] and the threshold oracle is well-defined");
assert_eq!(res.kind, FunctionKind::Balanced);
}
#[test]
fn single_qubit_constant() {
let res = deutsch_jozsa(|_| false, 1)
.expect("n=1 is within [1, 30] and the constant-zero oracle is well-defined");
assert_eq!(res.kind, FunctionKind::Constant);
assert!((res.all_zero_probability - 1.0).abs() < 1e-5);
}
#[test]
fn single_qubit_balanced() {
let res = deutsch_jozsa(|x| x == 1, 1)
.expect("n=1 is within [1, 30] and the identity oracle is well-defined");
assert_eq!(res.kind, FunctionKind::Balanced);
assert!(res.all_zero_probability < 1e-5);
}
#[test]
fn n_qubits_zero_errors() {
assert!(deutsch_jozsa(|_| false, 0).is_err());
}
#[test]
fn probability_in_range_and_finite() {
let res = deutsch_jozsa(|x| (x & 2) != 0, 4)
.expect("n=4 is within [1, 30] and the bit-1 mask oracle is well-defined");
assert!(res.all_zero_probability.is_finite());
assert!(
(0.0..=1.0 + 1e-5).contains(&res.all_zero_probability),
"p={}",
res.all_zero_probability
);
}
#[test]
fn deterministic() {
let a = deutsch_jozsa(|x| (x & 1) == 1, 4)
.expect("n=4 is within [1, 30] and the LSB oracle is well-defined");
let b = deutsch_jozsa(|x| (x & 1) == 1, 4)
.expect("n=4 is within [1, 30] and the LSB oracle is well-defined");
assert_eq!(a.kind, b.kind);
assert!((a.all_zero_probability - b.all_zero_probability).abs() < 1e-7);
}
}