From ddf8837d87be8aa21affadbdff483eebe25c2e76 Mon Sep 17 00:00:00 2001 From: Callum Macpherson Date: Wed, 29 Apr 2026 17:02:21 +0100 Subject: [PATCH 01/12] start binding the QSystemPasss --- tket-py/src/passes.rs | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/tket-py/src/passes.rs b/tket-py/src/passes.rs index c69de7947..6592df5dd 100644 --- a/tket-py/src/passes.rs +++ b/tket-py/src/passes.rs @@ -11,10 +11,11 @@ use std::{cmp::min, convert::TryInto, fs, num::NonZeroUsize, path::PathBuf}; use pyo3::prelude::*; use tket::optimiser::badger::BadgerOptions; -use tket::passes; use tket::passes::composable::{ComposablePass, WithScope}; use tket::{Circuit, TketOp, op_matches}; +use tket::passes; + use crate::optimiser::PyBadgerOptimiser; use crate::state::CompilationState; use crate::utils::{ConvertPyErr, create_py_exception}; @@ -32,6 +33,7 @@ pub fn module(py: Python<'_>) -> PyResult> { m.add_function(wrap_pyfunction!(self::chunks::chunks, &m)?)?; m.add_function(wrap_pyfunction!(self::tket1::tket1_pass, &m)?)?; m.add_function(wrap_pyfunction!(resolve_modifiers, &m)?)?; + m.add_function(wrap_pyfunction!(qsystem_rebase_pass, &m)?)?; m.add("PullForwardError", py.get_type::())?; m.add( "InlineFunctionsError", @@ -64,6 +66,13 @@ create_py_exception!( PyInlineFunctionsError, "Errors from the function inlining pass." ); + +create_py_exception!( + tket_qsystem::QSystemPassError, + PyQSystemPassError, + "Errors from the QSystem rebase pass." +); + /// Flatten the structure of a Guppy-generated program to enable additional optimisations. /// /// This should normally be called first before other optimisations. @@ -203,3 +212,24 @@ fn resolve_modifiers(circ: &mut CompilationState, scope: Option) -> pass.run(&mut circ.hugr).convert_pyerrs()?; Ok(()) } + +#[pyfunction] +#[pyo3(signature=(circ, *, constant_fold = true, monomorphize = true, force_order = true, lazify = true, scope = None))] +fn qsystem_rebase_pass( + circ: &mut CompilationState, + constant_fold: bool, + monomorphize: bool, + force_order: bool, + lazify: bool, + scope: Option, +) -> PyResult<()> { + let py_scope = scope.unwrap_or_default(); + let qsystem_pass = tket_qsystem::QSystemPass::default_with_scope(py_scope.scope) + .with_constant_fold(constant_fold) + .with_monomorphize(monomorphize) + .with_force_order(force_order) + .with_lazify(lazify); + + qsystem_pass.run(&mut circ.hugr).convert_pyerrs()?; + Ok(()) +} From 47fd2ae1a8fd0ba1b2d1c78c1d9b1ece91399641 Mon Sep 17 00:00:00 2001 From: Callum Macpherson Date: Wed, 29 Apr 2026 17:29:44 +0100 Subject: [PATCH 02/12] add Python definition --- tket-py/test/test_pass.py | 4 +++ tket-py/tket/_tket/passes.pyi | 10 +++++++ tket-py/tket/passes/__init__.py | 46 +++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/tket-py/test/test_pass.py b/tket-py/test/test_pass.py index 8f4bc3ae4..8c3a5b076 100644 --- a/tket-py/test/test_pass.py +++ b/tket-py/test/test_pass.py @@ -297,3 +297,7 @@ def test_inline_functions() -> None: all = InlineFunctions(heuristic=inline_funcs.All())(hugr) assert _count_ops(all, "Call") == 0 + + +def test_qsystem_pass() -> None: + pass diff --git a/tket-py/tket/_tket/passes.pyi b/tket-py/tket/_tket/passes.pyi index 699a3beea..eba7a48be 100644 --- a/tket-py/tket/_tket/passes.pyi +++ b/tket-py/tket/_tket/passes.pyi @@ -99,3 +99,13 @@ def resolve_modifiers( :param circ: The input program as a CompilationState. :param scope: A scope to control how the pass is applied to HUGR regions. """ + +def qsystem_rebase_pass( + circ: CompilationState, + constant_fold: bool = True, + monomorphize: bool = True, + force_order: bool = True, + lazify: bool = True, + scope: PassScope | None = None, +) -> None: + """Runs a rust backed pass to convert quantum ops to qsystem ops.""" diff --git a/tket-py/tket/passes/__init__.py b/tket-py/tket/passes/__init__.py index e21f78e71..c0047d51e 100644 --- a/tket-py/tket/passes/__init__.py +++ b/tket-py/tket/passes/__init__.py @@ -294,3 +294,49 @@ def _run_tk(self, program: _state.CompilationState) -> _state.CompilationState: scope=self._scope, ) return program + + +@dataclass +class QSystemPass(ComposablePass): + """A pass to convert quantum ops to qsystem ops.""" + + constant_fold: bool = True + monomorphize: bool = True + force_order: bool = True + lazify: bool = True + _scope: PassScope = GlobalScope.PRESERVE_PUBLIC + + def run(self, hugr: Hugr, *, inplace: bool = True) -> PassResult: + return implement_pass_run( + self, + hugr=hugr, + inplace=inplace, + copy_call=lambda h: self._qsystem_rebase(h, inplace), + ) + + def with_scope(self, scope: PassScope) -> QSystemPass: + """Set the scope of this pass and return self.""" + self._scope = scope + return self + + def _qsystem_rebase(self, hugr: Hugr, inplace: bool) -> PassResult: + tk_program = _state.CompilationState.from_python(hugr) + + self._run_tk(tk_program) + + package = tk_program.to_python() + return PassResult.for_pass( + self, hugr=package.modules[0], inplace=inplace, result=None + ) + + def _run_tk(self, program: _state.CompilationState) -> _state.CompilationState: + """Run the pass in the CompilationState""" + _passes.qsystem_rebase_pass( + program._inner, + constant_fold=self.constant_fold, + monomorphize=self.monomorphize, + force_order=self.force_order, + lazify=self.lazify, + scope=self._scope, + ) + return program From 72a7df2d57ce8713260d5dca13e12419064815c1 Mon Sep 17 00:00:00 2001 From: Callum Macpherson Date: Wed, 29 Apr 2026 17:47:35 +0100 Subject: [PATCH 03/12] add a test --- tket-py/test/test_pass.py | 11 ++++++++--- tket-py/tket/passes/__init__.py | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/tket-py/test/test_pass.py b/tket-py/test/test_pass.py index 8c3a5b076..c302bb10d 100644 --- a/tket-py/test/test_pass.py +++ b/tket-py/test/test_pass.py @@ -20,7 +20,7 @@ from hypothesis.strategies._internal import SearchStrategy from hypothesis import given, settings -from tket.passes import PytketHugrPass +from tket.passes import PytketHugrPass, QSystemPass from pytket.passes import CliffordSimp, SquashRzPhasedX, SequencePass from hugr.build.base import Hugr @@ -299,5 +299,10 @@ def test_inline_functions() -> None: assert _count_ops(all, "Call") == 0 -def test_qsystem_pass() -> None: - pass +def test_python_qsystem_pass() -> None: + circ = Circuit(2).H(0).Rz(0.75, 0).CX(0, 1) + c = CompilationState.from_tket1(circ) + hugr = Hugr.from_str(c.to_str(), tket_registry()) + qsystem_pass = QSystemPass() + qsystem_hugr = qsystem_pass(hugr) + assert _count_ops(qsystem_hugr, "ZZPhase") == 1 diff --git a/tket-py/tket/passes/__init__.py b/tket-py/tket/passes/__init__.py index c0047d51e..d1b6a142b 100644 --- a/tket-py/tket/passes/__init__.py +++ b/tket-py/tket/passes/__init__.py @@ -29,6 +29,7 @@ "InlineFunctions", "NormalizeGuppy", "ModifierResolverPass", + "QSystemPass", ] From 0747f9e812ac03a8cb28cf025bc713be57bf9ce2 Mon Sep 17 00:00:00 2001 From: Callum Macpherson Date: Wed, 29 Apr 2026 18:09:21 +0100 Subject: [PATCH 04/12] add a test for a guppy generated hugr --- test_files/guppy_examples/flat_quantum.hugr | Bin 0 -> 3976 bytes test_files/guppy_examples/flat_quantum.py | 23 ++++++++++++++++++++ tket-py/test/test_pass.py | 5 ++--- 3 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 test_files/guppy_examples/flat_quantum.hugr create mode 100644 test_files/guppy_examples/flat_quantum.py diff --git a/test_files/guppy_examples/flat_quantum.hugr b/test_files/guppy_examples/flat_quantum.hugr new file mode 100644 index 0000000000000000000000000000000000000000..1241e3149414f187b7bc3b7341317edc638a223c GIT binary patch literal 3976 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 None: + h(q0) + rz(q0, 3 * pi / 4) + cx(q0, q1) + + +program = flat_quantum_func.compile_function() +Path(argv[0]).with_suffix(".hugr").write_bytes(program.to_bytes()) diff --git a/tket-py/test/test_pass.py b/tket-py/test/test_pass.py index c302bb10d..86f847a60 100644 --- a/tket-py/test/test_pass.py +++ b/tket-py/test/test_pass.py @@ -300,9 +300,8 @@ def test_inline_functions() -> None: def test_python_qsystem_pass() -> None: - circ = Circuit(2).H(0).Rz(0.75, 0).CX(0, 1) - c = CompilationState.from_tket1(circ) - hugr = Hugr.from_str(c.to_str(), tket_registry()) + normalize = NormalizeGuppy() + hugr = normalize(_hugr_from_path("test_files/guppy_examples/flat_quantum.hugr")) qsystem_pass = QSystemPass() qsystem_hugr = qsystem_pass(hugr) assert _count_ops(qsystem_hugr, "ZZPhase") == 1 From 076c1761220fab4d067d09ed17c454259af802c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Borgna?= Date: Fri, 1 May 2026 14:29:02 +0100 Subject: [PATCH 05/12] fix: Use bundled extension registry when loading a CompilationState into python --- tket-py/tket/_state/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tket-py/tket/_state/__init__.py b/tket-py/tket/_state/__init__.py index 6212cbadd..d4b043d6c 100644 --- a/tket-py/tket/_state/__init__.py +++ b/tket-py/tket/_state/__init__.py @@ -6,6 +6,7 @@ from hugr.envelope import EnvelopeConfig from hugr.ext import ExtensionRegistry +from tket_exts import tket_registry from .._tket import state as _state from .build import CircBuild, Command @@ -97,11 +98,10 @@ def to_python(self) -> Package: """Convert this CompilationState back to a python Hugr package.""" # Convert the inner hugr to bytes and load it in Python. hugr_bytes = self._inner.to_bytes() - package = Package.from_bytes(hugr_bytes, self._py_extensions) + package = Package.from_bytes(hugr_bytes, tket_registry()) if self._py_extensions is not None: # Resolve the extensions in the loaded package using the python registry, if needed. - # TODO: Use the `package.resolve_extensions` for clarity once it's been released in `hugr-py 0.16.0`. - package.used_extensions(self._py_extensions) + package.resolve_extensions(self._py_extensions) return package @staticmethod From 70557a8d8b3a9c94064d79074f8ce8bfd0acfb35 Mon Sep 17 00:00:00 2001 From: Callum Macpherson Date: Fri, 1 May 2026 15:15:53 +0100 Subject: [PATCH 06/12] add more assertions --- tket-py/test/test_pass.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tket-py/test/test_pass.py b/tket-py/test/test_pass.py index 86f847a60..2eebb9b67 100644 --- a/tket-py/test/test_pass.py +++ b/tket-py/test/test_pass.py @@ -305,3 +305,5 @@ def test_python_qsystem_pass() -> None: qsystem_pass = QSystemPass() qsystem_hugr = qsystem_pass(hugr) assert _count_ops(qsystem_hugr, "ZZPhase") == 1 + assert _count_ops(qsystem_hugr, "Custom") == 0 + assert _count_ops(qsystem_hugr, "tket.quantum") == 0 From ca348e8daea08eaad0b2ab6a41e3315ec259918d Mon Sep 17 00:00:00 2001 From: Callum Macpherson Date: Fri, 1 May 2026 15:48:57 +0100 Subject: [PATCH 07/12] document Boolean args --- tket-py/tket/_tket/passes.pyi | 8 +++++++- tket-py/tket/passes/__init__.py | 9 ++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/tket-py/tket/_tket/passes.pyi b/tket-py/tket/_tket/passes.pyi index eba7a48be..cec319670 100644 --- a/tket-py/tket/_tket/passes.pyi +++ b/tket-py/tket/_tket/passes.pyi @@ -108,4 +108,10 @@ def qsystem_rebase_pass( lazify: bool = True, scope: PassScope | None = None, ) -> None: - """Runs a rust backed pass to convert quantum ops to qsystem ops.""" + """Runs a rust backed pass to convert quantum ops to qsystem ops. + + :param constant_fold: Whether to perform constant folding. + :param monomorphize: Whether to monomorphize generic functions. + :param force_order: Whether to enforce total ordering of all HUGR operations. + :param lazify: Whether to replace measurements with lazy measurements. + """ diff --git a/tket-py/tket/passes/__init__.py b/tket-py/tket/passes/__init__.py index d1b6a142b..1b99e5d40 100644 --- a/tket-py/tket/passes/__init__.py +++ b/tket-py/tket/passes/__init__.py @@ -299,7 +299,14 @@ def _run_tk(self, program: _state.CompilationState) -> _state.CompilationState: @dataclass class QSystemPass(ComposablePass): - """A pass to convert quantum ops to qsystem ops.""" + """A pass to convert quantum ops to qsystem ops. + + Parameters: + - constant_fold: Whether to perform constant folding. + - monomorphize: Whether to monomorphize generic functions. + - force_order: Whether to enforce total ordering of all HUGR operations. + - lazify: Whether to replace measurements with lazy measurements. + """ constant_fold: bool = True monomorphize: bool = True From 739870dc8167dcb5ca911346a044d51c0e354b5a Mon Sep 17 00:00:00 2001 From: Callum Macpherson Date: Tue, 5 May 2026 10:54:10 +0100 Subject: [PATCH 08/12] add a with_hide_funcs method --- tket-py/src/passes.rs | 6 ++++-- tket-py/tket/_tket/passes.pyi | 2 ++ tket-py/tket/passes/__init__.py | 2 ++ tket-qsystem/src/lib.rs | 10 ++++++++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/tket-py/src/passes.rs b/tket-py/src/passes.rs index 6592df5dd..65f601a6b 100644 --- a/tket-py/src/passes.rs +++ b/tket-py/src/passes.rs @@ -214,13 +214,14 @@ fn resolve_modifiers(circ: &mut CompilationState, scope: Option) -> } #[pyfunction] -#[pyo3(signature=(circ, *, constant_fold = true, monomorphize = true, force_order = true, lazify = true, scope = None))] +#[pyo3(signature=(circ, *, constant_fold = true, monomorphize = true, force_order = true, lazify = true, hide_funcs = true, scope = None))] fn qsystem_rebase_pass( circ: &mut CompilationState, constant_fold: bool, monomorphize: bool, force_order: bool, lazify: bool, + hide_funcs: bool, scope: Option, ) -> PyResult<()> { let py_scope = scope.unwrap_or_default(); @@ -228,7 +229,8 @@ fn qsystem_rebase_pass( .with_constant_fold(constant_fold) .with_monomorphize(monomorphize) .with_force_order(force_order) - .with_lazify(lazify); + .with_lazify(lazify) + .with_hide_funcs(hide_funcs); qsystem_pass.run(&mut circ.hugr).convert_pyerrs()?; Ok(()) diff --git a/tket-py/tket/_tket/passes.pyi b/tket-py/tket/_tket/passes.pyi index cec319670..6fdbdbfb5 100644 --- a/tket-py/tket/_tket/passes.pyi +++ b/tket-py/tket/_tket/passes.pyi @@ -106,6 +106,7 @@ def qsystem_rebase_pass( monomorphize: bool = True, force_order: bool = True, lazify: bool = True, + hide_funcs: bool = True, scope: PassScope | None = None, ) -> None: """Runs a rust backed pass to convert quantum ops to qsystem ops. @@ -114,4 +115,5 @@ def qsystem_rebase_pass( :param monomorphize: Whether to monomorphize generic functions. :param force_order: Whether to enforce total ordering of all HUGR operations. :param lazify: Whether to replace measurements with lazy measurements. + :param hide_funcs: Make all HUGR functions private. """ diff --git a/tket-py/tket/passes/__init__.py b/tket-py/tket/passes/__init__.py index 1b99e5d40..2fc1d1ccc 100644 --- a/tket-py/tket/passes/__init__.py +++ b/tket-py/tket/passes/__init__.py @@ -312,6 +312,7 @@ class QSystemPass(ComposablePass): monomorphize: bool = True force_order: bool = True lazify: bool = True + hide_funcs: bool = True _scope: PassScope = GlobalScope.PRESERVE_PUBLIC def run(self, hugr: Hugr, *, inplace: bool = True) -> PassResult: @@ -345,6 +346,7 @@ def _run_tk(self, program: _state.CompilationState) -> _state.CompilationState: monomorphize=self.monomorphize, force_order=self.force_order, lazify=self.lazify, + hide_funcs=self.hide_funcs, scope=self._scope, ) return program diff --git a/tket-qsystem/src/lib.rs b/tket-qsystem/src/lib.rs index 7ba4c4f4f..48396faec 100644 --- a/tket-qsystem/src/lib.rs +++ b/tket-qsystem/src/lib.rs @@ -140,6 +140,16 @@ impl QSystemPass { self } + /// Makes all functions private. + /// + /// On by default + /// + /// When enabled all functions are marked as private. This enables LLVM to drop functions unless which are not called. + pub fn with_hide_funcs(mut self, hide_funcs: bool) -> Self { + self.hide_funcs = hide_funcs; + self + } + /// Add order edges in the HUGR regions to force qubit frees to be as early /// as possible, quantum ops to be as early as possible, and Future::Reads /// to be as late as possible. From a20b808bda907e87d3b45c2c1075f77bbe82146e Mon Sep 17 00:00:00 2001 From: Callum Macpherson Date: Tue, 5 May 2026 10:54:51 +0100 Subject: [PATCH 09/12] Make QSystemPass a kw_only dataclass --- tket-py/tket/passes/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tket-py/tket/passes/__init__.py b/tket-py/tket/passes/__init__.py index 2fc1d1ccc..98cde6064 100644 --- a/tket-py/tket/passes/__init__.py +++ b/tket-py/tket/passes/__init__.py @@ -297,7 +297,7 @@ def _run_tk(self, program: _state.CompilationState) -> _state.CompilationState: return program -@dataclass +@dataclass(kw_only=True) class QSystemPass(ComposablePass): """A pass to convert quantum ops to qsystem ops. From 5d8f28da6c3a0ab2f2ce96de42e04003da2ff89e Mon Sep 17 00:00:00 2001 From: Callum Macpherson Date: Tue, 5 May 2026 11:26:54 +0100 Subject: [PATCH 10/12] add missing docstring --- tket-py/tket/passes/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tket-py/tket/passes/__init__.py b/tket-py/tket/passes/__init__.py index 98cde6064..259e2c5e7 100644 --- a/tket-py/tket/passes/__init__.py +++ b/tket-py/tket/passes/__init__.py @@ -306,6 +306,7 @@ class QSystemPass(ComposablePass): - monomorphize: Whether to monomorphize generic functions. - force_order: Whether to enforce total ordering of all HUGR operations. - lazify: Whether to replace measurements with lazy measurements. + - hide_funcs: Whether to mark all functions as private. """ constant_fold: bool = True From 89b8b66525904025214f150d2a0f150d7cf4c795 Mon Sep 17 00:00:00 2001 From: Callum Macpherson Date: Tue, 5 May 2026 11:28:22 +0100 Subject: [PATCH 11/12] fix bad wording --- tket-qsystem/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tket-qsystem/src/lib.rs b/tket-qsystem/src/lib.rs index 48396faec..e5022af37 100644 --- a/tket-qsystem/src/lib.rs +++ b/tket-qsystem/src/lib.rs @@ -144,7 +144,7 @@ impl QSystemPass { /// /// On by default /// - /// When enabled all functions are marked as private. This enables LLVM to drop functions unless which are not called. + /// When enabled all functions are marked as private. This enables LLVM to drop functions which are not called. pub fn with_hide_funcs(mut self, hide_funcs: bool) -> Self { self.hide_funcs = hide_funcs; self From a69f0cd2175241eda97d8a64abcc6dad2d16af12 Mon Sep 17 00:00:00 2001 From: Callum Macpherson Date: Tue, 5 May 2026 14:21:42 +0100 Subject: [PATCH 12/12] use new with_hide_funcs method in test to improve coverage --- tket-qsystem/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tket-qsystem/src/lib.rs b/tket-qsystem/src/lib.rs index e5022af37..faf83971b 100644 --- a/tket-qsystem/src/lib.rs +++ b/tket-qsystem/src/lib.rs @@ -454,9 +454,9 @@ mod test { // Run again without hiding... let mut hugr_public = orig; QSystemPass { - hide_funcs: false, ..Default::default() } + .with_hide_funcs(false) .run(&mut hugr_public) .unwrap();