use crate::pipeline::{get_owned_opfns, StateModifierType};
use crate::state_ops::{invert_op, UnitaryOp};
use crate::{CircuitError, OpBuilder, Register, UnitaryBuilder};
#[macro_export]
macro_rules! wrap_and_invert {
(pub $newfunc:ident, pub $newinvert:ident, $($tail:tt)*) => {
wrap_fn!(pub $newfunc, $($tail)*);
invert_fn!(pub $newinvert, $newfunc);
};
($newfunc:ident, pub $newinvert:ident, $($tail:tt)*) => {
wrap_fn!($newfunc, $($tail)*);
invert_fn!(pub $newinvert, $newfunc);
};
(pub $newfunc:ident, $newinvert:ident, $($tail:tt)*) => {
wrap_fn!(pub $newfunc, $($tail)*);
invert_fn!($newinvert, $newfunc);
};
($newfunc:ident, $newinvert:ident, $($tail:tt)*) => {
wrap_fn!($newfunc, $($tail)*);
invert_fn!($newinvert, $newfunc);
}
}
#[macro_export]
macro_rules! invert_fn {
(pub $newinvert:ident, $func:expr) => {
pub fn $newinvert(
b: &mut dyn $crate::UnitaryBuilder,
rs: Vec<Register>,
) -> Result<Vec<Register>, $crate::CircuitError> {
$crate::inverter(b, rs, $func)
}
};
($newinvert:ident, $func:expr) => {
fn $newinvert(
b: &mut dyn $crate::UnitaryBuilder,
rs: Vec<Register>,
) -> Result<Vec<Register>, $crate::CircuitError> {
$crate::inverter(b, rs, $func)
}
};
}
pub fn inverter<
F: Fn(&mut dyn UnitaryBuilder, Vec<Register>) -> Result<Vec<Register>, CircuitError>,
>(
b: &mut dyn UnitaryBuilder,
mut rs: Vec<Register>,
f: F,
) -> Result<Vec<Register>, CircuitError> {
let original_indices: Vec<_> = rs.iter().map(|r| r.indices.clone()).collect();
let mut inv_builder = OpBuilder::new();
let new_rs: Vec<_> = original_indices
.iter()
.map(|indices| {
let n = indices.len() as u64;
inv_builder.register(n).unwrap()
})
.collect();
let flat_indices: Vec<_> = original_indices.iter().flatten().cloned().collect();
let before_n = inv_builder.get_qubit_count();
let new_rs = f(&mut inv_builder, new_rs)?;
let end_reg = inv_builder.merge(new_rs)?;
let after_n = inv_builder.get_qubit_count();
let temps = after_n - before_n;
let temp_indices = if temps > 0 {
let temp_reg = b.get_temp_register(temps, false);
let temp_indices = temp_reg.indices.clone();
rs.push(temp_reg);
temp_indices
} else {
vec![]
};
let reg = b.merge(rs)?;
let reg: Register = get_owned_opfns(end_reg)
.into_iter()
.fold(vec![], |mut acc, modifier| {
let name = format!("Inverse({})", modifier.name);
let op = match modifier.modifier {
StateModifierType::UnitaryOp(op) => remap_indices(invert_op(op), &flat_indices),
StateModifierType::Debug(_, _) => unimplemented!(),
StateModifierType::SideChannelModifiers(_, _) => unimplemented!(),
StateModifierType::MeasureState(_, _, _) => unimplemented!(),
StateModifierType::StochasticMeasureState(_, _, _) => unimplemented!(),
};
acc.push((name, op));
acc
})
.into_iter()
.rev()
.try_fold(reg, |reg, (name, op)| {
let indices = match &op {
UnitaryOp::Matrix(indices, _) => indices.clone(),
UnitaryOp::SparseMatrix(indices, _) => indices.clone(),
UnitaryOp::Swap(a_indices, b_indices) => {
let vecs = [a_indices.clone(), b_indices.clone()];
vecs.iter().flatten().cloned().collect()
}
UnitaryOp::Control(c_indices, op_indices, _) => {
let vecs = [c_indices.clone(), op_indices.clone()];
vecs.iter().flatten().cloned().collect()
}
UnitaryOp::Function(x_indices, y_indices, _) => {
let vecs = [x_indices.clone(), y_indices.clone()];
vecs.iter().flatten().cloned().collect()
}
};
let (sel_reg, reg) = b.split_absolute(reg, &indices)?;
if let Some(reg) = reg {
let sel_reg = b.merge_with_op(vec![sel_reg], Some((name, op)))?;
b.merge(vec![sel_reg, reg])
} else {
b.merge_with_op(vec![sel_reg], Some((name, op)))
}
})?;
let (zero_temps, one_temps) = inv_builder.get_temp_indices();
let temp_vecs = vec![(zero_temps, false), (one_temps, true)];
let reg = temp_vecs.into_iter().try_fold(reg, |reg, (temps, value)| {
if temps.is_empty() {
Ok(reg)
} else {
let temps: Vec<_> = temps
.into_iter()
.map(|indx| temp_indices[(indx - before_n) as usize])
.collect();
let (ts, reg) = b.split_absolute(reg, &temps)?;
b.return_temp_register(ts, value);
Ok(reg.unwrap())
}
})?;
let (rs, _) = b.split_absolute_many(reg, &original_indices)?;
Ok(rs)
}
fn remap_indices(op: UnitaryOp, new_indices: &[u64]) -> UnitaryOp {
let remap = |indices: Vec<u64>| -> Vec<u64> {
indices
.into_iter()
.map(|indx| new_indices[indx as usize])
.collect()
};
match op {
UnitaryOp::Matrix(indices, mat) => UnitaryOp::Matrix(remap(indices), mat),
UnitaryOp::SparseMatrix(indices, mat) => UnitaryOp::SparseMatrix(remap(indices), mat),
UnitaryOp::Swap(a_indices, b_indices) => {
UnitaryOp::Swap(remap(a_indices), remap(b_indices))
}
UnitaryOp::Control(c_indices, op_indices, op) => {
let op = Box::new(remap_indices(*op, new_indices));
UnitaryOp::Control(remap(c_indices), remap(op_indices), op)
}
UnitaryOp::Function(x_indices, y_indices, f) => {
UnitaryOp::Function(remap(x_indices), remap(y_indices), f)
}
}
}
#[cfg(test)]
mod inverter_test {
use super::*;
use crate::boolean_circuits::arithmetic::{add, add_op};
use crate::pipeline::{get_required_state_size_from_frontier, InitialState, Representation};
use crate::utils::flip_bits;
use crate::{run_debug, run_local_with_init, Complex, QuantumState};
use num::One;
fn test_inversion<
F: Fn(&mut dyn UnitaryBuilder, Vec<Register>) -> Result<Vec<Register>, CircuitError>,
>(
b: &mut OpBuilder,
rs: Vec<Register>,
f: F,
) -> Result<(), CircuitError> {
let rs = f(b, rs)?;
let rs = inverter(b, rs, f)?;
let r = b.merge(rs)?;
run_debug(&r)?;
let n = get_required_state_size_from_frontier(&[&r]);
let indices: Vec<_> = (0..n).collect();
(0..1 << n).for_each(|indx| {
let (state, _) =
run_local_with_init::<f64>(&r, &[(indices.clone(), InitialState::Index(indx))])
.unwrap();
let pos = state
.into_state(Representation::BigEndian)
.into_iter()
.position(|v| v == Complex::one())
.map(|pos| flip_bits(n as usize, pos as u64));
assert!(pos.is_some());
assert_eq!(pos.unwrap(), indx);
});
Ok(())
}
#[test]
fn test_invert_x() -> Result<(), CircuitError> {
wrap_fn!(x_op, UnitaryBuilder::x, r);
let mut b = OpBuilder::new();
let r = b.qubit();
test_inversion(&mut b, vec![r], x_op)
}
#[test]
fn test_invert_y() -> Result<(), CircuitError> {
wrap_fn!(x_op, UnitaryBuilder::y, r);
let mut b = OpBuilder::new();
let r = b.qubit();
test_inversion(&mut b, vec![r], x_op)
}
#[test]
fn test_invert_multi() -> Result<(), CircuitError> {
fn gamma(
b: &mut dyn UnitaryBuilder,
ra: Register,
rb: Register,
) -> Result<(Register, Register), CircuitError> {
let (ra, rb) = b.cy(ra, rb);
b.swap(ra, rb)
}
wrap_fn!(wrap_gamma, (gamma), ra, rb);
let mut b = OpBuilder::new();
let ra = b.qubit();
let rb = b.qubit();
test_inversion(&mut b, vec![ra, rb], wrap_gamma)
}
#[test]
fn test_invert_add() -> Result<(), CircuitError> {
let mut b = OpBuilder::new();
let rc = b.qubit();
let ra = b.qubit();
let rb = b.register(2)?;
test_inversion(&mut b, vec![rc, ra, rb], add_op)
}
#[test]
fn test_invert_add_larger() -> Result<(), CircuitError> {
let mut b = OpBuilder::new();
let rc = b.register(2)?;
let ra = b.register(2)?;
let rb = b.register(3)?;
test_inversion(&mut b, vec![rc, ra, rb], add_op)
}
#[test]
fn test_invert_and_wrap_add() -> Result<(), CircuitError> {
wrap_and_invert!(add_op, inv_add, (add), rc, ra, rb);
let mut b = OpBuilder::new();
let rc = b.register(2)?;
let ra = b.register(2)?;
let rb = b.register(3)?;
let rs = add_op(&mut b, vec![rc, ra, rb])?;
let rs = inv_add(&mut b, rs)?;
let _r = b.merge(rs)?;
Ok(())
}
#[test]
fn test_invert_add_larger_macro() -> Result<(), CircuitError> {
invert_fn!(inv_add, add_op);
let mut b = OpBuilder::new();
let rc = b.register(2)?;
let ra = b.register(2)?;
let rb = b.register(3)?;
let rs = inv_add(&mut b, vec![rc, ra, rb])?;
let _r = b.merge(rs)?;
Ok(())
}
}