rqism 0.4.1

A multi-backend quantum circuit simulator
Documentation
#![doc = include_str!("../README.md")]

pub mod circuit;
pub mod counts;
pub mod instruction;
pub mod matrix_product_state;
pub mod prelude;
pub mod rayon_cond;
pub mod simulator_traits;
pub mod sparse_mat;
pub mod sparse_quantum_state;
pub mod stabilizer;
pub mod state_vector;
pub use ndarray;
pub use num;
pub use rand;
pub use rayon;

#[cfg(test)]
mod tests {
    use ndarray::Array2;
    use num::Complex;

    use crate::circuit::Circuit;
    use crate::instruction::quantum_fourier_transform;
    use crate::instruction::Instruction;
    use crate::instruction::PauliKind;
    use crate::matrix_product_state::MPS;
    use crate::simulator_traits::Simulator;
    use crate::stabilizer::Stabilizer;
    use crate::state_vector::QuantumStateVector;
    use num::complex::ComplexFloat;

    #[macro_export]

    macro_rules! assert_approx_eq {
        ($a:expr, $b:expr) => {{
            let eps = 1.0e-6;

            let (a, b) = (&$a, &$b);

            assert!(
                (*a - *b).abs() < eps,
                "assertion failed: `(left !== right)`
             (left: `{:?}`, right: `{:?}`, expect diff: `{:?}`, real diff: `{:?}`)",
                *a,
                *b,
                eps,
                (*a - *b).abs()
            );
        }};

        ($a:expr, $b:expr, $eps:expr) => {{
            let (a, b) = (&$a, &$b);

            let eps = $eps;

            assert!(
                (*a - *b).abs() < eps,
                "assertion failed: `(left !== right)` 
             (left: `{:?}`, right: `{:?}`, expect diff: `{:?}`, real diff: `{:?}`)",
                *a,
                *b,
                eps,
                (*a - *b).abs()
            );
        }};
    }

    #[test]
    fn sparse() {
        // can only handle up to 2^31
        QuantumStateVector::new(31);
    }

    #[test]
    fn bell() {
        let circuit = Circuit {
            ins: vec![
                Instruction::hadamard(0),
                Instruction::cnot([0, 1]),
                Instruction::Measure {
                    indices: vec![0, 1],
                },
                //Instruction::MeasureState,
            ],
            n: 2,
        };

        let machine = QuantumStateVector::new(2);

        let counts: Vec<i32> = machine
            .get_counts(&circuit, 1000)
            .data
            .values()
            .cloned()
            .collect();

        assert!((counts[0] - 500).abs() + (counts[1] - 500).abs() < 50);

        let machine = Stabilizer::new(2);

        let counts: Vec<i32> = machine
            .get_counts(&circuit, 1000)
            .data
            .values()
            .cloned()
            .collect();

        assert!((counts[0] - 500).abs() + (counts[1] - 500).abs() < 50);
    }

    #[test]
    fn statevec_pauli() {
        let mut machine = QuantumStateVector::new(1);
        machine.execute_instruction(&Instruction::not(0));

        assert_eq!(machine.state.get(0), Complex::new(0.0, 0.0)); // Should be |1⟩
        assert_eq!(machine.state.get(1), Complex::new(1.0, 0.0));

        let mut machine = QuantumStateVector::new(1);
        machine.execute_instruction(&Instruction::Pauli {
            kind: PauliKind::Y,
            target: 0,
        });

        assert_eq!(machine.state.get(1), Complex::new(0.0, 1.0));

        let mut machine = QuantumStateVector::new(1);

        machine.state.set(0, Complex::new(1.0 / 2.0.sqrt(), 0.0));
        machine.state.set(1, Complex::new(1.0 / 2.0.sqrt(), 0.0));

        machine.execute_instruction(&Instruction::Pauli {
            kind: PauliKind::Z,
            target: 0,
        });

        assert_approx_eq!(machine.state.get(0), Complex::new(1.0 / 2.0.sqrt(), 0.0));
        assert_approx_eq!(machine.state.get(1), Complex::new(-1.0 / 2.0.sqrt(), 0.0));
    }

    #[test]
    fn deutsch() {
        let circuit = Circuit {
            ins: vec![
                Instruction::not(1),
                Instruction::hadamard(0),
                Instruction::hadamard(1),
                // mystery function
                Instruction::cnot([0, 1]),
                Instruction::hadamard(0),
                Instruction::Measure { indices: vec![0] },
            ],
            n: 2,
        };

        let machine = QuantumStateVector::new(2);

        let counts: Vec<i32> = machine
            .get_counts(&circuit, 1000)
            .data
            .values()
            .cloned()
            .collect();

        assert_eq!(counts[0], 999);

        let machine = Stabilizer::new(2);

        let counts: Vec<i32> = machine
            .get_counts(&circuit, 1000)
            .data
            .values()
            .cloned()
            .collect();

        assert_eq!(counts[0], 999);
    }

    #[test]
    fn qft() {
        // the qft of a single qubit is equivalent to the hadamard gate
        let machine = QuantumStateVector::new(1);

        let hadamard = {
            let mut c = machine.clone();
            c.execute_instruction(&Instruction::hadamard(0));

            c
        };

        let quantumft = {
            let mut k = machine.clone();

            let c = quantum_fourier_transform(1);

            k.execute_circuit(&c);

            k
        };

        assert_eq!(hadamard.state.to_vec(), quantumft.state.to_vec())
    }

    #[test]
    fn rng() {
        let n = 8;
        let machine = QuantumStateVector::new(n);

        let mut h: Vec<Instruction> = (0..n).map(|x| Instruction::hadamard(x)).collect();

        h.push(Instruction::Measure {
            indices: (0..n).collect(),
        });

        h.push(Instruction::MeasureState);

        let ci = Circuit { ins: h, n };

        let co = machine.get_counts(&ci, 1).data;

        let k = co.keys().nth(0).unwrap();

        assert!(u8::from_str_radix(k, 2).is_ok());
    }

    #[test]
    fn mps() {
        let pauli_z = Array2::from_shape_vec((2, 2), vec![1.0_f32, 0.0, 0.0, -1.0])
            .unwrap()
            .mapv(|x| Complex::new(x, 0.0));

        let pauli_x = Array2::from_shape_vec((2, 2), vec![0.0_f32, 1.0, 1.0, 0.0])
            .unwrap()
            .mapv(|x| Complex::new(x, 0.0));

        let pauli_y = Array2::from_shape_vec((2, 2), vec![0.0_f32, -1.0, 1.0, 0.0])
            .unwrap()
            .mapv(|x| Complex::new(0.0, x));

        let mut mps = MPS::new(5, 1);

        //test case 1: pauli_z on |1>, expe val = -1
        mps.execute_instruction(&Instruction::not(0));

        let expe = mps.expectation_value_sq(&pauli_z, 0);

        assert_approx_eq!(expe.re, -1.0);

        //test case 2: pauli z on |0>, expe val = 1
        let expe = mps.expectation_value_sq(&pauli_z, 1);

        assert_approx_eq!(expe.re, 1.0);

        //test case 3: pauli_x on |+>, expe val = 1

        mps.execute_instruction(&Instruction::hadamard(2));

        let expe = mps.expectation_value_sq(&pauli_x, 2);

        assert_approx_eq!(expe.re, 1.0);

        //test case 4: pauli_x on |->, expe val = -1

        mps.execute_instruction(&Instruction::not(3));
        mps.execute_instruction(&Instruction::hadamard(3));

        let expe = mps.expectation_value_sq(&pauli_x, 3);

        assert_approx_eq!(expe.re, -1.0);

        //test case 5: pauli_y on |+i>, expe val = -1
        mps.execute_instruction(&Instruction::hadamard(4));
        mps.execute_instruction(&Instruction::s_gate(4));

        let expe = mps.expectation_value_sq(&pauli_y, 4);

        assert_approx_eq!(expe, 1.0);
    }

    #[test]
    pub fn mps_hadamard() {
        let mut mps = MPS::new(1, 1);

        mps.execute_instruction(&Instruction::hadamard(0));

        mps.measure_state();

        assert!(mps.reg == 0 || mps.reg == 1)
    }

    #[test]
    pub fn test_no_depolarization() {
        let mut machine = QuantumStateVector::new(1);
        let not_applied = machine.clone();

        machine.apply_local_depolarizing_noise(0, 0.0);

        assert_eq!(machine.state.to_vec(), not_applied.state.to_vec());
    }

    #[test]
    pub fn test_full_depolarization() {
        let mut machine = QuantumStateVector::new(1);

        machine.apply_local_depolarizing_noise(0, 1.0);
    }

    #[test]
    pub fn test_conditional() {
        let mut machine = QuantumStateVector::new(2);

        machine.execute_instruction(&Instruction::Pauli {
            kind: PauliKind::X,
            target: 0,
        });

        machine.measure_qubits(&[0], 0.0);

        machine.execute_instruction(&Instruction::ConditionalInstruction {
            indices: vec![0],
            condition: |a| a[0],
            inst: Box::new([Instruction::Pauli {
                kind: PauliKind::X,
                target: 1,
            }]),
        });

        machine.measure_qubits(&[1], 0.0);

        assert_eq!(machine.reg, 0b11);
    }

    #[test]
    pub fn circuit_to_gate() {
        let pure_circuit = Circuit {
            ins: vec![
                Instruction::not(1),
                Instruction::hadamard(0),
                Instruction::hadamard(1),
                // mystery function
                Instruction::cnot([0, 1]),
                Instruction::hadamard(0),
            ],
            n: 2,
        };

        let gate = pure_circuit.to_gate().unwrap();

        let circuit = Circuit {
            ins: vec![gate, Instruction::Measure { indices: vec![0] }],
            n: 2,
        };

        let machine = QuantumStateVector::new(2);

        let counts: Vec<i32> = machine
            .get_counts(&circuit, 1000)
            .data
            .values()
            .cloned()
            .collect();

        assert_eq!(counts[0], 999);
    }
}