use crate::error::{QuantumError, QuantumResult};
use crate::gates::hadamard::gate_h;
use crate::gates::parametric::gate_phase;
use crate::handle::LcgRng;
use crate::statevec::apply_1q::apply_1q_inplace;
use crate::statevec::state::StateVector;
use num_complex::Complex;
type Complex32 = Complex<f32>;
type Complex64 = Complex<f64>;
const TWO_PI: f64 = std::f64::consts::TAU;
#[derive(Debug, Clone)]
pub struct IterativeQpeResult {
pub phase: f64,
pub integer: usize,
pub bits: Vec<u8>,
pub rounds: usize,
}
fn diagonal_eigenphase(u: &[[Complex32; 2]; 2], eig_index: usize) -> Complex64 {
let z = u[eig_index][eig_index];
Complex64::new(z.re as f64, z.im as f64)
}
fn validate_eigenstate(u: &[[Complex32; 2]; 2], eig_index: usize) -> QuantumResult<()> {
if eig_index > 1 {
return Err(QuantumError::InvalidParameter {
name: format!("eig_index={eig_index} must be 0 or 1"),
});
}
let other = 1 - eig_index;
let leak = u[other][eig_index].norm();
if leak > 1e-4 {
return Err(QuantumError::InvalidParameter {
name: format!("basis state |{eig_index}⟩ is not an eigenvector of U (leakage {leak})"),
});
}
let lam = u[eig_index][eig_index];
if (lam.norm() - 1.0).abs() > 1e-3 {
return Err(QuantumError::InvalidParameter {
name: format!("eigenvalue modulus {} must be 1", lam.norm()),
});
}
Ok(())
}
fn run_round(
eig_phase_2pi: f64,
power: u32,
feedback_angle_2pi: f64,
rng: &mut LcgRng,
) -> QuantumResult<u8> {
let mut anc = StateVector::new_zero_state(1)?;
apply_1q_inplace(&mut anc, 0, &gate_h())?;
let two_pow = (1u64 << power) as f64;
let kick = two_pow * eig_phase_2pi;
let net = kick - feedback_angle_2pi;
apply_1q_inplace(&mut anc, 0, &gate_phase(net as f32))?;
apply_1q_inplace(&mut anc, 0, &gate_h())?;
let p1 = anc.measure_prob(0, true)?;
let _draw = rng.next_f32();
Ok(u8::from(p1 >= 0.5))
}
pub fn iterative_phase_estimation(
u: &[[Complex32; 2]; 2],
eig_index: usize,
n_bits: usize,
seed: u64,
) -> QuantumResult<IterativeQpeResult> {
if n_bits == 0 || n_bits > 53 {
return Err(QuantumError::InvalidParameter {
name: format!("n_bits={n_bits} must be in 1..=53"),
});
}
validate_eigenstate(u, eig_index)?;
let eig_phase = diagonal_eigenphase(u, eig_index);
let mut arg = eig_phase.arg();
if arg < 0.0 {
arg += TWO_PI;
}
let eig_phase_2pi = arg;
let mut rng = LcgRng::new(seed);
let mut bits = vec![0u8; n_bits];
for b in 0..n_bits {
let power = (n_bits - 1 - b) as u32;
let mut omega = 0.0_f64;
for (j, &lower_bit) in bits.iter().enumerate().take(b) {
omega += lower_bit as f64 * 2.0_f64.powi(j as i32 - b as i32 - 1);
}
let feedback = TWO_PI * omega;
let bit = run_round(eig_phase_2pi, power, feedback, &mut rng)?;
bits[b] = bit;
}
let mut integer = 0usize;
let mut phase = 0.0_f64;
for (b, &bit) in bits.iter().enumerate() {
if bit == 1 {
integer |= 1usize << b;
phase += 2.0_f64.powi(b as i32 - n_bits as i32);
}
}
Ok(IterativeQpeResult {
phase,
integer,
bits,
rounds: n_bits,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::fourier::phase_estimation;
use crate::gates::hadamard::{gate_s, gate_t};
use crate::gates::parametric::gate_phase as phase_gate;
use crate::gates::pauli::{gate_i, gate_x, gate_z};
use std::f32::consts::PI;
#[test]
fn recovers_exact_dyadic_phases() {
let u = phase_gate(2.0 * PI * 0.25); let res = iterative_phase_estimation(&u, 1, 2, 7)
.expect("phase gate φ=1/4 is a valid 2×2 unitary with |1⟩ as eigenvector");
assert_eq!(res.bits, vec![1, 0], "bits={:?}", res.bits);
assert_eq!(res.integer, 1, "integer={}", res.integer);
assert!((res.phase - 0.25).abs() < 1e-12, "phase={}", res.phase);
let u = phase_gate(2.0 * PI * 0.125);
let res = iterative_phase_estimation(&u, 1, 3, 7)
.expect("phase gate φ=1/8 is a valid 2×2 unitary with |1⟩ as eigenvector");
assert_eq!(res.bits, vec![1, 0, 0], "bits={:?}", res.bits);
assert_eq!(res.integer, 1, "integer={}", res.integer);
assert!((res.phase - 0.125).abs() < 1e-12, "phase={}", res.phase);
let u = phase_gate(2.0 * PI * 0.375);
let res = iterative_phase_estimation(&u, 1, 3, 7)
.expect("phase gate φ=3/8 is a valid 2×2 unitary with |1⟩ as eigenvector");
assert_eq!(res.bits, vec![1, 1, 0], "bits={:?}", res.bits);
assert_eq!(res.integer, 3, "integer={}", res.integer);
assert!((res.phase - 0.375).abs() < 1e-12, "phase={}", res.phase);
let u = phase_gate(2.0 * PI * 0.625);
let res = iterative_phase_estimation(&u, 1, 3, 7)
.expect("phase gate φ=5/8 is a valid 2×2 unitary with |1⟩ as eigenvector");
assert_eq!(res.bits, vec![1, 0, 1], "bits={:?}", res.bits);
assert_eq!(res.integer, 5, "integer={}", res.integer);
assert!((res.phase - 0.625).abs() < 1e-12, "phase={}", res.phase);
}
#[test]
fn matches_register_qpe() {
let cases: &[(f32, usize)] = &[
(0.25, 2),
(0.125, 3),
(0.375, 3),
(0.625, 3),
(0.4375, 4), ];
for &(phi, n) in cases {
let u = phase_gate(2.0 * PI * phi);
let iter_res = iterative_phase_estimation(&u, 1, n, 11)
.expect("iterative QPE with exact dyadic phase and valid eig_index should succeed");
let mut sv = StateVector::new_zero_state(n + 1)
.expect("n+1 is a valid qubit count for state vector allocation");
apply_1q_inplace(&mut sv, n, &gate_x())
.expect("qubit n exists in the (n+1)-qubit register, gate_x application is valid");
let count: Vec<usize> = (0..n).collect();
let reg_res = phase_estimation(&mut sv, &count, n, &u).expect(
"register QPE with valid qubit mapping and exact dyadic phase should succeed",
);
assert_eq!(
iter_res.integer, reg_res.integer,
"phi={phi}: iter int {} vs reg int {}",
iter_res.integer, reg_res.integer
);
assert!(
(iter_res.phase - reg_res.phase as f64).abs() < 1e-6,
"phi={phi}: iter phase {} vs reg {}",
iter_res.phase,
reg_res.phase
);
}
}
#[test]
fn uses_single_ancilla_qubit() {
let anc = StateVector::new_zero_state(1)
.expect("single-qubit zero state allocation is always valid");
assert_eq!(anc.n_qubits, 1, "working register must be a single qubit");
assert_eq!(anc.amps.len(), 2);
let u = phase_gate(2.0 * PI * 0.4375);
let res = iterative_phase_estimation(&u, 1, 4, 3)
.expect("phase gate φ=7/16 is a valid unitary with |1⟩ as eigenvector");
assert_eq!(res.rounds, 4);
assert_eq!(res.bits.len(), 4);
}
#[test]
fn feedback_correction_is_required() {
let phi = 0.375_f64;
let eig_phase_2pi = TWO_PI * phi;
let n = 3usize;
let mut rng = LcgRng::new(5);
let mut wrong_bits = vec![0u8; n];
for (b, slot) in wrong_bits.iter_mut().enumerate() {
let power = (n - 1 - b) as u32;
*slot = run_round(eig_phase_2pi, power, 0.0, &mut rng)
.expect("run_round with valid phase and power should always succeed");
}
let correct = iterative_phase_estimation(&phase_gate(2.0 * PI * phi as f32), 1, n, 5)
.expect("phase gate φ=3/8 is a valid unitary, iterative QPE should succeed");
assert_ne!(
wrong_bits, correct.bits,
"feedback-free bits {wrong_bits:?} unexpectedly matched correct {:?}",
correct.bits
);
}
#[test]
fn s_and_t_gate_phases() {
let res_s = iterative_phase_estimation(&gate_s(), 1, 2, 1)
.expect("S gate has unit-modulus eigenvalue on |1⟩, valid eig_index and n_bits");
assert!(
(res_s.phase - 0.25).abs() < 1e-12,
"S phase={}",
res_s.phase
);
assert_eq!(res_s.integer, 1);
let res_t = iterative_phase_estimation(&gate_t(), 1, 3, 1)
.expect("T gate has unit-modulus eigenvalue on |1⟩, valid eig_index and n_bits");
assert!(
(res_t.phase - 0.125).abs() < 1e-12,
"T phase={}",
res_t.phase
);
assert_eq!(res_t.integer, 1);
}
#[test]
fn zero_and_half_edge_cases() {
let res0 = iterative_phase_estimation(&gate_i(), 1, 3, 9)
.expect("identity gate is a valid unitary with |1⟩ as eigenvector");
assert_eq!(res0.bits, vec![0, 0, 0], "bits={:?}", res0.bits);
assert_eq!(res0.integer, 0);
assert!(res0.phase.abs() < 1e-12, "phase={}", res0.phase);
let res_z1 = iterative_phase_estimation(&gate_z(), 1, 1, 9)
.expect("Z gate has eigenvalue -1 on |1⟩ with unit modulus, QPE should succeed");
assert_eq!(res_z1.bits, vec![1], "bits={:?}", res_z1.bits);
assert!((res_z1.phase - 0.5).abs() < 1e-12, "phase={}", res_z1.phase);
let res_z3 = iterative_phase_estimation(&gate_z(), 1, 3, 9)
.expect("Z gate is a valid 2×2 unitary with |1⟩ as eigenvector");
assert_eq!(res_z3.bits, vec![0, 0, 1], "bits={:?}", res_z3.bits);
assert_eq!(res_z3.integer, 4);
assert!((res_z3.phase - 0.5).abs() < 1e-12, "phase={}", res_z3.phase);
}
#[test]
fn rejects_invalid_inputs() {
let u = gate_s();
assert!(iterative_phase_estimation(&u, 1, 0, 0).is_err()); assert!(iterative_phase_estimation(&u, 2, 3, 0).is_err()); assert!(iterative_phase_estimation(&u, 1, 99, 0).is_err()); assert!(iterative_phase_estimation(&gate_h(), 0, 3, 0).is_err());
}
#[test]
fn zero_eigenvector_has_zero_phase() {
let u = gate_phase(2.0 * PI * 0.3);
let res = iterative_phase_estimation(&u, 0, 4, 2)
.expect("P(θ) has |0⟩ as eigenvector with eigenvalue 1, eig_index 0 is valid");
assert_eq!(res.integer, 0, "integer={}", res.integer);
assert!(res.phase.abs() < 1e-12, "phase={}", res.phase);
}
}