diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 4e30aadbb5b..6cde67ccd64 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -179,6 +179,10 @@

Internal changes ⚙️

+* An xDSL `Universe` containing all custom dialects and passes has been registered as an entry point, allowing + usage of PennyLane's dialects and passes with xDSL's command-line tools + [(#8372)](https://github.com/PennyLaneAI/pennylane/pull/8372) + * Fix all NumPy 1.X `DeprecationWarnings` in our source code. [(#8497)](https://github.com/PennyLaneAI/pennylane/pull/8497) diff --git a/pennylane/compiler/python_compiler/universe.py b/pennylane/compiler/python_compiler/universe.py new file mode 100644 index 00000000000..fec6306945a --- /dev/null +++ b/pennylane/compiler/python_compiler/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/pyproject.toml b/pyproject.toml index 79b5ef5958a..0c631af5118 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,6 +68,9 @@ pl-device-test = "pennylane.devices.tests:cli" "default.qutrit.mixed" = "pennylane.devices.default_qutrit_mixed:DefaultQutritMixed" "default.tensor" = "pennylane.devices.default_tensor:DefaultTensor" +[project.entry-points."xdsl.universe"] +"pennylane-xdsl-universe" = "pennylane.compiler.python_compiler.universe:XDSL_UNIVERSE" + [project.entry-points."console_scripts"] "pl-device-test" = "pennylane.devices.tests:cli" diff --git a/tests/python_compiler/test_universe.py b/tests/python_compiler/test_universe.py new file mode 100644 index 00000000000..b598eb01927 --- /dev/null +++ b/tests/python_compiler/test_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.external +pytest.importorskip("xdsl") + +# pylint: disable=wrong-import-position +from xdsl.passes import ModulePass +from xdsl.universe import Universe as xUniverse + +from pennylane.compiler.python_compiler import dialects, transforms +from pennylane.compiler.python_compiler.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