use std::f64::consts::{FRAC_PI_2, FRAC_PI_4, PI};
use itertools::chain;
use crate::{
decompose::{su2::cz_ap, x::c2to4x::cnot},
ir::gate::{DecomposedGate, Param, QuantumGate},
matrix::Matrix,
};
use super::util::{exp_gate, zyz};
pub fn cu2(matrix: Matrix, control: usize, target: usize) -> Vec<DecomposedGate> {
let (alpha, beta, gamma, delta) = zyz(matrix);
vec![
(QuantumGate::Phase(Param::Value(alpha)), control).into(),
(
QuantumGate::RotationZ(Param::Value((delta - beta) / 2.0)),
target,
)
.into(),
cnot(control, target),
(
QuantumGate::RotationZ(Param::Value(-(delta + beta) / 2.0)),
target,
)
.into(),
(QuantumGate::RotationY(Param::Value(-gamma / 2.0)), target).into(),
cnot(control, target),
(QuantumGate::RotationY(Param::Value(gamma / 2.0)), target).into(),
(QuantumGate::RotationZ(Param::Value(beta)), target).into(),
]
}
fn mcu2_step(matrix: Matrix, qubits: &[usize], first: bool, inverse: bool) -> Vec<DecomposedGate> {
let mut instructions = Vec::new();
let start = usize::from(inverse);
let mut qubit_pairs: Vec<(usize, usize)> = (0..qubits.len())
.enumerate()
.flat_map(|(i, t)| {
if i > start {
(start..i).map(|c| (c, t)).collect::<Vec<(usize, usize)>>()
} else {
vec![]
}
})
.collect();
qubit_pairs.sort_by_key(|(c, t)| c + t);
if !inverse {
qubit_pairs.reverse();
}
for (control, target) in qubit_pairs {
let exponent = i32::try_from(target - control).unwrap();
let exponent = if control == 0 { exponent - 1 } else { exponent };
let param = 2.0_f64.powi(exponent);
let signal = control == 0 && !first;
let signal = signal ^ inverse;
if target == qubits.len() - 1 && first {
let gate = exp_gate(matrix, 1.0 / param, signal);
instructions.extend(cu2(gate, qubits[control], qubits[target]));
} else {
instructions.extend(cu2(
QuantumGate::RotationX(Param::Value(
std::f64::consts::PI * (if signal { -1.0 } else { 1.0 }) / param,
))
.matrix(),
qubits[control],
qubits[target],
));
}
}
instructions
}
pub fn linear_depth(gate: QuantumGate, control: &[usize], target: usize) -> Vec<DecomposedGate> {
let matrix = gate.matrix();
let mut control_target = control.to_vec();
control_target.push(target);
chain![
mcu2_step(matrix, &control_target, true, false),
mcu2_step(matrix, &control_target, true, true),
mcu2_step(matrix, control, false, false),
mcu2_step(matrix, control, false, true),
]
.collect()
}
fn czz_ap(
control: &[usize],
aux_qubits: &[usize],
target1: usize,
target2: usize,
) -> Vec<DecomposedGate> {
chain![
[cnot(target2, target1)],
cz_ap(control, aux_qubits, target1),
[cnot(target2, target1)],
]
.collect()
}
pub fn su2_rewrite_hadamard(
control: &[usize],
aux_qubit: usize,
target: usize,
) -> Vec<DecomposedGate> {
let a4_1 = QuantumGate::RotationY(Param::Value(FRAC_PI_4));
let a4_2 = QuantumGate::Hadamard;
let theta1 = PI;
let theta2 = -2.0 * FRAC_PI_2;
let a2_1 = QuantumGate::RotationX(Param::Value(-theta1 / 4.0));
let a3_1 = a2_1.inverse();
let a1_1 = [a3_1, a4_1.inverse()];
let a2_2 = QuantumGate::RotationX(Param::Value(-theta2 / 4.0));
let a3_2 = a2_2.inverse();
let a1_2 = [a3_2, a4_2.inverse()];
let n = control.len() / 2;
let c1 = &control[..n];
let c2 = &control[n..];
let czz_c1_c2_ap = czz_ap(c1, c2, target, aux_qubit);
let czz_c2_c1_ap = czz_ap(c2, c1, target, aux_qubit);
chain![
[(a4_1, target).into(), (a4_2, aux_qubit).into()],
czz_c1_c2_ap.clone(),
[(a2_1, target).into(), (a2_2, aux_qubit).into()],
czz_c2_c1_ap.clone(),
[(a3_1, target).into(), (a3_2, aux_qubit).into()],
czz_c1_c2_ap.iter().rev().map(DecomposedGate::inverse),
[(a2_1, target).into(), (a2_2, aux_qubit).into()],
czz_c2_c1_ap.iter().rev().map(DecomposedGate::inverse),
a1_1.iter().map(|gate| (*gate, target).into()),
a1_2.iter().map(|gate| (*gate, aux_qubit).into()),
]
.collect()
}