Skip to content
Open
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
05e3fb5
WIP add inlining annotation + pass
acl-cqc Mar 16, 2026
51a4e89
tests
acl-cqc Mar 16, 2026
9e9e722
Simplify cycle check, only need first cycle
acl-cqc Mar 17, 2026
30d924f
inline always funcs leaves first
acl-cqc Mar 17, 2026
ef247fe
Respect entrypoint? Oooph!
acl-cqc Mar 17, 2026
2a70f5b
Strange derive_more, make compile
acl-cqc Mar 23, 2026
865b6dd
Don't RemDeadFuncs (be selective); skip cycles for only-called-once
acl-cqc Mar 23, 2026
4ad40ea
Fix test: inlined func is removed
acl-cqc Mar 23, 2026
896e36a
TODO test entrypoint_scope
acl-cqc Mar 23, 2026
07871d5
cycles return iter, clippy
acl-cqc Mar 23, 2026
849bf73
more TODOs
acl-cqc Mar 24, 2026
eecf9b7
rm doclink
acl-cqc Mar 25, 2026
d1d1615
Merge remote-tracking branch 'origin/main' into acl/inline
acl-cqc Apr 28, 2026
1dd388a
Move annotation
acl-cqc Apr 28, 2026
c25a2cd
Remove only-called-once, simplify test
acl-cqc Apr 28, 2026
2bd976d
Simplify, clarify/comment
acl-cqc Apr 28, 2026
6fd9f0e
simplify: inline do_inline
acl-cqc Apr 28, 2026
5bbabec
renaming, re-export
acl-cqc Apr 28, 2026
0e3ad97
entrypoint_scope test
acl-cqc Apr 28, 2026
9fd49cf
test cycle of one always and one not
acl-cqc Apr 28, 2026
66e02c1
Add python (needs test)
acl-cqc Apr 28, 2026
677e48e
clippy
acl-cqc Apr 28, 2026
9284958
doc
acl-cqc Apr 28, 2026
312f07b
fmt + missing,
acl-cqc Apr 28, 2026
dbcf612
first python test
acl-cqc Apr 28, 2026
4554462
test failure, fiddling with imports, including type: ignore
acl-cqc Apr 28, 2026
8151e43
ruff format
acl-cqc Apr 28, 2026
a521495
improve tket-py rs comment
acl-cqc Apr 28, 2026
ce8d5b7
Declare InlineAlwaysError in passes.pyi
acl-cqc Apr 29, 2026
cb98230
Merge remote-tracking branch 'origin/main' into acl/inline
acl-cqc May 21, 2026
f5eeaca
fix hashiter lint
acl-cqc May 21, 2026
c955955
update py metadata and improve test
acl-cqc May 21, 2026
ea67492
Merge remote-tracking branch 'origin/main' into acl/inline
acl-cqc Jun 23, 2026
516749f
Move InlineAlways into InlineFuncs (first), transferring tests
acl-cqc Jun 23, 2026
8c7d299
do less if not removing functions, test avoid RemoveDeadFuncs
acl-cqc Jun 23, 2026
cec5bf8
revert/update python bindings (AI-powered)
acl-cqc Jun 23, 2026
96e4173
InlineAnnotation: update doc + from_json
acl-cqc Jun 23, 2026
4ac8c4d
Revert change to doc comment, acyclic still correct
acl-cqc Jun 23, 2026
c7d1290
Drop commented-out code
acl-cqc Jun 23, 2026
db049d2
more thorough in-scope
acl-cqc Jun 23, 2026
d4d298a
tket-py use typed Metadata API (thanks Agustin)
acl-cqc Jun 23, 2026
566dc2b
format rust imports
acl-cqc Jun 23, 2026
421ca89
move new enum member to preserve position
acl-cqc Jun 24, 2026
489d479
Properly handle LoadFunction..need tests
acl-cqc Jun 29, 2026
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
47 changes: 47 additions & 0 deletions tket-py/test/test_pass.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
from typing import Callable, Any
import subprocess
from tket._ops import TketOp
from tket.metadata import InlineAnnotation
from tket.passes import (
_badger_optimise,
_greedy_depth_reduce,
InlineFunctionsError,
InlineFunctions,
inline_funcs,
NormalizeGuppy,
Expand All @@ -23,6 +25,7 @@

from tket.passes import PytketHugrPass, QSystemPass
from hugr.build.base import Hugr
import hugr.tys as tys

import numpy as np
import pytest
Expand Down Expand Up @@ -334,6 +337,50 @@ def test_modifier_execution() -> None:
np.testing.assert_allclose(computed_statevector, expected_statevector)


@pytest.mark.parametrize("annotate", [True, False])
def test_inline_always(annotate: bool) -> None:
import hugr.ops as ops
from hugr.build.dfg import Dfg

d = Dfg(tys.Tuple(tys.Qubit, tys.Qubit))

f_id = d.module_root_builder().define_function(
"id",
[tys.Qubit],
)
f_id.set_outputs(f_id.input_node[0])

if annotate:
f_id.metadata[InlineAnnotation] = "always"

(tup,) = d.inputs()
(q1, q2) = d.add(ops.UnpackTuple()(tup))
call1 = d.call(f_id, q1)
call2 = d.call(f_id, q2)
(tup,) = d.add(ops.MakeTuple()(call1, call2))

d.set_outputs(tup)

InlineFunctions(heuristic=inline_funcs.MaxSize(0))(d.hugr)
CompilationState.from_python(d.hugr).validate()
assert _count_ops(d.hugr, "Call") == 0 if annotate else 2


def test_inline_always_cycle() -> None:
from hugr.build.function import Module

mod = Module()

f_recursive = mod.define_function("recurse", [tys.Qubit])
f_recursive.declare_outputs([tys.Qubit])
call = f_recursive.call(f_recursive, f_recursive.input_node[0])
f_recursive.set_outputs(call)

f_recursive.metadata[InlineAnnotation] = "always"
with pytest.raises(InlineFunctionsError):
InlineFunctions(heuristic=inline_funcs.MaxSize(0))(mod.hugr)


def test_inline_functions() -> None:
hugr = _hugr_from_path("test_files/guppy_examples/fn_calls.hugr")

Expand Down
3 changes: 3 additions & 0 deletions tket-py/tket/_tket/passes.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ class CircuitChunks:
class PullForwardError(Exception):
"""Error from a `PullForward` operation."""

class InlineFunctionsError(Exception):
"""Errors from the function inlining pass."""

def normalize_guppy(
circ: CompilationState,
*,
Expand Down
9 changes: 6 additions & 3 deletions tket-py/tket/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,19 @@ class MaxQubitsHint(Metadata[int]):
KEY = _metadata.MAX_QUBITS_HINT


InlineAnnotationValue: TypeAlias = Literal["never"] | Literal["best_effort"]
InlineAnnotationValue: TypeAlias = Literal["never", "best_effort", "always"]


class InlineAnnotation(Metadata[InlineAnnotationValue]):
Comment thread
aborgna-q marked this conversation as resolved.
"""Metadata hinting the compiler that a function declaration should be inlined at its call sites.

For functions annotated with "always", an error will be raised if the function is on a cycle.

When a function is not annotated, we use a heuristic to determine whether to inline.

Values:
- "never": Never inline this function.
- "always": Always inline this function; raise an error if this is not possible.
- "best_effort":
Inline the function if possible.
This is not guaranteed, the compiler may choose not to inline functions with this annotation.
Expand All @@ -98,10 +101,10 @@ def to_json(cls, value: InlineAnnotationValue) -> JsonType:
@classmethod
def from_json(cls, value: JsonType) -> InlineAnnotationValue:
match value:
case "never" | "best_effort":
case "never" | "best_effort" | "always":
return value
case _:
msg = f"Expected {cls.KEY} metadata to be 'never' or 'best_effort', but got {value!r}"
msg = f"Expected {cls.KEY} metadata to be 'never', 'best_effort', or 'always', but got {value!r}"
raise TypeError(msg)


Expand Down
5 changes: 4 additions & 1 deletion tket-py/tket/passes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from .._pattern import Rule, RuleMatcher
from .._state.build import OneQbGate, from_coms
from .._tket import passes as _passes, optimiser as _optimiser

from .._tket.passes import InlineFunctionsError
from hugr.passes.composable import (
ComposablePass,
ComposedPass,
Expand All @@ -29,6 +29,7 @@
__all__ = [
"PytketHugrPass",
"PassResult",
"InlineFunctionsError",
"InlineFuncsHeuristic",
"InlineFunctions",
"NormalizeGuppy",
Expand Down Expand Up @@ -235,6 +236,8 @@ class InlineFunctions(ComposablePass):
Parameters:
- heuristic: Heuristic used to choose which non-recursive functions to
inline. Defaults to `MaxSize(64)`.

Calls to functions annotated with `inline="always"` are processed first.
"""

heuristic: inline_funcs.InlineFuncsHeuristic = inline_funcs.MaxSize(64)
Expand Down
4 changes: 4 additions & 0 deletions tket/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ impl Metadata for MaxQubitsHint {
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum InlineAnnotation {
/// Always inline calls to this function.
///
/// If this cannot be done, an error will be raised.
Comment thread
acl-cqc marked this conversation as resolved.
Outdated
Always,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The semver checks are failing because this was added before all existing variants, and changed their inner tag id.
It also breaks the Ord derive.

Can you move the new variant to the end?
Alternatively, do

    Always = 2,
    BestEffort = 0,

/// Inline the function if we know it won't produce an invalid Hugr.
///
/// This is a best effort option; the compiler may choose not to inline
Expand Down
Loading
Loading