diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 08485b3669..debad683e8 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -10,6 +10,10 @@ `catalyst.python_interface` namespace. [(#2199)](https://github.com/PennyLaneAI/catalyst/pull/2199) + * An xDSL `Universe` containing all custom xDSL dialects and passes has been registered as an entry point, + allowing usage of PennyLane's dialects and passes with xDSL's command-line tools. + [(#2208)](https://github.com/PennyLaneAI/catalyst/pull/2208) + * A new `catalyst.python_interface.inspection.mlir_specs` function has been added to facilitate PennyLane's new pass-by-pass specs feature. This function returns information gathered by parsing the xDSL generated by a given QJIT object, such as gate counts, measurements, or qubit allocations. diff --git a/frontend/catalyst/python_interface/xdsl_universe.py b/frontend/catalyst/python_interface/xdsl_universe.py new file mode 100644 index 0000000000..fec6306945 --- /dev/null +++ b/frontend/catalyst/python_interface/xdsl_universe.py @@ -0,0 +1,57 @@ +# Copyright 2025 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. +"""xDSL universe for containing all dialects and passes.""" + +xdsl_available = True + +try: + from xdsl.passes import ModulePass + from xdsl.universe import Universe +except (ImportError, ModuleNotFoundError): + xdsl_available = False # pragma: no cover + +# We must check that xDSL is installed because we're adding an entry point to +# PennyLane that references this file, and we must ensure that PennyLane can +# be installed in environments where xDSL is not installed. +XDSL_UNIVERSE = None + +if xdsl_available: + # pylint: disable=import-outside-toplevel + from . import dialects, transforms + + shared_dialects = ("stablehlo", "transform") + + # Create a map from dialect names to dialect classes. Dialects that are already + # provided by xDSL cannot be loaded into the multiverse, so we don't add them to + # our universe. + names_to_dialects = { + d.name: d + for name in dialects.__all__ + if (d := getattr(dialects, name)).name not in shared_dialects + } + + # Create a map from pass names to their respective ModulePass. The transforms module + # contains PassDispatcher instances as well as ModulePasses. We only want to collect + # the ModulePasses. We cannot use issubclass with instances, which is why we first + # check if isinstance(transform, type). + names_to_passes = { + t.name: t + for name in transforms.__all__ + if isinstance((t := getattr(transforms, name)), type) and issubclass(t, ModulePass) + } + + # The Universe is used to expose custom dialects and transforms to xDSL. It is + # specified as an entry point in PennyLane's pyproject.toml file, which makes + # it available to look up by xDSL for tools such as xdsl-opt, xdsl-gui, etc. + XDSL_UNIVERSE = Universe(all_dialects=names_to_dialects, all_passes=names_to_passes) diff --git a/frontend/test/pytest/python_interface/test_xdsl_universe.py b/frontend/test/pytest/python_interface/test_xdsl_universe.py new file mode 100644 index 0000000000..f748164b1e --- /dev/null +++ b/frontend/test/pytest/python_interface/test_xdsl_universe.py @@ -0,0 +1,60 @@ +# Copyright 2025 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. +"""Unit tests for the xDSL universe.""" + +import pytest + +pytestmark = pytest.mark.xdsl +xdsl = pytest.importorskip("xdsl") + +# pylint: disable=wrong-import-position +from xdsl.passes import ModulePass +from xdsl.universe import Universe as xUniverse + +from catalyst.python_interface import dialects, transforms +from catalyst.python_interface.xdsl_universe import XDSL_UNIVERSE, shared_dialects + +all_dialects = tuple(getattr(dialects, name) for name in dialects.__all__) +all_transforms = tuple( + transform + for name in transforms.__all__ + if isinstance((transform := getattr(transforms, name)), type) + and issubclass(transform, ModulePass) +) + + +def test_correct_universe(): + """Test that all the available dialects and transforms are available in the universe.""" + for d in all_dialects: + if d.name not in shared_dialects: + assert d.name in XDSL_UNIVERSE.all_dialects + assert XDSL_UNIVERSE.all_dialects[d.name] == d + + for t in all_transforms: + assert t.name in XDSL_UNIVERSE.all_passes + assert XDSL_UNIVERSE.all_passes[t.name] == t + + +def test_correct_multiverse(): + """Test that all the available dialects and transforms are available in the multiverse.""" + multiverse = xUniverse.get_multiverse() + + for d in all_dialects: + assert d.name in multiverse.all_dialects + if d.name not in shared_dialects: + assert multiverse.all_dialects[d.name] == d + + for t in all_transforms: + assert t.name in multiverse.all_passes + assert multiverse.all_passes[t.name] == t diff --git a/setup.py b/setup.py index ba25f84038..9199110a37 100644 --- a/setup.py +++ b/setup.py @@ -147,6 +147,9 @@ def parse_dep_versions(): "cuda_quantum.ops = catalyst.api_extensions", "cuda_quantum.qjit = catalyst.third_party.cuda:cudaqjit", ], + "xdsl.universe": [ + "catalyst-xdsl-universe = catalyst.python_interface.xdsl_universe:XDSL_UNIVERSE" + ], } classifiers = [