Skip to content

Python bindings

The qoala-mlir build produces an MLIR Python bindings package named qnet. This is the package that the euqalyptus frontend imports to construct Qoala HIR programmatically.

What's in the package

After a successful build the package lives under build/python_packages/qnet_bindings/qnet/. The importable surface is split in three. qnet.ir re-exports MLIR's core Python API — Module, Context, Location, InsertionPoint, Operation, Block, and Region are the names you will typically reach for. qnet.dialects.qnet contains the Python builders for every operation in the QNet dialect, generated from lib/Python/mlir_qnet/dialects/QNetOps.td together with qnet.py. Finally, qnet._mlir_libs bundles the C/C++ runtime libraries; it is loaded automatically and not imported directly.

The release wheel attached to the GitHub releases page ships the same package contents.

How euqalyptus consumes it

euqalyptus's QoalaModule.generate_qoala_hir() walks its internal AST and, for each node, calls into qnet.dialects.qnet to produce the equivalent MLIR op. The output is a qnet.ir.Module whose .operation.get_asm() is the textual HIR fed into qoala-opt. If you are building tools on top of qoala-mlir directly, without euqalyptus, the same idiom applies:

from qnet.dialects import qnet
from qnet.ir import Context, Location, Module, InsertionPoint

with Context() as ctx, Location.unknown() as loc:
    module = Module.create()
    with InsertionPoint(module.body):
        # Module-scope ops here, e.g. qnet.remote.
        ...
    print(str(module))

Build artifacts and how it's wired

The CMake configuration that produces the package lives in lib/Python/CMakeLists.txt. mlir_configure_python_dev_packages() resolves the Python dev environment from MLIR, and add_compile_definitions("MLIR_PYTHON_PACKAGE_PREFIX=qnet.") sets the target package prefix to qnet.. declare_mlir_python_sources(QNetPythonSources) declares the Python source roots; declare_mlir_dialect_python_bindings(... TD_FILE dialects/QNetOps.td DIALECT_NAME qnet) invokes mlir-tblgen -gen-python-op-bindings against lib/Python/mlir_qnet/dialects/QNetOps.td, which itself just includes the canonical Dialect/QNet/QNet.td. declare_mlir_python_extension(QNetPythonSources.Extension MODULE_NAME _qnetTypes ... SOURCES QNetExtension.cpp ...) builds the C++ extension that registers the !qnet.qubit type with the Python bindings — the dialect-binding generator only emits ops, so types have to be registered manually. add_mlir_python_common_capi_library(QNetPythonCAPI ...) produces the bundled native library that every binding-using Python process loads, and add_mlir_python_modules(QNetPythonModules ...) wires the whole thing into the final installable Python package.

Using the bindings without installing the wheel

If you don't want to build a wheel, point PYTHONPATH at the build tree. You can either edit your venv's activate script:

export PYTHONPATH=$PYTHONPATH:/path/to/this/repo/build/python_packages/qnet_bindings

or set it on the command line:

PYTHONPATH=$PYTHONPATH:/path/to/this/repo/build/python_packages/qnet_bindings python script.py

Either way you also need MLIR's runtime Python requirements installed in the same venv (pip install -r llvm/mlir/python/requirements.txt).

Other dialects

Only the qnet dialect has a Python builder layer in this repo, because that is the only one euqalyptus emits. The MIR and LIR dialects (qmem, qoalahost, netqasm, qremote) are not exposed through the Python bindings — they are produced by qoala-opt from HIR input.

API reference

The only hand-written Python class in the package is the FuncOp helper that ports MLIR's upstream func.FuncOp into the qnet dialect. The op builders for every other qnet operation are auto-generated by TableGen from lib/Python/mlir_qnet/dialects/QNetOps.td and follow the standard MLIR Python bindings calling conventions; see the QNet operations reference for their signatures.

FuncOp

FuncOp(name, type, *, visibility=None, body_builder=None, loc=None, ip=None)

Bases: FuncOp

Convenience wrapper around the qnet.func operation.

Ported from the upstream FuncOp class in MLIR's func dialect. The wrapper preserves the same API (so you can use it as a drop-in replacement when constructing HIR programmatically) and re-registers it against the qnet dialect so that qnet.func-typed ops parse and print correctly through the Python bindings.

Build a qnet.func operation.

Parameters:

Name Type Description Default
name

The function name, used as the symbol attribute. Coerced to str.

required
type

The function type. Either a :class:FunctionType or a tuple (inputs, results) of lists describing the input and result types — in the latter case a :class:FunctionType is built on the fly.

required
visibility

One of "public", "private", or "nested". Defaults to None, which implies private visibility.

None
body_builder

Optional callback. When provided, a new entry block is created on the function and the callback is invoked with the new op as argument inside an :class:InsertionPoint context already set for the block. The callback is expected to insert a terminator in the block.

None
loc

Optional :class:Location for the op.

None
ip

Optional :class:InsertionPoint for the op.

None

is_external property

is_external

True if the function has no body (i.e. it is a declaration only).

body property

body

The :class:Region holding the function's body blocks.

type property

type

The function's :class:FunctionType (inputs and results).

visibility property

visibility

The function's visibility attribute (StringAttr).

name property

name: StringAttr

The function name as a :class:StringAttr.

entry_block property

entry_block

The function's entry block.

Returns:

Type Description

The first :class:Block of the function's body region.

Raises:

Type Description
IndexError

If the function has no body (i.e. it is an external declaration). Use :attr:is_external to check before accessing.

arg_attrs property writable

arg_attrs

The function's argument attributes as an :class:ArrayAttr.

arguments property

arguments

The list of block arguments of the function's entry block.

result_attrs property writable

result_attrs

The function's result attributes (as the raw attribute payload).

add_entry_block

add_entry_block(arg_locs: Optional[Sequence[Location]] = None)

Add an entry block to the function body.

Uses the function signature to infer the block arguments (their types come from the function's input types).

Parameters:

Name Type Description Default
arg_locs Optional[Sequence[Location]]

Optional sequence of :class:Location values, one per block argument, used as the locations of the inferred block arguments.

None

Returns:

Type Description

The newly created :class:Block.

Raises:

Type Description
IndexError

If the function already has an entry block.