-
Notifications
You must be signed in to change notification settings - Fork 11
fix: Don't use opgroup in pytket barrier encoding #1251
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,7 +10,7 @@ use crate::serialize::pytket::decoder::{ | |
| DecodeStatus, LoadedParameter, PytketDecoderContext, TrackedBit, TrackedQubit, | ||
| }; | ||
| use crate::serialize::pytket::extension::PytketDecoder; | ||
| use crate::serialize::pytket::opaque::{OpaqueSubgraphPayload, OPGROUP_OPAQUE_HUGR}; | ||
| use crate::serialize::pytket::opaque::OpaqueSubgraphPayload; | ||
| use crate::serialize::pytket::{DecodeInsertionTarget, DecodeOptions, PytketDecodeError}; | ||
| use hugr::builder::Container; | ||
| use hugr::extension::prelude::{bool_t, qb_t}; | ||
|
|
@@ -36,19 +36,18 @@ impl PytketDecoder for CoreDecoder { | |
| qubits: &[TrackedQubit], | ||
| bits: &[TrackedBit], | ||
| params: &[LoadedParameter], | ||
| opgroup: Option<&str>, | ||
| _opgroup: Option<&str>, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. deprecate parameter?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. :-( I guess there is no plan to deprecate this? (Some third-party encoder/decoder might still use it?)
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here, it's part of the |
||
| decoder: &mut PytketDecoderContext<'h>, | ||
| ) -> Result<DecodeStatus, PytketDecodeError> { | ||
| match &op { | ||
| PytketOperation { | ||
| op_type: PytketOptype::Barrier, | ||
| data: Some(payload), | ||
| .. | ||
| } if opgroup == Some(OPGROUP_OPAQUE_HUGR) => { | ||
| let payload = | ||
| OpaqueSubgraphPayload::load_str(payload, decoder.extension_registry())?; | ||
| decoder.insert_subgraph_from_payload(qubits, bits, params, &payload) | ||
| } | ||
| } => match OpaqueSubgraphPayload::load_str(payload, decoder.extension_registry()) { | ||
| Ok(payload) => decoder.insert_subgraph_from_payload(qubits, bits, params, &payload), | ||
| _ => Ok(DecodeStatus::Unsupported), | ||
| }, | ||
| PytketOperation { | ||
| op_type: PytketOptype::CircBox, | ||
| op_box: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,7 +6,10 @@ mod subgraph; | |
|
|
||
| pub use subgraph::OpaqueSubgraph; | ||
|
|
||
| pub use payload::{EncodedEdgeID, OpaqueSubgraphPayload, OPGROUP_OPAQUE_HUGR}; | ||
| pub use payload::{EncodedEdgeID, OpaqueSubgraphPayload}; | ||
|
|
||
| #[expect(deprecated)] | ||
| pub use payload::OPGROUP_OPAQUE_HUGR; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No deprecation on re-exports, the warning is emitted if the main definition is deprecated. |
||
|
|
||
| use std::collections::BTreeMap; | ||
| use std::ops::Index; | ||
|
|
@@ -117,31 +120,25 @@ impl<N: HugrNode> OpaqueSubgraphs<N> { | |
| /// If the pytket command is a barrier operation encoding an opaque subgraph, replace its [`OpaqueSubgraphPayload::External`] pointer | ||
| /// if present with a [`OpaqueSubgraphPayload::Inline`] payload. | ||
| /// | ||
| /// # Errors | ||
| /// | ||
| /// Returns an error if a barrier operation with the [`OPGROUP_OPAQUE_HUGR`] opgroup has an invalid payload. | ||
| /// Ignores barriers whose data payload cannot be decoded into an [`OpaqueSubgraphPayload`]. | ||
| 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 | ||
| || command.opgroup.as_deref() != Some(OPGROUP_OPAQUE_HUGR) | ||
| { | ||
| if command.op.op_type != tket_json_rs::OpType::Barrier { | ||
| return Ok(()); | ||
| } | ||
| let Some(payload) = command.op.data.take() else { | ||
| return Err(PytketEncodeError::custom(format!( | ||
| "Barrier operation with opgroup {OPGROUP_OPAQUE_HUGR} has no data payload." | ||
| ))); | ||
| return Ok(()); | ||
| }; | ||
|
|
||
| let Some(subgraph_id) = parse_external_payload_id(&payload)? else { | ||
| // Inline payload, nothing to do. | ||
| let Some(subgraph_id) = OpaqueSubgraphPayload::parse_external_id(&payload) else { | ||
| // Not an External Payload, nothing to do. | ||
| return Ok(()); | ||
| }; | ||
| if !self.contains(subgraph_id) { | ||
| return Err(PytketEncodeError::custom(format!("Barrier operation with opgroup {OPGROUP_OPAQUE_HUGR} points to an unknown subgraph: {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)?; | ||
|
|
@@ -169,38 +166,3 @@ impl<N> Default for OpaqueSubgraphs<N> { | |
| } | ||
| } | ||
| } | ||
|
|
||
| /// Parse an external payload from a string payload. | ||
| /// | ||
| /// Returns `None` if the payload is inline. | ||
| /// | ||
| /// # Errors | ||
| /// | ||
| /// Returns an error if the payload is invalid. | ||
| fn parse_external_payload_id<N: HugrNode>( | ||
| payload: &str, | ||
| ) -> Result<Option<SubgraphId>, PytketEncodeError<N>> { | ||
| // Check if the payload is inline, without fully copying it to memory. | ||
| #[derive(serde::Deserialize)] | ||
| struct PartialPayload { | ||
| pub typ: String, | ||
| pub id: Option<SubgraphId>, | ||
| } | ||
|
|
||
| // Don't do the full deserialization of the payload to avoid allocating a new String for the | ||
| // encoded envelope. | ||
| let partial_payload: PartialPayload = | ||
| serde_json::from_str(payload).map_err(|e: serde_json::Error| { | ||
| PytketEncodeError::custom(format!( | ||
| "Barrier operation with opgroup {OPGROUP_OPAQUE_HUGR} has corrupt data payload: {e}" | ||
| )) | ||
| })?; | ||
|
|
||
| match (partial_payload.typ.as_str(), partial_payload.id) { | ||
| ("Inline", None) => Ok(None), | ||
| ("External", Some(id)) => Ok(Some(id)), | ||
| _ => Err(PytketEncodeError::custom(format!( | ||
| "Barrier operation with opgroup {OPGROUP_OPAQUE_HUGR} has invalid data payload: {payload:?}" | ||
| ))), | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,10 @@ use super::SubgraphId; | |
| /// Pytket opgroup used to identify opaque barrier operations that encode opaque HUGR subgraphs. | ||
| /// | ||
| /// See [`OpaqueSubgraphPayload`]. | ||
| #[deprecated( | ||
| note = "Opaque barriers do not set an opgroup anymore", | ||
| since = "0.16.1" | ||
| )] | ||
| pub const OPGROUP_OPAQUE_HUGR: &str = "OPAQUE_HUGR"; | ||
|
|
||
| /// Identifier for a wire in the Hugr, encoded as a 64-bit hash that is | ||
|
|
@@ -77,11 +81,13 @@ pub enum OpaqueSubgraphPayload { | |
| /// A reference to a subgraph tracked by an `OpaqueSubgraphs` registry | ||
| /// in an [`EncodedCircuit`][super::super::circuit::EncodedCircuit] | ||
| /// structure. | ||
| #[serde(rename = "HugrExternal")] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this mean we should declare this as a breaking PR?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These external payloads are meant to be short-lived, and are only valid when decoded by the same runtime that generated them. You could make the case that we are breaking |
||
| External { | ||
| /// The ID of the subgraph in the `OpaqueSubgraphs` registry. | ||
| id: SubgraphId, | ||
| }, | ||
| /// An inline payload, carrying the encoded envelope for the HUGR subgraph. | ||
| #[serde(rename = "HugrInline")] | ||
| Inline { | ||
| /// A string envelope containing the encoded HUGR subgraph. | ||
| hugr_envelope: String, | ||
|
|
@@ -208,4 +214,43 @@ impl OpaqueSubgraphPayload { | |
| pub fn is_external(&self) -> bool { | ||
| matches!(self, Self::External { .. }) | ||
| } | ||
|
|
||
| /// Parse the contents of an [`OpaqueSubgraphPayload::External`] from a string payload. | ||
| /// | ||
| /// Returns `None` if the payload is [`OpaqueSubgraphPayload::Inline`] or not an | ||
| /// [`OpaqueSubgraphPayload`]. | ||
| /// | ||
| /// This method is more efficient than calling [Self::load_str], as it | ||
| /// requires no allocations. | ||
| pub fn parse_external_id(payload: &str) -> Option<SubgraphId> { | ||
| #[derive(serde::Deserialize)] | ||
| #[serde(rename = "HugrExternal")] | ||
| #[serde(tag = "typ")] | ||
| struct PartialPayload { | ||
| pub id: SubgraphId, | ||
| } | ||
|
|
||
| // Deserialize the payload if it is External, avoiding a full copy to memory | ||
| // for the other variant. | ||
| serde_json::from_str::<PartialPayload>(payload) | ||
| .ok() | ||
| .map(|payload| payload.id) | ||
| } | ||
|
|
||
| /// Returns `true` if a string encodes an [`OpaqueSubgraphPayload`]. | ||
| /// | ||
| /// This method is more efficient than calling [Self::load_str], as it | ||
| /// requires no allocations. | ||
| pub fn is_valid_payload(payload: &str) -> bool { | ||
| #[derive(serde::Deserialize)] | ||
| #[serde(tag = "typ")] | ||
| enum PartialPayload { | ||
| HugrExternal {}, | ||
| HugrInline {}, | ||
| } | ||
|
|
||
| // Deserialize the payload if it is External, avoiding a full copy to memory | ||
| // for the other variant. | ||
| serde_json::from_str::<PartialPayload>(payload).is_ok() | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is
pubwrt the whole crate so you can't just drop the param?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
optypesif someone wants to use them.