#![allow(clippy::doc_markdown)]
use std::f64::consts::TAU;
use crate::circuit::Circuit;
#[must_use]
pub fn qft(n: usize) -> Circuit {
let mut c = Circuit::new(n);
apply_qft_no_swap(&mut c, n);
for i in 0..n / 2 {
c.swap(i, n - 1 - i);
}
c
}
#[must_use]
pub fn iqft(n: usize) -> Circuit {
let mut c = Circuit::new(n);
for i in 0..n / 2 {
c.swap(i, n - 1 - i);
}
apply_iqft_no_swap(&mut c, n);
c
}
fn apply_qft_no_swap(c: &mut Circuit, n: usize) {
for j in (0..n).rev() {
c.h(j);
for k in (0..j).rev() {
let m = (j - k + 1) as u32;
let angle = TAU / f64::from(1u32 << m);
c.controlled(&[k], crate::gate::Gate1::phase(angle), j);
}
}
}
fn apply_iqft_no_swap(c: &mut Circuit, n: usize) {
for j in 0..n {
for k in 0..j {
let m = (j - k + 1) as u32;
let angle = -TAU / f64::from(1u32 << m);
c.controlled(&[k], crate::gate::Gate1::phase(angle), j);
}
c.h(j);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::StateVectorBackend;
use crate::complex::Complex64;
use crate::state::State;
fn run(c: &Circuit) -> State {
StateVectorBackend::run(c).unwrap().into_state()
}
#[test]
fn qft_of_zero_is_uniform() {
for n in 1..=5 {
let state = run(&qft(n));
let expected = 1.0 / f64::from(1u32 << n);
for basis in 0..(1usize << n) {
assert!(
(state.probability(basis) - expected).abs() < 1e-12,
"n={n} basis={basis}"
);
}
}
}
#[test]
fn qft_then_iqft_is_identity() {
let n = 3;
for input_basis in [0, 1, 3, 5, 7] {
let mut amps = vec![Complex64::ZERO; 1 << n];
amps[input_basis] = Complex64::ONE;
let input = State::from_amplitudes(amps.clone()).unwrap();
let mut c = Circuit::new(n);
for bit in 0..n {
if input_basis & (1 << bit) != 0 {
c.x(bit);
}
}
c.compose(&qft(n)).compose(&iqft(n));
let output = run(&c);
assert!(output.fidelity(&input) > 1.0 - 1e-12, "basis={input_basis}");
}
}
}