Skip to content

Commit 052ae9d

Browse files
authored
feat!: Avoid eagerly cloning SerialCircuits when decoding from pytket (#1048)
BREAKING CHANGE: TKETDecode now decodes `SerialCircuit`s by reference instead of by value
1 parent 3be68d8 commit 052ae9d

File tree

6 files changed

+21
-34
lines changed

6 files changed

+21
-34
lines changed

tket-qsystem/src/pytket/tests.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,7 @@ fn json_roundtrip(#[case] circ_s: &str, #[case] num_commands: usize, #[case] num
168168
let ser: circuit_json::SerialCircuit = serde_json::from_str(circ_s).unwrap();
169169
assert_eq!(ser.commands.len(), num_commands);
170170

171-
let circ: Circuit = ser
172-
.clone()
173-
.decode_with_config(qsystem_decoder_config())
174-
.unwrap();
171+
let circ: Circuit = ser.decode_with_config(qsystem_decoder_config()).unwrap();
175172

176173
assert_eq!(circ.qubit_count(), num_qubits);
177174

@@ -189,10 +186,7 @@ fn json_roundtrip(#[case] circ_s: &str, #[case] num_commands: usize, #[case] num
189186
fn circuit_roundtrip(#[case] circ: Circuit, #[case] decoded_sig: Signature) {
190187
let ser: SerialCircuit =
191188
SerialCircuit::encode_with_config(&circ, qsystem_encoder_config()).unwrap();
192-
let deser: Circuit = ser
193-
.clone()
194-
.decode_with_config(qsystem_decoder_config())
195-
.unwrap();
189+
let deser: Circuit = ser.decode_with_config(qsystem_decoder_config()).unwrap();
196190

197191
let deser_sig = deser.circuit_signature();
198192
assert_eq!(

tket/src/serialize/pytket.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,10 @@ pub trait TKETDecode: Sized {
6969
/// Convert the serialized circuit to a circuit.
7070
///
7171
/// Uses a default set of extension decoders to translate operations.
72-
fn decode(self) -> Result<Circuit, Self::DecodeError>;
72+
fn decode(&self) -> Result<Circuit, Self::DecodeError>;
7373
/// Convert the serialized circuit to a circuit.
7474
fn decode_with_config(
75-
self,
75+
&self,
7676
config: impl Into<Arc<PytketDecoderConfig>>,
7777
) -> Result<Circuit, Self::DecodeError>;
7878
/// Convert a circuit to a serialized pytket circuit.
@@ -95,20 +95,20 @@ impl TKETDecode for SerialCircuit {
9595
type DecodeError = PytketDecodeError;
9696
type EncodeError = PytketEncodeError;
9797

98-
fn decode(self) -> Result<Circuit, Self::DecodeError> {
98+
fn decode(&self) -> Result<Circuit, Self::DecodeError> {
9999
let config = default_decoder_config();
100100
Self::decode_with_config(self, config)
101101
}
102102

103103
fn decode_with_config(
104-
self,
104+
&self,
105105
config: impl Into<Arc<PytketDecoderConfig>>,
106106
) -> Result<Circuit, Self::DecodeError> {
107107
let mut hugr = Hugr::new();
108108

109109
let mut decoder =
110-
PytketDecoderContext::new(&self, &mut hugr, None, None, Vec::new(), config)?;
111-
decoder.run_decoder(self.commands)?;
110+
PytketDecoderContext::new(self, &mut hugr, None, None, Vec::new(), config)?;
111+
decoder.run_decoder(&self.commands)?;
112112
let main_func = decoder.finish()?;
113113
hugr.set_entrypoint(main_func.node());
114114
Ok(hugr.into())

tket/src/serialize/pytket/decoder.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ impl<'h> PytketDecoderContext<'h> {
333333
/// Decode a list of pytket commands.
334334
pub(super) fn run_decoder(
335335
&mut self,
336-
commands: Vec<circuit_json::Command>,
336+
commands: &[circuit_json::Command],
337337
) -> Result<(), PytketDecodeError> {
338338
let config = self.config.clone();
339339
for com in commands {
@@ -348,13 +348,13 @@ impl<'h> PytketDecoderContext<'h> {
348348
/// decoder.
349349
pub(super) fn process_command(
350350
&mut self,
351-
command: circuit_json::Command,
351+
command: &circuit_json::Command,
352352
config: &PytketDecoderConfig,
353353
) -> Result<(), PytketDecodeError> {
354354
let circuit_json::Command { op, args, opgroup } = command;
355355

356356
// Find the latest [`TrackedQubit`] and [`TrackedBit`] for the command registers.
357-
let (qubits, bits) = self.wire_tracker.pytket_args_to_tracked_elems(&args)?;
357+
let (qubits, bits) = self.wire_tracker.pytket_args_to_tracked_elems(args)?;
358358

359359
// Collect the parameters used in the command.
360360
let params: Vec<Arc<LoadedParameter>> = match &op.params {
@@ -366,12 +366,12 @@ impl<'h> PytketDecoderContext<'h> {
366366
};
367367

368368
// Try to decode the command with the configured decoders.
369-
match config.op_to_hugr(&op, &qubits, &bits, &params, &opgroup, self)? {
369+
match config.op_to_hugr(op, &qubits, &bits, &params, opgroup, self)? {
370370
DecodeStatus::Success => {}
371371
DecodeStatus::Unsupported => {
372372
// The command couldn't be translated into a native HUGR counterpart, so
373373
// we generate an opaque `Tk1Op` instead.
374-
build_opaque_tket_op(op, &qubits, &bits, &params, &opgroup, self)?;
374+
build_opaque_tket_op(op, &qubits, &bits, &params, opgroup, self)?;
375375
}
376376
}
377377
Ok(())

tket/src/serialize/pytket/extension/prelude.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,6 @@ impl PytketDecoder for PreludeEmitter {
124124
opgroup: Option<&str>,
125125
decoder: &mut PytketDecoderContext<'h>,
126126
) -> Result<DecodeStatus, PytketDecodeError> {
127-
// Qubits, bits and parameters that will be used to register the node outputs.
128-
//
129-
// These should be modified by the match branches if the node does not have all
130-
// its input registers in the outputs.
131-
132127
let op: OpType = match op.op_type {
133128
PytketOptype::noop => Noop::new(qb_t()).into(),
134129
PytketOptype::Barrier => {

tket/src/serialize/pytket/extension/tk1.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ impl<H: HugrView> PytketEmitter<H> for Tk1Emitter {
7777
/// We should accept arbitrary wires, but the opaque extension op needs to be modified (or replaced with a new one)
7878
/// since it currently has a limited signature definition.
7979
pub(crate) fn build_opaque_tket_op<'h>(
80-
op: tket_json_rs::circuit_json::Operation,
80+
op: &tket_json_rs::circuit_json::Operation,
8181
qubits: &[TrackedQubit],
8282
bits: &[TrackedBit],
8383
params: &[Arc<LoadedParameter>],
@@ -164,11 +164,9 @@ impl OpaqueTk1Op {
164164
///
165165
/// If the operation does not define a signature, one is generated with the
166166
/// given amounts.
167-
pub fn new_from_op(
168-
mut op: circuit_json::Operation,
169-
num_qubits: usize,
170-
num_bits: usize,
171-
) -> Self {
167+
pub fn new_from_op(op: &circuit_json::Operation, num_qubits: usize, num_bits: usize) -> Self {
168+
let mut op = op.clone();
169+
172170
if op.signature.is_none() {
173171
op.signature =
174172
Some([vec!["Q".into(); num_qubits], vec!["B".into(); num_bits]].concat());

tket/src/serialize/pytket/tests.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ fn json_roundtrip(#[case] circ_s: &str, #[case] num_commands: usize, #[case] num
397397
let ser: circuit_json::SerialCircuit = serde_json::from_str(circ_s).unwrap();
398398
assert_eq!(ser.commands.len(), num_commands);
399399

400-
let circ: Circuit = ser.clone().decode().unwrap();
400+
let circ: Circuit = ser.decode().unwrap();
401401

402402
assert_eq!(circ.qubit_count(), num_qubits);
403403

@@ -412,7 +412,7 @@ fn json_roundtrip(#[case] circ_s: &str, #[case] num_commands: usize, #[case] num
412412
fn json_file_roundtrip(#[case] circ: impl AsRef<std::path::Path>) {
413413
let reader = BufReader::new(std::fs::File::open(circ).unwrap());
414414
let ser: circuit_json::SerialCircuit = serde_json::from_reader(reader).unwrap();
415-
let circ: Circuit = ser.clone().decode().unwrap();
415+
let circ: Circuit = ser.decode().unwrap();
416416
let reser: SerialCircuit = SerialCircuit::encode(&circ).unwrap();
417417
validate_serial_circ(&reser);
418418
compare_serial_circs(&ser, &reser);
@@ -427,7 +427,7 @@ fn json_file_roundtrip(#[case] circ: impl AsRef<std::path::Path>) {
427427
#[case::preset_parameterized(circ_parameterized(), Signature::new(vec![qb_t(), rotation_type(), rotation_type(), rotation_type()], vec![qb_t()]))]
428428
fn circuit_roundtrip(#[case] circ: Circuit, #[case] decoded_sig: Signature) {
429429
let ser: SerialCircuit = SerialCircuit::encode(&circ).unwrap();
430-
let deser: Circuit = ser.clone().decode().unwrap();
430+
let deser: Circuit = ser.decode().unwrap();
431431

432432
let deser_sig = deser.circuit_signature();
433433
assert_eq!(
@@ -463,7 +463,7 @@ fn test_add_angle_serialise(#[case] circ_add_angles: (Circuit, String)) {
463463
assert_eq!(ser.commands[0].op.op_type, optype::OpType::Rx);
464464
assert_eq!(ser.commands[0].op.params, Some(vec![expected]));
465465

466-
let deser: Circuit = ser.clone().decode().unwrap();
466+
let deser: Circuit = ser.decode().unwrap();
467467
let reser = SerialCircuit::encode(&deser).unwrap();
468468
validate_serial_circ(&reser);
469469
compare_serial_circs(&ser, &reser);

0 commit comments

Comments
 (0)