Skip to content

Commit

Permalink
Update Lightning device TOML files to the new schema (#988)
Browse files Browse the repository at this point in the history
**Context:**

This PR is part of the new device capabilities initiative to improve
feature parity across the ecosystem. See [this
ADR](PennyLaneAI/adrs#78) for more context. A
new TOML schema has been defined, and the relevant module implemented in
PennyLane:
- PennyLaneAI/pennylane#6407
- PennyLaneAI/pennylane#6433.

As well as updates made to Catalyst:
- PennyLaneAI/catalyst#1275

**Description of the Change:**

- Updates `lightning_qubit.toml`, `lightning_kokkos.toml`,
`lightning_gpu.toml` to the new schema
- Removes `_operations` and `_observables` from `LightningQubit`,
`LightningKokkos`, and `LightningGPU` as they are now available via
`Device.capabilities` that is loaded from the TOML file.

**Benefits:**

A step towards feature parity across the ecosystem.

**Possible Drawbacks:**

- Per discussions when the ADR was developed, `operator.gates.decomp`
and `operator.gates.matrix` are removed, and the TOML file no longer
prescribes to the framework how an operator should be handled. To ensure
consistency of behaviour, this information is temporarily moved to a
`_to_matrix_ops` class property to be used by Catalyst, until we have
better support for customizable multi-pathway decompositions.

**Related GitHub Issues:**

[sc-71729]

---------

Co-authored-by: ringo-but-quantum <[email protected]>
Co-authored-by: Ali Asadi <[email protected]>
Co-authored-by: Joseph Lee <[email protected]>
Co-authored-by: Joseph Lee <[email protected]>
  • Loading branch information
5 people authored Nov 22, 2024
1 parent 9fc9633 commit 45a67d4
Show file tree
Hide file tree
Showing 10 changed files with 276 additions and 507 deletions.
5 changes: 4 additions & 1 deletion .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@

### Improvements

* The TOML files for the devices are updated to use the new schema for declaring device capabilities.
[(#988)](https://github.com/PennyLaneAI/pennylane-lightning/pull/988)

* Unify excitation gates memory layout to row-major for both LGPU and LT.
[(#959)](https://github.com/PennyLaneAI/pennylane-lightning/pull/959)

Expand Down Expand Up @@ -52,7 +55,7 @@

This release contains contributions from (in alphabetical order):

Ali Asadi, Joseph Lee, Anton Naim Ibrahim, Luis Alfredo Nuñez Meneses, Andrija Paurevic, Shuli Shu, Raul Torres, Haochen Paul Wang
Ali Asadi, Astral Cai, Joseph Lee, Anton Naim Ibrahim, Luis Alfredo Nuñez Meneses, Andrija Paurevic, Shuli Shu, Raul Torres, Haochen Paul Wang

---

Expand Down
1 change: 1 addition & 0 deletions doc/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ cutensornet-cu12
wheel
sphinxext-opengraph
matplotlib
git+https://github.com/PennyLaneAI/pennylane.git@master
2 changes: 1 addition & 1 deletion pennylane_lightning/core/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@
Version number (major.minor.patch[-label])
"""

__version__ = "0.40.0-dev13"
__version__ = "0.40.0-dev14"
171 changes: 33 additions & 138 deletions pennylane_lightning/lightning_gpu/lightning_gpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import numpy as np
import pennylane as qml
from pennylane.devices import DefaultExecutionConfig, ExecutionConfig
from pennylane.devices.default_qubit import adjoint_ops
from pennylane.devices.capabilities import OperatorProperties
from pennylane.devices.modifiers import simulator_tracking, single_tape_support
from pennylane.devices.preprocess import (
decompose,
Expand All @@ -43,7 +43,7 @@
)
from pennylane.measurements import MidMeasureMP
from pennylane.operation import DecompositionUndefinedError, Operator
from pennylane.ops import Prod, SProd, Sum
from pennylane.ops import Conditional, PauliRot, Prod, SProd, Sum
from pennylane.tape import QuantumScript
from pennylane.transforms.core import TransformProgram
from pennylane.typing import Result
Expand Down Expand Up @@ -74,135 +74,25 @@
from ._mpi_handler import MPIHandler
from ._state_vector import LightningGPUStateVector

# The set of supported operations.
_operations = frozenset(
{
"Identity",
"QubitUnitary",
"ControlledQubitUnitary",
"MultiControlledX",
"DiagonalQubitUnitary",
"PauliX",
"PauliY",
"PauliZ",
"MultiRZ",
"GlobalPhase",
"C(PauliX)",
"C(PauliY)",
"C(PauliZ)",
"C(Hadamard)",
"C(S)",
"C(T)",
"C(PhaseShift)",
"C(RX)",
"C(RY)",
"C(RZ)",
"C(Rot)",
"C(SWAP)",
"C(IsingXX)",
"C(IsingXY)",
"C(IsingYY)",
"C(IsingZZ)",
"C(SingleExcitation)",
"C(SingleExcitationMinus)",
"C(SingleExcitationPlus)",
"C(DoubleExcitation)",
"C(DoubleExcitationMinus)",
"C(DoubleExcitationPlus)",
"C(MultiRZ)",
"C(GlobalPhase)",
"Hadamard",
"S",
"Adjoint(S)",
"T",
"Adjoint(T)",
"SX",
"Adjoint(SX)",
"CNOT",
"SWAP",
"ISWAP",
"PSWAP",
"Adjoint(ISWAP)",
"SISWAP",
"Adjoint(SISWAP)",
"SQISW",
"CSWAP",
"Toffoli",
"CY",
"CZ",
"PhaseShift",
"ControlledPhaseShift",
"RX",
"RY",
"RZ",
"Rot",
"CRX",
"CRY",
"CRZ",
"CRot",
"IsingXX",
"IsingYY",
"IsingZZ",
"IsingXY",
"SingleExcitation",
"SingleExcitationPlus",
"SingleExcitationMinus",
"DoubleExcitation",
"DoubleExcitationPlus",
"DoubleExcitationMinus",
"Adjoint(MultiRZ)",
"Adjoint(GlobalPhase)",
"Adjoint(PhaseShift)",
"Adjoint(ControlledPhaseShift)",
"Adjoint(RX)",
"Adjoint(RY)",
"Adjoint(RZ)",
"Adjoint(CRX)",
"Adjoint(CRY)",
"Adjoint(CRZ)",
"Adjoint(IsingXX)",
"Adjoint(IsingYY)",
"Adjoint(IsingZZ)",
"Adjoint(IsingXY)",
"Adjoint(SingleExcitation)",
"Adjoint(SingleExcitationPlus)",
"Adjoint(SingleExcitationMinus)",
"Adjoint(DoubleExcitation)",
"Adjoint(DoubleExcitationPlus)",
"Adjoint(DoubleExcitationMinus)",
"QubitCarry",
"QubitSum",
"OrbitalRotation",
"ECR",
"BlockEncode",
"C(BlockEncode)",
}
)
# End the set of supported operations.

# The set of supported observables.
_observables = frozenset(
{
"PauliX",
"PauliY",
"PauliZ",
"Hadamard",
"SparseHamiltonian",
"LinearCombination",
"Hermitian",
"Identity",
"Projector",
"Sum",
"Prod",
"SProd",
"Exp",
}
)
_to_matrix_ops = {
"BlockEncode": OperatorProperties(controllable=True),
"ControlledQubitUnitary": OperatorProperties(),
"ECR": OperatorProperties(),
"SX": OperatorProperties(),
"ISWAP": OperatorProperties(),
"PSWAP": OperatorProperties(),
"SISWAP": OperatorProperties(),
"SQISW": OperatorProperties(),
"OrbitalRotation": OperatorProperties(),
"QubitCarry": OperatorProperties(),
"QubitSum": OperatorProperties(),
"DiagonalQubitUnitary": OperatorProperties(),
}


def stopping_condition(op: Operator) -> bool:
"""A function that determines whether or not an operation is supported by ``lightning.gpu``."""
return op.name in _operations
return _supports_operation(op.name)


def stopping_condition_shots(op: Operator) -> bool:
Expand All @@ -213,7 +103,7 @@ def stopping_condition_shots(op: Operator) -> bool:

def accepted_observables(obs: Operator) -> bool:
"""A function that determines whether or not an observable is supported by ``lightning.gpu``."""
return obs.name in _observables
return _supports_observable(obs.name)


def adjoint_observables(obs: Operator) -> bool:
Expand All @@ -228,7 +118,7 @@ def adjoint_observables(obs: Operator) -> bool:
if isinstance(obs, (Sum, Prod)):
return all(adjoint_observables(o) for o in obs)

return obs.name in _observables
return _supports_observable(obs.name)


def adjoint_measurements(mp: qml.measurements.MeasurementProcess) -> bool:
Expand All @@ -252,7 +142,10 @@ def _supports_adjoint(circuit):

def _adjoint_ops(op: qml.operation.Operator) -> bool:
"""Specify whether or not an Operator is supported by adjoint differentiation."""
return adjoint_ops(op) and not isinstance(op, qml.PauliRot)

return not isinstance(op, (Conditional, MidMeasureMP, PauliRot)) and (
not qml.operation.is_trainable(op) or (op.num_params == 1 and op.has_generator)
)


def _add_adjoint_transforms(program: TransformProgram) -> None:
Expand Down Expand Up @@ -333,15 +226,13 @@ class LightningGPU(LightningBase):
_CPP_BINARY_AVAILABLE = LGPU_CPP_BINARY_AVAILABLE
_backend_info = backend_info if LGPU_CPP_BINARY_AVAILABLE else None

# This `config` is used in Catalyst-Frontend
config = Path(__file__).parent / "lightning_gpu.toml"
# TODO: This is to communicate to Catalyst in qjit-compiled workflows that these operations
# should be converted to QubitUnitary instead of their original decompositions. Remove
# this when customizable multiple decomposition pathways are implemented
_to_matrix_ops = _to_matrix_ops

# TODO: Move supported ops/obs to TOML file
operations = _operations
# The names of the supported operations.

observables = _observables
# The names of the supported observables.
# This configuration file declares capabilities of the device
config_filepath = Path(__file__).parent / "lightning_gpu.toml"

def __init__( # pylint: disable=too-many-arguments
self,
Expand Down Expand Up @@ -607,3 +498,7 @@ def get_c_interface():
return "LightningGPUSimulator", lib_location

raise RuntimeError("'LightningGPUSimulator' shared library not found") # pragma: no cover


_supports_operation = LightningGPU.capabilities.supports_operation
_supports_observable = LightningGPU.capabilities.supports_observable
88 changes: 50 additions & 38 deletions pennylane_lightning/lightning_gpu/lightning_gpu.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
schema = 2
schema = 3

# The union of all gate types listed in this section must match what
# the device considers "supported" through PennyLane's device API.
[operators.gates.native]
# The set of all gate types supported at the runtime execution interface of the
# device, i.e., what is supported by the `execute` method of the Device API.
# The gate definition has the following format:
#
# GATE = { properties = [ PROPS ], conditions = [ CONDS ] }
#
# where PROPS and CONS are zero or more comma separated quoted strings.
#
# PROPS: zero or more comma-separated quoted strings:
# - "controllable": if a controlled version of this gate is supported.
# - "invertible": if the adjoint of this operation is supported.
# - "differentiable": if device gradient is supported for this gate.
# CONDS: zero or more comma-separated quoted strings:
# - "analytic" or "finiteshots": if this operation is only supported in
# either analytic execution or with shots, respectively.
# - "terms-commute": if this composite operator is only supported
# given that its terms commute. Only relevant for Prod, SProd, Sum,
# LinearCombination, and Hamiltonian.
#
[operators.gates]

Identity = { properties = [ "invertible", "differentiable" ] }
PauliX = { properties = [ "invertible", "controllable", "differentiable" ] }
Expand All @@ -15,7 +32,7 @@ PhaseShift = { properties = [ "invertible", "controllable", "differe
RX = { properties = [ "invertible", "controllable", "differentiable" ] }
RY = { properties = [ "invertible", "controllable", "differentiable" ] }
RZ = { properties = [ "invertible", "controllable", "differentiable" ] }
Rot = { properties = [ "invertible", "controllable", "differentiable" ] }
Rot = { properties = [ "controllable", "differentiable" ] }
CNOT = { properties = [ "invertible", "differentiable" ] }
CY = { properties = [ "invertible", "differentiable" ] }
CZ = { properties = [ "invertible", "differentiable" ] }
Expand All @@ -30,7 +47,7 @@ ControlledPhaseShift = { properties = [ "invertible", "differe
CRX = { properties = [ "invertible", "differentiable" ] }
CRY = { properties = [ "invertible", "differentiable" ] }
CRZ = { properties = [ "invertible", "differentiable" ] }
CRot = { properties = [ "invertible" ] }
CRot = { }
SingleExcitation = { properties = [ "invertible", "controllable", "differentiable" ] }
SingleExcitationPlus = { properties = [ "invertible", "controllable", "differentiable" ] }
SingleExcitationMinus = { properties = [ "invertible", "controllable", "differentiable" ] }
Expand All @@ -41,21 +58,10 @@ MultiRZ = { properties = [ "invertible", "controllable", "differe
QubitUnitary = { properties = [ "invertible", "controllable" ] }
GlobalPhase = { properties = [ "invertible", "controllable", "differentiable" ] }

# Operators that should be decomposed according to the algorithm used
# by PennyLane's device API.
# Optional, since gates not listed in this list will typically be decomposed by
# default, but can be useful to express a deviation from this device's regular
# strategy in PennyLane.
[operators.gates.decomp]
# Operations supported by the execution in Python but not directly supported by the backend
[pennylane.operators.gates]

BasisState = {}
StatePrep = {}
MultiControlledX = {}

# Gates which should be translated to QubitUnitary
[operators.gates.matrix]

BlockEncode = {properties = [ "controllable" ]}
BlockEncode = { properties = [ "controllable" ] }
ControlledQubitUnitary = {}
ECR = {}
SX = {}
Expand All @@ -67,6 +73,7 @@ OrbitalRotation = {}
QubitCarry = {}
QubitSum = {}
DiagonalQubitUnitary = {}
MultiControlledX = {}

# Observables supported by the device
[operators.observables]
Expand All @@ -84,30 +91,35 @@ Prod = { properties = [ "differentiable" ] }
Exp = { properties = [ "differentiable" ] }
LinearCombination = { properties = [ "differentiable" ] }

[pennylane.operators.observables]

Projector = {}

[measurement_processes]

Expval = {}
Var = {}
Probs = {}
State = { condition = [ "analytic" ] }
Sample = { condition = [ "finiteshots" ] }
Counts = { condition = [ "finiteshots" ] }
ExpectationMP = {}
VarianceMP = {}
ProbabilityMP = {}
StateMP = { conditions = [ "analytic" ] }
SampleMP = { conditions = [ "finiteshots" ] }
CountsMP = { conditions = [ "finiteshots" ] }

# Additional support that the device may provide. All accepted fields and their
# default values are listed below. Any fields missing from the TOML file will be
# set to their default values.
[compilation]
# If the device is compatible with qjit

# Whether the device is compatible with qjit.
qjit_compatible = true
# If the device requires run time generation of the quantum circuit.
# Whether the device requires run time generation of the quantum circuit.
runtime_code_generation = false
# If the device supports mid circuit measurements natively
mid_circuit_measurement = true

# This field is currently unchecked but it is reserved for the purpose of
# determining if the device supports dynamic qubit allocation/deallocation.
# The methods of handling mid-circuit measurements that the device supports, e.g.,
# "one-shot", "device", "tree-traversal", etc. An empty list indicates that the device
# does not support mid-circuit measurements.
supported_mcm_methods = [ "one-shot" ]
# Whether the device supports dynamic qubit allocation/deallocation.
dynamic_qubit_management = false

# whether the device can support non-commuting measurements together
# in a single execution
# Whether simultaneous measurements of non-commuting observables is supported.
non_commuting_observables = true

# Whether the device supports (arbitrary) initial state preparation.
# Whether the device supports initial state preparation.
initial_state_prep = true
Loading

0 comments on commit 45a67d4

Please sign in to comment.