Skip to content

Commit 88c5c79

Browse files
authored
feat!: Use qsystem encoder/decoders in tket-py (#1041)
Depends on #1039 - Adds an optional config parameter to all the `load_tk1` / `save_tk1` utilities, so we don't use the default by mistake. - Uses the new qsystem encoder/decoders in `tket-py`, so now we can load `ZZMax`/`ZZPhase`/`PhasedX` BREAKING CHANGE: Added encode/decode config arguments to pytket load/store utilities This is not a breaking change for the python lib, so I'll override the flag for release-please: BEGIN_COMMIT_OVERRIDE feat: Support qsystem native operations when loading pytket circuits END_COMMIT_OVERRIDE
1 parent 20582a8 commit 88c5c79

12 files changed

Lines changed: 143 additions & 41 deletions

File tree

‎Cargo.lock‎

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

‎badger-optimiser/Cargo.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ tket = { path = "../tket", features = [
1616
"rewrite-tracing",
1717
"binary-eccs",
1818
] }
19+
tket-qsystem = { path = "../tket-qsystem", version = "0.18.1" }
1920
tracing = { workspace = true }
2021
tracing-subscriber = { workspace = true }
2122
tracing-appender = { workspace = true }

‎badger-optimiser/src/main.rs‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
143143

144144
let badger_logger = BadgerLogger::new(circ_candidates_csv);
145145

146-
let mut circ = load_tk1_json_file(input_path)?;
146+
let mut circ = load_tk1_json_file(
147+
input_path,
148+
Some(tket_qsystem::pytket::qsystem_decoder_config()),
149+
)?;
147150
if opts.rewrite_tracing {
148151
circ.enable_rewrite_tracing();
149152
}
@@ -181,7 +184,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
181184
);
182185

183186
println!("Saving result");
184-
save_tk1_json_file(&opt_circ, output_path)?;
187+
save_tk1_json_file(
188+
&opt_circ,
189+
output_path,
190+
Some(tket_qsystem::pytket::qsystem_encoder_config()),
191+
)?;
185192

186193
#[cfg(feature = "peak_alloc")]
187194
println!("Peak memory usage: {} GB", PEAK_ALLOC.peak_usage_as_gb());

‎tket-py/Cargo.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ tket = { path = "../tket", version = "0.13.2", features = [
2323
"portmatching",
2424
"binary-eccs",
2525
] }
26+
tket-qsystem = { path = "../tket-qsystem", version = "0.18.1" }
2627
serde = { workspace = true, features = ["derive"] }
2728
serde_json = { workspace = true }
2829
tket-json-rs = { workspace = true, features = ["pyo3"] }

‎tket-py/src/circuit/convert.rs‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,12 @@ impl CircuitType {
4343
/// Converts a circuit into the format indicated by the flag.
4444
pub fn convert(self, py: Python, circ: Circuit) -> PyResult<Bound<PyAny>> {
4545
match self {
46-
CircuitType::Tket1 => SerialCircuit::encode(&circ).convert_pyerrs()?.to_tket1(py),
46+
CircuitType::Tket1 => SerialCircuit::encode_with_config(
47+
&circ,
48+
tket_qsystem::pytket::qsystem_encoder_config(),
49+
)
50+
.convert_pyerrs()?
51+
.to_tket1(py),
4752
CircuitType::Tket => Ok(Bound::new(py, Tk2Circuit { circ })?.into_any()),
4853
}
4954
}
@@ -62,7 +67,9 @@ where
6267
Ok(t2circ) => (t2circ.circ, CircuitType::Tket),
6368
// tket1 circuit
6469
Err(_) => (
65-
SerialCircuit::from_tket1(circ)?.decode().convert_pyerrs()?,
70+
SerialCircuit::from_tket1(circ)?
71+
.decode_with_config(tket_qsystem::pytket::qsystem_decoder_config())
72+
.convert_pyerrs()?,
6673
CircuitType::Tket1,
6774
),
6875
};

‎tket-py/src/circuit/tk2circuit.rs‎

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ impl Tk2Circuit {
8181
/// Convert the [`Tk2Circuit`] to a tket1 circuit.
8282
pub fn to_tket1<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
8383
let circ = lower_to_pytket(&self.circ).convert_pyerrs()?;
84-
SerialCircuit::encode(&circ).convert_pyerrs()?.to_tket1(py)
84+
SerialCircuit::encode_with_config(&circ, tket_qsystem::pytket::qsystem_encoder_config())
85+
.convert_pyerrs()?
86+
.to_tket1(py)
8587
}
8688

8789
/// Apply a rewrite on the circuit.
@@ -151,15 +153,26 @@ impl Tk2Circuit {
151153
pub fn to_tket1_json(&self) -> PyResult<String> {
152154
// Try to simplify tuple pack-unpack pairs, and other operations not supported by pytket.
153155
let circ = lower_to_pytket(&self.circ).convert_pyerrs()?;
154-
serde_json::to_string(&SerialCircuit::encode(&circ).convert_pyerrs()?).map_err(|e| {
156+
serde_json::to_string(
157+
&SerialCircuit::encode_with_config(
158+
&circ,
159+
tket_qsystem::pytket::qsystem_encoder_config(),
160+
)
161+
.convert_pyerrs()?,
162+
)
163+
.map_err(|e| {
155164
PyErr::new::<PyValueError, _>(format!("Could not encode pytket circuit to str: {e}"))
156165
})
157166
}
158167

159168
/// Decode a tket1 json string to a circuit.
160169
#[staticmethod]
161170
pub fn from_tket1_json(json: &str) -> PyResult<Self> {
162-
let circ = tket::serialize::load_tk1_json_str(json).map_err(|e| {
171+
let circ = tket::serialize::load_tk1_json_str(
172+
json,
173+
Some(tket_qsystem::pytket::qsystem_decoder_config()),
174+
)
175+
.map_err(|e| {
163176
PyErr::new::<PyAttributeError, _>(format!("Could not load pytket circuit: {e}"))
164177
})?;
165178
Ok(Tk2Circuit { circ })
@@ -169,15 +182,26 @@ impl Tk2Circuit {
169182
pub fn to_tket1_json_bytes(&self) -> PyResult<Vec<u8>> {
170183
// Try to simplify tuple pack-unpack pairs, and other operations not supported by pytket.
171184
let circ = lower_to_pytket(&self.circ).convert_pyerrs()?;
172-
serde_json::to_vec(&SerialCircuit::encode(&circ).convert_pyerrs()?).map_err(|e| {
185+
serde_json::to_vec(
186+
&SerialCircuit::encode_with_config(
187+
&circ,
188+
tket_qsystem::pytket::qsystem_encoder_config(),
189+
)
190+
.convert_pyerrs()?,
191+
)
192+
.map_err(|e| {
173193
PyErr::new::<PyValueError, _>(format!("Could not encode pytket circuit to bytes: {e}"))
174194
})
175195
}
176196

177197
/// Decode a tket1 json utf8 bytes to a circuit.
178198
#[staticmethod]
179199
pub fn from_tket1_json_bytes(json: &[u8]) -> PyResult<Self> {
180-
let circ = tket::serialize::load_tk1_json_reader(json).map_err(|e| {
200+
let circ = tket::serialize::load_tk1_json_reader(
201+
json,
202+
Some(tket_qsystem::pytket::qsystem_decoder_config()),
203+
)
204+
.map_err(|e| {
181205
PyErr::new::<PyAttributeError, _>(format!("Could not load pytket circuit: {e}"))
182206
})?;
183207
Ok(Tk2Circuit { circ })

‎tket-py/test/test_circuit.py‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,25 @@ def test_conversion():
5454

5555
assert tk1_back == tk1
5656
assert type(tk1_back) is Circuit
57+
58+
59+
def test_conversion_qsystem():
60+
tk1 = Circuit(2).ZZPhase(0.75, 0, 1).PhasedX(0.25, 0.33, 1)
61+
tk1_dot = render_circuit_dot(tk1)
62+
63+
tk2 = Tk2Circuit(tk1)
64+
tk2_dot = render_circuit_dot(tk2)
65+
66+
assert type(tk2) is Tk2Circuit
67+
assert tk1_dot == tk2_dot
68+
69+
# Check that we didn't use the opaque tk1 op fallback.
70+
# TODO: There's no way to traverse the circuit nodes??
71+
assert "TKET1.tk1op" not in tk1_dot
72+
assert "tket.qsystem.PhasedX" in tk1_dot
73+
assert "tket.qsystem.ZZPhase" in tk1_dot
74+
75+
tk1_back = tk2.to_tket1()
76+
77+
assert tk1_back == tk1
78+
assert type(tk1_back) is Circuit

‎tket/src/circuit.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,7 @@ mod tests {
674674
],
675675
"implicit_permutation": [[["q", [0]], ["q", [0]]], [["q", [1]], ["q", [1]]]]
676676
}"#,
677+
None,
677678
)
678679
.unwrap()
679680
}

‎tket/src/lib.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
//! use hugr::HugrView;
2525
//!
2626
//! // Load a tket1 circuit.
27-
//! let mut circ: Circuit = tket::serialize::load_tk1_json_file("../test_files/barenco_tof_5.json").unwrap();
27+
//! let mut circ: Circuit = tket::serialize::load_tk1_json_file("../test_files/barenco_tof_5.json", None).unwrap();
2828
//!
2929
//! assert_eq!(circ.qubit_count(), 9);
3030
//! assert_eq!(circ.num_operations(), 170);

‎tket/src/optimiser/badger.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,7 @@ mod tests {
641641
/// A circuit that would trigger non-composable rewrites, if we applied them blindly from nam_6_3 matches.
642642
#[fixture]
643643
fn non_composable_rw_hugr() -> Circuit {
644-
load_tk1_json_str(NON_COMPOSABLE).unwrap()
644+
load_tk1_json_str(NON_COMPOSABLE, None).unwrap()
645645
}
646646

647647
/// A badger optimiser using a reduced set of rewrite rules.

0 commit comments

Comments
 (0)