Skip to content

Commit 576d9d5

Browse files
committed
feat: Add emitters for tket-qsystem
1 parent c015fad commit 576d9d5

File tree

6 files changed

+272
-8
lines changed

6 files changed

+272
-8
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tket-qsystem/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ required-features = ["cli"]
2525
[dependencies]
2626
hugr.workspace = true
2727
tket = { path = "../tket", version = "0.13.2" }
28+
tket-json-rs = { workspace = true }
2829
lazy_static.workspace = true
2930
serde = { workspace = true, features = ["derive"] }
3031
smol_str.workspace = true

tket-qsystem/src/lib.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
//! Provides a preparation and validation workflow for Hugrs targeting
22
//! Quantinuum H-series quantum computers.
33
4+
#[cfg(feature = "cli")]
5+
pub mod cli;
6+
pub mod extension;
7+
#[cfg(feature = "llvm")]
8+
pub mod llvm;
9+
mod lower_drops;
10+
pub mod pytket;
11+
pub mod replace_bools;
12+
413
use derive_more::{Display, Error, From};
514
use hugr::{
615
algorithms::{
@@ -25,14 +34,6 @@ use extension::{
2534
#[cfg(feature = "llvm")]
2635
use hugr::llvm::utils::inline_constant_functions;
2736

28-
#[cfg(feature = "cli")]
29-
pub mod cli;
30-
pub mod extension;
31-
#[cfg(feature = "llvm")]
32-
pub mod llvm;
33-
mod lower_drops;
34-
pub mod replace_bools;
35-
3637
/// Modify a [hugr::Hugr] into a form that is acceptable for ingress into a
3738
/// Q-System. Returns an error if this cannot be done.
3839
///

tket-qsystem/src/pytket.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//! Encoder/decoder definitions for translating tket-qsystem operations to/from legacy Pytket circuits.
2+
3+
mod future;
4+
mod qsystem;
5+
6+
pub use future::FutureEmitter;
7+
use hugr::HugrView;
8+
pub use qsystem::QSystemEmitter;
9+
use tket::serialize::pytket::{
10+
default_decoder_config, default_encoder_config, PytketDecoderConfig, PytketEncoderConfig,
11+
};
12+
13+
/// Default pytket decoder configuration for [`Circuit`][tket::Circuit]s with
14+
/// native qsystem operations.
15+
///
16+
/// Contains a list of custom decoders that define translations of legacy tket
17+
/// primitives into HUGR operations.
18+
pub fn qsystem_decoder_config() -> PytketDecoderConfig {
19+
let mut config = default_decoder_config();
20+
config.add_decoder(QSystemEmitter);
21+
22+
config.add_type_translator(FutureEmitter);
23+
24+
config
25+
}
26+
27+
/// Default encoder configuration for [`Circuit`][crate::Circuit]s.
28+
///
29+
/// Contains emitters for std and tket operations.
30+
pub fn qsystem_encoder_config<H: HugrView>() -> PytketEncoderConfig<H> {
31+
let mut config = default_encoder_config();
32+
config.add_emitter(QSystemEmitter);
33+
config.add_emitter(FutureEmitter);
34+
35+
config.add_type_translator(FutureEmitter);
36+
37+
config
38+
}

tket-qsystem/src/pytket/futures.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//! Encoder and decoder for floating point operations.
2+
3+
use hugr::extension::simple_op::MakeExtensionOp;
4+
use hugr::extension::ExtensionId;
5+
use hugr::ops::ExtensionOp;
6+
use hugr::types::Term;
7+
use hugr::HugrView;
8+
use itertools::Itertools;
9+
use tket::serialize::pytket::encoder::EncodeStatus;
10+
use tket::serialize::pytket::extension::{PytketTypeTranslator, RegisterCount};
11+
use tket::serialize::pytket::{
12+
PytketEmitter, PytketEncodeError, PytketEncoderContext, TypeTranslatorSet,
13+
};
14+
use tket::Circuit;
15+
16+
use crate::extension::futures::{self, FutureOpDef};
17+
18+
/// Emitter for [futures](hugr::extension::futures) operations and types.
19+
///
20+
/// The `Future<T>` type is treated as a transparent wrapper when translated to
21+
/// pytket circuits, and operations dealing with futures do not produce pytket
22+
/// commands.
23+
#[derive(Debug, Clone, Default)]
24+
pub struct FutureEmitter;
25+
26+
impl<H: HugrView> PytketEmitter<H> for FutureEmitter {
27+
fn extensions(&self) -> Option<Vec<ExtensionId>> {
28+
Some(vec![futures::EXTENSION_ID])
29+
}
30+
31+
fn op_to_pytket(
32+
&self,
33+
node: H::Node,
34+
op: &ExtensionOp,
35+
circ: &Circuit<H>,
36+
encoder: &mut PytketEncoderContext<H>,
37+
) -> Result<EncodeStatus, PytketEncodeError<H::Node>> {
38+
let Ok(rot_op) = FutureOpDef::from_extension_op(op) else {
39+
return Ok(EncodeStatus::Unsupported);
40+
};
41+
42+
match rot_op {
43+
FutureOpDef::Read => {
44+
// Transparent map
45+
encoder.emit_transparent_node(node, circ, |ps| ps.input_params.to_vec())?;
46+
Ok(EncodeStatus::Success)
47+
}
48+
FutureOpDef::Dup => {
49+
// Register the same input values for each output.
50+
let values = encoder.get_input_values(node, circ)?;
51+
let outputs = circ.hugr().node_outputs(node).collect_vec();
52+
let out0 = hugr::Wire::new(node, outputs[0]);
53+
let out1 = hugr::Wire::new(node, outputs[1]);
54+
55+
encoder.values.register_wire(out0, values.clone(), circ)?;
56+
encoder.values.register_wire(out1, values, circ)?;
57+
58+
Ok(EncodeStatus::Success)
59+
}
60+
FutureOpDef::Free => Ok(EncodeStatus::Success),
61+
}
62+
}
63+
}
64+
65+
impl PytketTypeTranslator for FutureEmitter {
66+
fn extensions(&self) -> Vec<ExtensionId> {
67+
vec![futures::EXTENSION_ID]
68+
}
69+
70+
fn type_to_pytket(
71+
&self,
72+
typ: &hugr::types::CustomType,
73+
type_translators: &TypeTranslatorSet,
74+
) -> Option<RegisterCount> {
75+
if typ.name() != futures::FUTURE_TYPE_NAME.as_str() {
76+
return None;
77+
}
78+
let Some(Term::Runtime(inner_ty)) = typ.args().first() else {
79+
return None;
80+
};
81+
82+
type_translators.type_to_pytket(inner_ty)
83+
}
84+
}

tket-qsystem/src/pytket/qsystem.rs

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
//! Encoder/decoder for [qsystem::EXTENSION][use crate::extension::qsystem::EXTENSION] operations.
2+
3+
use std::sync::Arc;
4+
5+
use hugr::extension::simple_op::MakeExtensionOp;
6+
use hugr::extension::ExtensionId;
7+
use hugr::ops::ExtensionOp;
8+
use hugr::HugrView;
9+
use tket::serialize::pytket::decoder::{
10+
DecodeStatus, LoadedParameter, PytketDecoderContext, TrackedBit, TrackedQubit,
11+
};
12+
use tket::serialize::pytket::encoder::EncodeStatus;
13+
use tket::serialize::pytket::extension::PytketDecoder;
14+
use tket::serialize::pytket::{
15+
PytketDecodeError, PytketEmitter, PytketEncodeError, PytketEncoderContext,
16+
};
17+
use tket::Circuit;
18+
use tket_json_rs::optype::OpType as PytketOptype;
19+
20+
use crate::extension;
21+
use crate::extension::qsystem::{QSystemOp, RuntimeBarrierDef};
22+
23+
/// Encoder for [TketOp] operations.
24+
#[derive(Debug, Clone, Default)]
25+
pub struct QSystemEmitter;
26+
27+
impl<H: HugrView> PytketEmitter<H> for QSystemEmitter {
28+
fn extensions(&self) -> Option<Vec<ExtensionId>> {
29+
Some(vec![extension::qsystem::EXTENSION_ID])
30+
}
31+
32+
fn op_to_pytket(
33+
&self,
34+
node: H::Node,
35+
op: &ExtensionOp,
36+
circ: &Circuit<H>,
37+
encoder: &mut PytketEncoderContext<H>,
38+
) -> Result<EncodeStatus, PytketEncodeError<H::Node>> {
39+
if let Ok(tket_op) = QSystemOp::from_extension_op(op) {
40+
self.encode_qsystem_op(node, tket_op, circ, encoder)
41+
} else if let Ok(sympy_op) = RuntimeBarrierDef::from_extension_op(op) {
42+
self.encode_runtime_barrier_op(node, sympy_op, circ, encoder)
43+
} else {
44+
Ok(EncodeStatus::Unsupported)
45+
}
46+
}
47+
}
48+
49+
impl QSystemEmitter {
50+
/// Encode a tket operation into a pytket operation.
51+
fn encode_qsystem_op<H: HugrView>(
52+
&self,
53+
node: H::Node,
54+
qsystem_op: QSystemOp,
55+
circ: &Circuit<H>,
56+
encoder: &mut PytketEncoderContext<H>,
57+
) -> Result<EncodeStatus, PytketEncodeError<H::Node>> {
58+
let serial_op = match qsystem_op {
59+
QSystemOp::Measure => PytketOptype::Measure,
60+
// "Lazy" operations are translated as eager measurements in pytket,
61+
// as there is no `Future<T>` type there.
62+
QSystemOp::LazyMeasure => PytketOptype::Measure,
63+
QSystemOp::LazyMeasureReset => PytketOptype::Measure,
64+
QSystemOp::MeasureReset => PytketOptype::Measure,
65+
QSystemOp::Rz => PytketOptype::Rz,
66+
QSystemOp::PhasedX => PytketOptype::PhasedX,
67+
QSystemOp::ZZPhase => PytketOptype::ZZPhase,
68+
QSystemOp::Reset => PytketOptype::Reset,
69+
QSystemOp::QFree => {
70+
// Mark the qubit inputs as explored and forget about them.
71+
encoder.get_input_values(node, circ)?;
72+
return Ok(EncodeStatus::Success);
73+
}
74+
QSystemOp::LazyMeasureLeaked => {
75+
// No equivalent pytket operation.
76+
return Ok(EncodeStatus::Unsupported);
77+
}
78+
QSystemOp::TryQAlloc => {
79+
// Pytket circuits don't support the optional type returned by `TryQAlloc`.
80+
return Ok(EncodeStatus::Unsupported);
81+
}
82+
};
83+
84+
// Most operations map directly to a pytket one.
85+
encoder.emit_node(serial_op, node, circ)?;
86+
87+
Ok(EncodeStatus::Success)
88+
}
89+
90+
fn encode_runtime_barrier_op<H: HugrView>(
91+
&self,
92+
node: H::Node,
93+
_runtime_barrier_op: RuntimeBarrierDef,
94+
circ: &Circuit<H>,
95+
encoder: &mut PytketEncoderContext<H>,
96+
) -> Result<EncodeStatus, PytketEncodeError<H::Node>> {
97+
encoder.emit_node(PytketOptype::Barrier, node, circ)?;
98+
99+
Ok(EncodeStatus::Success)
100+
}
101+
}
102+
103+
impl PytketDecoder for QSystemEmitter {
104+
fn op_types(&self) -> Vec<PytketOptype> {
105+
// Process native optypes that are not supported by the `TketOp` emitter.
106+
vec![
107+
PytketOptype::PhasedX,
108+
PytketOptype::ZZPhase,
109+
PytketOptype::ZZMax,
110+
]
111+
}
112+
113+
fn op_to_hugr<'h>(
114+
&self,
115+
op: &tket_json_rs::circuit_json::Operation,
116+
qubits: &[TrackedQubit],
117+
bits: &[TrackedBit],
118+
params: &[Arc<LoadedParameter>],
119+
_opgroup: Option<&str>,
120+
decoder: &mut PytketDecoderContext<'h>,
121+
) -> Result<DecodeStatus, PytketDecodeError> {
122+
let op = match op.op_type {
123+
PytketOptype::PhasedX => QSystemOp::PhasedX,
124+
PytketOptype::ZZPhase => QSystemOp::ZZPhase,
125+
PytketOptype::ZZMax => {
126+
// This is a ZZPhase with a 1/2 angle.
127+
let param = decoder.load_parameter("pi/2");
128+
decoder.add_node_with_wires(QSystemOp::ZZPhase, qubits, bits, &[param])?;
129+
return Ok(DecodeStatus::Success);
130+
}
131+
_ => {
132+
return Ok(DecodeStatus::Unsupported);
133+
}
134+
};
135+
decoder.add_node_with_wires(op, qubits, bits, params)?;
136+
137+
Ok(DecodeStatus::Success)
138+
}
139+
}

0 commit comments

Comments
 (0)