Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion tket-py/src/circuit/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ where
Err(_) => (
SerialCircuit::from_tket1(circ)?
.decode(
DecodeOptions::new()
DecodeOptions::new_any()
.with_config(tket_qsystem::pytket::qsystem_decoder_config()),
)
.convert_pyerrs()?,
Expand Down
2 changes: 1 addition & 1 deletion tket-qsystem/src/pytket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use tket::serialize::pytket::{
///
/// Contains a list of custom decoders that define translations of legacy tket
/// primitives into HUGR operations.
pub fn qsystem_decoder_config() -> PytketDecoderConfig {
pub fn qsystem_decoder_config<H: HugrView>() -> PytketDecoderConfig<H> {
let mut config = default_decoder_config();
config.add_decoder(QSystemEmitter);

Expand Down
4 changes: 2 additions & 2 deletions tket-qsystem/src/pytket/qsystem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ impl QSystemEmitter {
}
}

impl PytketDecoder for QSystemEmitter {
impl<H: HugrView> PytketDecoder<H> for QSystemEmitter {
fn op_types(&self) -> Vec<PytketOptype> {
// Process native optypes that are not supported by the `TketOp` emitter.
vec![
Expand All @@ -132,7 +132,7 @@ impl PytketDecoder for QSystemEmitter {
bits: &[TrackedBit],
params: &[LoadedParameter],
_opgroup: Option<&str>,
decoder: &mut PytketDecoderContext<'h>,
decoder: &mut PytketDecoderContext<'h, H>,
) -> Result<DecodeStatus, PytketDecodeError> {
let op = match op.op_type {
PytketOptype::PhasedX => QSystemOp::PhasedX,
Expand Down
4 changes: 2 additions & 2 deletions tket-qsystem/src/pytket/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ fn json_roundtrip(
assert_eq!(ser.commands.len(), num_commands);

let circ: Circuit = ser
.decode(DecodeOptions::new().with_config(qsystem_decoder_config()))
.decode(DecodeOptions::new_any().with_config(qsystem_decoder_config()))
.unwrap();
assert_eq!(circ.qubit_count(), num_qubits);

Expand Down Expand Up @@ -229,7 +229,7 @@ fn circuit_roundtrip(#[case] circ: Circuit, #[case] decoded_sig: Signature) {
)
.unwrap();
let deser: Circuit = ser
.decode(DecodeOptions::new().with_config(qsystem_decoder_config()))
.decode(DecodeOptions::new_any().with_config(qsystem_decoder_config()))
.unwrap();

let deser_sig = deser.circuit_signature();
Expand Down
4 changes: 2 additions & 2 deletions tket/src/circuit/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ mod test {
fn hash_constants() {
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]]]}"#;
let ser: circuit_json::SerialCircuit = serde_json::from_str(c_str).unwrap();
let circ: Circuit = ser.decode(DecodeOptions::new()).unwrap();
let circ: Circuit = ser.decode(DecodeOptions::new_any()).unwrap();
circ.circuit_hash(circ.parent()).unwrap();
}

Expand All @@ -220,7 +220,7 @@ mod test {
let mut all_hashes = Vec::with_capacity(2);
for c_str in [c_str1, c_str2] {
let ser: circuit_json::SerialCircuit = serde_json::from_str(c_str).unwrap();
let circ: Circuit = ser.decode(DecodeOptions::new()).unwrap();
let circ: Circuit = ser.decode(DecodeOptions::new_any()).unwrap();
all_hashes.push(circ.circuit_hash(circ.parent()).unwrap());
}
assert_ne!(all_hashes[0], all_hashes[1]);
Expand Down
30 changes: 11 additions & 19 deletions tket/src/serialize/pytket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub use options::{DecodeInsertionTarget, DecodeOptions, EncodeOptions};

use hugr::hugr::hugrmut::HugrMut;
use hugr::ops::handle::NodeHandle;
use hugr::{Hugr, Node};
use hugr::{Hugr, HugrView, Node};
#[cfg(test)]
mod tests;

Expand Down Expand Up @@ -70,7 +70,7 @@ pub trait TKETDecode: Sized {
/// # Returns
///
/// The encoded circuit.
fn decode(&self, options: DecodeOptions) -> Result<Circuit, Self::DecodeError>;
fn decode(&self, options: DecodeOptions<impl HugrView>) -> Result<Circuit, Self::DecodeError>;
/// Convert the serialized circuit into a function definition in an existing HUGR.
///
/// Does **not** modify the HUGR's entrypoint.
Expand All @@ -92,7 +92,7 @@ pub trait TKETDecode: Sized {
// (so that the extension decoder traits are dyn-compatible).
hugr: &mut Hugr,
target: DecodeInsertionTarget,
options: DecodeOptions,
options: DecodeOptions<impl HugrView>,
) -> Result<Node, Self::DecodeError>;
/// Convert a circuit to a serialized pytket circuit.
///
Expand All @@ -113,9 +113,13 @@ impl TKETDecode for SerialCircuit {
type DecodeError = PytketDecodeError;
type EncodeError = PytketEncodeError;

fn decode(&self, options: DecodeOptions) -> Result<Circuit, Self::DecodeError> {
fn decode(&self, options: DecodeOptions<impl HugrView>) -> Result<Circuit, Self::DecodeError> {
let mut hugr = Hugr::new();
let main_func = self.decode_inplace(&mut hugr, DecodeInsertionTarget::Function, options)?;
let main_func = self.decode_inplace(
&mut hugr,
DecodeInsertionTarget::Function { fn_name: None },
options,
)?;
hugr.set_entrypoint(main_func);
Ok(hugr.into())
}
Expand All @@ -124,21 +128,9 @@ impl TKETDecode for SerialCircuit {
&self,
hugr: &mut Hugr,
target: DecodeInsertionTarget,
options: DecodeOptions,
options: DecodeOptions<impl HugrView>,
) -> Result<Node, Self::DecodeError> {
let config = options
.config
.unwrap_or_else(|| default_decoder_config().into());

let mut decoder = PytketDecoderContext::new(
self,
hugr,
target,
options.fn_name,
options.signature,
options.input_params,
config,
)?;
let mut decoder = PytketDecoderContext::new(self, hugr, target, options)?;
decoder.run_decoder(&self.commands)?;
Ok(decoder.finish()?.node())
}
Expand Down
88 changes: 86 additions & 2 deletions tket/src/serialize/pytket/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ use std::collections::{HashMap, VecDeque};
use std::ops::{Index, IndexMut};

use hugr::core::HugrNode;
use hugr::hugr::hugrmut::HugrMut;
use hugr::ops::handle::NodeHandle;
use hugr::ops::{OpTag, OpTrait};
use hugr::{Hugr, HugrView};
use hugr::{Hugr, HugrView, Node};
use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator};
use tket_json_rs::circuit_json::{Command as PytketCommand, SerialCircuit};

use crate::serialize::pytket::decoder::PytketDecoderContext;
use crate::serialize::pytket::{
default_encoder_config, EncodeOptions, PytketEncodeError, PytketEncoderContext,
default_encoder_config, DecodeInsertionTarget, DecodeOptions, EncodeOptions, PytketDecodeError,
PytketDecodeErrorInner, PytketEncodeError, PytketEncoderContext,
};
use crate::Circuit;

Expand Down Expand Up @@ -149,6 +153,86 @@ impl<'a, H: HugrView> EncodedCircuit<'a, H> {
Ok(())
}

/// Reassemble the encoded circuits into a new [`Hugr`], containing a
/// function defining the [`Self::head_region`] and expanding any opaque
/// hugrs in pytket barrier operations back into Hugr subgraphs.
///
/// Functions called by the internal hugrs may be added to the hugr module
/// as well.
///
/// # Arguments
///
/// - `fn_name`: The name of the function to create. If `None`, we will use
/// the name of the circuit, or "main" if the circuit has no name.
/// - `options`: The options for the decoder.
///
/// # Errors
///
/// Returns a [`PytketDecodeErrorInner::NonDataflowHeadRegion`] error if
/// [`Self::head_region`] is not a dataflow container in the hugr.
///
/// Returns an error if a circuit being decoded is invalid. See
/// [`PytketDecodeErrorInner`][super::error::PytketDecodeErrorInner] for
/// more details.
pub fn reassemble(
&self,
fn_name: Option<String>,
options: DecodeOptions<H>,
) -> Result<Hugr, PytketDecodeError> {
let mut hugr = Hugr::new();
let main_func = self.reassemble_inline(
&mut hugr,
DecodeInsertionTarget::Function { fn_name },
options,
)?;
hugr.set_entrypoint(main_func);
Ok(hugr)
}

/// Reassemble the encoded circuits inside an existing [`Hugr`], containing
/// the [`Self::head_region`] at the given insertion target.
///
/// Functions called by the internal hugrs may be added to the hugr module
/// as well.
///
/// # Arguments
///
/// - `hugr`: The [`Hugr`] to reassemble the circuits in.
/// - `target`: The target to insert the function at.
/// - `options`: The options for the decoder.
///
/// # Errors
///
/// Returns a [`PytketDecodeErrorInner::NonDataflowHeadRegion`] error if
/// [`Self::head_region`] is not a dataflow container in the hugr.
///
/// Returns an error if a circuit being decoded is invalid. See
/// [`PytketDecodeErrorInner`][super::error::PytketDecodeErrorInner] for
/// more details.
pub fn reassemble_inline(
&self,
hugr: &mut Hugr,
target: DecodeInsertionTarget,
options: DecodeOptions<H>,
) -> Result<Node, PytketDecodeError> {
if !self.check_dataflow_head_region() {
let head_op = self.hugr.get_optype(self.head_region).to_string();
return Err(PytketDecodeErrorInner::NonDataflowHeadRegion { head_op }.wrap());
};
let serial_circuit = &self[self.head_region];

if self.len() > 1 {
unimplemented!(
"Reassembling an `EncodedCircuit` with nested subcircuits is not yet implemented."
);
};

let mut decoder = PytketDecoderContext::<H>::new(serial_circuit, hugr, target, options)?;
decoder.register_opaque_subgraphs(&self.opaque_subgraphs, self.hugr);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would these fit better within the DecodeOptions rather than the Context?

Whilst the boundary between Options and Config is clear (Config is basically static), I'm not clear other what scope you'd reuse an Options. signature and input_params look specific to one pytket circuit, whereas the extension registry is probably specific to an EncodedCircuit (as would the opaque-subgraphs). Does it make sense to establish DecodeOptions as per-EncodedCircuit (moving signature/input-params back out to Context, and putting opaque-subgraphs stuff in Options)?

decoder.run_decoder(&serial_circuit.commands)?;
Ok(decoder.finish()?.node())
}

/// Extract the top-level pytket circuit as a standalone definition
/// containing the whole original HUGR.
///
Expand Down
2 changes: 1 addition & 1 deletion tket/src/serialize/pytket/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use hugr::HugrView;
///
/// Contains a list of custom decoders that define translations of legacy tket
/// primitives into HUGR operations.
pub fn default_decoder_config() -> PytketDecoderConfig {
pub fn default_decoder_config<H: HugrView>() -> PytketDecoderConfig<H> {
let mut config = PytketDecoderConfig::new();
config.add_decoder(CoreDecoder);
config.add_decoder(PreludeEmitter);
Expand Down
13 changes: 7 additions & 6 deletions tket/src/serialize/pytket/config/decoder_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//! translations of legacy tket primitives into HUGR operations.

use hugr::types::Type;
use hugr::HugrView;
use itertools::Itertools;
use std::collections::HashMap;

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

impl PytketDecoderConfig {
impl<H: HugrView> PytketDecoderConfig<H> {
/// Create a new [`PytketDecoderConfig`] with no decoders.
pub fn new() -> Self {
Self {
Expand All @@ -45,7 +46,7 @@ impl PytketDecoderConfig {
}

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

for optype in decoder.op_types() {
Expand Down Expand Up @@ -74,7 +75,7 @@ impl PytketDecoderConfig {
bits: &[TrackedBit],
params: &[LoadedParameter],
opgroup: &Option<String>,
decoder: &mut PytketDecoderContext<'a>,
decoder: &mut PytketDecoderContext<'a, H>,
) -> Result<DecodeStatus, PytketDecodeError> {
let mut result = DecodeStatus::Unsupported;
let opgroup = opgroup.as_deref();
Expand All @@ -91,7 +92,7 @@ impl PytketDecoderConfig {
fn decoders_for_optype(
&self,
optype: &tket_json_rs::OpType,
) -> impl Iterator<Item = &Box<dyn PytketDecoder + Send + Sync>> {
) -> impl Iterator<Item = &Box<dyn PytketDecoder<H> + Send + Sync>> {
self.optype_decoders
.get(optype)
.into_iter()
Expand Down
Loading
Loading