pennylane-lightning-kokkos 0.42.0__cp312-cp312-manylinux_2_28_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,459 @@
1
+ # Copyright 2018-2023 Xanadu Quantum Technologies Inc.
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ r"""
15
+ This module contains the :class:`~.LightningKokkos` class, a PennyLane simulator device that
16
+ interfaces with C++ for fast linear algebra calculations.
17
+ """
18
+ from dataclasses import replace
19
+ from functools import reduce
20
+ from pathlib import Path
21
+ from typing import List, Optional, Union
22
+ from warnings import warn
23
+
24
+ import numpy as np
25
+ import pennylane as qml
26
+ from numpy.random import BitGenerator, Generator, SeedSequence
27
+ from numpy.typing import ArrayLike
28
+ from pennylane.devices import DefaultExecutionConfig, ExecutionConfig
29
+ from pennylane.devices.capabilities import OperatorProperties
30
+ from pennylane.devices.modifiers import simulator_tracking, single_tape_support
31
+ from pennylane.devices.preprocess import (
32
+ decompose,
33
+ mid_circuit_measurements,
34
+ no_sampling,
35
+ validate_adjoint_trainable_params,
36
+ validate_device_wires,
37
+ validate_measurements,
38
+ validate_observables,
39
+ )
40
+ from pennylane.exceptions import DeviceError
41
+ from pennylane.measurements import MidMeasureMP
42
+ from pennylane.operation import DecompositionUndefinedError, Operator
43
+ from pennylane.ops import Conditional, PauliRot, Prod, SProd, Sum
44
+ from pennylane.transforms.core import TransformProgram
45
+
46
+ from pennylane_lightning.lightning_base.lightning_base import (
47
+ LightningBase,
48
+ QuantumTape_or_Batch,
49
+ Result_or_ResultBatch,
50
+ )
51
+
52
+ try:
53
+ from pennylane_lightning.lightning_kokkos_ops import backend_info
54
+
55
+ LK_CPP_BINARY_AVAILABLE = True
56
+ except ImportError as ex:
57
+ warn(str(ex), UserWarning)
58
+ LK_CPP_BINARY_AVAILABLE = False
59
+ backend_info = None
60
+
61
+ from ._adjoint_jacobian import LightningKokkosAdjointJacobian
62
+ from ._measurements import LightningKokkosMeasurements
63
+ from ._state_vector import LightningKokkosStateVector
64
+
65
+ _to_matrix_ops = {
66
+ "BlockEncode": OperatorProperties(),
67
+ "DiagonalQubitUnitary": OperatorProperties(),
68
+ "ECR": OperatorProperties(),
69
+ "ISWAP": OperatorProperties(),
70
+ "OrbitalRotation": OperatorProperties(),
71
+ "QubitCarry": OperatorProperties(),
72
+ "QubitSum": OperatorProperties(),
73
+ "SISWAP": OperatorProperties(),
74
+ "SQISW": OperatorProperties(),
75
+ }
76
+
77
+
78
+ def stopping_condition(op: Operator) -> bool:
79
+ """A function that determines whether or not an operation is supported by ``lightning.kokkos``."""
80
+ if isinstance(op, qml.PauliRot):
81
+ word = op._hyperparameters["pauli_word"] # pylint: disable=protected-access
82
+ # decomposes to IsingXX, etc. for n <= 2
83
+ return reduce(lambda x, y: x + (y != "I"), word, 0) > 2
84
+ if op.name in ("C(SProd)", "C(Exp)"):
85
+ return True
86
+
87
+ if (isinstance(op, Conditional) and stopping_condition(op.base)) or isinstance(
88
+ op, MidMeasureMP
89
+ ):
90
+ # Conditional and MidMeasureMP should not be decomposed
91
+ return True
92
+
93
+ return _supports_operation(op.name)
94
+
95
+
96
+ def stopping_condition_shots(op: Operator) -> bool:
97
+ """A function that determines whether or not an operation is supported by ``lightning.kokkos``
98
+ with finite shots."""
99
+ return stopping_condition(op) or isinstance(op, (MidMeasureMP, qml.ops.op_math.Conditional))
100
+
101
+
102
+ def accepted_observables(obs: Operator) -> bool:
103
+ """A function that determines whether or not an observable is supported by ``lightning.kokkos``."""
104
+ return _supports_observable(obs.name)
105
+
106
+
107
+ def adjoint_observables(obs: Operator) -> bool:
108
+ """A function that determines whether or not an observable is supported by ``lightning.kokkos``
109
+ when using the adjoint differentiation method."""
110
+ if isinstance(obs, qml.Projector):
111
+ return False
112
+
113
+ if isinstance(obs, SProd):
114
+ return adjoint_observables(obs.base)
115
+
116
+ if isinstance(obs, (Sum, Prod)):
117
+ return all(adjoint_observables(o) for o in obs)
118
+
119
+ return _supports_observable(obs.name)
120
+
121
+
122
+ def adjoint_measurements(mp: qml.measurements.MeasurementProcess) -> bool:
123
+ """Specifies whether or not an observable is compatible with adjoint differentiation on DefaultQubit."""
124
+ return isinstance(mp, qml.measurements.ExpectationMP)
125
+
126
+
127
+ def _supports_adjoint(circuit):
128
+ if circuit is None:
129
+ return True
130
+
131
+ prog = TransformProgram()
132
+ _add_adjoint_transforms(prog)
133
+
134
+ try:
135
+ prog((circuit,))
136
+ except (DecompositionUndefinedError, DeviceError, AttributeError):
137
+ return False
138
+ return True
139
+
140
+
141
+ def _adjoint_ops(op: qml.operation.Operator) -> bool:
142
+ """Specify whether or not an Operator is supported by adjoint differentiation."""
143
+
144
+ return not isinstance(op, (Conditional, MidMeasureMP, PauliRot)) and (
145
+ not any(qml.math.requires_grad(d) for d in op.data)
146
+ or (op.num_params == 1 and op.has_generator)
147
+ )
148
+
149
+
150
+ def _add_adjoint_transforms(program: TransformProgram) -> None:
151
+ """Private helper function for ``preprocess`` that adds the transforms specific
152
+ for adjoint differentiation.
153
+
154
+ Args:
155
+ program (TransformProgram): where we will add the adjoint differentiation transforms
156
+
157
+ Side Effects:
158
+ Adds transforms to the input program.
159
+
160
+ """
161
+
162
+ name = "adjoint + lightning.kokkos"
163
+ program.add_transform(no_sampling, name=name)
164
+ program.add_transform(qml.transforms.broadcast_expand)
165
+ program.add_transform(
166
+ decompose,
167
+ stopping_condition=_adjoint_ops,
168
+ stopping_condition_shots=stopping_condition_shots,
169
+ name=name,
170
+ skip_initial_state_prep=False,
171
+ )
172
+ program.add_transform(validate_observables, accepted_observables, name=name)
173
+ program.add_transform(
174
+ validate_measurements, analytic_measurements=adjoint_measurements, name=name
175
+ )
176
+ program.add_transform(validate_adjoint_trainable_params)
177
+
178
+
179
+ @simulator_tracking
180
+ @single_tape_support
181
+ class LightningKokkos(LightningBase):
182
+ """PennyLane Lightning Kokkos device.
183
+
184
+ A device that interfaces with C++ to perform fast linear algebra calculations on CPUs or GPUs using `Kokkos`.
185
+
186
+ Use of this device requires pre-built binaries or compilation from source. Check out the
187
+ :doc:`/lightning_kokkos/installation` guide for more details.
188
+
189
+ Args:
190
+ wires (Optional[int, list]): the number of wires to initialize the device with. Defaults to ``None`` if not specified, and the device will allocate the number of wires depending on the circuit to execute.
191
+ c_dtype: Datatypes for statevector representation. Must be one of
192
+ ``np.complex64`` or ``np.complex128``.
193
+ shots (int): How many times the circuit should be evaluated (or sampled) to estimate
194
+ the expectation values. Defaults to ``None`` if not specified. Setting
195
+ to ``None`` results in computing statistics like expectation values and
196
+ variances analytically.
197
+ seed (Union[str, None, int, array_like[int], SeedSequence, BitGenerator, Generator]): A
198
+ seed-like parameter matching that of ``seed`` for ``numpy.random.default_rng``, or
199
+ a request to seed from numpy's global random number generator.
200
+ The default, ``seed="global"`` pulls a seed from NumPy's global generator. ``seed=None``
201
+ will pull a seed from the OS entropy.
202
+ mpi (bool): Use MPI to distribute statevector across multiple processes.
203
+ kokkos_args (InitializationSettings): binding for Kokkos::InitializationSettings
204
+ (threading parameters).
205
+ """
206
+
207
+ # General device options
208
+ _device_options = ("c_dtype", "batch_obs")
209
+
210
+ # Device specific options
211
+ _CPP_BINARY_AVAILABLE = LK_CPP_BINARY_AVAILABLE
212
+ _backend_info = backend_info if LK_CPP_BINARY_AVAILABLE else None
213
+ kokkos_config = {}
214
+
215
+ # The configuration file declares the capabilities of the device
216
+ config_filepath = Path(__file__).parent / "lightning_kokkos.toml"
217
+
218
+ # TODO: This is to communicate to Catalyst in qjit-compiled workflows that these operations
219
+ # should be converted to QubitUnitary instead of their original decompositions. Remove
220
+ # this when customizable multiple decomposition pathways are implemented
221
+ _to_matrix_ops = _to_matrix_ops
222
+
223
+ def __init__( # pylint: disable=too-many-arguments
224
+ self,
225
+ wires: Union[int, List] = None,
226
+ *,
227
+ c_dtype: Union[np.complex128, np.complex64] = np.complex128,
228
+ shots: Union[int, List] = None,
229
+ batch_obs: bool = False,
230
+ seed: Union[str, None, int, ArrayLike, SeedSequence, BitGenerator, Generator] = "global",
231
+ # Kokkos arguments
232
+ mpi: bool = False,
233
+ kokkos_args=None,
234
+ ):
235
+ if not self._CPP_BINARY_AVAILABLE:
236
+ raise ImportError(
237
+ "Pre-compiled binaries for lightning.kokkos are not available. "
238
+ "To manually compile from source, follow the instructions at "
239
+ "https://docs.pennylane.ai/projects/lightning/en/stable/dev/installation.html."
240
+ )
241
+
242
+ super().__init__(
243
+ wires=wires,
244
+ c_dtype=c_dtype,
245
+ shots=shots,
246
+ seed=seed,
247
+ batch_obs=batch_obs,
248
+ )
249
+
250
+ # Set the attributes to call the Lightning classes
251
+ self._set_lightning_classes()
252
+
253
+ self._mpi = mpi
254
+ if mpi:
255
+ if wires is None:
256
+ raise DeviceError("Lightning-Kokkos-MPI does not support dynamic wires allocation.")
257
+ self._statevector = self.LightningStateVector(
258
+ num_wires=len(self.wires),
259
+ dtype=self.c_dtype,
260
+ kokkos_args=kokkos_args,
261
+ mpi=True,
262
+ rng=self._rng,
263
+ )
264
+ else:
265
+ self._statevector = None
266
+ self._sv_init_kwargs = {"kokkos_args": kokkos_args}
267
+
268
+ @property
269
+ def name(self):
270
+ """The name of the device."""
271
+ return "lightning.kokkos"
272
+
273
+ def _set_lightning_classes(self):
274
+ """Load the LightningStateVector, LightningMeasurements, LightningAdjointJacobian as class attribute"""
275
+ self.LightningStateVector = LightningKokkosStateVector
276
+ self.LightningMeasurements = LightningKokkosMeasurements
277
+ self.LightningAdjointJacobian = LightningKokkosAdjointJacobian
278
+
279
+ def _setup_execution_config(self, config):
280
+ """
281
+ Update the execution config with choices for how the device should be used and the device options.
282
+ """
283
+ updated_values = {}
284
+
285
+ # It is necessary to set the mcmc default configuration to complete the requirements of ExecuteConfig
286
+ mcmc_default = {"mcmc": False, "kernel_name": None, "num_burnin": 0, "rng": None}
287
+
288
+ for option, _ in config.device_options.items():
289
+ if option not in self._device_options and option not in mcmc_default:
290
+ raise DeviceError(f"device option {option} not present on {self}")
291
+
292
+ if config.gradient_method == "best":
293
+ updated_values["gradient_method"] = "adjoint"
294
+ if config.use_device_jacobian_product is None:
295
+ updated_values["use_device_jacobian_product"] = config.gradient_method in (
296
+ "best",
297
+ "adjoint",
298
+ )
299
+ if config.use_device_gradient is None:
300
+ updated_values["use_device_gradient"] = config.gradient_method in ("best", "adjoint")
301
+ if (
302
+ config.use_device_gradient
303
+ or updated_values.get("use_device_gradient", False)
304
+ and config.grad_on_execution is None
305
+ ):
306
+ updated_values["grad_on_execution"] = True
307
+
308
+ new_device_options = dict(config.device_options)
309
+ for option in self._device_options:
310
+ if option not in new_device_options:
311
+ new_device_options[option] = getattr(self, f"_{option}", None)
312
+
313
+ new_device_options.update(mcmc_default)
314
+
315
+ mcm_supported_methods = (
316
+ ("deferred", "tree-traversal", "one-shot", None)
317
+ if not qml.capture.enabled()
318
+ else ("deferred", "single-branch-statistics", None)
319
+ )
320
+
321
+ mcm_config = config.mcm_config
322
+
323
+ if (mcm_method := mcm_config.mcm_method) not in mcm_supported_methods:
324
+ raise DeviceError(f"mcm_method='{mcm_method}' is not supported with lightning.kokkos.")
325
+
326
+ if qml.capture.enabled():
327
+
328
+ mcm_updated_values = {}
329
+
330
+ if mcm_method == "single-branch-statistics" and mcm_config.postselect_mode is not None:
331
+ warn(
332
+ "Setting 'postselect_mode' is not supported with mcm_method='single-branch-"
333
+ "statistics'. 'postselect_mode' will be ignored.",
334
+ UserWarning,
335
+ )
336
+ mcm_updated_values["postselect_mode"] = None
337
+ elif mcm_method is None:
338
+ mcm_updated_values["mcm_method"] = "deferred"
339
+
340
+ updated_values["mcm_config"] = replace(mcm_config, **mcm_updated_values)
341
+
342
+ return replace(config, **updated_values, device_options=new_device_options)
343
+
344
+ def preprocess(self, execution_config: ExecutionConfig = DefaultExecutionConfig):
345
+ """This function defines the device transform program to be applied and an updated device configuration.
346
+
347
+ Args:
348
+ execution_config (Union[ExecutionConfig, Sequence[ExecutionConfig]]): A data structure describing the
349
+ parameters needed to fully describe the execution.
350
+
351
+ Returns:
352
+ TransformProgram, ExecutionConfig: A transform program that when called returns :class:`~.QuantumTape`'s that the
353
+ device can natively execute as well as a postprocessing function to be called after execution, and a configuration
354
+ with unset specifications filled in.
355
+
356
+ This device:
357
+
358
+ * Supports any qubit operations that provide a matrix
359
+ * Currently does not support finite shots
360
+ * Currently does not intrinsically support parameter broadcasting
361
+
362
+ """
363
+ exec_config = self._setup_execution_config(execution_config)
364
+ program = TransformProgram()
365
+
366
+ if qml.capture.enabled():
367
+
368
+ if exec_config.mcm_config.mcm_method == "deferred":
369
+ program.add_transform(qml.defer_measurements, num_wires=len(self.wires))
370
+ # Using stopping_condition_shots because we don't want to decompose Conditionals or MCMs
371
+ program.add_transform(qml.transforms.decompose, gate_set=stopping_condition_shots)
372
+ return program, exec_config
373
+
374
+ program.add_transform(validate_measurements, name=self.name)
375
+ program.add_transform(validate_observables, accepted_observables, name=self.name)
376
+ program.add_transform(
377
+ mid_circuit_measurements, device=self, mcm_config=exec_config.mcm_config
378
+ )
379
+ program.add_transform(validate_device_wires, self.wires, name=self.name)
380
+
381
+ program.add_transform(
382
+ decompose,
383
+ stopping_condition=stopping_condition,
384
+ stopping_condition_shots=stopping_condition_shots,
385
+ skip_initial_state_prep=True,
386
+ name=self.name,
387
+ )
388
+ program.add_transform(qml.transforms.broadcast_expand)
389
+
390
+ if exec_config.gradient_method == "adjoint":
391
+ _add_adjoint_transforms(program)
392
+ return program, exec_config
393
+
394
+ # pylint: disable=unused-argument
395
+ def execute(
396
+ self,
397
+ circuits: QuantumTape_or_Batch,
398
+ execution_config: ExecutionConfig = DefaultExecutionConfig,
399
+ ) -> Result_or_ResultBatch:
400
+ """Execute a circuit or a batch of circuits and turn it into results.
401
+
402
+ Args:
403
+ circuits (Union[QuantumTape, Sequence[QuantumTape]]): the quantum circuits to be executed
404
+ execution_config (ExecutionConfig): a datastructure with additional information required for execution
405
+
406
+ Returns:
407
+ TensorLike, tuple[TensorLike], tuple[tuple[TensorLike]]: A numeric result of the computation.
408
+ """
409
+ results = []
410
+ for circuit in circuits:
411
+ if self._wire_map is not None:
412
+ [circuit], _ = qml.map_wires(circuit, self._wire_map)
413
+ results.append(
414
+ self.simulate(
415
+ self.dynamic_wires_from_circuit(circuit),
416
+ self._statevector,
417
+ postselect_mode=execution_config.mcm_config.postselect_mode,
418
+ mcm_method=execution_config.mcm_config.mcm_method,
419
+ )
420
+ )
421
+
422
+ return tuple(results)
423
+
424
+ def supports_derivatives(
425
+ self,
426
+ execution_config: Optional[ExecutionConfig] = None,
427
+ circuit: Optional[qml.tape.QuantumTape] = None,
428
+ ) -> bool:
429
+ """Check whether or not derivatives are available for a given configuration and circuit.
430
+
431
+ ``LightningKokkos`` supports adjoint differentiation with analytic results.
432
+
433
+ Args:
434
+ execution_config (ExecutionConfig): The configuration of the desired derivative calculation
435
+ circuit (QuantumTape): An optional circuit to check derivatives support for.
436
+
437
+ Returns:
438
+ Bool: Whether or not a derivative can be calculated provided the given information
439
+
440
+ """
441
+ if execution_config is None and circuit is None:
442
+ return True
443
+ if execution_config.gradient_method not in {"adjoint", "best"}:
444
+ return False
445
+ if circuit is None:
446
+ return True
447
+ return _supports_adjoint(circuit=circuit)
448
+
449
+ @staticmethod
450
+ def get_c_interface():
451
+ """Returns a tuple consisting of the device name, and
452
+ the location to the shared object with the C/C++ device implementation.
453
+ """
454
+
455
+ return LightningBase.get_c_interface_impl("LightningKokkosSimulator", "lightning_kokkos")
456
+
457
+
458
+ _supports_operation = LightningKokkos.capabilities.supports_operation
459
+ _supports_observable = LightningKokkos.capabilities.supports_observable
@@ -0,0 +1,131 @@
1
+ schema = 3
2
+
3
+ # The set of all gate types supported at the runtime execution interface of the
4
+ # device, i.e., what is supported by the `execute` method of the Device API.
5
+ # The gate definition has the following format:
6
+ #
7
+ # GATE = { properties = [ PROPS ], conditions = [ CONDS ] }
8
+ #
9
+ # where PROPS and CONS are zero or more comma separated quoted strings.
10
+ #
11
+ # PROPS: zero or more comma-separated quoted strings:
12
+ # - "controllable": if a controlled version of this gate is supported.
13
+ # - "invertible": if the adjoint of this operation is supported.
14
+ # - "differentiable": if device gradient is supported for this gate.
15
+ # CONDS: zero or more comma-separated quoted strings:
16
+ # - "analytic" or "finiteshots": if this operation is only supported in
17
+ # either analytic execution or with shots, respectively.
18
+ # - "terms-commute": if this composite operator is only supported
19
+ # given that its terms commute. Only relevant for Prod, SProd, Sum,
20
+ # LinearCombination, and Hamiltonian.
21
+ #
22
+ [operators.gates]
23
+
24
+ CNOT = { properties = [ "invertible", "differentiable" ] }
25
+ ControlledPhaseShift = { properties = [ "invertible", "differentiable" ] }
26
+ CRot = { properties = [ "invertible" ] }
27
+ CRX = { properties = [ "invertible", "differentiable" ] }
28
+ CRY = { properties = [ "invertible", "differentiable" ] }
29
+ CRZ = { properties = [ "invertible", "differentiable" ] }
30
+ CSWAP = { properties = [ "invertible", "differentiable" ] }
31
+ CY = { properties = [ "invertible", "differentiable" ] }
32
+ CZ = { properties = [ "invertible", "differentiable" ] }
33
+ DoubleExcitationMinus = { properties = [ "invertible", "controllable", "differentiable" ] }
34
+ DoubleExcitationPlus = { properties = [ "invertible", "controllable", "differentiable" ] }
35
+ DoubleExcitation = { properties = [ "invertible", "controllable", "differentiable" ] }
36
+ GlobalPhase = { properties = [ "invertible", "controllable", "differentiable" ] }
37
+ Hadamard = { properties = [ "invertible", "controllable", "differentiable" ] }
38
+ Identity = { properties = [ "invertible", "differentiable" ] }
39
+ IsingXX = { properties = [ "invertible", "controllable", "differentiable" ] }
40
+ IsingXY = { properties = [ "invertible", "controllable", "differentiable" ] }
41
+ IsingYY = { properties = [ "invertible", "controllable", "differentiable" ] }
42
+ IsingZZ = { properties = [ "invertible", "controllable", "differentiable" ] }
43
+ MultiRZ = { properties = [ "invertible", "controllable", "differentiable" ] }
44
+ PauliX = { properties = [ "invertible", "controllable", "differentiable" ] }
45
+ PauliY = { properties = [ "invertible", "controllable", "differentiable" ] }
46
+ PauliZ = { properties = [ "invertible", "controllable", "differentiable" ] }
47
+ PhaseShift = { properties = [ "invertible", "controllable", "differentiable" ] }
48
+ PSWAP = { properties = [ "invertible", "controllable", "differentiable" ] }
49
+ QubitUnitary = { properties = [ "invertible", "controllable" ] }
50
+ Rot = { properties = [ "invertible", "controllable" ] }
51
+ RX = { properties = [ "invertible", "controllable", "differentiable" ] }
52
+ RY = { properties = [ "invertible", "controllable", "differentiable" ] }
53
+ RZ = { properties = [ "invertible", "controllable", "differentiable" ] }
54
+ SingleExcitationMinus = { properties = [ "invertible", "controllable", "differentiable" ] }
55
+ SingleExcitationPlus = { properties = [ "invertible", "controllable", "differentiable" ] }
56
+ SingleExcitation = { properties = [ "invertible", "controllable", "differentiable" ] }
57
+ S = { properties = [ "invertible", "controllable", "differentiable" ] }
58
+ SWAP = { properties = [ "invertible", "controllable", "differentiable" ] }
59
+ Toffoli = { properties = [ "invertible", "differentiable" ] }
60
+ T = { properties = [ "invertible", "controllable", "differentiable" ] }
61
+ SX = { properties = [ "invertible", "controllable", "differentiable" ] }
62
+
63
+ # Operations supported by the execution in Python but not directly supported by the backend
64
+ [pennylane.operators.gates]
65
+
66
+ MultiControlledX = {}
67
+ ControlledQubitUnitary = {}
68
+ GlobalPhase = { properties = [ "invertible", "controllable", "differentiable" ] }
69
+ BlockEncode = { properties = [ "controllable" ] }
70
+ DiagonalQubitUnitary = {}
71
+ ECR = {}
72
+ ISWAP = {}
73
+ OrbitalRotation = {}
74
+ QubitCarry = {}
75
+ QubitSum = {}
76
+ SISWAP = {}
77
+ SQISW = {}
78
+
79
+ # Observables supported by the device
80
+ [operators.observables]
81
+
82
+ Identity = { properties = [ "differentiable" ] }
83
+ PauliX = { properties = [ "differentiable" ] }
84
+ PauliY = { properties = [ "differentiable" ] }
85
+ PauliZ = { properties = [ "differentiable" ] }
86
+ Hadamard = { properties = [ "differentiable" ] }
87
+ Hermitian = { properties = [ "differentiable" ] }
88
+ SparseHamiltonian = { properties = [ "differentiable" ] }
89
+ Sum = { properties = [ "differentiable" ] }
90
+ SProd = { properties = [ "differentiable" ] }
91
+ Prod = { properties = [ "differentiable" ] }
92
+ Exp = { properties = [ "differentiable" ] }
93
+ LinearCombination = { properties = [ "differentiable" ] }
94
+
95
+ [pennylane.operators.observables]
96
+
97
+ Projector = {}
98
+
99
+ [measurement_processes]
100
+
101
+ ExpectationMP = {}
102
+ VarianceMP = {}
103
+ ProbabilityMP = {}
104
+ StateMP = { conditions = [ "analytic" ] }
105
+ SampleMP = { conditions = [ "finiteshots" ] }
106
+ CountsMP = { conditions = [ "finiteshots" ] }
107
+
108
+ # Additional support that the device may provide. All accepted fields and their
109
+ # default values are listed below. Any fields missing from the TOML file will be
110
+ # set to their default values.
111
+ [compilation]
112
+
113
+ # Whether the device is compatible with qjit.
114
+ qjit_compatible = true
115
+
116
+ # Whether the device requires run time generation of the quantum circuit.
117
+ runtime_code_generation = false
118
+
119
+ # The methods of handling mid-circuit measurements that the device supports, e.g.,
120
+ # "one-shot", "device", "tree-traversal", etc. An empty list indicates that the device
121
+ # does not support mid-circuit measurements.
122
+ supported_mcm_methods = [ "one-shot", "tree-traversal" ]
123
+
124
+ # Whether the device supports dynamic qubit allocation/deallocation.
125
+ dynamic_qubit_management = false
126
+
127
+ # Whether simultaneous measurements of non-commuting observables is supported.
128
+ non_commuting_observables = true
129
+
130
+ # Whether the device supports initial state preparation.
131
+ initial_state_prep = true