use crate::UnitaryExpression;
use crate::UnitarySystemExpression;
#[cfg(feature = "python")]
use pyo3_stub_gen::derive::gen_stub_pyfunction;
use qudit_core::QuditSystem;
use qudit_core::Radices;
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
#[cfg_attr(feature = "python", pyo3(signature = (radix = 2)))]
pub fn IGate(radix: usize) -> UnitaryExpression {
let proto = format!("I<{}>()", radix);
let mut body = "".to_string();
body += "[";
for i in 0..radix {
body += "[";
for j in 0..radix {
if i == j {
body += "1,";
} else {
body += "0,";
}
}
body += "],";
}
body += "]";
UnitaryExpression::new(proto + "{" + &body + "}")
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
#[cfg_attr(feature = "python", pyo3(signature = (radix = 2)))]
pub fn HGate(radix: usize) -> UnitaryExpression {
let proto = format!("H<{}>()", radix);
let mut body = "".to_string();
if radix == 2 {
body += "[[sqrt(2)/2, sqrt(2)/2], [sqrt(2)/2, ~sqrt(2)/2]]";
return UnitaryExpression::new(proto + "{" + &body + "}");
}
let invsqrt = format!("1/sqrt({})", radix);
body += invsqrt.as_str();
body += " * ";
body += "[";
for i in 0..radix {
body += "[";
for j in 0..radix {
body += &format!("e^(2*π*i*{}*{}/{}), ", i, j, radix);
}
body += "],";
}
body += "]";
UnitaryExpression::new(proto + "{" + &body + "}")
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
#[cfg_attr(feature = "python", pyo3(signature = (radix = 2)))]
pub fn SwapGate(radix: usize) -> UnitaryExpression {
let proto = format!("Swap<{}, {}>()", radix, radix);
let dim = radix * radix;
let mut body = "".to_string();
body += "[";
for i in 0..dim {
body += "[";
let a_i = i / radix;
let b_i = i % radix;
for j in 0..dim {
let a_j = j / radix;
let b_j = j % radix;
if a_i == b_j && b_i == a_j {
body += "1,";
} else {
body += "0,";
}
}
body += "],";
}
body += "]";
UnitaryExpression::new(proto + "{" + &body + "}")
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
#[cfg_attr(feature = "python", pyo3(signature = (radix = 2)))]
pub fn XGate(radix: usize) -> UnitaryExpression {
let proto = format!("X<{}>()", radix);
let mut body = "[".to_string();
for i in 0..radix {
body += "[";
for j in 0..radix {
if (j + 1) % radix == i {
body += "1, ";
} else {
body += "0, ";
}
}
body += "],";
}
body += "]";
UnitaryExpression::new(proto + "{" + &body + "}")
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
#[cfg_attr(feature = "python", pyo3(signature = (radix = 2)))]
pub fn ZGate(radix: usize) -> UnitaryExpression {
let proto = format!("Z<{}>()", radix);
let mut body = "[".to_string();
for i in 0..radix {
body += "[";
for j in 0..radix {
if i == j {
if i == 0 {
body += "1, ";
} else {
body += &format!("e^((2*{}*π*i)/{}), ", j, radix);
}
} else {
body += "0, ";
}
}
body += "],";
}
body += "]";
UnitaryExpression::new(proto + "{" + &body + "}")
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
#[cfg_attr(feature = "python", pyo3(signature = (radix = 2)))]
pub fn YGate(radix: usize) -> UnitaryExpression {
let proto = format!("Y<{}>()", radix);
let mut body = "[".to_string();
for i in 0..radix {
body += "[";
for j in 0..radix {
if (j + 1) % radix == i {
body += &format!("e^(2*π*i*(2*{}+{}-1)/(2*{})), ", j, radix, radix);
} else {
body += "0, ";
}
}
body += "],";
}
body += "]";
UnitaryExpression::new(proto + "{" + &body + "}")
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
#[cfg_attr(feature = "python", pyo3(signature = (radix = 2)))]
pub fn SGate(radix: usize) -> UnitaryExpression {
let proto = format!("S<{}>()", radix);
let mut body = "[".to_string();
for i in 0..radix {
body += "[";
for j in 0..radix {
if i == j {
if i == 0 {
body += "1, ";
} else {
body += &format!("e^(π*i*{}*{}/{}), ", i, i, radix);
}
} else {
body += "0, ";
}
}
body += "],";
}
body += "]";
UnitaryExpression::new(proto + "{" + &body + "}")
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
#[cfg_attr(feature = "python", pyo3(signature = (radix = 2)))]
pub fn TGate(radix: usize) -> UnitaryExpression {
let proto = format!("T<{}>()", radix);
let mut body = "[".to_string();
for i in 0..radix {
body += "[";
for j in 0..radix {
if i == j {
if i == 0 {
body += "1, ";
} else {
body += &format!("e^(π*i*{}*{}/(2*{})), ", i, i, radix);
}
} else {
body += "0, ";
}
}
body += "],";
}
body += "]";
UnitaryExpression::new(proto + "{" + &body + "}")
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
#[cfg_attr(feature = "python", pyo3(signature = (radix = 2)))]
pub fn PGate(radix: usize) -> UnitaryExpression {
let mut proto = format!("P<{}>(", radix);
for i in 0..radix - 1 {
proto += "θ";
proto += &i.to_string();
proto += ", ";
}
proto += ")";
let mut body = "".to_string();
body += "[";
for i in 0..radix {
body += "[";
for j in 0..radix {
if i == j {
if i == 0 {
body += "1, ";
} else {
body += &format!("e^(i*θ{}), ", i - 1);
}
} else {
body += "0, ";
}
}
body += "],";
}
body += "]";
UnitaryExpression::new(proto + "{" + &body + "}")
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
pub fn U3Gate() -> UnitaryExpression {
let proto = "U(θ0, θ1, θ2)";
let body = "[
[cos(θ0/2), ~e^(i*θ2)*sin(θ0/2)],
[e^(i*θ1)*sin(θ0/2), e^(i*(θ1+θ2))*cos(θ0/2)]
]";
UnitaryExpression::new(proto.to_owned() + "{" + body + "}")
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
pub fn U1Gate() -> UnitaryExpression {
let proto = "U1(θ0)";
let body = "[[1, 0], [0, e^(i*θ0)]]";
UnitaryExpression::new(proto.to_owned() + "{" + body + "}")
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
pub fn U2Gate() -> UnitaryExpression {
let proto = "U2(θ0, θ1)";
let body = "1/sqrt(2) * [[1, ~e^(i*θ1)], [e^(i*θ0), e^(i*(θ0+θ1))]]";
UnitaryExpression::new(proto.to_owned() + "{" + body + "}")
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
pub fn SXGate() -> UnitaryExpression {
let proto = "SX()";
let body = "1/2 * [[1+i, 1-i], [1-i, 1+i]]";
UnitaryExpression::new(proto.to_owned() + "{" + body + "}")
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
pub fn RXGate() -> UnitaryExpression {
let proto = "RX(θ0)";
let body = "[[cos(θ0/2), ~i*sin(θ0/2)], [~i*sin(θ0/2), cos(θ0/2)]]";
UnitaryExpression::new(proto.to_owned() + "{" + body + "}")
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
pub fn RYGate() -> UnitaryExpression {
let proto = "RY(θ0)";
let body = "[[cos(θ0/2), ~sin(θ0/2)], [sin(θ0/2), cos(θ0/2)]]";
UnitaryExpression::new(proto.to_owned() + "{" + body + "}")
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
pub fn RZGate() -> UnitaryExpression {
let proto = "RZ(θ0)";
let body = "[[e^(~i*θ0/2), 0], [0, e^(i*θ0/2)]]";
UnitaryExpression::new(proto.to_owned() + "{" + body + "}")
}
fn generate_embedded_two_param_su2(dimension: usize, i: usize, j: usize) -> UnitaryExpression {
let proto = format!("U<{dimension}>(θ0, θ1)");
let mut body = String::from("[");
for row in 0..dimension {
body += "[";
for col in 0..dimension {
if row == i && col == i {
body += "e^(i*θ1)*cos(θ0/2)";
} else if row == i && col == j {
body += "~sin(θ0/2)";
} else if row == j && col == i {
body += "sin(θ0/2)";
} else if row == j && col == j {
body += "e^(i*(~θ1))*cos(θ0/2)";
} else if row == col {
body += "1";
} else {
body += "0";
}
body += ",";
}
body += "]";
}
body += "]";
UnitaryExpression::new(proto.to_owned() + "{" + &body + "}")
}
fn generate_embedded_su2(dimension: usize, i: usize, j: usize) -> UnitaryExpression {
let proto = format!("U<{dimension}>(θ0, θ1, θ2)");
let mut body = String::from("[");
for row in 0..dimension {
body += "[";
for col in 0..dimension {
if row == i && col == i {
body += "cos(θ0/2)";
} else if row == i && col == j {
body += "~e^(i*θ2)*sin(θ0/2)";
} else if row == j && col == i {
body += "e^(i*θ1)*sin(θ0/2)";
} else if row == j && col == j {
body += "e^(i*(θ1+θ2))*cos(θ0/2)";
} else if row == col {
body += "1";
} else {
body += "0";
}
body += ",";
}
body += "]";
}
body += "]";
UnitaryExpression::new(proto.to_owned() + "{" + &body + "}")
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
pub fn RXXGate() -> UnitaryExpression {
let proto = "RXX<2, 2>(θ0)";
let body = "[\
[cos(θ0/2), 0, 0, ~i*sin(θ0/2)],\
[0, cos(θ0/2), ~i*sin(θ0/2), 0],\
[0, ~i*sin(θ0/2), cos(θ0/2), 0],\
[~i*sin(θ0/2), 0, 0, cos(θ0/2)]\
]";
UnitaryExpression::new(proto.to_owned() + "{" + body + "}")
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
pub fn RYYGate() -> UnitaryExpression {
let proto = "RYY<2, 2>(θ0)";
let body = "[\
[cos(θ0/2), 0, 0, i*sin(θ0/2)],\
[0, cos(θ0/2), ~i*sin(θ0/2), 0],\
[0, ~i*sin(θ0/2), cos(θ0/2), 0],\
[i*sin(θ0/2), 0, 0, cos(θ0/2)]\
]";
UnitaryExpression::new(proto.to_owned() + "{" + body + "}")
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
pub fn RZZGate() -> UnitaryExpression {
let proto = "RZZ<2, 2>(θ0)";
let body = "[\
[e^(~i*θ0/2), 0, 0, 0],\
[0, e^(i*θ0/2), 0, 0],\
[0, 0, e^(i*θ0/2), 0],\
[0, 0, 0, e^(~i*θ0/2)]\
]";
UnitaryExpression::new(proto.to_owned() + "{" + body + "}")
}
fn embed_one_larger(unitary: UnitaryExpression) -> UnitaryExpression {
let dimension = unitary.dimension() + 1;
let mut one_larger = UnitaryExpression::identity(unitary.name(), [dimension]);
one_larger.embed(unitary, 1, 1);
one_larger
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
#[cfg_attr(feature = "python", pyo3(signature = (radices = Radices::new([2]))))]
pub fn ParameterizedUnitary(radices: Radices) -> UnitaryExpression {
let dimension = radices.dimension();
if dimension < 2 {
panic!("Cannot generate parameterized unitary for dimension 1 or less");
}
if dimension == 2 {
return U3Gate();
}
let right = {
let one_smaller = ParameterizedUnitary(Radices::new([dimension - 1]));
embed_one_larger(one_smaller)
};
let left = {
let mut acm = generate_embedded_su2(dimension, dimension - 2, dimension - 1);
for i in (0..=(dimension - 3)).rev() {
let j = i + 1;
let two = generate_embedded_two_param_su2(dimension, i, j);
acm = acm.dot(two)
}
acm
};
let mut expression = left.dot(right);
expression.set_radices(radices);
expression
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
pub fn Invert(mut expr: UnitaryExpression) -> UnitaryExpression {
expr.dagger();
expr
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
pub fn Dagger(mut expr: UnitaryExpression) -> UnitaryExpression {
let new_name = format!("Dagger({})", expr.name());
expr.dagger();
expr.set_name(new_name);
expr
}
fn cartesian_product(control_levels: Vec<Vec<usize>>) -> Vec<Vec<usize>> {
let mut prod = vec![];
for level in control_levels.into_iter() {
if prod.is_empty() {
for l in level.into_iter() {
prod.push(vec![l]);
}
} else {
let mut new_prod = vec![];
for l in level.into_iter() {
for v in prod.iter() {
let mut v_new = v.clone();
v_new.push(l);
new_prod.push(v_new);
}
}
prod = new_prod;
}
}
prod
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
#[cfg_attr(feature = "python", pyo3(signature = (expr, control_radices = Radices::new([2]), control_levels = None)))]
pub fn Controlled(
expr: UnitaryExpression,
control_radices: Radices,
control_levels: Option<Vec<Vec<usize>>>,
) -> UnitaryExpression {
let control_levels = match control_levels {
Some(levels) => levels,
None => {
control_radices
.iter()
.map(|&radix| vec![(usize::from(radix) - 1)])
.collect()
}
};
if control_radices.len() != control_levels.len() {
panic!("control_radices and control_levels must have the same length");
}
if control_levels.iter().any(|levels| levels.is_empty()) {
panic!("control_levels must not contain empty levels");
}
if control_levels
.iter()
.map(|levels| levels.iter().copied())
.zip(control_radices.iter())
.any(|(mut levels, radix)| levels.any(|level| level >= usize::from(*radix)))
{
panic!("Expected control levels to be less than the number of levels.");
}
let mut control_level_sets = control_levels.clone();
for level in control_level_sets.iter_mut() {
level.sort();
level.dedup();
}
if control_level_sets
.iter()
.zip(control_levels.iter())
.any(|(level_dedup, level)| level.len() != level_dedup.len())
{
panic!("Expected control levels to be unique.");
}
let gate_expr = expr;
let gate_dim = gate_expr.dimension();
let name = format!("Controlled({})", gate_expr.name());
let radices = control_radices.concat(&gate_expr.radices());
let mut expr = UnitaryExpression::identity(&name, radices);
let diagonal_indices: Vec<usize> = cartesian_product(control_levels)
.into_iter()
.map(|block_idx_expansion| control_radices.compress(&block_idx_expansion))
.map(|block_diag_idx| block_diag_idx * gate_dim)
.collect();
for diagonal_idx in diagonal_indices.iter() {
expr.embed(gate_expr.clone(), *diagonal_idx, *diagonal_idx);
}
expr
}
#[cfg_attr(
feature = "python",
gen_stub_pyfunction(module = "openqudit.expressions")
)]
#[cfg_attr(feature = "python", pyo3::pyfunction)]
#[cfg_attr(feature = "python", pyo3(signature = (expr, control_radices = Radices::new([2]), control_levels = None)))]
pub fn ClassicallyControlled(
expr: UnitaryExpression,
control_radices: Radices,
control_levels: Option<Vec<Vec<usize>>>,
) -> UnitarySystemExpression {
let control_levels = match control_levels {
Some(levels) => levels,
None => {
control_radices
.iter()
.map(|&radix| vec![(usize::from(radix) - 1)])
.collect()
}
};
if control_radices.len() != control_levels.len() {
panic!("control_radices and control_levels must have the same length");
}
if control_levels.iter().any(|levels| levels.is_empty()) {
panic!("control_levels must not contain empty levels");
}
if control_levels
.iter()
.map(|levels| levels.iter().copied())
.zip(control_radices.iter())
.any(|(mut levels, radix)| levels.any(|level| level >= usize::from(*radix)))
{
panic!("Expected control levels to be less than the number of levels.");
}
let mut control_level_sets = control_levels.clone();
for level in control_level_sets.iter_mut() {
level.sort();
level.dedup();
}
if control_level_sets
.iter()
.zip(control_levels.iter())
.any(|(level_dedup, level)| level.len() != level_dedup.len())
{
panic!("Expected control levels to be unique.");
}
let diagonal_indices: Vec<usize> = cartesian_product(control_levels)
.into_iter()
.map(|block_idx_expansion| control_radices.compress(&block_idx_expansion))
.collect();
let new_radices = control_radices
.iter()
.map(|&r| r.into())
.collect::<Vec<_>>();
expr.classically_control(&diagonal_indices, &new_radices)
}
#[cfg(feature = "python")]
mod python {
use crate::python::PyExpressionRegistrar;
use pyo3::prelude::*;
fn register(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
parent_module.add_function(wrap_pyfunction!(super::IGate, parent_module)?)?;
parent_module.add_function(wrap_pyfunction!(super::HGate, parent_module)?)?;
parent_module.add_function(wrap_pyfunction!(super::SwapGate, parent_module)?)?;
parent_module.add_function(wrap_pyfunction!(super::XGate, parent_module)?)?;
parent_module.add_function(wrap_pyfunction!(super::ZGate, parent_module)?)?;
parent_module.add_function(wrap_pyfunction!(super::YGate, parent_module)?)?;
parent_module.add_function(wrap_pyfunction!(super::SGate, parent_module)?)?;
parent_module.add_function(wrap_pyfunction!(super::TGate, parent_module)?)?;
parent_module.add_function(wrap_pyfunction!(super::PGate, parent_module)?)?;
parent_module.add_function(wrap_pyfunction!(super::U3Gate, parent_module)?)?;
parent_module.add_function(wrap_pyfunction!(super::U1Gate, parent_module)?)?;
parent_module.add_function(wrap_pyfunction!(super::U2Gate, parent_module)?)?;
parent_module.add_function(wrap_pyfunction!(super::SXGate, parent_module)?)?;
parent_module.add_function(wrap_pyfunction!(super::RXGate, parent_module)?)?;
parent_module.add_function(wrap_pyfunction!(super::RYGate, parent_module)?)?;
parent_module.add_function(wrap_pyfunction!(super::RZGate, parent_module)?)?;
parent_module.add_function(wrap_pyfunction!(super::RXXGate, parent_module)?)?;
parent_module.add_function(wrap_pyfunction!(super::RYYGate, parent_module)?)?;
parent_module.add_function(wrap_pyfunction!(super::RZZGate, parent_module)?)?;
parent_module.add_function(wrap_pyfunction!(
super::ParameterizedUnitary,
parent_module
)?)?;
parent_module.add_function(wrap_pyfunction!(super::Invert, parent_module)?)?;
parent_module.add_function(wrap_pyfunction!(super::Dagger, parent_module)?)?;
parent_module.add_function(wrap_pyfunction!(super::Controlled, parent_module)?)?;
parent_module.add_function(wrap_pyfunction!(
super::ClassicallyControlled,
parent_module
)?)?;
Ok(())
}
inventory::submit!(PyExpressionRegistrar { func: register });
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dagger_renames_s() {
let dg = Dagger(SGate(2));
assert_eq!(dg.name(), "Dagger(S)");
}
#[test]
fn dagger_renames_t() {
let dg = Dagger(TGate(2));
assert_eq!(dg.name(), "Dagger(T)");
}
}