use cmatrix;
use gates;
use export;
use gates::Gate;
pub struct Loop
{
label: String,
nr_iterations: usize,
body: gates::Composite,
desc: String
}
impl Loop
{
pub fn new(label: &str, nr_iterations: usize, body: gates::Composite) -> Self
{
let desc = format!("{}({})", nr_iterations, body.description());
Loop
{
label: String::from(label),
nr_iterations: nr_iterations,
body: body,
desc: desc
}
}
}
impl gates::Gate for Loop
{
fn cost(&self) -> f64
{
self.nr_iterations as f64 * self.body.cost()
}
fn description(&self) -> &str
{
&self.desc
}
fn nr_affected_bits(&self) -> usize
{
self.body.nr_affected_bits()
}
fn matrix(&self) -> cmatrix::CMatrix
{
let mut res = cmatrix::CMatrix::eye(1 << self.nr_affected_bits());
for _ in 0..self.nr_iterations
{
self.body.apply_mat(&mut res);
}
res
}
fn apply_slice(&self, state: &mut cmatrix::CVecSliceMut)
{
for _ in 0..self.nr_iterations
{
self.body.apply_slice(state);
}
}
fn apply_mat_slice(&self, state: &mut cmatrix::CMatSliceMut)
{
for _ in 0..self.nr_iterations
{
self.body.apply_mat_slice(state);
}
}
}
impl export::OpenQasm for Loop
{
fn open_qasm(&self, bit_names: &[String], bits: &[usize]) -> String
{
if self.nr_iterations == 0
{
String::new()
}
else
{
let qasm_body = self.body.open_qasm(bit_names, bits);
let mut res = qasm_body.clone();
for _ in 1..self.nr_iterations
{
res += ";\n";
res += &qasm_body;
}
res
}
}
fn conditional_open_qasm(&self, _condition: &str, _bit_names: &[String],
_bits: &[usize]) -> Result<String, String>
{
Err(String::from("Classical conditions cannot be used in conjunction with a static loop"))
}
}
impl export::CQasm for Loop
{
fn c_qasm(&self, bit_names: &[String], bits: &[usize]) -> String
{
format!(".{}({})\n{}\n.end", self.label, self.nr_iterations,
self.body.c_qasm(bit_names, bits))
}
fn conditional_c_qasm(&self, _condition: &str, _bit_names: &[String],
_bits: &[usize]) -> Result<String, String>
{
Err(String::from("Classical conditions cannot be used in conjunction with a static loop"))
}
}
impl export::Latex for Loop
{
fn latex(&self, bits: &[usize], state: &mut export::LatexExportState)
{
if self.nr_iterations == 1
{
self.body.latex(bits, state);
}
else if self.nr_iterations == 2
{
self.body.latex(bits, state);
self.body.latex_checked(bits, state);
}
else if self.nr_iterations > 2
{
let min = *bits.iter().min().unwrap();
let max = *bits.iter().max().unwrap();
state.start_loop(self.nr_iterations);
self.body.latex(bits, state);
state.add_cds(min, max - min, r"\cdots");
self.body.latex_checked(bits, state);
state.end_loop();
}
}
}
#[cfg(test)]
mod tests
{
use super::Loop;
use gates::{gate_test, Composite, Gate};
use export::{CQasm, OpenQasm, Latex, LatexExportState};
use cmatrix;
#[test]
fn test_description()
{
let body = Composite::from_string("body", "RX(1.0471975511965976) 0").unwrap();
let gate = Loop::new("myloop", 3, body);
assert_eq!(gate.description(), "3(body)");
}
#[test]
fn test_cost()
{
let body = Composite::from_string("body", "H 0").unwrap();
let count = 15;
let gate = Loop::new("myloop", count, body);
assert_eq!(gate.cost(), count as f64 * 104.0);
let body = Composite::from_string("body", "H 1; CX 0 1; H 1").unwrap();
let count = 9;
let gate = Loop::new("myloop", count, body);
assert_eq!(gate.cost(), count as f64 * 1209.0);
}
#[test]
fn test_matrix()
{
let body = Composite::from_string("body", "RX(1.0471975511965976) 0").unwrap();
let gate = Loop::new("myloop", 3, body);
let z = cmatrix::COMPLEX_ZERO;
let i = cmatrix::COMPLEX_I;
assert_complex_matrix_eq!(gate.matrix(), array![[z, -i], [-i, z]]);
}
#[test]
fn test_apply()
{
let body = Composite::from_string("body", "RX(1.0471975511965976) 0; RX(-1.0471975511965976) 1").unwrap();
let gate = Loop::new("myloop", 3, body);
let z = cmatrix::COMPLEX_ZERO;
let o = cmatrix::COMPLEX_ONE;
let x = cmatrix::COMPLEX_HSQRT2;
let mut state = array![
[o, z, x, x],
[z, o, z, x],
[z, z, z, z],
[z, z, x, z],
];
let result = array![
[ z, z, x, z ],
[ z, z, z, z ],
[ z, o, z, x ],
[ o, z, x, x ]
];
gate_test(gate, &mut state, &result);
}
#[test]
fn test_apply_mat()
{
let body = Composite::from_string("body", "Sdg 0").unwrap();
let gate = Loop::new("myloop", 3, body);
let z = cmatrix::COMPLEX_ZERO;
let o = cmatrix::COMPLEX_ONE;
let i = cmatrix::COMPLEX_I;
let x = cmatrix::COMPLEX_HSQRT2;
let mut state = array![
[o, z, x, z],
[z, z, z, z],
[z, o, z, x],
[z, z, x, x],
];
let result = array![
[ o, z, x, z ],
[ z, z, z, z ],
[ z, i, z, i*x ],
[ z, z, i*x, i*x ]
];
gate.apply_mat(&mut state);
assert_complex_matrix_eq!(&state, &result);
}
#[test]
fn test_open_qasm()
{
let body = Composite::from_string("body", "H 0").unwrap();
let gate = Loop::new("myloop", 0, body);
let bit_names = [String::from("qb0")];
let qasm = gate.open_qasm(&bit_names, &[0]);
assert_eq!(qasm, "");
let body = Composite::from_string("body", "H 0; H 1; CX 0 1").unwrap();
let gate = Loop::new("myloop", 3, body);
let bit_names = [String::from("qb0"), String::from("qb1")];
let qasm = gate.open_qasm(&bit_names, &[0, 1]);
assert_eq!(qasm, "h qb0; h qb1; cx qb0, qb1;\nh qb0; h qb1; cx qb0, qb1;\nh qb0; h qb1; cx qb0, qb1");
}
#[test]
fn test_conditional_open_qasm()
{
let body = Composite::from_string("body", "H 0; H 1; CX 0 1").unwrap();
let gate = Loop::new("myloop", 3, body);
let bit_names = [String::from("qb0"), String::from("qb1")];
let res = gate.conditional_open_qasm("c == 3", &bit_names, &[0, 1]);
assert!(matches!(res, Err(_)));
}
#[test]
fn test_c_qasm()
{
let body = Composite::from_string("body", "H 0; H 1; CX 0 1").unwrap();
let gate = Loop::new("myloop", 3, body);
let bit_names = [String::from("qb0"), String::from("qb1")];
let qasm = gate.c_qasm(&bit_names, &[0, 1]);
assert_eq!(qasm, ".myloop(3)\nh qb0\nh qb1\ncnot qb0, qb1\n.end");
}
#[test]
fn test_conditional_c_qasm()
{
let body = Composite::from_string("body", "H 0; H 1; CX 0 1").unwrap();
let gate = Loop::new("myloop", 3, body);
let bit_names = [String::from("qb0"), String::from("qb1")];
let res = gate.conditional_c_qasm("c == 3", &bit_names, &[0, 1]);
assert!(matches!(res, Err(_)));
}
#[test]
fn test_latex()
{
let body = Composite::from_string("body", "H 0; CX 0 1").unwrap();
let gate = Loop::new("myloop", 0, body);
let mut state = LatexExportState::new(2, 0);
gate.latex(&[0, 1], &mut state);
assert_eq!(state.code(),
r#"\Qcircuit @C=1em @R=.7em {
\lstick{\ket{0}} & \qw \\
\lstick{\ket{0}} & \qw \\
}
"#);
let body = Composite::from_string("body", "H 0; CX 0 1").unwrap();
let gate = Loop::new("myloop", 1, body);
let mut state = LatexExportState::new(2, 0);
gate.latex(&[0, 1], &mut state);
assert_eq!(state.code(),
r#"\Qcircuit @C=1em @R=.7em {
\lstick{\ket{0}} & \gate{H} & \ctrl{1} & \qw \\
\lstick{\ket{0}} & \qw & \targ & \qw \\
}
"#);
let body = Composite::from_string("body", "H 0; CX 0 1").unwrap();
let gate = Loop::new("myloop", 2, body);
let mut state = LatexExportState::new(2, 0);
gate.latex(&[0, 1], &mut state);
assert_eq!(state.code(),
r#"\Qcircuit @C=1em @R=.7em {
\lstick{\ket{0}} & \gate{H} & \ctrl{1} & \gate{H} & \ctrl{1} & \qw \\
\lstick{\ket{0}} & \qw & \targ & \qw & \targ & \qw \\
}
"#);
let body = Composite::from_string("body", "H 0; CX 0 1").unwrap();
let gate = Loop::new("myloop", 15, body);
let mut state = LatexExportState::new(2, 0);
gate.latex(&[0, 1], &mut state);
assert_eq!(state.code(),
r#"\Qcircuit @C=1em @R=.7em {
& \mbox{} \POS"2,2"."2,2"."2,6"."2,6"!C*+<.7em>\frm{^\}},+U*++!D{15\times}\\
& & & & & & \\
\lstick{\ket{0}} & \gate{H} & \ctrl{1} & \cds{1}{\cdots} & \gate{H} & \ctrl{1} & \qw \\
\lstick{\ket{0}} & \qw & \targ & \qw & \qw & \targ & \qw \\
}
"#);
}
}