use super::CheatedInputWrapper;
use crate::CircuitWrapper;
use bincode::{deserialize, serialize};
use pyo3::exceptions::{PyRuntimeError, PyTypeError, PyValueError};
use pyo3::prelude::*;
use pyo3::types::PyByteArray;
use roqoqo::measurements::Cheated;
use roqoqo::prelude::*;
use roqoqo::registers::{BitOutputRegister, ComplexOutputRegister, FloatOutputRegister};
use roqoqo::Circuit;
#[cfg(feature = "json_schema")]
use roqoqo::ROQOQO_VERSION;
use std::collections::HashMap;
#[pyclass(name = "Cheated", module = "qoqo.measurements")]
#[derive(Clone, Debug)]
pub struct CheatedWrapper {
pub internal: Cheated,
}
#[pymethods]
impl CheatedWrapper {
#[new]
#[pyo3(signature=(constant_circuit, circuits, input))]
pub fn new(
constant_circuit: Option<Py<PyAny>>,
circuits: Vec<Py<PyAny>>,
input: Py<PyAny>,
) -> PyResult<Self> {
Python::with_gil(|py| -> PyResult<Self> {
let mut new_circuits: Vec<Circuit> = Vec::new();
for c in circuits.into_iter() {
let tmp_c = CircuitWrapper::from_pyany(c.bind(py)).map_err(|err| {
PyTypeError::new_err(format!(
"`circuits` argument is not a list of qoqo Circuits: {}",
err
))
})?;
new_circuits.push(tmp_c)
}
let new_constant: Option<Circuit> = match constant_circuit {
None => None,
Some(c) => {
let tmp_c = CircuitWrapper::from_pyany(c.bind(py)).map_err(|err| {
PyTypeError::new_err(format!(
"`constant_circuit` argument is not None or a qoqo Circuit: {}",
err
))
})?;
Some(tmp_c)
}
};
let input = CheatedInputWrapper::from_pyany(input.bind(py)).map_err(|err| {
PyTypeError::new_err(format!(
"`input` argument is not a qoqo CheatedInput: {}",
err
))
})?;
Ok(Self {
internal: Cheated {
input,
constant_circuit: new_constant,
circuits: new_circuits,
},
})
})
}
pub fn evaluate(
&mut self,
input_bit_registers: &Bound<PyAny>,
float_registers: HashMap<String, FloatOutputRegister>,
complex_registers: HashMap<String, ComplexOutputRegister>,
) -> PyResult<Option<HashMap<String, f64>>> {
let mut bit_registers: HashMap<String, BitOutputRegister> = HashMap::new();
let bit_registers_bool: PyResult<HashMap<String, Vec<Vec<bool>>>> =
input_bit_registers.extract::<HashMap<String, BitOutputRegister>>();
if let Ok(try_downcast) = bit_registers_bool {
bit_registers = try_downcast
} else {
let tmp_bit_registers =
input_bit_registers.extract::<HashMap<String, Vec<Vec<usize>>>>()?;
for (name, output_reg) in tmp_bit_registers {
let mut tmp_output_reg: Vec<Vec<bool>> = Vec::with_capacity(output_reg.len());
for reg in output_reg {
tmp_output_reg.push(reg.into_iter().map(|x| !matches!(x, 0)).collect());
}
bit_registers.insert(name, tmp_output_reg);
}
}
self.internal
.evaluate(bit_registers, float_registers, complex_registers)
.map_err(|x| {
PyRuntimeError::new_err(format!("Error evaluating cheated measurement {:?}", x))
})
}
pub fn circuits(&self) -> Vec<CircuitWrapper> {
self.internal
.circuits()
.map(|c| CircuitWrapper {
internal: c.clone(),
})
.collect()
}
pub fn constant_circuit(&self) -> Option<CircuitWrapper> {
self.internal
.constant_circuit()
.clone()
.map(|c| CircuitWrapper { internal: c })
}
pub fn input(&self) -> CheatedInputWrapper {
let input = self.internal.input.clone();
CheatedInputWrapper { internal: input }
}
pub fn measurement_type(&self) -> &'static str {
"Cheated"
}
pub fn substitute_parameters(
&self,
substituted_parameters: HashMap<String, f64>,
) -> PyResult<Self> {
Ok(Self {
internal: self
.internal
.substitute_parameters(substituted_parameters)
.map_err(|x| {
PyRuntimeError::new_err(format!(
"Error substituting symbolic parameters {:?}",
x
))
})?,
})
}
pub fn _internal_to_bincode(&self) -> PyResult<(&'static str, Py<PyByteArray>)> {
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_bound(py, &serialized[..]).into()
});
Ok(("Cheated", b))
}
pub fn to_bincode(&self) -> PyResult<Py<PyByteArray>> {
let serialized = serialize(&self.internal)
.map_err(|_| PyValueError::new_err("Cannot serialize Cheated to bytes"))?;
let b: Py<PyByteArray> = Python::with_gil(|py| -> Py<PyByteArray> {
PyByteArray::new_bound(py, &serialized[..]).into()
});
Ok(b)
}
#[staticmethod]
pub fn from_bincode(input: &Bound<PyAny>) -> PyResult<Self> {
let bytes = input
.as_gil_ref()
.extract::<Vec<u8>>()
.map_err(|_| PyTypeError::new_err("Input cannot be converted to byte array"))?;
Ok(Self {
internal: deserialize(&bytes[..])
.map_err(|_| PyValueError::new_err("Input cannot be deserialized to Cheated"))?,
})
}
pub fn to_json(&self) -> PyResult<String> {
serde_json::to_string(&self.internal)
.map_err(|_| PyRuntimeError::new_err("Unexpected error serializing Cheated"))
}
#[staticmethod]
pub fn from_json(json_string: &str) -> PyResult<Self> {
Ok(Self {
internal: serde_json::from_str(json_string)
.map_err(|_| PyValueError::new_err("Cannot deserialize string to Cheated"))?,
})
}
pub fn __repr__(&self) -> String {
format!("{:?}", self.internal)
}
pub fn __copy__(&self) -> Self {
self.clone()
}
pub fn __deepcopy__(&self, _memodict: &Bound<PyAny>) -> Self {
self.clone()
}
fn __richcmp__(
&self,
other: CheatedWrapper,
op: pyo3::class::basic::CompareOp,
) -> PyResult<bool> {
match op {
pyo3::class::basic::CompareOp::Eq => Ok(self.internal == other.internal),
pyo3::class::basic::CompareOp::Ne => Ok(self.internal != other.internal),
_ => Err(pyo3::exceptions::PyNotImplementedError::new_err(
"Other comparison not implemented",
)),
}
}
#[cfg(feature = "json_schema")]
#[staticmethod]
pub fn json_schema() -> String {
let schema = schemars::schema_for!(Cheated);
serde_json::to_string_pretty(&schema).expect("Unexpected failure to serialize schema")
}
#[cfg(feature = "json_schema")]
#[staticmethod]
pub fn current_version() -> String {
ROQOQO_VERSION.to_string()
}
#[cfg(feature = "json_schema")]
pub fn min_supported_version(&self) -> String {
let min_version: (u32, u32, u32) =
Cheated::minimum_supported_roqoqo_version(&self.internal);
format!("{}.{}.{}", min_version.0, min_version.1, min_version.2)
}
}
impl CheatedWrapper {
pub fn from_pyany(input: &Bound<PyAny>) -> PyResult<Cheated> {
if let Ok(try_downcast) = input.extract::<CheatedWrapper>() {
Ok(try_downcast.internal)
} else {
let get_bytes = input.call_method0("to_bincode").map_err(|_| {
PyTypeError::new_err("Python object cannot be converted to qoqo Cheated: Cast to binary representation failed".to_string())
})?;
let bytes = get_bytes.extract::<Vec<u8>>().map_err(|_| {
PyTypeError::new_err("Python object cannot be converted to qoqo Cheated: Cast to binary representation failed".to_string())
})?;
deserialize(&bytes[..]).map_err(|err| {
PyTypeError::new_err(format!(
"Python object cannot be converted to qoqo Cheated: Deserialization failed: {}",
err
))
})
}
}
}