use hugr::core::HugrNode;
use hugr::envelope::EnvelopeConfig;
use hugr::extension::ExtensionRegistry;
use hugr::extension::resolution::{WeakExtensionRegistry, resolve_type_extensions};
use hugr::package::Package;
use hugr::types::Type;
use hugr::{HugrView, Wire};
use itertools::Itertools;
use crate::serialize::pytket::error::BarrierPayloadError;
use crate::serialize::pytket::opaque::OpaqueSubgraph;
use crate::serialize::pytket::{PytketDecodeError, PytketEncodeError, PytketEncodeOpError};
use super::SubgraphId;
#[deprecated(
note = "Opaque barriers do not set an opgroup anymore",
since = "0.17.0"
)]
pub const OPGROUP_OPAQUE_HUGR: &str = "OPAQUE_HUGR";
#[derive(
Debug,
derive_more::Display,
Default,
Clone,
Copy,
PartialEq,
Eq,
Hash,
serde::Serialize,
serde::Deserialize,
)]
#[display("Edge#{id}", id = self.0)]
#[serde(transparent)]
pub struct EncodedEdgeID(u64);
impl EncodedEdgeID {
pub fn new<N: HugrNode>(wire: Wire<N>) -> Self {
let hash = fxhash::hash64(&wire);
Self(hash)
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(tag = "typ")]
pub enum OpaqueSubgraphPayload {
#[serde(rename = "HugrExternal")]
External {
id: SubgraphId,
input_params: Vec<String>,
},
#[serde(rename = "HugrInline")]
Inline {
hugr_envelope: String,
inputs: Vec<(Type, EncodedEdgeID)>,
outputs: Vec<(Type, EncodedEdgeID)>,
input_params: Vec<String>,
},
}
impl OpaqueSubgraphPayload {
pub fn new_external(
subgraph_id: SubgraphId,
input_params: impl IntoIterator<Item = String>,
) -> Self {
Self::External {
id: subgraph_id,
input_params: input_params.into_iter().collect(),
}
}
pub fn new_inline<N: HugrNode>(
subgraph: &OpaqueSubgraph<N>,
hugr: &impl HugrView<Node = N>,
input_params: impl IntoIterator<Item = String>,
) -> Result<Self, PytketEncodeError<N>> {
let signature = subgraph.signature();
let Some(opaque_hugr) = subgraph
.is_sibling_subgraph_compatible()
.then(|| subgraph.extract_subgraph(hugr).ok())
.flatten()
else {
return Err(PytketEncodeOpError::UnsupportedStandaloneSubgraph {
nodes: subgraph.nodes().iter().cloned().collect_vec(),
}
.into());
};
let mut inputs = Vec::with_capacity(subgraph.incoming_ports().len());
for (inp_node, inp_port) in subgraph.incoming_ports() {
let input_wire = Wire::from_connected_port(*inp_node, *inp_port, hugr);
let edge_id = EncodedEdgeID::new(input_wire);
inputs.push(edge_id);
}
let outputs = subgraph
.outgoing_ports()
.iter()
.map(|(n, p)| EncodedEdgeID::new(Wire::new(*n, *p)));
let hugr_envelope = Package::from_hugr(opaque_hugr)
.store_str(EnvelopeConfig::text())
.unwrap();
Ok(Self::Inline {
hugr_envelope,
inputs: signature.input().iter().cloned().zip(inputs).collect(),
outputs: signature.output().iter().cloned().zip(outputs).collect(),
input_params: input_params.into_iter().collect(),
})
}
pub fn load_str(json: &str, extensions: &ExtensionRegistry) -> Result<Self, PytketDecodeError> {
let mut payload: Self =
serde_json::from_str(json).map_err(|e| BarrierPayloadError::SerdeDecoding(e).wrap())?;
if let Self::Inline {
inputs, outputs, ..
} = &mut payload
{
let extensions: WeakExtensionRegistry = extensions.into();
for (ty, _) in inputs.iter_mut().chain(outputs.iter_mut()) {
resolve_type_extensions(ty, &extensions)
.map_err(|e| BarrierPayloadError::ExtensionResolution(e).wrap())?;
}
}
Ok(payload)
}
pub fn is_inline(&self) -> bool {
matches!(self, Self::Inline { .. })
}
pub fn is_external(&self) -> bool {
matches!(self, Self::External { .. })
}
pub fn parse_external_payload(payload: &str) -> Option<(SubgraphId, Vec<String>)> {
#[derive(serde::Deserialize)]
#[serde(rename = "HugrExternal")]
#[serde(tag = "typ")]
struct PartialPayload {
pub id: SubgraphId,
pub input_params: Vec<String>,
}
serde_json::from_str::<PartialPayload>(payload)
.ok()
.map(|payload| (payload.id, payload.input_params))
}
pub fn is_valid_payload(payload: &str) -> bool {
#[derive(serde::Deserialize)]
#[serde(tag = "typ")]
enum PartialPayload {
HugrExternal {},
HugrInline {},
}
serde_json::from_str::<PartialPayload>(payload).is_ok()
}
}