use crate::CircuitWrapper;
use num_complex::Complex64;
use numpy::{PyArray2, ToPyArray};
use pyo3::exceptions::{PyRuntimeError, PyValueError};
use pyo3::prelude::*;
use pyo3::types::PySet;
use qoqo_calculator::CalculatorFloat;
use qoqo_calculator_pyo3::convert_into_calculator_float;
use qoqo_calculator_pyo3::CalculatorFloatWrapper;
use qoqo_macros::*;
use roqoqo::operations::*;
#[cfg(feature = "json_schema")]
use roqoqo::ROQOQO_VERSION;
use std::collections::HashMap;
#[allow(clippy::upper_case_acronyms)]
#[wrap(
Operate,
Rotate,
OperateMultiQubit,
OperateGate,
OperateMultiQubitGate,
JsonSchema
)]
pub struct MultiQubitMS {
qubits: Vec<usize>,
theta: CalculatorFloat,
}
#[allow(clippy::upper_case_acronyms)]
#[wrap(
Operate,
Rotate,
OperateMultiQubit,
OperateGate,
OperateMultiQubitGate,
JsonSchema
)]
pub struct MultiQubitZZ {
qubits: Vec<usize>,
theta: CalculatorFloat,
}
#[cfg(feature = "unstable_operation_definition")]
#[pyclass(name = "CallDefinedGate", module = "qoqo")]
#[derive(Debug, Clone, PartialEq)]
pub struct CallDefinedGateWrapper {
pub internal: CallDefinedGate,
}
#[cfg(feature = "unstable_operation_definition")]
insert_pyany_to_operation!(
"CallDefinedGate" =>{
let gatenm = op.call_method0("gate_name")
.map_err(|_| QoqoError::ConversionError)?;
let gate_name: String = gatenm.extract().map_err(|_| QoqoError::ConversionError)?;
let qbts = op.call_method0("qubits")
.map_err(|_| QoqoError::ConversionError)?;
let qubits: Vec<usize> = qbts.extract()
.map_err(|_| QoqoError::ConversionError)?;
let params = op.call_method0("free_parameters")
.map_err(|_| QoqoError::ConversionError)?;
let param_vec: Bound<pyo3::types::PyList> = params.extract().map_err(|_| QoqoError::ConversionError)?;
let mut free_parameters: Vec<CalculatorFloat> = vec![];
for param in pyo3::types::PyListMethods::iter(¶m_vec) {
free_parameters.push(convert_into_calculator_float(¶m.as_borrowed()).map_err(|_| QoqoError::ConversionError)?);
}
Ok(CallDefinedGate::new(gate_name, qubits, free_parameters).into())
}
);
#[cfg(feature = "unstable_operation_definition")]
insert_operation_to_pyobject!(
Operation::CallDefinedGate(internal) => {
{
let pyref: Py<CallDefinedGateWrapper> =
Py::new(py, CallDefinedGateWrapper { internal }).unwrap();
let pyobject: PyObject = pyref.to_object(py);
Ok(pyobject)
}
}
);
#[cfg(feature = "unstable_operation_definition")]
#[pymethods]
impl CallDefinedGateWrapper {
#[new]
fn new(
gate_name: String,
qubits: Vec<usize>,
free_parameters: Vec<Py<PyAny>>,
) -> PyResult<Self> {
let free_parameters_cf: Vec<CalculatorFloat> =
Python::with_gil(|py| -> PyResult<Vec<CalculatorFloat>> {
let mut a = vec![];
for param in free_parameters {
a.push(convert_into_calculator_float(param.bind(py)).map_err(|_| {
pyo3::exceptions::PyTypeError::new_err(
"Argument gate time cannot be converted to CalculatorFloat",
)
})?)
}
Ok(a)
})?;
Ok(Self {
internal: CallDefinedGate::new(gate_name, qubits, free_parameters_cf),
})
}
fn gate_name(&self) -> String {
self.internal.gate_name().clone()
}
fn qubits(&self) -> Vec<usize> {
self.internal.qubits().clone()
}
fn free_parameters(&self) -> Vec<CalculatorFloatWrapper> {
self.internal
.free_parameters()
.iter()
.map(|param| CalculatorFloatWrapper {
internal: param.clone(),
})
.collect::<Vec<CalculatorFloatWrapper>>()
}
fn involved_qubits(&self) -> PyObject {
let pyobject: PyObject = Python::with_gil(|py| -> PyObject {
PySet::new(py, &[self.internal.qubits().clone()])
.unwrap()
.to_object(py)
});
pyobject
}
fn tags(&self) -> Vec<String> {
self.internal.tags().iter().map(|s| s.to_string()).collect()
}
fn hqslang(&self) -> &'static str {
self.internal.hqslang()
}
fn is_parametrized(&self) -> bool {
self.internal.is_parametrized()
}
fn substitute_parameters(
&self,
substitution_parameters: std::collections::HashMap<String, f64>,
) -> PyResult<Self> {
let mut calculator = qoqo_calculator::Calculator::new();
for (key, val) in substitution_parameters.iter() {
calculator.set_variable(key, *val);
}
Ok(Self {
internal: self
.internal
.substitute_parameters(&calculator)
.map_err(|x| {
pyo3::exceptions::PyRuntimeError::new_err(format!(
"Parameter Substitution failed: {:?}",
x
))
})?,
})
}
fn remap_qubits(&self, mapping: std::collections::HashMap<usize, usize>) -> PyResult<Self> {
let new_internal = self
.internal
.remap_qubits(&mapping)
.map_err(|_| pyo3::exceptions::PyRuntimeError::new_err("Qubit remapping failed: "))?;
Ok(Self {
internal: new_internal,
})
}
fn __copy__(&self) -> CallDefinedGateWrapper {
self.clone()
}
fn __deepcopy__(&self, _memodict: Py<PyAny>) -> CallDefinedGateWrapper {
self.clone()
}
fn __format__(&self, _format_spec: &str) -> PyResult<String> {
Ok(format!("{:?}", self.internal))
}
fn __repr__(&self) -> PyResult<String> {
Ok(format!("{:?}", self.internal))
}
fn __richcmp__(
&self,
other: &Bound<PyAny>,
op: pyo3::class::basic::CompareOp,
) -> PyResult<bool> {
let other: Operation =
crate::operations::convert_pyany_to_operation(other).map_err(|_| {
pyo3::exceptions::PyTypeError::new_err(
"Right hand side cannot be converted to Operation",
)
})?;
match op {
pyo3::class::basic::CompareOp::Eq => {
Ok(Operation::from(self.internal.clone()) == other)
}
pyo3::class::basic::CompareOp::Ne => {
Ok(Operation::from(self.internal.clone()) != other)
}
_ => 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!(CallDefinedGate);
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) =
CallDefinedGate::minimum_supported_roqoqo_version(&self.internal);
format!("{}.{}.{}", min_version.0, min_version.1, min_version.2)
}
}