Control Flow and Recursion¶
H-Series programs cannot contain loops. Loops and recursion are supported when
hugr-qir can fully expand them during compilation.
Supported: if / elif / else¶
Source file: guppy_examples/guppy-features/supported/guppy-if-elif-else.py
import sys
from typing import no_type_check
from guppylang import guppy, qubit
from guppylang.std.builtins import output
from guppylang.std.quantum import h, measure, x, y, z
@guppy
@no_type_check
def main() -> None:
q0, q1, q2, q3 = qubit(), qubit(), qubit(), qubit()
h(q0)
h(q1)
h(q2)
h(q3)
h(q3)
h(q2)
h(q1)
h(q0)
my_int = 0
a = measure(q0).read()
b = measure(q1).read()
c = measure(q2).read()
if a:
my_int += 1
if b:
my_int += 1
if c:
my_int += 1
if my_int == 0:
x(q3)
elif my_int == 1:
h(q3)
elif my_int == 2:
y(q3)
else:
z(q3)
d = measure(q3).read()
output("a", a)
output("b", b)
output("c", c)
output("d", d)
if __name__ == "__main__":
sys.stdout.buffer.write(main.compile().to_bytes())
Unsupported: exit / panic¶
Source file: guppy_examples/guppy-features/unsupported/early-exit.py
from guppylang import guppy, qubit
from guppylang.std.builtins import exit, output # noqa: A004
from guppylang.std.quantum import h, measure, x
@guppy
def main() -> None:
q = qubit()
fake_ancilla = qubit()
h(fake_ancilla)
if measure(fake_ancilla).read():
exit("Postselected: Criteria not met", 1)
x(q)
output("q", measure(q).read())
Source file: guppy_examples/guppy-features/unsupported/panic.py
from guppylang import guppy, qubit
from guppylang.std.platform import output, panic
from guppylang.std.quantum import h, measure, x
@guppy
def main() -> None:
q = qubit()
fake_ancilla = qubit()
h(fake_ancilla)
if measure(fake_ancilla).read():
panic("Criteria not met")
x(q)
output("q", measure(q).read())
Early exit using either exit or panic is unsupported on H-Series.
Expected error (for both examples):
Compilation failed: Program may panic: Postselected: Criteria not met. Runtime panics are unsupported on H-Series.
Supported: unrollable loops¶
Source file: guppy_examples/guppy-features/supported/unrollable-loops.py
import sys
from guppylang import guppy
from guppylang.std.platform import output
from guppylang.std.quantum import h, measure, qubit, x
@guppy
def main() -> None:
i = 10
q1 = qubit()
while True:
if i % 2 == 0:
h(q1)
else:
x(q1)
if i == 0:
break
i -= 1
for _ in range(90):
x(q1)
output("q", measure(q1).read())
if __name__ == "__main__":
sys.stdout.buffer.write(main.compile().to_bytes())
This loop has a fixed number of iterations, so hugr-qir can fully expand it.
The default maximum is 800 iterations and can be configured using
max_loop_unroll in Python or --max-loop-unroll on the command line.
For larger static loops, consider using @guppy.comptime so Guppy expands the
loop during compilation. This cannot be used when the loop itself depends on a
runtime value such as a measurement result.
Unsupported: non-unrollable loops¶
Source file: guppy_examples/guppy-features/unsupported/non-unrollable-loops.py
import sys
from guppylang import guppy
from guppylang.std.quantum import h, measure, qubit
@guppy
def main() -> None:
while True:
q1 = qubit()
h(q1)
if not measure(q1).read():
break
if __name__ == "__main__":
sys.stdout.buffer.write(main.compile().to_bytes())
The number of iterations depends on measurement results, so the loop cannot be fully expanded during compilation.
Expected error:
Compilation failed: Loop cannot be unrolled: its iteration count is not known at compile time or exceeds the limit of 800.
Supported: simple recursion¶
Source file: guppy_examples/guppy-features/supported/simple-recursion.py
import sys
from typing import no_type_check
from guppylang import guppy
from guppylang.std.builtins import output, qubit
from guppylang.std.quantum import measure, x
@guppy
@no_type_check
def recursive_func(q: qubit, n: int) -> None:
x(q)
if n < 10:
return recursive_func(q, n + 1)
return None
@guppy
@no_type_check
def main() -> None:
q = qubit()
x(q)
recursive_func(q, 0)
output("q", measure(q).read())
if __name__ == "__main__":
sys.stdout.buffer.write(main.compile().to_bytes())
This recursive form has a fixed depth and can be fully expanded.
Unsupported: complex recursion¶
Source file: guppy_examples/guppy-features/unsupported/complex-recursion.py
import sys
from typing import no_type_check
from guppylang import guppy
from guppylang.std.builtins import output, qubit
from guppylang.std.quantum import h, measure, x
@guppy
@no_type_check
def recursive_func(q: qubit) -> None:
x(q)
q_temp = qubit()
if measure(q_temp).read():
return recursive_func(q)
return None
@guppy
@no_type_check
def main() -> None:
q = qubit()
h(q)
recursive_func(q)
output("q", measure(q).read())
if __name__ == "__main__":
sys.stdout.buffer.write(main.compile().to_bytes())
Here the recursive path depends on a measurement result, so it cannot be fully expanded.
Expected error:
Compilation failed: Loop cannot be unrolled: its iteration count is not known at compile time or exceeds the limit of 800.
Supported: non-cyclic call graphs¶
Source file: guppy_examples/guppy-features/supported/inline-noncyclic-call-graph.py
from typing import no_type_check
from guppylang import guppy
from guppylang.std.builtins import output, qubit
from guppylang.std.quantum import measure, x
@guppy
@no_type_check
def a(q: qubit) -> None:
x(q)
@guppy
@no_type_check
def b(q: qubit) -> None:
x(q)
a(q)
@guppy
@no_type_check
def c(q: qubit) -> None:
x(q)
b(q)
@guppy
@no_type_check
def d(q: qubit) -> None:
x(q)
b(q)
@guppy
@no_type_check
def e(q: qubit) -> None:
x(q)
d(q)
c(q)
@guppy
@no_type_check
def f(q: qubit) -> None:
x(q)
d(q)
e(q)
@guppy
@no_type_check
def main() -> None:
q = qubit()
d(q)
f(q)
output("0", measure(q).read())
Unsupported: cyclic call graphs¶
Source file: guppy_examples/guppy-features/unsupported/cyclic-call-graph.py
from typing import no_type_check
from guppylang import guppy
from guppylang.std.builtins import output, qubit
from guppylang.std.quantum import measure, x
@guppy
@no_type_check
def a(q: qubit) -> None:
b(q)
@guppy
@no_type_check
def b(q: qubit) -> None:
x(q)
d(q)
@guppy
@no_type_check
def c(q: qubit) -> None:
x(q)
a(q)
@guppy
@no_type_check
def d(q: qubit) -> None:
x(q)
c(q)
@guppy
@no_type_check
def main() -> None:
q = qubit()
d(q)
output("0", measure(q).read())
This example creates recursion across multiple Guppy functions that cannot be fully expanded.
Expected error:
Compilation failed: Loop cannot be unrolled: its iteration count is not known at compile time or exceeds the limit of 800.