1#![deny(missing_docs)]
14#![deny(rustdoc::missing_crate_level_docs)]
15#![deny(missing_debug_implementations)]
16
17use pyo3::exceptions::PyValueError;
23use pyo3::prelude::*;
24
25use pyo3::types::PyDict;
26
27use pyo3::{wrap_pyfunction, wrap_pymodule};
28
29pub mod operations;
30
31pub mod measurements;
32
33pub mod devices;
34
35mod circuit;
36pub use circuit::{convert_into_circuit, CircuitWrapper, OperationIteratorWrapper};
37
38mod quantum_program;
39pub use quantum_program::{convert_into_quantum_program, QuantumProgramWrapper};
40
41pub mod noise_models;
42
43#[cfg(feature = "circuitdag")]
44mod circuitdag;
45#[cfg(feature = "circuitdag")]
46pub use circuitdag::{convert_into_circuitdag, CircuitDagWrapper};
47
48pub const QOQO_VERSION: &str = env!("CARGO_PKG_VERSION");
50
51use roqoqo::{operations::AVAILABLE_GATES_HQSLANG, RoqoqoBackendError, RoqoqoError};
52use struqture::spins::PlusMinusLindbladNoiseOperator;
53use struqture_py::spins::PlusMinusLindbladNoiseOperatorWrapper;
54use thiserror::Error;
55
56#[derive(Error, Debug, PartialEq)]
58pub enum QoqoError {
59 #[error("Converting PyAny to Operation not possible")]
61 ConversionError,
62 #[error("Cannot extract roqoqo object from python object")]
64 CannotExtractObject,
65 #[error("Package versions of qoqo and roqoqo do not match versions of qoqo object passed from python")]
71 VersionMismatch,
72 #[error(transparent)]
74 RoqoqoError(#[from] RoqoqoError),
75}
76
77#[derive(Error, Debug, PartialEq)]
79pub enum QoqoBackendError {
80 #[error("Cannot extract rust object from python object")]
82 CannotExtractObject,
83 #[error("Package versions of qoqo backend and roqoqo backend do not match versions of qoqo object passed from python")]
89 VersionMismatch,
90 #[error(transparent)]
92 RoqoqoBackendError(#[from] RoqoqoBackendError),
93}
94
95#[pyfunction]
97pub fn available_gates_hqslang() -> Vec<String> {
98 AVAILABLE_GATES_HQSLANG
99 .iter()
100 .map(|&s| String::from(s))
101 .collect::<Vec<String>>()
102}
103
104use std::sync::OnceLock;
105pub static STRUQTURE_VERSION: OnceLock<String> = OnceLock::new();
107pub static STRUQTURE_OPERATOR: OnceLock<Py<PyAny>> = OnceLock::new();
109
110pub(crate) fn get_operator<'py>(
111 py: Python<'py>,
112 noise: &PlusMinusLindbladNoiseOperator,
113) -> PyResult<Bound<'py, PyAny>> {
114 let version: &String = STRUQTURE_VERSION
115 .get()
116 .expect("Could not get STRUQTURE_VERSION");
117 if version.starts_with('1') {
118 let class: &Py<PyAny> = STRUQTURE_OPERATOR
119 .get()
120 .expect("No struqture operator found");
121 let json_string = serde_json::to_string(
122 &noise
123 .to_struqture_1()
124 .expect("Could not convert struqture 2 object to struqture 1"),
125 )
126 .expect("Could not serialize to JSON");
127 Ok(class
128 .bind(py)
129 .call_method1("from_json", (json_string.as_str(),))
130 .expect("Could not create struqture 1.x PlusMinusLindbladNoiseOperator from JSON")
131 .to_owned())
132 } else {
133 let pmlno = PlusMinusLindbladNoiseOperatorWrapper {
134 internal: noise.clone(),
135 };
136 pmlno
137 .into_pyobject(py)
138 .map_err(|_| {
139 PyValueError::new_err(
140 "Could not convert PlusMinusLindbladNoiseOperator into a python object.",
141 )
142 })
143 .map(|bound| bound.as_any().to_owned())
144 }
145}
146
147#[pymodule]
166fn qoqo(_py: Python, module: &Bound<PyModule>) -> PyResult<()> {
167 let binding = PyModule::import(_py, "importlib.metadata")
168 .expect("Could not import importlib.metadata module for function")
169 .getattr("version")
170 .expect("Could not get version function of importlib.metadata")
171 .call1(("struqture_py",))
172 .expect("Could not get version attribute of struqture_py")
173 .unbind();
174 let version: String = binding
175 .extract(_py)
176 .expect("Could not extract version string");
177 STRUQTURE_VERSION.get_or_init(|| version);
178 let operator: Py<PyAny> = _py
179 .import("struqture_py.spins")
180 .unwrap_or_else(|_| {
181 panic!("Could not import struqture_py.spins module for get_noise_operator")
182 })
183 .getattr("PlusMinusLindbladNoiseOperator")
184 .expect("Could not get PlusMinusLindbladOperator class")
185 .unbind();
186 STRUQTURE_OPERATOR.get_or_init(|| operator);
187
188 module.add_class::<CircuitWrapper>()?;
189 module.add_class::<QuantumProgramWrapper>()?;
190 #[cfg(feature = "circuitdag")]
191 module.add_class::<CircuitDagWrapper>()?;
192 module.add_function(wrap_pyfunction!(available_gates_hqslang, module)?)?;
193 let wrapper = wrap_pymodule!(operations::operations);
194 module.add_wrapped(wrapper)?;
195 let wrapper2 = wrap_pymodule!(measurements::measurements);
196 module.add_wrapped(wrapper2)?;
197 let wrapper3 = wrap_pymodule!(devices::devices);
198 module.add_wrapped(wrapper3)?;
199 let wrapper4 = wrap_pymodule!(noise_models::noise_models);
200 module.add_wrapped(wrapper4)?;
201 let system = PyModule::import(_py, "sys")?;
203 let binding = system.getattr("modules")?;
204 let system_modules: &Bound<PyDict> = binding.downcast()?;
205 system_modules.set_item("qoqo.operations", module.getattr("operations")?)?;
206 system_modules.set_item("qoqo.measurements", module.getattr("measurements")?)?;
207 system_modules.set_item("qoqo.devices", module.getattr("devices")?)?;
208 system_modules.set_item("qoqo.noise_models", module.getattr("noise_models")?)?;
209
210 Ok(())
211}