Skip to content

Commit ab376ea

Browse files
committed
feat!: Add original circuit/subgraphs in pytket DecoderCtx
1 parent ebc7f54 commit ab376ea

File tree

20 files changed

+259
-85
lines changed

20 files changed

+259
-85
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/circuit.rs

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@ use std::collections::{HashMap, VecDeque};
44
use std::ops::{Index, IndexMut};
55

66
use hugr::core::HugrNode;
7+
use hugr::hugr::hugrmut::HugrMut;
8+
use hugr::ops::handle::NodeHandle;
79
use hugr::ops::{OpTag, OpTrait};
8-
use hugr::{Hugr, HugrView};
10+
use hugr::{Hugr, HugrView, Node};
911
use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator};
1012
use tket_json_rs::circuit_json::{Command as PytketCommand, SerialCircuit};
1113

14+
use crate::serialize::pytket::decoder::PytketDecoderContext;
1215
use crate::serialize::pytket::{
13-
default_encoder_config, EncodeOptions, PytketEncodeError, PytketEncoderContext,
16+
default_decoder_config, default_encoder_config, DecodeInsertionTarget, DecodeOptions,
17+
EncodeOptions, PytketDecodeError, PytketDecodeErrorInner, PytketEncodeError,
18+
PytketEncoderContext,
1419
};
1520
use crate::Circuit;
1621

@@ -146,6 +151,97 @@ impl<'a, H: HugrView> EncodedCircuit<'a, H> {
146151
Ok(())
147152
}
148153

154+
/// Reassemble the encoded circuits into a new [`Hugr`], containing a
155+
/// function defining the [`Self::head_region`] and expanding any opaque
156+
/// hugrs in pytket barrier operations back into Hugr subgraphs.
157+
///
158+
/// Functions called by the internal hugrs may be added to the hugr module
159+
/// as well.
160+
///
161+
/// # Arguments
162+
///
163+
/// - `fn_name`: The name of the function to create. If `None`, we will use
164+
/// the name of the circuit, or "main" if the circuit has no name.
165+
/// - `options`: The options for the decoder.
166+
///
167+
/// # Errors
168+
///
169+
/// Returns a [`PytketDecodeErrorInner::NonDataflowHeadRegion`] error if
170+
/// [`Self::head_region`] is not a dataflow container in the hugr.
171+
///
172+
/// Returns an error if a circuit being decoded is invalid. See
173+
/// [`PytketDecodeErrorInner`][super::error::PytketDecodeErrorInner] for
174+
/// more details.
175+
pub fn reassemble(
176+
&self,
177+
fn_name: Option<String>,
178+
options: DecodeOptions<H>,
179+
) -> Result<Hugr, PytketDecodeError> {
180+
let mut hugr = Hugr::new();
181+
let main_func = self.reassemble_inline(
182+
&mut hugr,
183+
DecodeInsertionTarget::Function { fn_name },
184+
options,
185+
)?;
186+
hugr.set_entrypoint(main_func);
187+
Ok(hugr)
188+
}
189+
190+
/// Reassemble the encoded circuits inside an existing [`Hugr`], containing
191+
/// the [`Self::head_region`] at the given insertion target.
192+
///
193+
/// Functions called by the internal hugrs may be added to the hugr module
194+
/// as well.
195+
///
196+
/// # Arguments
197+
///
198+
/// - `hugr`: The [`Hugr`] to reassemble the circuits in.
199+
/// - `target`: The target to insert the function at.
200+
/// - `options`: The options for the decoder.
201+
///
202+
/// # Errors
203+
///
204+
/// Returns a [`PytketDecodeErrorInner::NonDataflowHeadRegion`] error if
205+
/// [`Self::head_region`] is not a dataflow container in the hugr.
206+
///
207+
/// Returns an error if a circuit being decoded is invalid. See
208+
/// [`PytketDecodeErrorInner`][super::error::PytketDecodeErrorInner] for
209+
/// more details.
210+
pub fn reassemble_inline(
211+
&self,
212+
hugr: &mut Hugr,
213+
target: DecodeInsertionTarget,
214+
options: DecodeOptions<H>,
215+
) -> Result<Node, PytketDecodeError> {
216+
if !self.check_dataflow_head_region() {
217+
let head_op = self.hugr.get_optype(self.head_region).to_string();
218+
return Err(PytketDecodeErrorInner::NonDataflowHeadRegion { head_op }.wrap());
219+
};
220+
let serial_circuit = &self[self.head_region];
221+
222+
if self.len() > 1 {
223+
unimplemented!(
224+
"Reassembling an `EncodedCircuit` with nested subcircuits is not yet implemented."
225+
);
226+
};
227+
228+
let config = options
229+
.config
230+
.unwrap_or_else(|| default_decoder_config().into());
231+
232+
let mut decoder = PytketDecoderContext::<H>::new(
233+
serial_circuit,
234+
hugr,
235+
target,
236+
options.signature,
237+
options.input_params,
238+
config,
239+
)?;
240+
decoder.register_unsupported_subgraphs(&self.opaque_subgraphs, self.hugr);
241+
decoder.run_decoder(&serial_circuit.commands)?;
242+
Ok(decoder.finish()?.node())
243+
}
244+
149245
/// Extract the top-level pytket circuit as a standalone definition
150246
/// containing the whole original HUGR.
151247
///

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()

0 commit comments

Comments
 (0)