Python features in kernel scope#

Quadrants kernels are compiled to GPU machine code, so only a subset of Python is supported inside @qd.kernel and @qd.func functions. This page documents what works, what doesn’t, and common workarounds.

Supported#

The following Python features work inside kernels:

  • Arithmetic operators: +, -, *, /, //, %, **

  • Comparison operators: <, <=, >, >=, ==, !=

  • Logical operators: and, or, not

  • Bitwise operators: &, |, ^, ~, <<, >>

  • Augmented assignment: +=, -=, *=, etc. (note: these are atomic)

  • if / elif / else

  • for loops over range(), qd.ndrange(), qd.grouped(), or qd.static()

  • while loops

  • break and continue in non-parallelized loops (inner loops, while loops)

  • assert (when debug=True is passed to qd.init())

  • print (for scalar values) (not on Metal)

  • Local variables (scalars, vectors, matrices)

  • Lists and tuples (as compile-time containers, e.g. [1, 2, 3])

  • F-strings (limited: scalar values only)

  • Type casts: int(), float()

Not supported#

The following Python features will raise errors or produce unexpected behavior inside kernels:

Statements#

Feature

Error

try / except / finally

Compilation error

raise

Unsupported node "Raise"

with

Unsupported node "With"

yield / yield from

Unsupported node "Yield"

import

Unsupported node "Import"

del

Unsupported node "Delete"

global / nonlocal

Unsupported node "Global"

Function definitions inside kernels

Function definition is not allowed in 'qd.kernel'

Class definitions

Unsupported node "ClassDef"

for ... else

'else' clause for 'for' not supported

while ... else

'else' clause for 'while' not supported

Expressions and operators#

Feature

Error

is / is not

Operator "is" in Quadrants scope is not supported

in / not in

Compilation error

Sets ({1, 2, 3})

Compilation error

lambda

Unsupported node "Lambda"

Types and data structures#

Feature

Notes

Strings

No string manipulation; only literal strings in print() and f-strings

Dictionaries

Only as compile-time constants via qd.static()

Classes / objects

Cannot define or instantiate arbitrary Python classes

Generators

Not supported

*args unpacking

Only in a narrow case

Control flow restrictions#

  • return inside non-static if/for: not allowed in @qd.func (allowed in @qd.real_func)

  • break in the outermost (parallelized) for loop: not allowed — the outermost loop is mapped to GPU threads

  • break/continue in a static for inside a non-static if: not allowed

  • Reassigning kernel arguments: kernel parameters are immutable; create a local copy instead

Common workarounds#

Use qd.static() for compile-time Python logic#

If you need Python-level logic (dictionaries, complex conditionals, etc.), wrap it in qd.static() so it’s evaluated at compile time:

options = {"mode_a": 1, "mode_b": 2}
selected = "mode_a"

@qd.kernel
def compute(a: qd.Template) -> None:
    val = qd.static(options[selected])
    a[0] = val

Local copy for immutable arguments#

@qd.kernel
def compute(n: int, a: qd.Template) -> None:
    local_n = n  # can now modify local_n
    local_n += 1

Argument unpacking#

Syntax like some_function(*args) is supported in a narrow case:

  • *args must be the last argument in the function call

  • *args must not contain dataclasses.dataclass objects