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 |
required | |
type
|
The function type. Either a :class: |
required | |
visibility
|
One of |
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: |
None
|
|
loc
|
Optional :class: |
None
|
|
ip
|
Optional :class: |
None
|
is_external
property
¶
is_external
True if the function has no body (i.e. it is a declaration only).
entry_block
property
¶
entry_block
The function's entry block.
Returns:
| Type | Description |
|---|---|
|
The first :class: |
Raises:
| Type | Description |
|---|---|
IndexError
|
If the function has no body (i.e. it is an
external declaration). Use :attr: |
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: |
None
|
Returns:
| Type | Description |
|---|---|
|
The newly created :class: |
Raises:
| Type | Description |
|---|---|
IndexError
|
If the function already has an entry block. |