use pyo3::prelude::*;
use rigetti_pyo3::{create_init_submodule, impl_repr, py_function_sync_async};
use serde::{Deserialize, Serialize};
#[cfg(feature = "stubs")]
use pyo3_stub_gen::{
derive::{gen_stub_pyclass, gen_stub_pyclass_enum, gen_stub_pyfunction, gen_stub_pymethods},
impl_stub_type,
};
use qcs_api_client_openapi::models;
use crate::{
client::Qcs,
python::errors,
qpu::{get_isa, list_isas},
};
create_init_submodule! {
classes: [
Architecture,
Characteristic,
Edge,
Family,
InstructionSetArchitecture,
Node,
Operation,
OperationSite,
Parameter
],
errors: [
errors::SerializeISAError,
errors::GetISAError,
errors::ListISAsError
],
funcs: [
py_get_instruction_set_architecture,
py_get_instruction_set_architecture_async,
py_list_instruction_set_architectures,
py_list_instruction_set_architectures_async
],
}
impl_repr!(Architecture);
impl_repr!(Characteristic);
impl_repr!(Edge);
impl_repr!(InstructionSetArchitecture);
impl_repr!(Node);
impl_repr!(Operation);
impl_repr!(OperationSite);
impl_repr!(Parameter);
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
#[pyclass(module = "qcs_sdk.qpu.isa", eq, get_all, set_all)]
pub(crate) struct InstructionSetArchitecture {
architecture: Architecture,
benchmarks: Vec<Operation>,
instructions: Vec<Operation>,
name: String,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
#[pyclass(module = "qcs_sdk.qpu.isa", eq, get_all, set_all)]
struct Architecture {
pub edges: Vec<Edge>,
pub family: Option<PyFamily>,
pub nodes: Vec<Node>,
}
#[derive(
Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize,
)]
#[cfg_attr(feature = "stubs", gen_stub_pyclass_enum)]
#[cfg_attr(
feature = "python",
pyo3::pyclass(module = "qcs_sdk.qpu.isa", eq, rename_all = "SCREAMING_SNAKE_CASE")
)]
enum Family {
#[default]
None,
Full,
Aspen,
Ankaa,
}
#[derive(
Clone,
Debug,
Eq,
PartialEq,
Ord,
PartialOrd,
Hash,
Serialize,
Deserialize,
pyo3::FromPyObject,
pyo3::IntoPyObject,
)]
#[serde(untagged)]
enum PyFamily {
#[pyo3(transparent)]
Known(Family),
#[pyo3(transparent)]
Unknown(String),
}
#[cfg(feature = "stubs")]
impl_stub_type!(PyFamily = Family | String);
impl Default for PyFamily {
fn default() -> Self {
Self::Known(Family::default())
}
}
impl From<models::Family> for PyFamily {
fn from(family: models::Family) -> Self {
match family {
models::Family::None => PyFamily::Known(Family::None),
models::Family::Full => PyFamily::Known(Family::Full),
models::Family::Aspen => PyFamily::Known(Family::Aspen),
models::Family::Ankaa => PyFamily::Known(Family::Ankaa),
models::Family::UnrecognizedValue(s) => PyFamily::Unknown(s.clone()),
}
}
}
impl From<PyFamily> for models::Family {
fn from(family: PyFamily) -> Self {
match family {
PyFamily::Known(f) => f.into(),
PyFamily::Unknown(s) => models::Family::UnrecognizedValue(s),
}
}
}
impl From<Family> for models::Family {
fn from(family: Family) -> Self {
match family {
Family::None => models::Family::None,
Family::Full => models::Family::Full,
Family::Aspen => models::Family::Aspen,
Family::Ankaa => models::Family::Ankaa,
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
#[pyclass(module = "qcs_sdk.qpu.isa", eq, get_all, set_all)]
struct Edge {
pub node_ids: Vec<i64>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
#[pyclass(module = "qcs_sdk.qpu.isa", eq, get_all, set_all)]
struct Node {
pub node_id: i64,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
#[pyclass(module = "qcs_sdk.qpu.isa", eq, get_all, set_all)]
struct Characteristic {
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<f64>,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub node_ids: Option<Vec<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parameter_values: Option<Vec<f64>>,
pub timestamp: String,
pub value: f64,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
#[pyclass(module = "qcs_sdk.qpu.isa", eq, get_all, set_all)]
struct Parameter {
pub name: String,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
#[pyclass(module = "qcs_sdk.qpu.isa", eq, get_all, set_all)]
struct OperationSite {
pub characteristics: Vec<Characteristic>,
pub node_ids: Vec<i64>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
#[pyclass(module = "qcs_sdk.qpu.isa", eq, get_all, set_all)]
struct Operation {
pub characteristics: Vec<Characteristic>,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub node_count: Option<u64>,
pub parameters: Vec<Parameter>,
pub sites: Vec<OperationSite>,
}
#[cfg(feature = "python")]
#[derive(Debug, thiserror::Error)]
#[error("Failed to serialize instruction set architecture: {0}")]
pub struct SerializeIsaError(#[from] serde_json::Error);
impl From<models::InstructionSetArchitecture> for InstructionSetArchitecture {
fn from(isa: models::InstructionSetArchitecture) -> Self {
Self {
architecture: isa.architecture.into(),
benchmarks: convert_vec(isa.benchmarks),
instructions: convert_vec(isa.instructions),
name: isa.name,
}
}
}
impl From<InstructionSetArchitecture> for models::InstructionSetArchitecture {
fn from(isa: InstructionSetArchitecture) -> Self {
Self {
architecture: isa.architecture.into(),
benchmarks: convert_vec(isa.benchmarks),
instructions: convert_vec(isa.instructions),
name: isa.name,
}
}
}
impl From<models::Architecture> for Architecture {
fn from(arch: models::Architecture) -> Self {
Self {
edges: arch
.edges
.into_iter()
.map(|e| Edge {
node_ids: e.node_ids,
})
.collect(),
family: Some(arch.family.into()),
nodes: convert_vec(arch.nodes),
}
}
}
impl From<Architecture> for models::Architecture {
fn from(arch: Architecture) -> Self {
Self {
edges: arch
.edges
.into_iter()
.map(|e| models::Edge {
node_ids: e.node_ids,
})
.collect(),
family: arch.family.map(Into::into).unwrap_or_default(),
nodes: convert_vec(arch.nodes),
}
}
}
impl From<models::Edge> for Edge {
fn from(edge: models::Edge) -> Self {
Self {
node_ids: edge.node_ids,
}
}
}
impl From<Edge> for models::Edge {
fn from(edge: Edge) -> Self {
Self {
node_ids: edge.node_ids,
}
}
}
impl From<models::Node> for Node {
fn from(node: models::Node) -> Self {
Self {
node_id: node.node_id,
}
}
}
impl From<Node> for models::Node {
fn from(node: Node) -> Self {
Self {
node_id: node.node_id,
}
}
}
impl From<models::Characteristic> for Characteristic {
fn from(characteristic: models::Characteristic) -> Self {
Self {
error: characteristic.error,
name: characteristic.name,
node_ids: characteristic.node_ids,
parameter_values: characteristic.parameter_values,
timestamp: characteristic.timestamp,
value: characteristic.value,
}
}
}
impl From<Characteristic> for models::Characteristic {
fn from(characteristic: Characteristic) -> Self {
Self {
error: characteristic.error,
name: characteristic.name,
node_ids: characteristic.node_ids,
parameter_values: characteristic.parameter_values,
timestamp: characteristic.timestamp,
value: characteristic.value,
}
}
}
impl From<models::Parameter> for Parameter {
fn from(parameter: models::Parameter) -> Self {
Self {
name: parameter.name,
}
}
}
impl From<Parameter> for models::Parameter {
fn from(parameter: Parameter) -> Self {
Self {
name: parameter.name,
}
}
}
impl From<models::OperationSite> for OperationSite {
fn from(site: models::OperationSite) -> Self {
Self {
characteristics: convert_vec(site.characteristics),
node_ids: site.node_ids,
}
}
}
impl From<OperationSite> for models::OperationSite {
fn from(site: OperationSite) -> Self {
Self {
characteristics: convert_vec(site.characteristics),
node_ids: site.node_ids,
}
}
}
impl From<models::Operation> for Operation {
fn from(operation: models::Operation) -> Self {
Self {
characteristics: convert_vec(operation.characteristics),
name: operation.name,
node_count: operation.node_count,
parameters: convert_vec(operation.parameters),
sites: convert_vec(operation.sites),
}
}
}
impl From<Operation> for models::Operation {
fn from(operation: Operation) -> Self {
Self {
characteristics: convert_vec(operation.characteristics),
name: operation.name,
node_count: operation.node_count,
parameters: convert_vec(operation.parameters),
sites: convert_vec(operation.sites),
}
}
}
fn convert_vec<T, U>(vec: Vec<T>) -> Vec<U>
where
T: Into<U>,
{
vec.into_iter().map(Into::into).collect()
}
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl InstructionSetArchitecture {
#[new]
fn __new__(
architecture: Architecture,
benchmarks: Vec<Operation>,
instructions: Vec<Operation>,
name: String,
) -> Self {
Self {
architecture,
benchmarks,
instructions,
name,
}
}
#[staticmethod]
pub(crate) fn from_raw(json: &str) -> Result<Self, SerializeIsaError> {
Ok(serde_json::from_str(json)?)
}
#[pyo3(signature = (pretty = false))]
pub(crate) fn json(&self, pretty: bool) -> Result<String, SerializeIsaError> {
let data = {
if pretty {
serde_json::to_string_pretty(&self)
} else {
serde_json::to_string(&self)
}
}?;
Ok(data)
}
}
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl Operation {
#[new]
fn __new__(
characteristics: Vec<Characteristic>,
name: String,
parameters: Vec<Parameter>,
sites: Vec<OperationSite>,
) -> Self {
Self {
characteristics,
name,
node_count: None,
parameters,
sites,
}
}
}
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl OperationSite {
#[new]
fn __new__(characteristics: Vec<Characteristic>, node_ids: Vec<i64>) -> Self {
Self {
characteristics,
node_ids,
}
}
}
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl Parameter {
#[new]
fn __new__(name: String) -> Self {
Self { name }
}
}
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl Characteristic {
#[new]
fn __new__(name: String, timestamp: String, value: f64) -> Self {
Self {
error: None,
name,
node_ids: None,
parameter_values: None,
timestamp,
value,
}
}
}
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl Architecture {
#[new]
fn __new__(edges: Vec<Edge>, family: Option<PyFamily>, nodes: Vec<Node>) -> Self {
Self {
edges,
family,
nodes,
}
}
}
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl Node {
#[new]
fn __new__(node_id: i64) -> Self {
Self { node_id }
}
}
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl Edge {
#[new]
fn __new__(node_ids: Vec<i64>) -> Self {
Self { node_ids }
}
}
py_function_sync_async! {
#[cfg_attr(feature = "stubs", gen_stub_pyfunction(module = "qcs_sdk.qpu.isa"))]
#[pyfunction]
#[pyo3(signature = (quantum_processor_id, client = None))]
async fn get_instruction_set_architecture(
quantum_processor_id: String,
client: Option<Qcs>,
) -> PyResult<InstructionSetArchitecture> {
let client = client.unwrap_or_else(Qcs::load);
get_isa(&quantum_processor_id, &client)
.await
.map(Into::into)
.map_err(Into::into)
}
}
py_function_sync_async! {
#[cfg_attr(feature = "stubs", gen_stub_pyfunction(module = "qcs_sdk.qpu.isa"))]
#[pyfunction]
#[pyo3(signature = (client = None))]
async fn list_instruction_set_architectures(client: Option<Qcs>) -> PyResult<Vec<String>> {
let client = client.unwrap_or_else(Qcs::load);
list_isas(&client, None)
.await
.map_err(Into::into)
}
}