use std::collections::HashSet;
use crate::{QoqoError, QOQO_VERSION};
use pyo3::exceptions::{PyIndexError, PyTypeError, PyValueError};
use pyo3::prelude::*;
use pyo3::types::{PyByteArray, PyDict};
use roqoqo::{Circuit, CircuitDag, ROQOQO_VERSION};
use crate::operations::{convert_operation_to_pyobject, convert_pyany_to_operation};
use crate::CircuitWrapper;
#[pymodule]
fn circuitdag(_py: Python, module: &Bound<PyModule>) -> PyResult<()> {
module.add_class::<CircuitDagWrapper>()?;
Ok(())
}
#[pyclass(from_py_object, name = "CircuitDag", module = "qoqo")]
#[derive(Clone, Debug, PartialEq)]
pub struct CircuitDagWrapper {
pub internal: CircuitDag,
}
impl Default for CircuitDagWrapper {
fn default() -> Self {
Self::new(100, 300)
}
}
impl CircuitDagWrapper {
pub fn from_pyany(input: &Bound<PyAny>) -> PyResult<CircuitDag> {
if let Ok(try_downcast) = input.extract::<CircuitDagWrapper>() {
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 CircuitDag: 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 CircuitDag: Cast to binary representation failed".to_string())
})?;
bincode::serde::decode_from_slice(&bytes[..], bincode::config::legacy()).map_err(|err| {
PyTypeError::new_err(format!(
"Python object cannot be converted to qoqo CircuitDag: Deserialization failed: {err}"
))}
).map(|(deserialized, _)| deserialized)
}
}
}
#[pymethods]
impl CircuitDagWrapper {
#[new]
#[pyo3(signature=(node_number = 100, edge_number = 300))]
pub fn new(node_number: usize, edge_number: usize) -> Self {
Self {
internal: CircuitDag::with_capacity(node_number, edge_number),
}
}
#[pyo3(text_signature = "(circuit)")]
pub fn from_circuit(&self, circuit: &Bound<PyAny>) -> PyResult<Self> {
let circuit = crate::convert_into_circuit(circuit).unwrap();
Ok(Self {
internal: CircuitDag::from(circuit),
})
}
#[pyo3(text_signature = "($self)")]
pub fn to_circuit(&self) -> PyResult<CircuitWrapper> {
Ok(CircuitWrapper {
internal: Circuit::from(self.internal.clone()),
})
}
#[pyo3(text_signature = "($self, op)")]
pub fn add_to_back(&mut self, op: &Bound<PyAny>) -> PyResult<Option<usize>> {
let operation = convert_pyany_to_operation(op).map_err(|x| {
PyTypeError::new_err(format!("Cannot convert python object to Operation {x:?}"))
})?;
Ok(self.internal.add_to_back(operation))
}
#[pyo3(text_signature = "($self, op)")]
pub fn add_to_front(&mut self, op: &Bound<PyAny>) -> PyResult<Option<usize>> {
let operation = convert_pyany_to_operation(op).map_err(|x| {
PyTypeError::new_err(format!("Cannot convert python object to Operation {x:?}"))
})?;
Ok(self.internal.add_to_front(operation))
}
#[pyo3(text_signature = "($self, already_executed, to_be_executed)")]
pub fn execution_blocked(
&self,
already_executed: Vec<usize>,
to_be_executed: usize,
) -> Vec<usize> {
self.internal
.execution_blocked(already_executed.as_slice(), &to_be_executed)
}
#[pyo3(text_signature = "($self, already_executed, to_be_executed)")]
pub fn blocking_predecessors(
&self,
already_executed: Vec<usize>,
to_be_executed: usize,
) -> Vec<usize> {
self.internal
.blocking_predecessors(already_executed.as_slice(), &to_be_executed)
}
#[pyo3(text_signature = "($self, already_executed, current_front_layer, to_be_executed)")]
pub fn new_front_layer(
&self,
already_executed: Vec<usize>,
current_front_layer: Vec<usize>,
to_be_executed: usize,
) -> PyResult<Vec<usize>> {
self.internal
.new_front_layer(
already_executed.as_slice(),
current_front_layer.as_slice(),
&to_be_executed,
)
.map_err(|_| {
PyValueError::new_err(
"The Operation to be executed is not in the current front layer.".to_string(),
)
})
}
#[pyo3(text_signature = "($self)")]
pub fn parallel_blocks(&self) -> Vec<Vec<usize>> {
let mut par_bl_vec: Vec<Vec<usize>> = Vec::new();
for block in self.internal.parallel_blocks() {
par_bl_vec.push(block.clone());
}
par_bl_vec
}
#[pyo3(text_signature = "($self, index)")]
pub fn get<'py>(&'py self, py: Python<'py>, index: usize) -> PyResult<Bound<'py, PyAny>> {
let operation = self
.internal
.get(index)
.ok_or_else(|| PyIndexError::new_err(format!("Index {index} out of range")))?
.clone();
convert_operation_to_pyobject(operation, py)
}
#[pyo3(text_signature = "($self)")]
pub fn __copy__(&self) -> CircuitDagWrapper {
self.clone()
}
fn __richcmp__(
&self,
other: &Bound<PyAny>,
op: pyo3::class::basic::CompareOp,
) -> PyResult<bool> {
let other = crate::convert_into_circuitdag(other);
match op {
pyo3::class::basic::CompareOp::Eq => match other {
Ok(dag) => Ok(self.internal == dag),
_ => Ok(false),
},
pyo3::class::basic::CompareOp::Ne => match other {
Ok(dag) => Ok(self.internal != dag),
_ => Ok(true),
},
_ => Err(pyo3::exceptions::PyNotImplementedError::new_err(
"Other comparison not implemented",
)),
}
}
#[pyo3(text_signature = "($self)")]
fn _qoqo_versions(&self) -> (String, String) {
let mut rsplit = ROQOQO_VERSION.split('.').take(2);
let mut qsplit = QOQO_VERSION.split('.').take(2);
let rver = format!(
"{}.{}",
rsplit.next().expect("ROQOQO_VERSION badly formatted"),
rsplit.next().expect("ROQOQO_VERSION badly formatted")
);
let qver = format!(
"{}.{}",
qsplit.next().expect("QOQO_VERSION badly formatted"),
qsplit.next().expect("QOQO_VERSION badly formatted")
);
(rver, qver)
}
#[pyo3(text_signature = "($self)")]
pub fn to_bincode(&self) -> PyResult<Py<PyByteArray>> {
let serialized =
bincode::serde::encode_to_vec(&self.internal, bincode::config::legacy())
.map_err(|_| PyValueError::new_err("Cannot serialize CircuitDag to bytes"))?;
let b: Py<PyByteArray> = Python::attach(|py| -> Py<PyByteArray> {
PyByteArray::new(py, &serialized[..]).into()
});
Ok(b)
}
#[staticmethod]
#[pyo3(text_signature = "(input)")]
pub fn from_bincode(input: &Bound<PyAny>) -> PyResult<Self> {
let bytes = input
.extract::<Vec<u8>>()
.map_err(|_| PyTypeError::new_err("Input cannot be converted to byte array"))?;
Ok(Self {
internal: bincode::serde::decode_from_slice(&bytes[..], bincode::config::legacy())
.map_err(|_| PyValueError::new_err("Input cannot be deserialized to CircuitDag"))?
.0,
})
}
pub fn successors(&self, node: usize) -> Vec<usize> {
self.internal.successors(node).map(|n| n.index()).collect()
}
#[pyo3(text_signature = "($self)")]
pub fn commuting_operations(&self) -> Vec<usize> {
self.internal.commuting_operations().to_vec()
}
#[pyo3(text_signature = "($self)")]
pub fn first_parallel_block(&self) -> HashSet<usize> {
self.internal.first_parallel_block().clone()
}
#[pyo3(text_signature = "($self)")]
pub fn last_parallel_block(&self) -> HashSet<usize> {
self.internal.last_parallel_block().clone()
}
#[pyo3(text_signature = "($self)")]
pub fn first_operation_involving_qubit<'py>(
&'py self,
py: Python<'py>,
) -> PyResult<Bound<'py, PyDict>> {
self.internal
.first_operation_involving_qubit()
.into_pyobject(py)
.map_err(|_| {
PyValueError::new_err("Cannot convert Rust object to a Python dictionary")
})
}
#[pyo3(text_signature = "($self)")]
pub fn last_operation_involving_qubit<'py>(
&'py self,
py: Python<'py>,
) -> PyResult<Bound<'py, PyDict>> {
self.internal
.last_operation_involving_qubit()
.into_pyobject(py)
.map_err(|_| {
PyValueError::new_err("Cannot convert Rust object to a Python dictionary")
})
}
#[pyo3(text_signature = "($self)")]
pub fn first_operation_involving_classical<'py>(
&'py self,
py: Python<'py>,
) -> PyResult<Bound<'py, PyDict>> {
self.internal
.first_operation_involving_classical()
.into_pyobject(py)
.map_err(|_| {
PyValueError::new_err("Cannot convert Rust object to a Python dictionary")
})
}
#[pyo3(text_signature = "($self)")]
pub fn last_operation_involving_classical<'py>(
&'py self,
py: Python<'py>,
) -> PyResult<Bound<'py, PyDict>> {
self.internal
.last_operation_involving_classical()
.into_pyobject(py)
.map_err(|_| {
PyValueError::new_err("Cannot convert Rust object to a Python dictionary")
})
}
}
pub fn convert_into_circuitdag(input: &Bound<PyAny>) -> Result<CircuitDag, QoqoError> {
if let Ok(try_downcast) = input.extract::<CircuitDagWrapper>() {
return Ok(try_downcast.internal);
}
let get_bytes = input
.call_method0("to_bincode")
.map_err(|_| QoqoError::CannotExtractObject)?;
let bytes = get_bytes
.extract::<Vec<u8>>()
.map_err(|_| QoqoError::CannotExtractObject)?;
bincode::serde::decode_from_slice(&bytes[..], bincode::config::legacy())
.map_err(|_| QoqoError::CannotExtractObject)
.map(|(deserialized, _)| deserialized)
}