Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
109 commits
Select commit Hold shift + click to select a range
498677b
[WIP] Add conversion pattern for qecl.qec ops
joeycarter Apr 24, 2026
e7ef5c6
[WIP] Continue adding QEC cycle conversion pattern
joeycarter Apr 27, 2026
e605612
Merge branch 'main' into joeycarter/qecl-to-qecp-qec-cycle
joeycarter Apr 27, 2026
f937de9
Insert Z-correction ops
joeycarter Apr 27, 2026
76109b9
[WIP] Apply both X and Z corrections
joeycarter Apr 27, 2026
60b7037
Update conversion pattern; it's now complete!
joeycarter Apr 27, 2026
c3760a0
Merge branch 'main' into joeycarter/qecl-to-qecp-qec-cycle
joeycarter Apr 27, 2026
0370bfb
Fix mistake in tensor shape result of qecp.decode_esm_css op
joeycarter Apr 27, 2026
887afd9
Add changelog entry
joeycarter Apr 27, 2026
3334ce8
Merge branch 'main' into joeycarter/qecl-to-qecp-qec-cycle
joeycarter Apr 28, 2026
2ed1413
Clean up
joeycarter Apr 28, 2026
0932f87
Merge branch 'main' into joeycarter/qecl-to-qecp-qec-cycle
joeycarter Apr 28, 2026
f7f0101
Fix np.ndarray type hints that caused pylint error
joeycarter Apr 28, 2026
3f6755d
Merge branch 'main' into joeycarter/qecl-to-qecp-qec-cycle
joeycarter Apr 28, 2026
b5fefb9
Fix the parity_check_matrix_to_tanner_csc function
joeycarter Apr 30, 2026
2737848
Update qecl.qec pattern to use correct Tanner graph
joeycarter Apr 30, 2026
3ed0dcb
Merge branch 'main' into joeycarter/qecl-to-qecp-qec-cycle
joeycarter Apr 30, 2026
2ed3442
Placate CodeFactor
joeycarter Apr 30, 2026
e38d82e
extend EncodedMemref ABI to include dimension sizes
mehrdad2m May 5, 2026
f8024d7
Add runtime_call lowering
mehrdad2m May 5, 2026
6c2d42c
add runtime_call frontend
mehrdad2m May 7, 2026
a118ec2
small fix
mehrdad2m May 8, 2026
f5adc08
add test
mehrdad2m May 8, 2026
004a82e
Merge branch 'main' into add-runtime-call
mehrdad2m May 8, 2026
03ea047
Apply suggestion from @mehrdad2m
mehrdad2m May 8, 2026
4520819
format
mehrdad2m May 8, 2026
e9f80f3
codefactor
mehrdad2m May 8, 2026
eeb38de
fix test
mehrdad2m May 11, 2026
847b9ca
add compile_mlir
mehrdad2m May 13, 2026
68aefa5
fix condition
mehrdad2m May 13, 2026
caa6e17
add changelog
mehrdad2m May 13, 2026
837c400
Merge branch 'main' into add-compile-mlir
mehrdad2m May 13, 2026
cfb7a98
Apply suggestion from @joeycarter
mehrdad2m May 13, 2026
52e8145
Apply suggestion from @mehrdad2m
mehrdad2m May 13, 2026
6fb3e23
support skipping inlining nested modules
mehrdad2m May 13, 2026
cb4b16c
fix test
mehrdad2m May 13, 2026
433ccd4
Merge branch 'main' into skip-inlining-targets
mehrdad2m May 13, 2026
397a47a
Merge branch 'main' into add-runtime-call
mehrdad2m May 13, 2026
c73cf13
keep the attributes on the decleration
mehrdad2m May 14, 2026
f5728d4
Merge branch 'add-compile-mlir' into add-runtime-call
mehrdad2m May 15, 2026
0685a82
refactor
mehrdad2m May 15, 2026
206252e
Catch exceptions thrown from setup/teardown
rniczh May 28, 2026
906c83c
update
rniczh May 28, 2026
8ff86af
fix codefactor
rniczh May 28, 2026
433319f
formatted
rniczh May 28, 2026
8bb8e8a
Add mark-entry-points pass
mehrdad2m Jun 1, 2026
48f27fe
add entry point to the main function in frontend
mehrdad2m Jun 1, 2026
61cae90
add changelog
mehrdad2m Jun 1, 2026
0508daf
fix test
mehrdad2m Jun 1, 2026
4c3193f
Merge branch 'main' into add-runtime-call
mehrdad2m Jun 1, 2026
423ea96
add catalyst.target frontend
mehrdad2m Jun 2, 2026
6b3c02d
Add cross-compile-targets pass
mehrdad2m Jun 2, 2026
7865776
Add remote dispatch pass
mehrdad2m Jun 2, 2026
b791bc5
Merge branch 'lowering-target-kernels' into integration-test-target
mehrdad2m Jun 2, 2026
41b2a5c
Merge branch 'add-dispatch' into integration-test-target
mehrdad2m Jun 2, 2026
0d3016a
Merge branch 'mark-entry-points' into integration-test-target
mehrdad2m Jun 2, 2026
9e9a4f8
Merge branch 'main' into skip-inlining-targets
mehrdad2m Jun 2, 2026
0d0a8f3
Merge remote-tracking branch 'origin/skip-inlining-targets' into inte…
mehrdad2m Jun 2, 2026
1404bbb
add bufferization.access
mehrdad2m Jun 2, 2026
6823286
Merge remote-tracking branch 'origin/skip-inlining-targets' into inte…
mehrdad2m Jun 2, 2026
1334e9b
Add remote
mehrdad2m Jun 2, 2026
d89e7fd
Merge branch 'add-target-frontend' into integration-test-target
mehrdad2m Jun 2, 2026
75492bb
fix typo
mehrdad2m Jun 2, 2026
3ec18f4
add to the driver
mehrdad2m Jun 2, 2026
d71cacb
Add integration test
mehrdad2m Jun 2, 2026
e011bea
only add decleration for entry points
mehrdad2m Jun 2, 2026
88927d7
Merge branch 'skip-inlining-targets' into integration-test-target
mehrdad2m Jun 2, 2026
4c2d06a
add kernel.define
mehrdad2m Jun 2, 2026
d25ad03
Merge branch 'add-kernel-frontend' into remote-exec-2
mehrdad2m Jun 2, 2026
716fbc4
add entry_points to test
mehrdad2m Jun 3, 2026
87f96a7
Merge branch 'skip-inlining-targets' into integration-test-target
mehrdad2m Jun 3, 2026
b28c757
Merge branch 'integration-test-target' into remote-exec-2
mehrdad2m Jun 3, 2026
7264f0a
Add remote dialect
rniczh Jun 3, 2026
3a31dcf
support custom lowering pipeline for the targets
mehrdad2m Jun 3, 2026
993a198
Merge branch 'lowering-target-kernels' into integration-test-target
mehrdad2m Jun 3, 2026
b73053a
Merge branch 'integration-test-target' into remote-exec-2
mehrdad2m Jun 3, 2026
2f38eb4
Add remote to llvm transform
rniczh Jun 3, 2026
b90471e
formattd
rniczh Jun 3, 2026
beacc03
skip t gates for cliffotd-only circuits
mehrdad2m Jun 3, 2026
8453f23
Merge branch 'skip-t-gate-for-clifford' into remote-exec-2
mehrdad2m Jun 3, 2026
ddd6ad3
Add remote runtime
rniczh Jun 4, 2026
b4db93e
Add cross compile pass for qnode
rniczh Jun 4, 2026
1178e1e
codecov
rniczh Jun 4, 2026
c2bd778
Merge branch 'rniczh/add-remote-runtime' into rniczh/add-cross-compil…
rniczh Jun 4, 2026
98cb7d1
fix
rniczh Jun 4, 2026
1ccef22
fix remote call
rniczh Jun 5, 2026
4275d4b
Merge branch 'rniczh/add-remote-runtime' into rniczh/add-cross-compil…
rniczh Jun 5, 2026
646da5e
Merge remote-tracking branch 'origin/rniczh/add-cross-compile-pass' i…
mehrdad2m Jun 5, 2026
3c67f36
Merge remote-tracking branch 'origin/rniczh/catch-exception-from-setu…
mehrdad2m Jun 5, 2026
520dce9
Adapt dispatch pass
mehrdad2m Jun 5, 2026
438a316
fix test
mehrdad2m Jun 5, 2026
81e06df
remote needs llvmdir
mehrdad2m Jun 5, 2026
4ec1cc2
smal dep fix
mehrdad2m Jun 5, 2026
68545f5
move target lowering to after bufferization
mehrdad2m Jun 5, 2026
67feece
add backend_config to customcall op
mehrdad2m Jun 9, 2026
fdedc60
Merge branch 'main' into remote-exec-2
mehrdad2m Jun 9, 2026
c32046d
Merge branch 'update-customop' into remote-exec-2
mehrdad2m Jun 9, 2026
e200dac
adapt dispatch to work with remote dialect
mehrdad2m Jun 9, 2026
5ab1635
add remote
mehrdad2m Jun 9, 2026
d9dd0b6
Merge branch 'add-runtime-call' into remote-exec-2
mehrdad2m Jun 9, 2026
c1cbf05
fix shutdown
mehrdad2m Jun 12, 2026
6570c00
fix makefile to parse multi target
mehrdad2m Jun 16, 2026
024d366
Apply rtcapi dlopen issue fic by hongsheng
mehrdad2m Jun 17, 2026
5314ca1
add launch_kernel bufferization
mehrdad2m Jun 26, 2026
f0ea8c3
Cross-compile local targets; derive entry points from launch_kernel; …
mehrdad2m Jun 26, 2026
864adb8
Consolidate target() deploy API and drop entry_point frontend stamp
mehrdad2m Jun 26, 2026
0eff2b9
Dispatch launch_kernel to remote.launch; per-object executor isolatio…
mehrdad2m Jun 26, 2026
6362e9e
format
josephleekl Jul 6, 2026
472c937
Merge branch 'main' into remote-exec-2
mehrdad2m Jul 17, 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
8 changes: 8 additions & 0 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@
argument is at an arbitrary position in the argument list.
[(#2836)](https://github.com/PennyLaneAI/catalyst/pull/2836)

* A new `catalyst.debug.compile_mlir` function has been added, allowing standalone MLIR files to be
compiled through the full Catalyst pipeline and returned as a callable Python object.
[(#2832)](https://github.com/PennyLaneAI/catalyst/pull/2832)

* PPRs and PPMs can now be lowered properly into MLIR directly in the non-capture workflow.
[(#2816)](https://github.com/PennyLaneAI/catalyst/pull/2816)

Expand Down Expand Up @@ -242,6 +246,10 @@

<h3>Internal changes ⚙️</h3>

* A new compiler pass `mark-entry-points` has been added, which annotates externally-callable
entry functions with the `catalyst.entry_point` attribute.
[(#2899)](https://github.com/PennyLaneAI/catalyst/pull/2899)

* The `dim` argument of the `quantum.pcphase` operation has been changed to a static integer attribute
(previously a dynamic float operand). This allows, among other things, the decomposition graph to
distinguish pcphase gates with different `dim` values, since they need different decomposition rules.
Expand Down
3 changes: 2 additions & 1 deletion frontend/catalyst/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
sys.modules["mlir_quantum.ir"] = __import__("jaxlib.mlir.ir").mlir.ir
sys.modules["mlir_quantum._mlir_libs"] = __import__("jaxlib.mlir._mlir_libs").mlir._mlir_libs

from catalyst import debug, logging, passes
from catalyst import debug, kernel, logging, passes
from catalyst.api_extensions import *
from catalyst.api_extensions import __all__ as _api_extension_list
from catalyst.autograph import *
Expand Down Expand Up @@ -196,6 +196,7 @@
"CompileOptions",
"debug",
"draw_graph",
"kernel",
"passes",
"pipeline",
"compile_without_static_conditionals",
Expand Down
2 changes: 2 additions & 0 deletions frontend/catalyst/api_extensions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
measure,
pauli_measure,
)
from catalyst.api_extensions.target import target

__all__ = (
"accelerate",
Expand All @@ -60,4 +61,5 @@
"pauli_measure",
"adjoint",
"ctrl",
"target",
)
85 changes: 85 additions & 0 deletions frontend/catalyst/api_extensions/target.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Copyright 2026 Xanadu Quantum Technologies Inc.

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Tag a PennyLane device as a separate cross-compilation target, optionally remote."""

from dataclasses import dataclass
from typing import Optional

_TARGET_ATTR = "_catalyst_target"
_DISPATCH_ATTR = "_catalyst_dispatch"


@dataclass(frozen=True)
class Target:
"""Cross-compilation target spec attached to a device by :func:`target`.

Args:
pipeline: Optional name of a lowering pipeline registered with the compiler.
triple: Optional LLVM target triple. Defaults to the host triple.
"""

pipeline: Optional[str] = None
triple: Optional[str] = None


@dataclass(frozen=True)
class RemoteDispatch:
"""Remote dispatch spec attached to a device by :func:`target` with an ``address``.

Args:
address: Executor address, e.g. ``"127.0.0.1:1373"``.
"""

address: str


def target(
device,
*,
pipeline: Optional[str] = None,
triple: Optional[str] = None,
address: Optional[str] = None,
):
"""Tag a PennyLane device as a separate cross-compilation target and return it.

Any QNode wrapping the returned device is kept as a separate compilation unit which carries
``catalyst.target = {pipeline, triple}`` and is cross-compiled to a standalone object rather
than being inlined into the host module. With ``address`` set, the object is additionally
dispatched to a remote executor (``catalyst.dispatch = {address}``); without it, the object is
statically linked and runs in-process.

Args:
device: A PennyLane device.
pipeline: Optional lowering-pipeline name registered with the compiler.
triple: Optional LLVM target triple. Defaults to the host triple.
address: Optional executor address; when set, the target is dispatched remotely.

Returns:
The same device, now tagged with target (and, if ``address`` is given, dispatch) metadata.
"""
setattr(device, _TARGET_ATTR, Target(pipeline=pipeline, triple=triple))
if address is not None:
setattr(device, _DISPATCH_ATTR, RemoteDispatch(address=address))
return device


def get_target(device) -> Optional[Target]:
"""Return the :class:`Target` attached via :func:`target`, or ``None``."""
return getattr(device, _TARGET_ATTR, None)


def get_dispatch(device) -> Optional[RemoteDispatch]:
"""Return the :class:`RemoteDispatch` attached via :func:`target` (``address=...``), or ``None``."""

Check notice on line 84 in frontend/catalyst/api_extensions/target.py

View check run for this annotation

codefactor.io / CodeFactor

frontend/catalyst/api_extensions/target.py#L84

Line too long (104/100) (line-too-long)
return getattr(device, _DISPATCH_ATTR, None)
8 changes: 2 additions & 6 deletions frontend/catalyst/compiled_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,11 @@ def load_symbols(self):
return function, setup, teardown, mem_transfer

def __enter__(self):
params_to_setup = [b"jitted-function"]
argc = len(params_to_setup)
array_of_char_ptrs = (ctypes.c_char_p * len(params_to_setup))()
array_of_char_ptrs[:] = params_to_setup
self.setup(ctypes.c_int(argc), array_of_char_ptrs)
wrapper.invoke_setup(self.setup, ["jitted-function"])
return self

def __exit__(self, _type, _value, _traceback):
self.teardown()
wrapper.invoke_teardown(self.teardown)


class CompiledFunction:
Expand Down
90 changes: 51 additions & 39 deletions frontend/catalyst/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,23 @@
"-lrt_decoder",
]

rt_remote_so = "librt_remote" + file_extension
if os.path.isfile(os.path.join(rt_lib_path, rt_remote_so)):
default_flags.append("-lrt_remote")

# If OQD runtime capi is built, link to it as well
# TODO: This is not ideal and should be replaced when the compiler is device aware
if os.path.isfile(os.path.join(rt_lib_path, "librt_OQD_capi" + file_extension)):
default_flags.append("-lrt_OQD_capi")

for artifact_path in options.runtime_artifacts:
dir_name = os.path.dirname(artifact_path)
default_flags += [
f"-Wl,-rpath,{dir_name}",
f"-L{dir_name}",
artifact_path,
]

return default_flags

@staticmethod
Expand Down Expand Up @@ -210,9 +222,9 @@
yield compiler

@staticmethod
def _attempt_link(compiler, flags, infile, outfile, options):
def _attempt_link(compiler, flags, infile, outfile, options, extra_objects=None):

Check notice on line 225 in frontend/catalyst/compiler.py

View check run for this annotation

codefactor.io / CodeFactor

frontend/catalyst/compiler.py#L225

Too many arguments (6/5) (too-many-arguments)

Check notice on line 225 in frontend/catalyst/compiler.py

View check run for this annotation

codefactor.io / CodeFactor

frontend/catalyst/compiler.py#L225

Too many positional arguments (6/5) (too-many-positional-arguments)
try:
command = [compiler] + flags + [infile, "-o", outfile]
command = [compiler] + flags + [infile] + list(extra_objects or []) + ["-o", outfile]
run_writing_command(command, options)
return True
except subprocess.CalledProcessError as e:
Expand All @@ -239,7 +251,9 @@

@staticmethod
@debug_logger
def run(infile, outfile=None, flags=None, fallback_compilers=None, options=None):
def run(

Check notice on line 254 in frontend/catalyst/compiler.py

View check run for this annotation

codefactor.io / CodeFactor

frontend/catalyst/compiler.py#L254

Too many positional arguments (6/5) (too-many-positional-arguments)

Check notice on line 254 in frontend/catalyst/compiler.py

View check run for this annotation

codefactor.io / CodeFactor

frontend/catalyst/compiler.py#L254

Too many arguments (6/5) (too-many-arguments)
infile, outfile=None, flags=None, fallback_compilers=None, options=None, extra_objects=None
):
"""
Link the infile against the necessary libraries and produce the outfile.

Expand All @@ -249,6 +263,8 @@
flags (Optional[List[str]]): flags to be passed down to the compiler
fallback_compilers (Optional[List[str]]): name of executables to be looked for in PATH
compile_options (Optional[CompileOptions]): generic compilation options.
extra_objects (Optional[List[str]]): additional object files to statically link
(e.g. locally cross-compiled catalyst.target kernels).
Raises:
EnvironmentError: The exception is raised when no compiler succeeded.
"""
Expand All @@ -261,7 +277,9 @@
if fallback_compilers is None:
fallback_compilers = LinkerDriver._default_fallback_compilers
for compiler in LinkerDriver._available_compilers(fallback_compilers):
success = LinkerDriver._attempt_link(compiler, flags, infile, outfile, options)
success = LinkerDriver._attempt_link(
compiler, flags, infile, outfile, options, extra_objects
)
if options.verbose:
print("Shared object linking successful", file=options.logfile)
if success:
Expand Down Expand Up @@ -498,7 +516,16 @@
out_IR = None

if self.options.link:
output = LinkerDriver.run(output_object_name, options=self.options)
# Locally cross-compiled catalyst.target kernels are emitted as separate objects; the
# driver lists them in `{module}.objects` for the linker to statically include.
extra_objects = []
objects_manifest = os.path.join(str(workspace), f"{module_name}.objects")
if os.path.exists(objects_manifest):
with open(objects_manifest, "r", encoding="utf-8") as f:
extra_objects = [line.strip() for line in f if line.strip()]
output = LinkerDriver.run(
output_object_name, options=self.options, extra_objects=extra_objects
)
output = str(pathlib.Path(output).absolute())
else:
output = None
Expand Down Expand Up @@ -706,41 +733,26 @@
Returns
(Optional[str]): output IR
"""
file_content = None
for dirpath, _, filenames in os.walk(str(workspace)):
filenames = [f for f in filenames if f.endswith(".mlir") or f.endswith(".ll")]
if not filenames:
break
filenames_no_ext = [os.path.splitext(f)[0] for f in filenames]
if pipeline == "mlir":
# Sort files and pick the first one
selected_file = [
sorted(filenames)[0],
]
elif pipeline == "last":
# Sort files and pick the last one
selected_file = [
sorted(filenames)[-1],
]
else:
selected_file = [
f
for f, name_no_ext in zip(filenames, filenames_no_ext)
if pipeline in name_no_ext
]
if len(selected_file) != 1:
msg = f"Attempting to get output for pipeline: {pipeline},"
msg += " but no or more than one file was found.\n"
raise CompileError(msg)
filename = selected_file[0]

full_path = os.path.join(dirpath, filename)
with open(full_path, "r", encoding="utf-8") as file:
file_content = file.read()

if file_content is None:
workspace = str(workspace)
filenames = [f for f in os.listdir(workspace) if f.endswith(".mlir") or f.endswith(".ll")]
if not filenames:
msg = f"Attempting to get output for pipeline: {pipeline},"
msg += " but no file was found.\n"
msg += "Are you sure the file exists?"
raise CompileError(msg)
return file_content

if pipeline == "mlir":
# Sort files and pick the first one
selected_file = [sorted(filenames)[0]]
elif pipeline == "last":
# Sort files and pick the last one
selected_file = [sorted(filenames)[-1]]
else:
selected_file = [f for f in filenames if pipeline in os.path.splitext(f)[0]]
if len(selected_file) != 1:
msg = f"Attempting to get output for pipeline: {pipeline},"
msg += " but no or more than one file was found.\n"
raise CompileError(msg)

with open(os.path.join(workspace, selected_file[0]), "r", encoding="utf-8") as file:
return file.read()
2 changes: 2 additions & 0 deletions frontend/catalyst/debug/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from catalyst.debug.callback import callback
from catalyst.debug.compiler_functions import (
compile_executable,
compile_mlir,
get_cmain,
get_compilation_stage,
get_compilation_stages_groups,
Expand All @@ -27,6 +28,7 @@

__all__ = (
"callback",
"compile_mlir",
"print",
"print_memref",
"get_compilation_stage",
Expand Down
Loading
Loading