mod payload;
mod subgraph;
pub use subgraph::OpaqueSubgraph;
pub use payload::{EncodedEdgeID, OpaqueSubgraphPayload};
#[expect(deprecated)]
pub use payload::OPGROUP_OPAQUE_HUGR;
use std::collections::BTreeMap;
use std::ops::Index;
use crate::serialize::pytket::PytketEncodeError;
use hugr::HugrView;
use hugr::core::HugrNode;
#[derive(Debug, derive_more::Display, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[display("{tracker_id}.{local_id}")]
pub struct SubgraphId {
tracker_id: usize,
local_id: usize,
}
impl SubgraphId {
pub(crate) fn output_parameter(&self, i: usize) -> String {
format!(
"p{tracker}_{local}_out{i}",
tracker = self.tracker_id,
local = self.local_id,
)
}
}
impl serde::Serialize for SubgraphId {
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
(&self.tracker_id, &self.local_id).serialize(s)
}
}
impl<'de> serde::Deserialize<'de> for SubgraphId {
fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
let (tracker_id, local_id) = serde::Deserialize::deserialize(d)?;
Ok(Self {
tracker_id,
local_id,
})
}
}
#[derive(Debug, Clone)]
pub(super) struct OpaqueSubgraphs<N> {
id: usize,
next_local_id: usize,
opaque_subgraphs: BTreeMap<SubgraphId, OpaqueSubgraph<N>>,
}
impl<N: HugrNode> OpaqueSubgraphs<N> {
pub fn new(id: usize) -> Self {
Self {
id,
next_local_id: 0,
opaque_subgraphs: BTreeMap::new(),
}
}
pub fn register_opaque_subgraph(&mut self, subgraph: OpaqueSubgraph<N>) -> SubgraphId {
let id = SubgraphId {
local_id: self.next_local_id,
tracker_id: self.id,
};
self.opaque_subgraphs.insert(id, subgraph);
self.next_local_id += 1;
id
}
pub fn get(&self, id: SubgraphId) -> Option<&OpaqueSubgraph<N>> {
self.opaque_subgraphs.get(&id)
}
pub fn contains(&self, id: SubgraphId) -> bool {
self.opaque_subgraphs.contains_key(&id)
}
pub fn ids(&self) -> impl Iterator<Item = SubgraphId> + '_ {
self.opaque_subgraphs.keys().copied()
}
pub fn merge(&mut self, other: Self) {
self.opaque_subgraphs.extend(other.opaque_subgraphs);
}
pub(super) fn inline_if_payload(
&self,
command: &mut tket_json_rs::circuit_json::Command,
hugr: &impl HugrView<Node = N>,
) -> Result<(), PytketEncodeError<N>> {
if command.op.op_type != tket_json_rs::OpType::Barrier {
return Ok(());
}
let Some(payload) = command.op.data.take() else {
return Ok(());
};
let Some((subgraph_id, input_arguments)) =
OpaqueSubgraphPayload::parse_external_payload(&payload)
else {
return Ok(());
};
if !self.contains(subgraph_id) {
return Err(PytketEncodeError::custom(format!(
"Barrier operation with external subgraph payload points to an unknown subgraph: {subgraph_id}"
)));
}
let payload = OpaqueSubgraphPayload::new_inline(&self[subgraph_id], hugr, input_arguments)?;
command.op.data = Some(serde_json::to_string(&payload).unwrap());
Ok(())
}
}
impl<N: HugrNode> Index<SubgraphId> for OpaqueSubgraphs<N> {
type Output = OpaqueSubgraph<N>;
fn index(&self, index: SubgraphId) -> &Self::Output {
self.get(index)
.unwrap_or_else(|| panic!("Invalid subgraph ID: {index}"))
}
}
impl<N> Default for OpaqueSubgraphs<N> {
fn default() -> Self {
Self {
id: 0,
next_local_id: 0,
opaque_subgraphs: BTreeMap::new(),
}
}
}