Skip to content

Commit b72b4a2

Browse files
authored
Revert RUNTIME_OPERATIONS in the frontend (#2252)
Revert PR #2215 changes that accidentally merged in PR #2131. Note that these changes don't break the current functionalities of Catalyst, but they introduce edge cases with lowering generic PL operators in MLIR that need to be properly addressed before the merge. (re: the future Operator class)
1 parent 4569252 commit b72b4a2

File tree

4 files changed

+76
-44
lines changed

4 files changed

+76
-44
lines changed

doc/releases/changelog-dev.md

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@
55
* Added ``catalyst.switch``, a qjit compatible, index-switch style control flow decorator.
66
[(#2171)](https://github.com/PennyLaneAI/catalyst/pull/2171)
77

8-
* Catalyst can now compile circuits that are directly expressed in terms of Pauli product rotation
9-
(PPR) and Pauli product measurement (PPM) operations: :class:`~.PauliRot` and
10-
:func:`~.pauli_measure`, respectively. This support enables research and development
8+
* Catalyst can now compile circuits that are directly expressed in terms of Pauli product rotation
9+
(PPR) and Pauli product measurement (PPM) operations: :class:`~.PauliRot` and
10+
:func:`~.pauli_measure`, respectively. This support enables research and development
1111
spurred from `A Game of Surface Codes (arXiv1808.02892) <https://arxiv.org/pdf/1808.02892>`_.
1212
[(#2145)](https://github.com/PennyLaneAI/catalyst/pull/2145)
1313

1414
:class:`~.PauliRot` and :func:`~.pauli_measure` can be manipulated with Catalyst's existing passes
15-
for PPR-PPM compilation, which includes :func:`catalyst.passes.to_ppr`,
16-
:func:`catalyst.passes.commute_ppr`, :func:`catalyst.passes.merge_ppr_ppm`,
17-
:func:`catalyst.passes.ppr_to_ppm`, :func:`catalyst.passes.reduce_t_depth`, and
18-
:func:`catalyst.passes.ppm_compilation`. For clear and inspectable results, use ``target="mlir"``
19-
in the ``qjit`` decorator, ensure that PennyLane's program capture is enabled,
20-
:func:`pennylane.capture.enable`, and call the Catalyst passes from the PennyLane frontend (e.g.,
15+
for PPR-PPM compilation, which includes :func:`catalyst.passes.to_ppr`,
16+
:func:`catalyst.passes.commute_ppr`, :func:`catalyst.passes.merge_ppr_ppm`,
17+
:func:`catalyst.passes.ppr_to_ppm`, :func:`catalyst.passes.reduce_t_depth`, and
18+
:func:`catalyst.passes.ppm_compilation`. For clear and inspectable results, use ``target="mlir"``
19+
in the ``qjit`` decorator, ensure that PennyLane's program capture is enabled,
20+
:func:`pennylane.capture.enable`, and call the Catalyst passes from the PennyLane frontend (e.g.,
2121
``qml.transforms.ppr_to_ppm`` instead of from ``catalyst.passes.``).
2222

2323
```python
@@ -54,14 +54,14 @@
5454
```pycon
5555
>>> print(qml.specs(circuit, level="all")()['resources'])
5656
{
57-
'No transforms': ...,
57+
'No transforms': ...,
5858
'Before MLIR Passes (MLIR-0)': ...,
5959
'ppm-compilation (MLIR-1)': Resources(
60-
num_wires=6,
61-
num_gates=14,
62-
gate_types=defaultdict(<class 'int'>, {'PPM-w3': 2, 'PPM-w2': 4, 'PPM-w1': 4, 'PPR-pi/2-w1': 4}),
63-
gate_sizes=defaultdict(<class 'int'>, {3: 2, 2: 4, 1: 8}),
64-
depth=None,
60+
num_wires=6,
61+
num_gates=14,
62+
gate_types=defaultdict(<class 'int'>, {'PPM-w3': 2, 'PPM-w2': 4, 'PPM-w1': 4, 'PPR-pi/2-w1': 4}),
63+
gate_sizes=defaultdict(<class 'int'>, {3: 2, 2: 4, 1: 8}),
64+
depth=None,
6565
shots=Shots(total_shots=None, shot_vector=())
6666
)
6767
}
@@ -119,7 +119,7 @@
119119
* Dynamically allocated wires can now be passed into control flow and subroutines.
120120
[(#2130)](https://github.com/PennyLaneAI/catalyst/pull/2130)
121121

122-
* Catalyst now supports arbitrary angle Pauli product rotations in the QEC dialect.
122+
* Catalyst now supports arbitrary angle Pauli product rotations in the QEC dialect.
123123
This will allow :class:`qml.PauliRot` with arbitrary angles to be lowered to QEC dialect.
124124
This is implemented as a new `qec.ppr.arbitrary` operation, which takes a Pauli product
125125
and an arbitrary angle (as a double) as input.

frontend/catalyst/device/qjit_device.py

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,44 @@
5656
logger = logging.getLogger(__name__)
5757
logger.addHandler(logging.NullHandler())
5858

59+
RUNTIME_OPERATIONS = [
60+
"CNOT",
61+
"ControlledPhaseShift",
62+
"CRot",
63+
"CRX",
64+
"CRY",
65+
"CRZ",
66+
"CSWAP",
67+
"CY",
68+
"CZ",
69+
"Hadamard",
70+
"Identity",
71+
"IsingXX",
72+
"IsingXY",
73+
"IsingYY",
74+
"IsingZZ",
75+
"SingleExcitation",
76+
"DoubleExcitation",
77+
"ISWAP",
78+
"MultiRZ",
79+
"PauliX",
80+
"PauliY",
81+
"PauliZ",
82+
"PCPhase",
83+
"PhaseShift",
84+
"PSWAP",
85+
"QubitUnitary",
86+
"Rot",
87+
"RX",
88+
"RY",
89+
"RZ",
90+
"S",
91+
"SWAP",
92+
"T",
93+
"Toffoli",
94+
"GlobalPhase",
95+
]
96+
5997
RUNTIME_OBSERVABLES = [
6098
"Identity",
6199
"PauliX",
@@ -71,9 +109,11 @@
71109

72110
RUNTIME_MPS = ["ExpectationMP", "SampleMP", "VarianceMP", "CountsMP", "StateMP", "ProbabilityMP"]
73111

74-
# A list of custom operations supported by the Catalyst compiler.
75-
# This is useful especially for testing a device with custom operations.
76-
CUSTOM_OPERATIONS = {}
112+
# The runtime interface does not care about specific gate properties, so set them all to True.
113+
RUNTIME_OPERATIONS = {
114+
op: OperatorProperties(invertible=True, controllable=True, differentiable=True)
115+
for op in RUNTIME_OPERATIONS
116+
}
77117

78118
RUNTIME_OBSERVABLES = {
79119
obs: OperatorProperties(invertible=True, controllable=True, differentiable=True)
@@ -159,14 +199,6 @@ def extract_backend_info(device: qml.devices.QubitDevice) -> BackendInfo:
159199
return BackendInfo(dname, device_name, device_lpath, device_kwargs)
160200

161201

162-
def union_operations(
163-
a: Dict[str, OperatorProperties], b: Dict[str, OperatorProperties]
164-
) -> Dict[str, OperatorProperties]:
165-
"""Union of two sets of operator properties"""
166-
return {**a, **b}
167-
# return {k: a[k] & b[k] for k in (a.keys() & b.keys())}
168-
169-
170202
def intersect_operations(
171203
a: Dict[str, OperatorProperties], b: Dict[str, OperatorProperties]
172204
) -> Dict[str, OperatorProperties]:
@@ -191,8 +223,8 @@ def get_qjit_device_capabilities(target_capabilities: DeviceCapabilities) -> Dev
191223
qjit_capabilities = deepcopy(target_capabilities)
192224

193225
# Intersection of gates and observables supported by the device and by Catalyst runtime.
194-
qjit_capabilities.operations = union_operations(
195-
target_capabilities.operations, CUSTOM_OPERATIONS
226+
qjit_capabilities.operations = intersect_operations(
227+
target_capabilities.operations, RUNTIME_OPERATIONS
196228
)
197229
qjit_capabilities.observables = intersect_operations(
198230
target_capabilities.observables, RUNTIME_OBSERVABLES

frontend/test/pytest/test_preprocess.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,14 @@ def test_decompose_integration(self):
118118

119119
@qml.qnode(dev)
120120
def circuit(theta: float):
121-
qml.OrbitalRotation(theta, wires=[0, 1, 2, 3])
121+
qml.SingleExcitationPlus(theta, wires=[0, 1])
122122
return qml.state()
123123

124124
mlir = qjit(circuit, target="mlir").mlir
125-
assert "SingleExcitation" in mlir
126125
assert "Hadamard" in mlir
127-
assert "RX" in mlir
128-
assert "OrbitalRotation" not in mlir
126+
assert "CNOT" in mlir
127+
assert "RY" in mlir
128+
assert "SingleExcitationPlus" not in mlir
129129

130130
def test_decompose_ops_to_unitary(self):
131131
"""Test the decompose ops to unitary transform."""

frontend/test/pytest/test_verification.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
from catalyst.api_extensions import HybridAdjoint, HybridCtrl
3838
from catalyst.compiler import get_lib_path
3939
from catalyst.device import get_device_capabilities
40-
from catalyst.device.qjit_device import CUSTOM_OPERATIONS, get_qjit_device_capabilities
40+
from catalyst.device.qjit_device import RUNTIME_OPERATIONS, get_qjit_device_capabilities
4141
from catalyst.device.verification import validate_measurements
4242

4343
# pylint: disable = unused-argument, unnecessary-lambda-assignment, unnecessary-lambda
@@ -290,7 +290,7 @@ def test_non_controllable_gate_hybridctrl(self):
290290
# Note: The HybridCtrl operator is not currently supported with the QJIT device, but the
291291
# verification structure is in place, so we test the verification of its nested operators by
292292
# adding HybridCtrl to the list of native gates for the custom base device and by patching
293-
# the list of CUSTOM_OPERATIONS for the QJIT device to include HybridCtrl for this test.
293+
# the list of RUNTIME_OPERATIONS for the QJIT device to include HybridCtrl for this test.
294294

295295
@qml.qnode(
296296
get_custom_device(
@@ -302,12 +302,12 @@ def f(x: float):
302302
assert isinstance(op, HybridCtrl), f"op expected to be HybridCtrl but got {type(op)}"
303303
return qml.expval(qml.PauliX(0))
304304

305-
runtime_ops_with_qctrl = deepcopy(CUSTOM_OPERATIONS)
305+
runtime_ops_with_qctrl = deepcopy(RUNTIME_OPERATIONS)
306306
runtime_ops_with_qctrl["HybridCtrl"] = OperatorProperties(
307307
invertible=True, controllable=True, differentiable=True
308308
)
309309

310-
with patch("catalyst.device.qjit_device.CUSTOM_OPERATIONS", runtime_ops_with_qctrl):
310+
with patch("catalyst.device.qjit_device.RUNTIME_OPERATIONS", runtime_ops_with_qctrl):
311311
with pytest.raises(CompileError, match="PauliZ is not controllable"):
312312
qjit(f)(1.2)
313313

@@ -321,7 +321,7 @@ def test_hybridctrl_raises_error(self):
321321
"""Test that a HybridCtrl operator is rejected by the verification."""
322322

323323
# TODO: If you are deleting this test because HybridCtrl support has been added, consider
324-
# updating the tests that patch CUSTOM_OPERATIONS to inclue HybridCtrl accordingly
324+
# updating the tests that patch RUNTIME_OPERATIONS to inclue HybridCtrl accordingly
325325

326326
@qml.qnode(get_custom_device(non_controllable_gates={"PauliZ"}, wires=4))
327327
def f(x: float):
@@ -391,7 +391,7 @@ def test_hybrid_ctrl_containing_adjoint(self, adjoint_type, unsupported_gate_att
391391
# Note: The HybridCtrl operator is not currently supported with the QJIT device, but the
392392
# verification structure is in place, so we test the verification of its nested operators by
393393
# adding HybridCtrl to the list of native gates for the custom base device and by patching
394-
# the list of CUSTOM_OPERATIONS for the QJIT device to include HybridCtrl for this test.
394+
# the list of RUNTIME_OPERATIONS for the QJIT device to include HybridCtrl for this test.
395395

396396
def _ops(x, wires):
397397
if adjoint_type == HybridAdjoint:
@@ -410,12 +410,12 @@ def f(x: float):
410410
assert isinstance(base, adjoint_type), f"expected {adjoint_type} but got {type(op)}"
411411
return qml.expval(qml.PauliX(0))
412412

413-
runtime_ops_with_qctrl = deepcopy(CUSTOM_OPERATIONS)
413+
runtime_ops_with_qctrl = deepcopy(RUNTIME_OPERATIONS)
414414
runtime_ops_with_qctrl["HybridCtrl"] = OperatorProperties(
415415
invertible=True, controllable=True, differentiable=True
416416
)
417417

418-
with patch("catalyst.device.qjit_device.CUSTOM_OPERATIONS", runtime_ops_with_qctrl):
418+
with patch("catalyst.device.qjit_device.RUNTIME_OPERATIONS", runtime_ops_with_qctrl):
419419
with pytest.raises(CompileError, match=f"PauliZ is not {unsupported_gate_attribute}"):
420420
qjit(f)(1.2)
421421

@@ -434,7 +434,7 @@ def test_hybrid_adjoint_containing_hybrid_ctrl(self, ctrl_type, unsupported_gate
434434
# Note: The HybridCtrl operator is not currently supported with the QJIT device, but the
435435
# verification structure is in place, so we test the verification of its nested operators by
436436
# adding HybridCtrl to the list of native gates for the custom base device and by patching
437-
# the list of CUSTOM_OPERATIONS for the QJIT device to include HybridCtrl for this test.
437+
# the list of RUNTIME_OPERATIONS for the QJIT device to include HybridCtrl for this test.
438438

439439
def _ops(x, wires):
440440
if ctrl_type == HybridCtrl:
@@ -453,12 +453,12 @@ def f(x: float):
453453
assert isinstance(base, ctrl_type), f"expected {ctrl_type} but got {type(op)}"
454454
return qml.expval(qml.PauliX(0))
455455

456-
runtime_ops_with_qctrl = deepcopy(CUSTOM_OPERATIONS)
456+
runtime_ops_with_qctrl = deepcopy(RUNTIME_OPERATIONS)
457457
runtime_ops_with_qctrl["HybridCtrl"] = OperatorProperties(
458458
invertible=True, controllable=True, differentiable=True
459459
)
460460

461-
with patch("catalyst.device.qjit_device.CUSTOM_OPERATIONS", runtime_ops_with_qctrl):
461+
with patch("catalyst.device.qjit_device.RUNTIME_OPERATIONS", runtime_ops_with_qctrl):
462462
with pytest.raises(CompileError, match=f"PauliZ is not {unsupported_gate_attribute}"):
463463
qjit(f)(1.2)
464464

0 commit comments

Comments
 (0)