q1tsim 0.5.0

A simple, efficient, quantum computer simulator.
Documentation
// Copyright 2019 Q1t BV
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::gates::Gate;

/// Rotation around `y` axis.
///
/// The `R`<sub>`Y`</sub>`(θ)` gate rotates the qubit around the `y` axis of the
/// Bloch sphere over an angle `θ`. The associated matrix is
/// ```text
/// ┌                    ┐
/// │ cos(θ/2) -sin(θ/2) │
/// │                    │
/// │ sin(θ/2)  cos(θ/2) │
/// └                    ┘
/// ```
pub struct RY
{
    theta: crate::gates::Parameter,
    desc: String
}

impl RY
{
    /// Create a new `R`<sub>`Y`</sub> gate.
    pub fn new<T>(theta: T) -> Self
    where crate::gates::Parameter: From<T>
    {
        let param = crate::gates::Parameter::from(theta);
        let desc = format!("RY({:.4})", param);
        RY { theta: param, desc: desc }
    }
}

impl crate::gates::Gate for RY
{
    fn cost(&self) -> f64
    {
        crate::gates::U3::cost()
    }

    fn description(&self) -> &str
    {
        &self.desc
    }

    fn nr_affected_bits(&self) -> usize
    {
        1
    }

    fn matrix(&self) -> crate::cmatrix::CMatrix
    {
        let htheta = 0.5 * self.theta.value();
        let c = num_complex::Complex::new((htheta).cos(), 0.0);
        let s = num_complex::Complex::new((htheta).sin(), 0.0);
        array![[c, -s], [s, c]]
    }

    fn apply_slice(&self, mut state: crate::cmatrix::CVecSliceMut)
    {
        let htheta = 0.5 * self.theta.value();
        let cos_t = num_complex::Complex::new((htheta).cos(), 0.0);
        let sin_t = num_complex::Complex::new((htheta).sin(), 0.0);

        let mut s = state.to_owned();
        s *= sin_t;
        state *= cos_t;

        let n = state.len() / 2;
        {
            let mut slice = state.slice_mut(s![..n]);
            slice -= &s.slice(s![n..]);
        }
        {
            let mut slice = state.slice_mut(s![n..]);
            slice += &s.slice(s![..n]);
        }
    }

    fn apply_mat_slice(&self, mut state: crate::cmatrix::CMatSliceMut)
    {
        let htheta = 0.5 * self.theta.value();
        let cos_t = num_complex::Complex::new((htheta).cos(), 0.0);
        let sin_t = num_complex::Complex::new((htheta).sin(), 0.0);

        let mut s = state.to_owned();
        s *= sin_t;
        state *= cos_t;

        let n = state.rows() / 2;
        {
            let mut slice = state.slice_mut(s![..n, ..]);
            slice -= &s.slice(s![n.., ..]);
        }
        {
            let mut slice = state.slice_mut(s![n.., ..]);
            slice += &s.slice(s![..n, ..]);
        }
    }
}

impl crate::export::OpenQasm for RY
{
    fn open_qasm(&self, bit_names: &[String], bits: &[usize])
        -> crate::error::Result<String>
    {
        // For some reason, the web interface on QX claims RY is not a defined
        // gate, even though it is defined in the specification. Replace by U3.
        //format!("ry({}) {}", self.theta, bit_names[bits[0]])
        Ok(format!("u3({}, 0, 0) {}", self.theta, bit_names[bits[0]]))
    }
}

impl crate::export::CQasm for RY
{
    fn c_qasm(&self, bit_names: &[String], bits: &[usize])
        -> crate::error::Result<String>
    {
        Ok(format!("ry {}, {}", bit_names[bits[0]], self.theta))
    }
}

impl crate::export::Latex for RY
{
    fn latex(&self, bits: &[usize], state: &mut crate::export::LatexExportState)
        -> crate::error::Result<()>
    {
        self.check_nr_bits(bits.len())?;
        let contents = format!("R_y({:.4})", self.theta);
        state.add_block_gate(bits, &contents)
    }
}

#[cfg(test)]
mod tests
{
    use crate::gates::{gate_test, Gate, RY};
    use crate::export::{Latex, LatexExportState, OpenQasm, CQasm};

    #[test]
    fn test_description()
    {
        let gate = RY::new(0.21675627161);
        assert_eq!(gate.description(), "RY(0.2168)");
    }

    #[test]
    fn test_cost()
    {
        let gate = RY::new(0.21675627161);
        assert_eq!(gate.cost(), 201.0);
    }

    #[test]
    fn test_matrix()
    {
        let z = crate::cmatrix::COMPLEX_ZERO;
        let o = crate::cmatrix::COMPLEX_ONE;
        let x = crate::cmatrix::COMPLEX_HSQRT2;

        let gate = RY::new(::std::f64::consts::FRAC_PI_2);
        assert_complex_matrix_eq!(gate.matrix(), array![[x, -x], [x, x]]);

        let gate = RY::new(::std::f64::consts::PI);
        assert_complex_matrix_eq!(gate.matrix(), array![[z, -o], [o, z]]);
    }

    #[test]
    fn test_apply()
    {
        let z = crate::cmatrix::COMPLEX_ZERO;
        let o = crate::cmatrix::COMPLEX_ONE;
        let x = crate::cmatrix::COMPLEX_HSQRT2;
        let mut state = array![
            [o, z, x,  x],
            [z, o, x, -x]
        ];
        let result = array![
            [x, -x, z, o],
            [x,  x, o, z]
        ];
        let gate = RY::new(::std::f64::consts::FRAC_PI_2);
        gate_test(gate, &mut state, &result);
    }

    #[test]
    fn test_open_qasm()
    {
        let bit_names = [String::from("qb")];
        let qasm = RY::new(2.25).open_qasm(&bit_names, &[0]);
        //assert_eq!(qasm, "ry(2.25) qb");
        assert_eq!(qasm, Ok(String::from("u3(2.25, 0, 0) qb")));
    }

    #[test]
    fn test_c_qasm()
    {
        let bit_names = [String::from("qb")];
        let qasm = RY::new(2.25).c_qasm(&bit_names, &[0]);
        assert_eq!(qasm, Ok(String::from("ry qb, 2.25")));
    }

    #[test]
    fn test_latex()
    {
        let gate = RY::new(::std::f64::consts::FRAC_PI_2);
        let mut state = LatexExportState::new(1, 0);
        assert_eq!(gate.latex(&[0], &mut state), Ok(()));
        assert_eq!(state.code(),
r#"\Qcircuit @C=1em @R=.7em {
    \lstick{\ket{0}} & \gate{R_y(1.5708)} & \qw \\
}
"#);

        let gate = RY::new(-24.0);
        let mut state = LatexExportState::new(1, 0);
        assert_eq!(gate.latex(&[0], &mut state), Ok(()));
        assert_eq!(state.code(),
r#"\Qcircuit @C=1em @R=.7em {
    \lstick{\ket{0}} & \gate{R_y(-24.0000)} & \qw \\
}
"#);
    }
}