use rand::Rng;
use rand::SeedableRng;
use rand_chacha::ChaCha8Rng;
use crate::circuit::Circuit;
use crate::gates::Gate;
pub fn qft_circuit(n: usize) -> Circuit {
let mut c = Circuit::new(n, 0);
for i in 0..n {
c.add_gate(Gate::H, &[i]);
for j in (i + 1)..n {
let theta = std::f64::consts::TAU / (1u64 << (j - i)) as f64;
c.add_gate(Gate::cphase(theta), &[i, j]);
}
}
for i in 0..n / 2 {
c.add_gate(Gate::Swap, &[i, n - 1 - i]);
}
c
}
pub fn random_circuit(n: usize, depth: usize, seed: u64) -> Circuit {
let mut rng = ChaCha8Rng::seed_from_u64(seed);
let mut c = Circuit::new(n, 0);
let singles = [Gate::H, Gate::X, Gate::Y, Gate::Z, Gate::S, Gate::T];
for layer in 0..depth {
for q in 0..n {
c.add_gate(singles[rng.random_range(0..singles.len())].clone(), &[q]);
}
let offset = layer % 2;
for q in (offset..n - 1).step_by(2) {
if rng.random_bool(0.5) {
c.add_gate(Gate::Cx, &[q, q + 1]);
}
}
}
c
}
pub fn hardware_efficient_ansatz(n: usize, layers: usize, seed: u64) -> Circuit {
let mut rng = ChaCha8Rng::seed_from_u64(seed);
let mut c = Circuit::new(n, 0);
for _ in 0..layers {
for q in 0..n {
c.add_gate(Gate::Ry(rng.random::<f64>() * std::f64::consts::TAU), &[q]);
c.add_gate(Gate::Rz(rng.random::<f64>() * std::f64::consts::TAU), &[q]);
}
for q in 0..n - 1 {
c.add_gate(Gate::Cx, &[q, q + 1]);
}
}
c
}
pub fn clifford_heavy_circuit(n: usize, depth: usize, seed: u64) -> Circuit {
let mut rng = ChaCha8Rng::seed_from_u64(seed);
let mut c = Circuit::new(n, 0);
let cliffords = [Gate::H, Gate::S, Gate::X, Gate::Y, Gate::Z];
for layer in 0..depth {
for q in 0..n {
c.add_gate(
cliffords[rng.random_range(0..cliffords.len())].clone(),
&[q],
);
}
let offset = layer % 2;
for q in (offset..n - 1).step_by(2) {
c.add_gate(Gate::Cx, &[q, q + 1]);
}
}
c
}
pub fn clifford_random_pairs(n: usize, depth: usize, seed: u64) -> Circuit {
let mut rng = ChaCha8Rng::seed_from_u64(seed);
let mut c = Circuit::new(n, 0);
let cliffords = [Gate::H, Gate::S, Gate::X, Gate::Y, Gate::Z];
for _ in 0..depth {
for q in 0..n {
c.add_gate(
cliffords[rng.random_range(0..cliffords.len())].clone(),
&[q],
);
}
let num_pairs = n / 2;
let mut available: Vec<usize> = (0..n).collect();
for _ in 0..num_pairs {
if available.len() < 2 {
break;
}
let i = rng.random_range(0..available.len());
let q0 = available.swap_remove(i);
let j = rng.random_range(0..available.len());
let q1 = available.swap_remove(j);
c.add_gate(Gate::Cx, &[q0, q1]);
}
}
c
}
pub fn independent_bell_pairs(n_pairs: usize) -> Circuit {
let n = n_pairs * 2;
let mut c = Circuit::new(n, 0);
for i in 0..n_pairs {
c.add_gate(Gate::H, &[2 * i]);
c.add_gate(Gate::Cx, &[2 * i, 2 * i + 1]);
}
c
}
pub fn independent_random_blocks(
num_blocks: usize,
block_size: usize,
depth: usize,
seed: u64,
) -> Circuit {
let n = num_blocks * block_size;
let mut rng = ChaCha8Rng::seed_from_u64(seed);
let mut c = Circuit::new(n, 0);
let singles = [Gate::H, Gate::X, Gate::Y, Gate::Z, Gate::S, Gate::T];
for block in 0..num_blocks {
let base = block * block_size;
for layer in 0..depth {
for q in 0..block_size {
c.add_gate(
singles[rng.random_range(0..singles.len())].clone(),
&[base + q],
);
}
if block_size >= 2 {
let offset = layer % 2;
for q in (offset..block_size - 1).step_by(2) {
if rng.random_bool(0.5) {
c.add_gate(Gate::Cx, &[base + q, base + q + 1]);
}
}
}
}
}
c
}
pub fn ghz_circuit(n: usize) -> Circuit {
let mut c = Circuit::new(n, 0);
c.add_gate(Gate::H, &[0]);
for q in 0..n - 1 {
c.add_gate(Gate::Cx, &[q, q + 1]);
}
c
}
pub fn qaoa_circuit(n: usize, layers: usize, seed: u64) -> Circuit {
let mut rng = ChaCha8Rng::seed_from_u64(seed);
let mut c = Circuit::new(n, 0);
for _ in 0..layers {
for q in 0..n - 1 {
let gamma: f64 = rng.random::<f64>() * std::f64::consts::TAU;
c.add_gate(Gate::Rzz(gamma), &[q, q + 1]);
}
for q in 0..n {
let beta: f64 = rng.random::<f64>() * std::f64::consts::TAU;
c.add_gate(Gate::Rx(beta), &[q]);
}
}
c
}
pub fn single_qubit_rotation_circuit(n: usize, depth: usize, seed: u64) -> Circuit {
let mut rng = ChaCha8Rng::seed_from_u64(seed);
let mut c = Circuit::new(n, 0);
for _ in 0..depth {
for q in 0..n {
let choice: usize = rng.random_range(0..3);
let angle: f64 = rng.random::<f64>() * std::f64::consts::TAU;
match choice {
0 => c.add_gate(Gate::Rx(angle), &[q]),
1 => c.add_gate(Gate::Ry(angle), &[q]),
_ => c.add_gate(Gate::Rz(angle), &[q]),
}
}
}
c
}
pub fn clifford_t_circuit(n: usize, depth: usize, t_fraction: f64, seed: u64) -> Circuit {
let mut rng = ChaCha8Rng::seed_from_u64(seed);
let mut c = Circuit::new(n, 0);
let cliffords = [Gate::H, Gate::S, Gate::X, Gate::Y, Gate::Z];
for layer in 0..depth {
for q in 0..n {
if rng.random::<f64>() < t_fraction {
if rng.random_bool(0.5) {
c.add_gate(Gate::T, &[q]);
} else {
c.add_gate(Gate::Tdg, &[q]);
}
} else {
c.add_gate(
cliffords[rng.random_range(0..cliffords.len())].clone(),
&[q],
);
}
}
let offset = layer % 2;
for q in (offset..n.saturating_sub(1)).step_by(2) {
c.add_gate(Gate::Cx, &[q, q + 1]);
}
}
c
}
pub fn w_state_circuit(n: usize) -> Circuit {
let mut c = Circuit::new(n, 0);
c.add_gate(Gate::X, &[0]);
for i in 0..n - 1 {
let remaining = (n - i) as f64;
let theta = 2.0 * (1.0 / remaining).sqrt().acos();
c.add_gate(Gate::Ry(theta), &[i + 1]);
c.add_gate(Gate::Cx, &[i + 1, i]);
c.add_gate(Gate::Ry(-theta), &[i + 1]);
c.add_gate(Gate::Cx, &[i, i + 1]);
}
c
}
pub fn quantum_volume_circuit(n: usize, depth: usize, seed: u64) -> Circuit {
let mut rng = ChaCha8Rng::seed_from_u64(seed);
let mut c = Circuit::new(n, 0);
let n_pairs = n / 2;
for _ in 0..depth {
let mut perm: Vec<usize> = (0..n).collect();
for i in (1..n).rev() {
let j = rng.random_range(0..=i);
perm.swap(i, j);
}
for p in 0..n_pairs {
let q0 = perm[2 * p];
let q1 = perm[2 * p + 1];
let a0: f64 = rng.random::<f64>() * std::f64::consts::TAU;
let a1: f64 = rng.random::<f64>() * std::f64::consts::TAU;
let a2: f64 = rng.random::<f64>() * std::f64::consts::TAU;
c.add_gate(Gate::Ry(a0), &[q0]);
c.add_gate(Gate::Rz(a1), &[q0]);
c.add_gate(Gate::Ry(a2), &[q1]);
c.add_gate(Gate::Cx, &[q0, q1]);
let b0: f64 = rng.random::<f64>() * std::f64::consts::TAU;
let b1: f64 = rng.random::<f64>() * std::f64::consts::TAU;
let b2: f64 = rng.random::<f64>() * std::f64::consts::TAU;
c.add_gate(Gate::Ry(b0), &[q0]);
c.add_gate(Gate::Rz(b1), &[q1]);
c.add_gate(Gate::Cx, &[q0, q1]);
c.add_gate(Gate::Ry(b2), &[q0]);
}
}
c
}
pub fn local_clifford_blocks(
num_blocks: usize,
block_size: usize,
depth: usize,
seed: u64,
) -> Circuit {
let n = num_blocks * block_size;
let mut rng = ChaCha8Rng::seed_from_u64(seed);
let mut c = Circuit::new(n, 0);
let cliffords = [Gate::H, Gate::S, Gate::X, Gate::Y, Gate::Z];
for block in 0..num_blocks {
let base = block * block_size;
for layer in 0..depth {
for q in 0..block_size {
c.add_gate(
cliffords[rng.random_range(0..cliffords.len())].clone(),
&[base + q],
);
}
if block_size >= 2 {
let offset = layer % 2;
for q in (offset..block_size - 1).step_by(2) {
c.add_gate(Gate::Cx, &[base + q, base + q + 1]);
}
}
}
}
c
}
pub fn cz_chain_circuit(n: usize, depth: usize, seed: u64) -> Circuit {
let mut rng = ChaCha8Rng::seed_from_u64(seed);
let mut c = Circuit::new(n, 0);
let singles = [Gate::H, Gate::S, Gate::T, Gate::X];
for layer in 0..depth {
for q in 0..n {
c.add_gate(singles[rng.random_range(0..singles.len())].clone(), &[q]);
}
let offset = layer % 2;
for q in (offset..n.saturating_sub(1)).step_by(2) {
c.add_gate(Gate::Cz, &[q, q + 1]);
}
}
c
}
pub(crate) fn apply_inverse_qft(c: &mut Circuit, start: usize, n: usize) {
for i in 0..n / 2 {
c.add_gate(Gate::Swap, &[start + i, start + n - 1 - i]);
}
for i in (0..n).rev() {
for j in ((i + 1)..n).rev() {
let theta = std::f64::consts::TAU / (1u64 << (j - i)) as f64;
c.add_gate(Gate::cphase(-theta), &[start + i, start + j]);
}
c.add_gate(Gate::H, &[start + i]);
}
}
pub fn phase_estimation_circuit(n: usize) -> Circuit {
let n_counting = n - 1;
let target = n_counting;
let mut c = Circuit::new(n, 0);
c.add_gate(Gate::X, &[target]);
for i in 0..n_counting {
c.add_gate(Gate::H, &[i]);
}
for k in 0..n_counting {
let theta = std::f64::consts::FRAC_PI_4 * (1u64 << k) as f64;
c.add_gate(Gate::cphase(theta), &[k, target]);
}
apply_inverse_qft(&mut c, 0, n_counting);
c
}