use crate::error::{QuantumError, QuantumResult};
use crate::fourier::qft::qft_inverse_inplace;
use crate::gates::hadamard::gate_h;
use crate::statevec::apply_1q::{apply_1q_controlled, apply_1q_inplace};
use crate::statevec::state::StateVector;
use num_complex::Complex;
type Complex32 = Complex<f32>;
#[derive(Debug, Clone)]
pub struct PhaseEstimationResult {
pub phase: f32,
pub integer: usize,
pub probability: f32,
pub counts_argmax: usize,
}
fn mat2_mul(a: &[[Complex32; 2]; 2], b: &[[Complex32; 2]; 2]) -> [[Complex32; 2]; 2] {
[
[
a[0][0] * b[0][0] + a[0][1] * b[1][0],
a[0][0] * b[0][1] + a[0][1] * b[1][1],
],
[
a[1][0] * b[0][0] + a[1][1] * b[1][0],
a[1][0] * b[0][1] + a[1][1] * b[1][1],
],
]
}
fn validate(count_qubits: &[usize], target_qubit: usize, n_qubits: usize) -> QuantumResult<()> {
if count_qubits.is_empty() {
return Err(QuantumError::EmptyInput);
}
if target_qubit >= n_qubits {
return Err(QuantumError::QubitIndexOutOfRange {
index: target_qubit,
n_qubits,
});
}
for (pos, &q) in count_qubits.iter().enumerate() {
if q >= n_qubits {
return Err(QuantumError::QubitIndexOutOfRange { index: q, n_qubits });
}
if q == target_qubit {
return Err(QuantumError::InvalidParameter {
name: format!("counting qubit {q} collides with target"),
});
}
if count_qubits[pos + 1..].contains(&q) {
return Err(QuantumError::InvalidParameter {
name: format!("duplicate counting qubit index {q}"),
});
}
}
Ok(())
}
pub fn phase_estimation(
sv: &mut StateVector,
count_qubits: &[usize],
target_qubit: usize,
u: &[[Complex32; 2]; 2],
) -> QuantumResult<PhaseEstimationResult> {
validate(count_qubits, target_qubit, sv.n_qubits)?;
let n = count_qubits.len();
for &q in count_qubits {
apply_1q_inplace(sv, q, &gate_h())?;
}
let mut u_pow = *u;
for &q in count_qubits.iter().take(n) {
apply_1q_controlled(sv, q, target_qubit, &u_pow)?;
u_pow = mat2_mul(&u_pow, &u_pow);
}
qft_inverse_inplace(sv, count_qubits)?;
let dim = sv.amps.len();
let n_outcomes = 1usize << n;
let mut probs = vec![0.0_f32; n_outcomes];
for (i, a) in sv.amps.iter().enumerate().take(dim) {
let mut c = 0usize;
for (k, &q) in count_qubits.iter().enumerate() {
c |= ((i >> q) & 1) << k;
}
probs[c] += a.norm_sqr();
}
let mut integer = 0usize;
let mut best = f32::NEG_INFINITY;
for (c, &p) in probs.iter().enumerate() {
if p > best {
best = p;
integer = c;
}
}
let phase = integer as f32 / 2f32.powi(n as i32);
Ok(PhaseEstimationResult {
phase,
integer,
probability: best,
counts_argmax: integer,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::gates::hadamard::{gate_s, gate_t};
use crate::gates::parametric::{gate_phase, gate_rz};
use crate::gates::pauli::{gate_i, gate_x};
use std::f32::consts::PI;
fn prepared_state(n_count: usize, target: usize) -> StateVector {
let mut sv = StateVector::new_zero_state(n_count + 1)
.expect("valid qubit count for QPE test register");
apply_1q_inplace(&mut sv, target, &gate_x()).expect("X gate applied to valid target qubit");
sv
}
#[test]
fn qpe_phase_one_quarter() {
let mut sv = prepared_state(2, 2);
let res = phase_estimation(&mut sv, &[0, 1], 2, &gate_s()).expect("phase estimation with valid non-overlapping count_qubits [0,1] and target 2 on a 3-qubit state should succeed");
assert_eq!(res.integer, 1, "integer={}", res.integer);
assert!((res.phase - 0.25).abs() < 1e-6, "phase={}", res.phase);
assert!(res.probability > 0.999, "prob={}", res.probability);
assert_eq!(res.counts_argmax, res.integer);
}
#[test]
fn qpe_phase_one_eighth() {
let mut sv = prepared_state(3, 3);
let res = phase_estimation(&mut sv, &[0, 1, 2], 3, &gate_t()).expect("phase estimation with valid non-overlapping count_qubits [0,1,2] and target 3 on a 4-qubit state should succeed");
assert_eq!(res.integer, 1, "integer={}", res.integer);
assert!((res.phase - 0.125).abs() < 1e-6, "phase={}", res.phase);
assert!(res.probability > 0.999, "prob={}", res.probability);
}
#[test]
fn qpe_phase_three_eighths() {
let mut sv = prepared_state(3, 3);
let u = gate_phase(3.0 * PI / 4.0);
let res = phase_estimation(&mut sv, &[0, 1, 2], 3, &u).expect("phase estimation with valid non-overlapping count_qubits [0,1,2] and target 3 on a 4-qubit state should succeed");
assert_eq!(res.integer, 3, "integer={}", res.integer);
assert!((res.phase - 0.375).abs() < 1e-6, "phase={}", res.phase);
assert!(res.probability > 0.999, "prob={}", res.probability);
}
#[test]
fn qpe_identity_phase_zero() {
let mut sv = prepared_state(2, 2);
let res =
phase_estimation(&mut sv, &[0, 1], 2, &gate_i()).expect("value should be present");
assert_eq!(res.integer, 0, "integer={}", res.integer);
assert!(res.phase.abs() < 1e-6, "phase={}", res.phase);
assert!(
(res.probability - 1.0).abs() < 1e-4,
"prob={}",
res.probability
);
}
#[test]
fn qpe_validation_rejects_bad_inputs() {
let mut sv = prepared_state(2, 2);
assert!(phase_estimation(&mut sv, &[], 2, &gate_s()).is_err());
assert!(phase_estimation(&mut sv, &[0, 1], 1, &gate_s()).is_err());
assert!(phase_estimation(&mut sv, &[0, 9], 2, &gate_s()).is_err());
assert!(phase_estimation(&mut sv, &[0, 1], 9, &gate_s()).is_err());
}
#[test]
fn qpe_rz_eigenphase() {
let mut sv = prepared_state(2, 2);
let res =
phase_estimation(&mut sv, &[0, 1], 2, &gate_rz(PI)).expect("value should be present");
assert_eq!(res.integer, 1, "integer={}", res.integer);
assert!((res.phase - 0.25).abs() < 1e-6, "phase={}", res.phase);
}
}