use std::{f64::consts::FRAC_PI_4, f64::consts::PI};
use itertools::chain;
use crate::{
decompose::{util::matrix_dot, x::c2to4x::cnot},
ir::gate::{DecomposedGate, Param, QuantumGate},
matrix::Matrix,
};
use super::{u2::cu2, util::zyz, x, AuxMode, DepthMode};
fn c2xp(a: usize, b: usize, c: usize) -> Vec<DecomposedGate> {
vec![
(QuantumGate::Hadamard, b).into(),
(QuantumGate::td(), b).into(),
cnot(a, b),
(QuantumGate::t(), b).into(),
cnot(c, b),
(QuantumGate::td(), b).into(),
cnot(a, b),
(QuantumGate::t(), b).into(),
(QuantumGate::Hadamard, b).into(),
]
}
fn c2iz(a: usize, b: usize, c: usize) -> Vec<DecomposedGate> {
vec![
(QuantumGate::td(), c).into(),
cnot(a, c),
(QuantumGate::t(), c).into(),
cnot(b, c),
(QuantumGate::td(), c).into(),
cnot(a, c),
(QuantumGate::t(), c).into(),
cnot(b, c),
]
}
pub fn cz_ap(control: &[usize], aux_qubits: &[usize], target: usize) -> Vec<DecomposedGate> {
let n = control.len();
let a = aux_qubits.len();
if n == 1 {
vec![
(QuantumGate::Hadamard, target).into(),
cnot(control[0], target),
(QuantumGate::Hadamard, target).into(),
]
} else if n == 2 {
c2iz(control[0], control[1], target)
} else {
chain![
c2xp(
*control.last().unwrap(),
*aux_qubits.last().unwrap(),
target,
),
cz_ap(
&control[..n - 1],
&aux_qubits[..a - 1],
*aux_qubits.last().unwrap(),
),
c2xp(
*control.last().unwrap(),
*aux_qubits.last().unwrap(),
target,
)
]
.collect()
}
}
pub(super) fn linear(gate: QuantumGate, control: &[usize], target: usize) -> Vec<DecomposedGate> {
let (a4, theta) = match gate {
QuantumGate::RotationX(theta) => (vec![], theta.value()),
QuantumGate::RotationY(theta) => {
(vec![QuantumGate::Hadamard, QuantumGate::s()], theta.value())
}
QuantumGate::RotationZ(theta) => (vec![QuantumGate::Hadamard], theta.value()),
QuantumGate::Hadamard => (vec![QuantumGate::RotationY(Param::Value(FRAC_PI_4))], PI),
_ => panic!("Not an SU2 gate"),
};
let a2 = QuantumGate::RotationX(Param::Value(-theta / 4.0));
let a3 = a2.inverse();
let a1: Vec<_> = chain![
[a3],
a4.iter()
.rev()
.map(super::super::ir::gate::QuantumGate::inverse)
]
.collect();
let n = control.len() / 2;
let c1 = &control[..n];
let c2 = &control[n..];
let cz_ap_c1_c2 = cz_ap(c1, c2, target);
let cz_ap_c2_c1 = cz_ap(c2, c1, target);
chain![
a4.iter().map(|gate| (*gate, target).into()),
cz_ap_c1_c2.clone(),
[(a2, target).into()],
cz_ap_c2_c1.clone(),
[(a3, target).into()],
cz_ap_c1_c2.iter().rev().map(DecomposedGate::inverse),
[(a2, target).into()],
cz_ap_c2_c1.iter().rev().map(DecomposedGate::inverse),
a1.iter().map(|gate| (*gate, target).into()),
]
.collect()
}
fn log(
matrix: Matrix,
control: &[usize],
target: usize,
approximated: bool,
) -> Vec<DecomposedGate> {
let n = control.len();
let (_, beta, gamma, delta) = zyz(matrix);
let c = QuantumGate::RotationZ(Param::Value((delta - beta) / 2.0)).matrix();
let b = matrix_dot(
&QuantumGate::RotationZ(Param::Value(-(delta + beta) / 2.0)).matrix(),
&QuantumGate::RotationY(Param::Value(-gamma / 2.0)).matrix(),
);
let a = matrix_dot(
&QuantumGate::RotationY(Param::Value(gamma / 2.0)).matrix(),
&QuantumGate::RotationZ(Param::Value(beta)).matrix(),
);
cu2(c, control[n - 1], target)
.into_iter()
.chain(x::single_aux::decompose(
&control[..n - 1],
control[n - 1],
target,
AuxMode::Dirty,
DepthMode::Log,
approximated,
))
.chain(cu2(b, control[n - 1], target))
.chain(x::single_aux::decompose(
&control[..n - 1],
control[n - 1],
target,
AuxMode::Dirty,
DepthMode::Log,
approximated,
))
.chain(cu2(a, control[n - 1], target))
.collect()
}
pub fn decompose(
gate: QuantumGate,
control: &[usize],
target: usize,
depth_mode: DepthMode,
approximated: bool,
) -> Vec<DecomposedGate> {
match depth_mode {
DepthMode::Log => log(gate.su2_matrix(), control, target, approximated),
DepthMode::Linear => linear(gate, control, target),
}
}