-
Notifications
You must be signed in to change notification settings - Fork 58
Remove the hardcoded list of runtime operations in the frontend #2215
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
1c52075
dd215d4
dfad6e1
3b775a2
0d1e2ad
ad95b8d
187e557
0a52b33
64cd133
6f9a34f
73b4865
ed5fa60
791033f
10bf1db
1aac684
c07216d
3593b5b
43a3031
2fc016b
ebeba80
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,6 +41,28 @@ def is_supported(op: Operator, capabilities: DeviceCapabilities) -> bool: | |
| return op.name in capabilities.operations | ||
|
|
||
|
|
||
| def is_lowering_compatible(op: Operator) -> bool: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to consider operations like quantum.measure at all, since its signature even has a return value 🤔 |
||
| """Check if an operation can be lowered to MLIR using JAX primitives.""" | ||
| # Exceptions for operations that are not quantum instructions but are allowed | ||
| # via custom lowering rules. | ||
| # TODO: Revisit this as more explicit ops will be added to Catalyst Compiler. | ||
| if isinstance(op, (qml.Snapshot, qml.PCPhase, qml.MultiRZ)): | ||
| return True | ||
|
|
||
| # Accepted hyperparameters for quantum instructions bind calls | ||
| _accepted_hyperparams = { | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @albi3ro Here's the minimum set of hyperparams that we always need to check for compatibility with the |
||
| "base", | ||
| "n_wires", | ||
| "num_wires", | ||
| "control_wires", | ||
| "control_values", | ||
| "work_wires", | ||
| "work_wire_type", | ||
|
Comment on lines
+59
to
+60
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we handle work wires when mapping to mlir? |
||
| } | ||
|
|
||
| return set(op.hyperparameters).issubset(_accepted_hyperparams) | ||
|
|
||
|
|
||
| def _is_grad_recipe_same_as_catalyst(op): | ||
| """Checks that the grad_recipe for the op matches the hard coded one in Catalyst.""" | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -56,44 +56,6 @@ | |
| logger = logging.getLogger(__name__) | ||
| logger.addHandler(logging.NullHandler()) | ||
|
|
||
| RUNTIME_OPERATIONS = [ | ||
| "CNOT", | ||
| "ControlledPhaseShift", | ||
| "CRot", | ||
| "CRX", | ||
| "CRY", | ||
| "CRZ", | ||
| "CSWAP", | ||
| "CY", | ||
| "CZ", | ||
| "Hadamard", | ||
| "Identity", | ||
| "IsingXX", | ||
| "IsingXY", | ||
| "IsingYY", | ||
| "IsingZZ", | ||
| "SingleExcitation", | ||
| "DoubleExcitation", | ||
| "ISWAP", | ||
| "MultiRZ", | ||
| "PauliX", | ||
| "PauliY", | ||
| "PauliZ", | ||
| "PCPhase", | ||
| "PhaseShift", | ||
| "PSWAP", | ||
| "QubitUnitary", | ||
| "Rot", | ||
| "RX", | ||
| "RY", | ||
| "RZ", | ||
| "S", | ||
| "SWAP", | ||
| "T", | ||
| "Toffoli", | ||
| "GlobalPhase", | ||
| ] | ||
|
|
||
| RUNTIME_OBSERVABLES = [ | ||
| "Identity", | ||
| "PauliX", | ||
|
|
@@ -109,11 +71,9 @@ | |
|
|
||
| RUNTIME_MPS = ["ExpectationMP", "SampleMP", "VarianceMP", "CountsMP", "StateMP", "ProbabilityMP"] | ||
|
|
||
| # The runtime interface does not care about specific gate properties, so set them all to True. | ||
| RUNTIME_OPERATIONS = { | ||
| op: OperatorProperties(invertible=True, controllable=True, differentiable=True) | ||
| for op in RUNTIME_OPERATIONS | ||
| } | ||
| # A list of custom operations supported by the Catalyst compiler. | ||
| # This is useful especially for testing a device with custom operations. | ||
| CUSTOM_OPERATIONS = {} | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a point to keep this empty set, considering its entire purpose is to be unioned with something else? Or is it just for tests? In that case possibly related: #2114 |
||
|
|
||
| RUNTIME_OBSERVABLES = { | ||
| obs: OperatorProperties(invertible=True, controllable=True, differentiable=True) | ||
|
|
@@ -199,6 +159,13 @@ def extract_backend_info(device: qml.devices.QubitDevice) -> BackendInfo: | |
| return BackendInfo(dname, device_name, device_lpath, device_kwargs) | ||
|
|
||
|
|
||
| def union_operations( | ||
| a: Dict[str, OperatorProperties], b: Dict[str, OperatorProperties] | ||
| ) -> Dict[str, OperatorProperties]: | ||
| """Union of two sets of operator properties""" | ||
| return {**a, **b} | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason why not just ? |
||
|
|
||
|
|
||
| def intersect_operations( | ||
| a: Dict[str, OperatorProperties], b: Dict[str, OperatorProperties] | ||
| ) -> Dict[str, OperatorProperties]: | ||
|
|
@@ -223,8 +190,8 @@ def get_qjit_device_capabilities(target_capabilities: DeviceCapabilities) -> Dev | |
| qjit_capabilities = deepcopy(target_capabilities) | ||
|
|
||
| # Intersection of gates and observables supported by the device and by Catalyst runtime. | ||
| qjit_capabilities.operations = intersect_operations( | ||
| target_capabilities.operations, RUNTIME_OPERATIONS | ||
| qjit_capabilities.operations = union_operations( | ||
| target_capabilities.operations, CUSTOM_OPERATIONS | ||
| ) | ||
| qjit_capabilities.observables = intersect_operations( | ||
| target_capabilities.observables, RUNTIME_OBSERVABLES | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not convinced this function is sufficient to guarantee compatibility with the quantum dialect. Looking at the structure of an operator:
Name and wires are always supported by
quantum.custom.Hyperparameters are being checked below, although I don't know if the check is not too generic (i.e. I don't know if all hyperparameters are supported by all operation types).
The thing that's missing entirely is verifying parameters. Pennylane only requires that they be tensor-like, so this could be an arbitrary sequence of arbitrary tensors. This cannot be mapped to the quantum dialect at the moment. Some special ops support certain tensors (like 2D complex for quantum.unitary), but for the generic quantum.custom we would only support a sequence of scalar floats (or perhaps a single tensor that is flattened into a sequence of floats, but this could get highly inefficient if the tensor is large).