Skip to content

Commit

Permalink
Increased unittest coverage (#127)
Browse files Browse the repository at this point in the history
* Temporary change of qoqo_calculator

* Removed qoqo_calculator branch dependency

* Increased version numbers and updated changelog

* Fixed input type for DeviceTrait

* Fixed unittest

* Simplified two qubit gate matrix unittests

* Updated to pyo3 0.15.0

* Fixed typo in ci workflow

* Removed faulty 3.10 (python) support, will address later

* Started unittesting qoqo quantum program

* Increased unittest coverage for quantum program

* Increased coverage for quantum program and measurements in qoqo

* Added unittests for multi qubit gates in qoqo

* Increased coverage for multi qubit gate operations and pragma operations

* Fixed formatting
  • Loading branch information
kbarkhqs authored Nov 18, 2021
1 parent 8d80f25 commit 4db72be
Show file tree
Hide file tree
Showing 17 changed files with 1,513 additions and 17 deletions.
4 changes: 2 additions & 2 deletions qoqo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ num-complex = "0.4"
thiserror = "1.0"
qoqo_calculator = { version="0.5" }
qoqo_calculator_pyo3 = {version="0.5", default-features=false}
qoqo-macros = {version="0.8", path="../qoqo-macros"}
roqoqo = {version="0.8", path="../roqoqo", features=["serialize", "overrotate"]}
qoqo-macros = {version="0.8.1", path="../qoqo-macros"}
roqoqo = {version="0.8.1", path="../roqoqo", features=["serialize", "overrotate"]}
numpy = "=0.15"
bincode = "1.3"
serde_json = "1.0"
Expand Down
10 changes: 5 additions & 5 deletions qoqo/qoqo/DEPENDENCIES
Original file line number Diff line number Diff line change
Expand Up @@ -7710,7 +7710,7 @@ LICENSE:


====================================================
qoqo 0.8.0
qoqo 0.8.1
https://github.com/HQSquantumsimulations/qoqo
by HQS Quantum Simulations <[email protected]>
Quantum computing circuit toolkit. Python interface of roqoqo
Expand Down Expand Up @@ -7922,7 +7922,7 @@ LICENSE:


====================================================
qoqo-macros 0.8.0
qoqo-macros 0.8.1
by HQS Quantum Simulations <[email protected]>
Macros for the qoqo crate
License: Apache-2.0
Expand Down Expand Up @@ -9865,7 +9865,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


====================================================
roqoqo 0.8.0
roqoqo 0.8.1
https://github.com/HQSquantumsimulations/qoqo
by HQS Quantum Simulations <[email protected]>
Rust Quantum Computing Toolkit by HQS
Expand Down Expand Up @@ -10077,7 +10077,7 @@ LICENSE:


====================================================
roqoqo-derive 0.8.0
roqoqo-derive 0.8.1
by HQS Quantum Simulations <[email protected]>
Maros for the roqoqo crate
License: Apache-2.0
Expand Down Expand Up @@ -10288,7 +10288,7 @@ LICENSE:


====================================================
roqoqo-test 0.8.0
roqoqo-test 0.8.1
https://github.com/HQSquantumsimulations/qoqo
by HQS Quantum Simulations <[email protected]>
Testing helper functions for roqoqo toolkit
Expand Down
2 changes: 1 addition & 1 deletion qoqo/src/measurements/basis_rotation_measurement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ impl BasisRotationWrapper {
/// ValueError: Cannot serialize Measurement to bytes.
pub fn _internal_to_bincode(&self) -> PyResult<(&'static str, Py<PyByteArray>)> {
let serialized = serialize(&self.internal).map_err(|_| {
PyValueError::new_err("Cannot serialize BasisRoationMeasurement to bytes")
PyValueError::new_err("Cannot serialize BasisRotationMeasurement to bytes")
})?;
let b: Py<PyByteArray> = Python::with_gil(|py| -> Py<PyByteArray> {
PyByteArray::new(py, &serialized[..]).into()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ impl CheatedBasisRotationWrapper {
/// ValueError: Cannot serialize Measurement to bytes.
pub fn _internal_to_bincode(&self) -> PyResult<(&'static str, Py<PyByteArray>)> {
let serialized = serialize(&self.internal).map_err(|_| {
PyValueError::new_err("Cannot serialize BasisRoationMeasurement to bytes")
PyValueError::new_err("Cannot serialize CheatedBasisRotationMeasurement to bytes")
})?;
let b: Py<PyByteArray> = Python::with_gil(|py| -> Py<PyByteArray> {
PyByteArray::new(py, &serialized[..]).into()
Expand Down
5 changes: 2 additions & 3 deletions qoqo/src/measurements/cheated_measurement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,8 @@ impl CheatedWrapper {
/// Raises:
/// ValueError: Cannot serialize Measurement to bytes.
pub fn _internal_to_bincode(&self) -> PyResult<(&'static str, Py<PyByteArray>)> {
let serialized = serialize(&self.internal).map_err(|_| {
PyValueError::new_err("Cannot serialize BasisRoationMeasurement to bytes")
})?;
let serialized = serialize(&self.internal)
.map_err(|_| PyValueError::new_err("Cannot serialize CheatedMeasurement to bytes"))?;
let b: Py<PyByteArray> = Python::with_gil(|py| -> Py<PyByteArray> {
PyByteArray::new(py, &serialized[..]).into()
});
Expand Down
5 changes: 2 additions & 3 deletions qoqo/src/measurements/classical_register_measurement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,8 @@ impl ClassicalRegisterWrapper {
/// Raises:
/// ValueError: Cannot serialize Measurement to bytes.
pub fn _internal_to_bincode(&self) -> PyResult<(&'static str, Py<PyByteArray>)> {
let serialized = serialize(&self.internal).map_err(|_| {
PyValueError::new_err("Cannot serialize BasisRoationMeasurement to bytes")
})?;
let serialized = serialize(&self.internal)
.map_err(|_| PyValueError::new_err("Cannot serialize ClassicalRegister to bytes"))?;
let b: Py<PyByteArray> = Python::with_gil(|py| -> Py<PyByteArray> {
PyByteArray::new(py, &serialized[..]).into()
});
Expand Down
232 changes: 232 additions & 0 deletions qoqo/src/operations/pragma_operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1411,3 +1411,235 @@ impl PyObjectProtocol for PragmaChangeDeviceWrapper {
}
}
}

#[cfg(test)]
mod tests {
use crate::operations::*;
use bincode::serialize;
use roqoqo::operations::*;
use std::collections::HashSet;

/// Test involved_qubits function for Pragmas with All
#[test]
fn test_pyo3_involved_qubits_all_change_device() {
let wrapped: Operation = PragmaActiveReset::new(0).into();
let input_definition: Operation = PragmaChangeDevice::new(&wrapped).unwrap().into();

pyo3::prepare_freethreaded_python();
let gil = pyo3::Python::acquire_gil();
let py = gil.python();
let operation = convert_operation_to_pyobject(input_definition).unwrap();
let to_involved = operation.call_method0(py, "involved_qubits").unwrap();
let involved_op: HashSet<&str> = HashSet::extract(to_involved.as_ref(py)).unwrap();
let mut involved_param: HashSet<&str> = HashSet::new();
involved_param.insert("All");
assert_eq!(involved_op, involved_param);

assert!(PragmaChangeDeviceWrapper::new().is_err());
}

#[test]
fn test_pyo3_format_repr_change_device() {
let wrapped: Operation = PragmaActiveReset::new(0).into();
let input_measurement: Operation = PragmaChangeDevice::new(&wrapped).unwrap().into();
let format_repr = format!("PragmaChangeDevice {{ wrapped_tags: {:?}, wrapped_hqslang: {:?}, wrapped_operation: {:?} }}", wrapped.tags(), wrapped.hqslang(), serialize(&wrapped).unwrap());

pyo3::prepare_freethreaded_python();
let gil = pyo3::Python::acquire_gil();
let py = gil.python();
let operation = convert_operation_to_pyobject(input_measurement).unwrap();
let to_format = operation.call_method1(py, "__format__", ("",)).unwrap();
let format_op: &str = <&str>::extract(to_format.as_ref(py)).unwrap();
let to_repr = operation.call_method0(py, "__repr__").unwrap();
let repr_op: &str = <&str>::extract(to_repr.as_ref(py)).unwrap();
assert_eq!(format_op, format_repr);
assert_eq!(repr_op, format_repr);
}

#[test]
fn test_pyo3_copy_deepcopy_change_device() {
let wrapped: Operation = PragmaActiveReset::new(0).into();
let input_measurement: Operation = PragmaChangeDevice::new(&wrapped).unwrap().into();

pyo3::prepare_freethreaded_python();
let gil = pyo3::Python::acquire_gil();
let py = gil.python();
let operation = convert_operation_to_pyobject(input_measurement).unwrap();
let copy_op = operation.call_method0(py, "__copy__").unwrap();
let deepcopy_op = operation.call_method1(py, "__deepcopy__", ("",)).unwrap();
let copy_deepcopy_param = operation.clone();

let comparison_copy = bool::extract(
copy_op
.as_ref(py)
.call_method1("__eq__", (copy_deepcopy_param.clone(),))
.unwrap(),
)
.unwrap();
assert!(comparison_copy);
let comparison_deepcopy = bool::extract(
deepcopy_op
.as_ref(py)
.call_method1("__eq__", (copy_deepcopy_param,))
.unwrap(),
)
.unwrap();
assert!(comparison_deepcopy);
}

#[test]
fn test_pyo3_tags_simple_change_device() {
let wrapped: Operation = PragmaActiveReset::new(0).into();
let input_measurement: Operation = PragmaChangeDevice::new(&wrapped).unwrap().into();

pyo3::prepare_freethreaded_python();
let gil = pyo3::Python::acquire_gil();
let py = gil.python();
let operation = convert_operation_to_pyobject(input_measurement).unwrap();
let to_tag = operation.call_method0(py, "tags").unwrap();
let tags_op: &Vec<&str> = &Vec::extract(to_tag.as_ref(py)).unwrap();
let tags_param: &[&str] = &["Operation", "PragmaOperation", "PragmaChangeDevice"];
assert_eq!(tags_op, tags_param);
}

#[test]
fn test_pyo3_hqslang_change_device() {
let wrapped: Operation = PragmaActiveReset::new(0).into();
let input_measurement: Operation = PragmaChangeDevice::new(&wrapped).unwrap().into();

pyo3::prepare_freethreaded_python();
let gil = pyo3::Python::acquire_gil();
let py = gil.python();
let operation = convert_operation_to_pyobject(input_measurement).unwrap();
let hqslang_op: String =
String::extract(operation.call_method0(py, "hqslang").unwrap().as_ref(py)).unwrap();
assert_eq!(hqslang_op, "PragmaChangeDevice".to_string());
}

#[test]
fn test_pyo3_is_parametrized_change_device() {
let wrapped: Operation = PragmaActiveReset::new(0).into();
let input_measurement: Operation = PragmaChangeDevice::new(&wrapped).unwrap().into();

pyo3::prepare_freethreaded_python();
let gil = pyo3::Python::acquire_gil();
let py = gil.python();
let operation = convert_operation_to_pyobject(input_measurement).unwrap();
assert!(!bool::extract(
operation
.call_method0(py, "is_parametrized")
.unwrap()
.as_ref(py)
)
.unwrap());
}

#[test]
fn test_pyo3_substitute_parameters() {
let wrapped: Operation = PragmaActiveReset::new(0).into();
let first_op: Operation = PragmaChangeDevice::new(&wrapped.clone()).unwrap().into();
let second_op: Operation = PragmaChangeDevice::new(&wrapped).unwrap().into();

pyo3::prepare_freethreaded_python();
let gil = pyo3::Python::acquire_gil();
let py = gil.python();
let operation = convert_operation_to_pyobject(first_op).unwrap();
let mut substitution_dict: HashMap<&str, f64> = HashMap::new();
substitution_dict.insert("test", 1.0);
let substitute_op = operation
.call_method1(py, "substitute_parameters", (substitution_dict,))
.unwrap();
let substitute_param = convert_operation_to_pyobject(second_op).unwrap();

let comparison = bool::extract(
substitute_op
.as_ref(py)
.call_method1("__eq__", (substitute_param.clone(),))
.unwrap(),
)
.unwrap();
assert!(comparison);
}

#[test]
fn test_pyo3_remap_qubits() {
let wrapped: Operation = PragmaActiveReset::new(0).into();
let first_op: Operation = PragmaChangeDevice::new(&wrapped.clone()).unwrap().into();
let second_op: Operation = PragmaChangeDevice::new(&wrapped).unwrap().into();

pyo3::prepare_freethreaded_python();
let gil = pyo3::Python::acquire_gil();
let py = gil.python();
let operation = convert_operation_to_pyobject(first_op).unwrap();

let mut qubit_mapping: HashMap<usize, usize> = HashMap::new();
qubit_mapping.insert(0, 0);
let remapped_op = operation
.call_method1(py, "remap_qubits", (qubit_mapping,))
.unwrap();
let comparison_op = convert_operation_to_pyobject(second_op).unwrap();

let comparison = bool::extract(
remapped_op
.call_method1(py, "__eq__", (comparison_op,))
.unwrap()
.as_ref(py),
)
.unwrap();
assert!(comparison);
}

#[test]
fn test_pyo3_remap_qubits_error() {
let wrapped: Operation = PragmaActiveReset::new(0).into();
let first_op: Operation = PragmaChangeDevice::new(&wrapped.clone()).unwrap().into();

pyo3::prepare_freethreaded_python();
let gil = pyo3::Python::acquire_gil();
let py = gil.python();
let operation = convert_operation_to_pyobject(first_op).unwrap();

let mut qubit_mapping: HashMap<usize, usize> = HashMap::new();
qubit_mapping.insert(0, 2);
let remapped_op = operation.call_method1(py, "remap_qubits", (qubit_mapping,));
assert!(remapped_op.is_err());
}

#[test]
fn test_pyo3_richcmp_change_device() {
let wrapped_1: Operation = PragmaActiveReset::new(0).into();
let definition_1: Operation = PragmaChangeDevice::new(&wrapped_1).unwrap().into();
let wrapped_2: Operation = PragmaActiveReset::new(1).into();
let definition_2: Operation = PragmaChangeDevice::new(&wrapped_2).unwrap().into();

pyo3::prepare_freethreaded_python();
let gil = pyo3::Python::acquire_gil();
let py = gil.python();
let operation_one = convert_operation_to_pyobject(definition_1).unwrap();
let operation_two = convert_operation_to_pyobject(definition_2).unwrap();

let comparison = bool::extract(
operation_one
.as_ref(py)
.call_method1("__eq__", (operation_two.clone(),))
.unwrap(),
)
.unwrap();
assert!(!comparison);

let comparison = bool::extract(
operation_one
.as_ref(py)
.call_method1("__ne__", (operation_two.clone(),))
.unwrap(),
)
.unwrap();
assert!(comparison);

let comparison = operation_one.call_method1(py, "__eq__", (vec!["fails"],));
assert!(comparison.is_err());

let comparison = operation_one.call_method1(py, "__ge__", (operation_two,));
assert!(comparison.is_err());
}
}
2 changes: 1 addition & 1 deletion qoqo/src/quantum_program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ impl QuantumProgramWrapper {
let (name, encoded) = get_measurement_internal
.extract::<(&str, &[u8])>()
.map_err(|_| PyTypeError::new_err("measurement is not of type Measurement. Are you using different versions of roqoqo?"))?;
match name{
match name {
"BasisRotation" => {
let measure: measurements::BasisRotation = deserialize(encoded).map_err(|_| PyTypeError::new_err("measurement is not of type Measurement. Are you using different versions of roqoqo?"))?;
Ok( Self{internal: QuantumProgram::BasisRotation{measurement: measure, input_parameter_names}})
Expand Down
3 changes: 3 additions & 0 deletions qoqo/tests/integration/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,8 @@ mod operations;
#[cfg(test)]
mod circuit;

#[cfg(test)]
mod quantum_program;

#[cfg(test)]
mod measurements;
Loading

0 comments on commit 4db72be

Please sign in to comment.