use num_complex::Complex;
use crate::{
components::{
gate::Gate, measurement::MeasurementBasis, operator::{Hadamard, Pauli, CNOT}, parametric::parameter::Parameter, pauli_string::PauliString, state::State
},
errors::Error, parametric_gate::{ParametricP, ParametricRy},
};
use std::f64::consts::{FRAC_1_SQRT_2, PI};
#[test]
fn test_gate_new_operator() {
let _gate: Gate = Gate::Operator(Box::new(Hadamard), vec![0, 1], vec![]);
}
#[test]
fn test_gate_new_measurement() {
let _gate: Gate = Gate::Measurement(MeasurementBasis::Computational, vec![0, 1]);
}
#[test]
fn test_gate_new_parametric() {
let _gate: Gate = Gate::Parametric(Box::new(ParametricP{ parameter: Parameter::new([0.5]) }), vec![0], vec![]);
}
#[test]
fn test_gate_new_pauli_string() {
let pauli_string = PauliString::new(1.0.into()).with_op(1, Pauli::X).with_op(2, Pauli::Y);
let _gate: Gate = Gate::PauliString(pauli_string);
}
#[test]
fn test_gate_new_pauli_time_evolution() {
let pauli_string = PauliString::new(1.0.into()).with_op(0, Pauli::Z).with_op(1, Pauli::X);
let _gate: Gate = Gate::PauliTimeEvolution(pauli_string, 0.01);
}
#[test]
fn test_gate_get_target_qubits() {
let gate: Gate = Gate::Operator(Box::new(Hadamard), vec![0, 1], vec![]);
let target_indices: &Vec<usize> = &gate.get_target_qubits();
assert_eq!(target_indices, &vec![0, 1]);
let gate: Gate = Gate::Measurement(MeasurementBasis::Computational, vec![0, 2]);
let target_indices: &Vec<usize> = &gate.get_target_qubits();
assert_eq!(target_indices, &vec![0, 2]);
let gate: Gate = Gate::Parametric(Box::new(ParametricP{ parameter: Parameter::new([0.5]) }), vec![0, 1], vec![]);
let target_indices: &Vec<usize> = &gate.get_target_qubits();
assert_eq!(target_indices, &vec![0, 1]);
let gate: Gate = Gate::PauliString(PauliString::new(2.0.into()).with_op(0, Pauli::X).with_op(1, Pauli::X));
let target_indices: &Vec<usize> = &gate.get_target_qubits();
assert_eq!(target_indices, &vec![0, 1]);
let gate: Gate = Gate::PauliTimeEvolution(PauliString::new(1.0.into()).with_op(0, Pauli::Z).with_op(1, Pauli::X), 0.01);
let target_indices: &Vec<usize> = &gate.get_target_qubits();
assert_eq!(target_indices, &vec![0, 1]);
}
#[test]
fn test_gate_get_control_qubits() {
let gate: Gate = Gate::Operator(Box::new(CNOT), vec![0, 1], vec![2]);
let control_indices: Option<&Vec<usize>> = gate.get_control_qubits();
assert_eq!(control_indices, Some(&vec![2]));
let gate: Gate = Gate::Measurement(MeasurementBasis::Computational, vec![0, 2]);
let control_indices: Option<&Vec<usize>> = gate.get_control_qubits();
assert_eq!(control_indices, None);
let gate: Gate = Gate::Parametric(Box::new(ParametricP{ parameter: Parameter::new([0.5]) }), vec![0], vec![1, 2]);
let control_indices: Option<&Vec<usize>> = gate.get_control_qubits();
assert_eq!(control_indices, Some(&vec![1, 2]));
}
#[test]
fn test_gate_apply() {
let gate: Gate = Gate::Operator(Box::new(Hadamard), vec![0], vec![]);
let state: State = State::new_zero(1).unwrap();
let result: Result<State, Error> = gate.apply(&state);
assert!(result.is_ok());
let new_state: State = result.unwrap();
assert_eq!(new_state, State::new_plus(1).unwrap());
let gate: Gate = Gate::Measurement(MeasurementBasis::Computational, vec![0]);
let state: State = State::new_plus(1).unwrap();
let result: Result<State, Error> = gate.apply(&state);
assert!(result.is_ok());
let new_state: State = result.unwrap();
assert!(
new_state == State::new_zero(1).unwrap() || new_state == State::new_basis_n(1, 1).unwrap()
);
let gate: Gate = Gate::Parametric(Box::new(ParametricRy{parameter: Parameter::new([PI/2.0])}), vec![0], vec![]);
let state: State = State::new_zero(1).unwrap();
let result: Result<State, Error> = gate.apply(&state);
assert!(result.is_ok());
let new_state: State = result.unwrap();
assert_eq!(new_state, State::new_plus(1).unwrap());
let gate: Gate = Gate::PauliString(PauliString::new(2.0.into()).with_op(0, Pauli::X).with_op(1, Pauli::X));
let state: State = State::new_zero(2).unwrap();
let result: Result<State, Error> = gate.apply(&state);
assert!(result.is_ok());
let new_state: State = result.unwrap();
assert_eq!(new_state, State::new_basis_n(2,3).unwrap());
let gate: Gate = Gate::PauliTimeEvolution(PauliString::new((0.5 * PI).into()).with_op(0, Pauli::Z).with_op(1, Pauli::X), 0.5);
let state: State = State::new_zero(2).unwrap();
let result: Result<State, Error> = gate.apply(&state);
let expected_state_vector = vec![Complex::new(FRAC_1_SQRT_2, 0.0), Complex::new(0.0, 0.0), Complex::new(0.0, -FRAC_1_SQRT_2), Complex::new(0.0, 0.0)];
assert!(result.is_ok());
let new_state: State = result.unwrap();
assert_eq!(new_state, State::new(expected_state_vector).unwrap());
}