Skip to content

Commit d49412e

Browse files
committed
feat!: Add original circuit/subgraphs in pytket DecoderCtx
1 parent 7e4fe66 commit d49412e

File tree

19 files changed

+162
-83
lines changed

19 files changed

+162
-83
lines changed

tket-py/src/circuit/convert.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ where
7070
Err(_) => (
7171
SerialCircuit::from_tket1(circ)?
7272
.decode(
73-
DecodeOptions::new()
73+
DecodeOptions::new_any()
7474
.with_config(tket_qsystem::pytket::qsystem_decoder_config()),
7575
)
7676
.convert_pyerrs()?,

tket-qsystem/src/pytket.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use tket::serialize::pytket::{
1515
///
1616
/// Contains a list of custom decoders that define translations of legacy tket
1717
/// primitives into HUGR operations.
18-
pub fn qsystem_decoder_config() -> PytketDecoderConfig {
18+
pub fn qsystem_decoder_config<H: HugrView>() -> PytketDecoderConfig<H> {
1919
let mut config = default_decoder_config();
2020
config.add_decoder(QSystemEmitter);
2121

tket-qsystem/src/pytket/qsystem.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ impl QSystemEmitter {
115115
}
116116
}
117117

118-
impl PytketDecoder for QSystemEmitter {
118+
impl<H: HugrView> PytketDecoder<H> for QSystemEmitter {
119119
fn op_types(&self) -> Vec<PytketOptype> {
120120
// Process native optypes that are not supported by the `TketOp` emitter.
121121
vec![
@@ -132,7 +132,7 @@ impl PytketDecoder for QSystemEmitter {
132132
bits: &[TrackedBit],
133133
params: &[LoadedParameter],
134134
_opgroup: Option<&str>,
135-
decoder: &mut PytketDecoderContext<'h>,
135+
decoder: &mut PytketDecoderContext<'h, H>,
136136
) -> Result<DecodeStatus, PytketDecodeError> {
137137
let op = match op.op_type {
138138
PytketOptype::PhasedX => QSystemOp::PhasedX,

tket-qsystem/src/pytket/tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ fn json_roundtrip(
198198
assert_eq!(ser.commands.len(), num_commands);
199199

200200
let circ: Circuit = ser
201-
.decode(DecodeOptions::new().with_config(qsystem_decoder_config()))
201+
.decode(DecodeOptions::new_any().with_config(qsystem_decoder_config()))
202202
.unwrap();
203203
assert_eq!(circ.qubit_count(), num_qubits);
204204

@@ -229,7 +229,7 @@ fn circuit_roundtrip(#[case] circ: Circuit, #[case] decoded_sig: Signature) {
229229
)
230230
.unwrap();
231231
let deser: Circuit = ser
232-
.decode(DecodeOptions::new().with_config(qsystem_decoder_config()))
232+
.decode(DecodeOptions::new_any().with_config(qsystem_decoder_config()))
233233
.unwrap();
234234

235235
let deser_sig = deser.circuit_signature();

tket/src/circuit/hash.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ mod test {
208208
fn hash_constants() {
209209
let c_str = r#"{"bits": [], "commands": [{"args": [["q", [0]]], "op": {"params": ["0.5"], "type": "Rz"}}], "created_qubits": [], "discarded_qubits": [], "implicit_permutation": [[["q", [0]], ["q", [0]]]], "phase": "0.0", "qubits": [["q", [0]]]}"#;
210210
let ser: circuit_json::SerialCircuit = serde_json::from_str(c_str).unwrap();
211-
let circ: Circuit = ser.decode(DecodeOptions::new()).unwrap();
211+
let circ: Circuit = ser.decode(DecodeOptions::new_any()).unwrap();
212212
circ.circuit_hash(circ.parent()).unwrap();
213213
}
214214

@@ -220,7 +220,7 @@ mod test {
220220
let mut all_hashes = Vec::with_capacity(2);
221221
for c_str in [c_str1, c_str2] {
222222
let ser: circuit_json::SerialCircuit = serde_json::from_str(c_str).unwrap();
223-
let circ: Circuit = ser.decode(DecodeOptions::new()).unwrap();
223+
let circ: Circuit = ser.decode(DecodeOptions::new_any()).unwrap();
224224
all_hashes.push(circ.circuit_hash(circ.parent()).unwrap());
225225
}
226226
assert_ne!(all_hashes[0], all_hashes[1]);

tket/src/serialize/pytket.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub use options::{DecodeInsertionTarget, DecodeOptions, EncodeOptions};
2121

2222
use hugr::hugr::hugrmut::HugrMut;
2323
use hugr::ops::handle::NodeHandle;
24-
use hugr::{Hugr, Node};
24+
use hugr::{Hugr, HugrView, Node};
2525
#[cfg(test)]
2626
mod tests;
2727

@@ -70,7 +70,7 @@ pub trait TKETDecode: Sized {
7070
/// # Returns
7171
///
7272
/// The encoded circuit.
73-
fn decode(&self, options: DecodeOptions) -> Result<Circuit, Self::DecodeError>;
73+
fn decode(&self, options: DecodeOptions<impl HugrView>) -> Result<Circuit, Self::DecodeError>;
7474
/// Convert the serialized circuit into a function definition in an existing HUGR.
7575
///
7676
/// Does **not** modify the HUGR's entrypoint.
@@ -92,7 +92,7 @@ pub trait TKETDecode: Sized {
9292
// (so that the extension decoder traits are dyn-compatible).
9393
hugr: &mut Hugr,
9494
target: DecodeInsertionTarget,
95-
options: DecodeOptions,
95+
options: DecodeOptions<impl HugrView>,
9696
) -> Result<Node, Self::DecodeError>;
9797
/// Convert a circuit to a serialized pytket circuit.
9898
///
@@ -113,9 +113,13 @@ impl TKETDecode for SerialCircuit {
113113
type DecodeError = PytketDecodeError;
114114
type EncodeError = PytketEncodeError;
115115

116-
fn decode(&self, options: DecodeOptions) -> Result<Circuit, Self::DecodeError> {
116+
fn decode(&self, options: DecodeOptions<impl HugrView>) -> Result<Circuit, Self::DecodeError> {
117117
let mut hugr = Hugr::new();
118-
let main_func = self.decode_inplace(&mut hugr, DecodeInsertionTarget::Function, options)?;
118+
let main_func = self.decode_inplace(
119+
&mut hugr,
120+
DecodeInsertionTarget::Function { fn_name: None },
121+
options,
122+
)?;
119123
hugr.set_entrypoint(main_func);
120124
Ok(hugr.into())
121125
}
@@ -124,7 +128,7 @@ impl TKETDecode for SerialCircuit {
124128
&self,
125129
hugr: &mut Hugr,
126130
target: DecodeInsertionTarget,
127-
options: DecodeOptions,
131+
options: DecodeOptions<impl HugrView>,
128132
) -> Result<Node, Self::DecodeError> {
129133
let config = options
130134
.config
@@ -134,7 +138,6 @@ impl TKETDecode for SerialCircuit {
134138
self,
135139
hugr,
136140
target,
137-
options.fn_name,
138141
options.signature,
139142
options.input_params,
140143
config,

tket/src/serialize/pytket/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use hugr::HugrView;
1818
///
1919
/// Contains a list of custom decoders that define translations of legacy tket
2020
/// primitives into HUGR operations.
21-
pub fn default_decoder_config() -> PytketDecoderConfig {
21+
pub fn default_decoder_config<H: HugrView>() -> PytketDecoderConfig<H> {
2222
let mut config = PytketDecoderConfig::new();
2323
config.add_decoder(CoreDecoder);
2424
config.add_decoder(PreludeEmitter);

tket/src/serialize/pytket/config/decoder_config.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
//! translations of legacy tket primitives into HUGR operations.
66
77
use hugr::types::Type;
8+
use hugr::HugrView;
89
use itertools::Itertools;
910
use std::collections::HashMap;
1011

@@ -22,10 +23,10 @@ use super::TypeTranslatorSet;
2223
/// Contains custom decoders that define translations for HUGR operations,
2324
/// types, and consts into pytket primitives.
2425
#[derive(Default, derive_more::Debug)]
25-
pub struct PytketDecoderConfig {
26+
pub struct PytketDecoderConfig<H: HugrView> {
2627
/// Operation emitters
2728
#[debug(skip)]
28-
pub(super) decoders: Vec<Box<dyn PytketDecoder + Send + Sync>>,
29+
pub(super) decoders: Vec<Box<dyn PytketDecoder<H> + Send + Sync>>,
2930
/// Pre-computed map from pytket optypes to corresponding decoders in
3031
/// `decoders`, identified by their index.
3132
#[debug("{:?}", optype_decoders.keys().collect_vec())]
@@ -34,7 +35,7 @@ pub struct PytketDecoderConfig {
3435
type_translators: TypeTranslatorSet,
3536
}
3637

37-
impl PytketDecoderConfig {
38+
impl<H: HugrView> PytketDecoderConfig<H> {
3839
/// Create a new [`PytketDecoderConfig`] with no decoders.
3940
pub fn new() -> Self {
4041
Self {
@@ -45,7 +46,7 @@ impl PytketDecoderConfig {
4546
}
4647

4748
/// Add a decoder to the configuration.
48-
pub fn add_decoder(&mut self, decoder: impl PytketDecoder + Send + Sync + 'static) {
49+
pub fn add_decoder(&mut self, decoder: impl PytketDecoder<H> + Send + Sync + 'static) {
4950
let idx = self.decoders.len();
5051

5152
for optype in decoder.op_types() {
@@ -74,7 +75,7 @@ impl PytketDecoderConfig {
7475
bits: &[TrackedBit],
7576
params: &[LoadedParameter],
7677
opgroup: &Option<String>,
77-
decoder: &mut PytketDecoderContext<'a>,
78+
decoder: &mut PytketDecoderContext<'a, H>,
7879
) -> Result<DecodeStatus, PytketDecodeError> {
7980
let mut result = DecodeStatus::Unsupported;
8081
let opgroup = opgroup.as_deref();
@@ -91,7 +92,7 @@ impl PytketDecoderConfig {
9192
fn decoders_for_optype(
9293
&self,
9394
optype: &tket_json_rs::OpType,
94-
) -> impl Iterator<Item = &Box<dyn PytketDecoder + Send + Sync>> {
95+
) -> impl Iterator<Item = &Box<dyn PytketDecoder<H> + Send + Sync>> {
9596
self.optype_decoders
9697
.get(optype)
9798
.into_iter()

tket/src/serialize/pytket/decoder.rs

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,22 @@ use crate::extension::rotation::rotation_type;
3434
use crate::serialize::pytket::config::PytketDecoderConfig;
3535
use crate::serialize::pytket::decoder::wires::WireTracker;
3636
use crate::serialize::pytket::extension::{build_opaque_tket_op, RegisterCount};
37+
use crate::serialize::pytket::opaque::OpaqueSubgraphs;
3738
use crate::serialize::pytket::{DecodeInsertionTarget, PytketDecodeErrorInner};
3839
use crate::TketOp;
3940

4041
/// State of the tket circuit being decoded.
4142
///
42-
/// The state of an in-progress [`FunctionBuilder`] being built from a [`SerialCircuit`].
43+
/// The state of an in-progress [`FunctionBuilder`] being built from a
44+
/// [`SerialCircuit`].
45+
///
46+
/// The generic parameter `H` is the HugrView type of the Hugr that was encoded
47+
/// into the circuit, if any. This is required when the encoded pytket circuit
48+
/// contains opaque barriers that reference subgraphs in the original HUGR. See
49+
/// [`UnsupportedSubgraphPayload`][super::unsupported::UnsupportedSubgraphPayload]
50+
/// for more details.
4351
#[derive(Debug)]
44-
pub struct PytketDecoderContext<'h> {
52+
pub struct PytketDecoderContext<'h, H: HugrView> {
4553
/// The Hugr being built.
4654
pub builder: DFGBuilder<&'h mut Hugr>,
4755
/// A tracker keeping track of the generated wires and their corresponding types.
@@ -50,10 +58,15 @@ pub struct PytketDecoderContext<'h> {
5058
///
5159
/// Contains custom operation decoders, that define translation of legacy tket
5260
/// commands into HUGR operations.
53-
config: Arc<PytketDecoderConfig>,
61+
config: Arc<PytketDecoderConfig<H>>,
62+
/// The HugrView type of the Hugr that originated the circuit, if any.
63+
original_hugr: Option<&'h H>,
64+
/// A registry of unsupported subgraphs from `original_hugr`, that are referenced by opaque barriers in the pytket circuit
65+
/// via their [`SubgraphId`].
66+
opaque_subgraphs: Option<&'h OpaqueSubgraphs<H::Node>>,
5467
}
5568

56-
impl<'h> PytketDecoderContext<'h> {
69+
impl<'h, H: HugrView> PytketDecoderContext<'h, H> {
5770
/// Initialize a new [`PytketDecoderContext`], using the metadata from a
5871
/// [`SerialCircuit`].
5972
///
@@ -94,12 +107,11 @@ impl<'h> PytketDecoderContext<'h> {
94107
serialcirc: &SerialCircuit,
95108
hugr: &'h mut Hugr,
96109
target: DecodeInsertionTarget,
97-
fn_name: Option<String>,
98110
signature: Option<Signature>,
99111
input_params: impl IntoIterator<Item = String>,
100-
config: impl Into<Arc<PytketDecoderConfig>>,
112+
config: impl Into<Arc<PytketDecoderConfig<H>>>,
101113
) -> Result<Self, PytketDecodeError> {
102-
let config: Arc<PytketDecoderConfig> = config.into();
114+
let config: Arc<PytketDecoderConfig<H>> = config.into();
103115
let signature = signature.unwrap_or_else(|| {
104116
let num_qubits = serialcirc.qubits.len();
105117
let num_bits = serialcirc.bits.len();
@@ -108,11 +120,11 @@ impl<'h> PytketDecoderContext<'h> {
108120
.into();
109121
Signature::new(types.clone(), types)
110122
});
111-
let name = fn_name
112-
.or_else(|| serialcirc.name.clone())
113-
.unwrap_or_default();
114123
let mut dfg: DFGBuilder<&mut Hugr> = match target {
115-
DecodeInsertionTarget::Function => {
124+
DecodeInsertionTarget::Function { fn_name } => {
125+
let name = fn_name
126+
.or_else(|| serialcirc.name.clone())
127+
.unwrap_or_default();
116128
FunctionBuilder::with_hugr(hugr, name, signature.clone())
117129
.unwrap()
118130
.into_dfg_builder()
@@ -146,6 +158,8 @@ impl<'h> PytketDecoderContext<'h> {
146158
builder: dfg,
147159
wire_tracker,
148160
config,
161+
original_hugr: None,
162+
opaque_subgraphs: None,
149163
})
150164
}
151165

@@ -177,7 +191,7 @@ impl<'h> PytketDecoderContext<'h> {
177191
dfg: &mut DFGBuilder<&mut Hugr>,
178192
input_types: &TypeRow,
179193
input_params: impl IntoIterator<Item = String>,
180-
config: &PytketDecoderConfig,
194+
config: &PytketDecoderConfig<H>,
181195
) -> Result<WireTracker, PytketDecodeError> {
182196
let num_qubits = serialcirc.qubits.len();
183197
let num_bits = serialcirc.bits.len();
@@ -332,6 +346,24 @@ impl<'h> PytketDecoderContext<'h> {
332346
.node())
333347
}
334348

349+
/// Register the set of unsupported subgraphs that were present in the original HUGR,
350+
/// along with a reference to the original HugrView.
351+
///
352+
/// # Arguments
353+
/// - `opaque_subgraphs`: A registry of opaque subgraphs from
354+
/// `original_hugr`, that are referenced by opaque barriers in the pytket
355+
/// circuit via their [`SubgraphId`].
356+
/// - `original_hugr`: The [`Hugr`] that originated the circuit, if any.
357+
#[expect(unused)]
358+
pub(super) fn register_unsupported_subgraphs(
359+
&mut self,
360+
opaque_subgraphs: &'h OpaqueSubgraphs<H::Node>,
361+
original_hugr: &'h H,
362+
) {
363+
self.opaque_subgraphs = Some(opaque_subgraphs);
364+
self.original_hugr = Some(original_hugr);
365+
}
366+
335367
/// Decode a list of pytket commands.
336368
pub(super) fn run_decoder(
337369
&mut self,
@@ -351,7 +383,7 @@ impl<'h> PytketDecoderContext<'h> {
351383
pub(super) fn process_command(
352384
&mut self,
353385
command: &circuit_json::Command,
354-
config: &PytketDecoderConfig,
386+
config: &PytketDecoderConfig<H>,
355387
) -> Result<(), PytketDecodeError> {
356388
let circuit_json::Command { op, args, opgroup } = command;
357389

@@ -380,13 +412,13 @@ impl<'h> PytketDecoderContext<'h> {
380412
}
381413

382414
/// Returns the configuration used by the decoder.
383-
pub fn config(&self) -> &Arc<PytketDecoderConfig> {
415+
pub fn config(&self) -> &Arc<PytketDecoderConfig<H>> {
384416
&self.config
385417
}
386418
}
387419

388420
/// Public API, used by the [`PytketDecoder`][super::extension::PytketDecoder] implementers.
389-
impl<'h> PytketDecoderContext<'h> {
421+
impl<'h, H: HugrView> PytketDecoderContext<'h, H> {
390422
/// Returns a new set of [TrackedWires] for a list of [`TrackedQubit`]s,
391423
/// [`TrackedBit`]s, and [`LoadedParameter`]s following the required types.
392424
///
@@ -723,7 +755,7 @@ pub enum DecodeStatus {
723755

724756
/// Helper to continue exhausting the iterators in [`PytketDecoderContext::register_node_outputs`] until we have the total number of elements to report.
725757
fn make_unexpected_node_out_error<'ty>(
726-
config: &PytketDecoderConfig,
758+
config: &PytketDecoderConfig<impl HugrView>,
727759
port_types: impl IntoIterator<Item = (OutgoingPort, &'ty Type)>,
728760
mut partial_count: RegisterCount,
729761
expected_qubits: usize,

0 commit comments

Comments
 (0)