pennylane-lightning-kokkos 0.43.0__cp312-cp312-manylinux_2_27_x86_64.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,18 @@
1
+ # Copyright 2018-2024 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
+ """PennyLane lightning_kokkos package."""
15
+
16
+ from pennylane_lightning.core import __version__
17
+
18
+ from .lightning_kokkos import LightningKokkos
@@ -0,0 +1,136 @@
1
+ # Copyright 2018-2024 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
+ Internal methods for adjoint Jacobian differentiation method.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from warnings import warn
21
+
22
+ try:
23
+ from pennylane_lightning.lightning_kokkos_ops.algorithms import (
24
+ AdjointJacobianC64,
25
+ AdjointJacobianC128,
26
+ create_ops_listC64,
27
+ create_ops_listC128,
28
+ )
29
+
30
+ try:
31
+ from pennylane_lightning.lightning_kokkos_ops.algorithmsMPI import (
32
+ AdjointJacobianMPIC64,
33
+ AdjointJacobianMPIC128,
34
+ create_ops_listMPIC64,
35
+ create_ops_listMPIC128,
36
+ )
37
+
38
+ mpi_error = None
39
+ MPI_SUPPORT = True
40
+ except ImportError as ex_mpi:
41
+ mpi_error = ex_mpi
42
+ MPI_SUPPORT = False
43
+
44
+ except ImportError as ex:
45
+ warn(str(ex), UserWarning)
46
+
47
+ import numpy as np
48
+ from pennylane.tape import QuantumTape
49
+
50
+ # pylint: disable=ungrouped-imports
51
+ from pennylane_lightning.lightning_base._adjoint_jacobian import LightningBaseAdjointJacobian
52
+
53
+
54
+ class LightningKokkosAdjointJacobian(LightningBaseAdjointJacobian):
55
+ """Check and execute the adjoint Jacobian differentiation method.
56
+
57
+ Args:
58
+ qubit_state(LightningKokkosStateVector): State Vector to calculate the adjoint Jacobian with.
59
+ batch_obs(bool): If serialized tape is to be batched or not.
60
+ """
61
+
62
+ # pylint: disable=too-few-public-methods
63
+ def __init__(
64
+ self,
65
+ qubit_state: LightningKokkosStateVector, # pylint: disable=undefined-variable
66
+ batch_obs: bool = False,
67
+ ) -> None:
68
+
69
+ self._use_mpi = qubit_state._mpi
70
+ super().__init__(qubit_state, batch_obs)
71
+
72
+ def _adjoint_jacobian_dtype(self):
73
+ """Binding to Lightning Kokkos Adjoint Jacobian C++ class.
74
+
75
+ Returns: A pair of the AdjointJacobian class and the create_ops_list function. Default is None.
76
+ """
77
+ if self._use_mpi:
78
+ if not MPI_SUPPORT:
79
+ warn(str(mpi_error), UserWarning)
80
+
81
+ jacobian_lightning = (
82
+ AdjointJacobianMPIC64() if self.dtype == np.complex64 else AdjointJacobianMPIC128()
83
+ )
84
+ create_ops_list_lightning = (
85
+ create_ops_listMPIC64 if self.dtype == np.complex64 else create_ops_listMPIC128
86
+ )
87
+
88
+ return jacobian_lightning, create_ops_list_lightning
89
+
90
+ # without MPI
91
+ jacobian_lightning = (
92
+ AdjointJacobianC64() if self.dtype == np.complex64 else AdjointJacobianC128()
93
+ )
94
+ create_ops_list_lightning = (
95
+ create_ops_listC64 if self.dtype == np.complex64 else create_ops_listC128
96
+ )
97
+ return jacobian_lightning, create_ops_list_lightning
98
+
99
+ def calculate_jacobian(self, tape: QuantumTape):
100
+ """Computes the Jacobian with the adjoint method.
101
+
102
+ .. code-block:: python
103
+
104
+ statevector = LightningKokkosStateVector(num_wires=num_wires)
105
+ statevector = statevector.get_final_state(tape)
106
+ jacobian = LightningKokkosAdjointJacobian(statevector).calculate_jacobian(tape)
107
+
108
+ Args:
109
+ tape (QuantumTape): Operations and measurements that represent instructions for execution on Lightning.
110
+
111
+ Returns:
112
+ The Jacobian of a tape.
113
+ """
114
+
115
+ empty_array = self._handle_raises(tape, is_jacobian=True)
116
+
117
+ if empty_array:
118
+ return np.array([], dtype=self.dtype)
119
+ processed_data = self._process_jacobian_tape(tape, use_mpi=self._use_mpi)
120
+
121
+ if not processed_data: # training_params is empty
122
+ return np.array([], dtype=self.dtype)
123
+
124
+ trainable_params = processed_data["tp_shift"]
125
+ jac = self._jacobian_lightning(
126
+ processed_data["state_vector"],
127
+ processed_data["obs_serialized"],
128
+ processed_data["ops_serialized"],
129
+ trainable_params,
130
+ )
131
+ jac = np.array(jac)
132
+ jac = jac.reshape(-1, len(trainable_params)) if len(jac) else jac
133
+ jac_r = np.zeros((jac.shape[0], processed_data["all_params"]))
134
+ jac_r[:, processed_data["record_tp_rows"]] = jac
135
+
136
+ return self._adjoint_jacobian_processing(jac_r)
@@ -0,0 +1,97 @@
1
+ # Copyright 2018-2024 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
+ """
15
+ Class implementation for state vector measurements.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from warnings import warn
21
+
22
+ try:
23
+ from pennylane_lightning.lightning_kokkos_ops import MeasurementsC64, MeasurementsC128
24
+
25
+ try:
26
+ from pennylane_lightning.lightning_kokkos_ops import MeasurementsMPIC64, MeasurementsMPIC128
27
+
28
+ mpi_error = None
29
+ MPI_SUPPORT = True
30
+ except ImportError as ex_mpi:
31
+ mpi_error = ex_mpi
32
+ MPI_SUPPORT = False
33
+
34
+ except ImportError as error_import:
35
+ warn(str(error_import), UserWarning)
36
+
37
+ import numpy as np
38
+ import pennylane as qml
39
+ from pennylane.measurements import MeasurementProcess
40
+
41
+ # pylint: disable=ungrouped-imports
42
+ from pennylane_lightning.lightning_base._measurements import LightningBaseMeasurements
43
+
44
+
45
+ class LightningKokkosMeasurements(
46
+ LightningBaseMeasurements
47
+ ): # pylint: disable=too-few-public-methods
48
+ """Lightning Kokkos Measurements class
49
+
50
+ Measures the state provided by the LightningKokkosStateVector class.
51
+
52
+ Args:
53
+ qubit_state(LightningKokkosStateVector): Lightning state-vector class containing the state vector to be measured.
54
+ """
55
+
56
+ def __init__(
57
+ self,
58
+ kokkos_state: LightningKokkosStateVector, # pylint: disable=undefined-variable
59
+ ) -> None:
60
+ super().__init__(kokkos_state)
61
+
62
+ self._use_mpi = kokkos_state._mpi
63
+
64
+ if self._use_mpi:
65
+ self._num_local_wires = kokkos_state._qubit_state.getNumLocalWires()
66
+
67
+ self._measurement_lightning = self._measurement_dtype()(kokkos_state.state_vector)
68
+ if kokkos_state._rng:
69
+ self._measurement_lightning.set_random_seed(kokkos_state._rng.integers(0, 2**31 - 1))
70
+
71
+ def _measurement_dtype(self):
72
+ """Binding to Lightning Kokkos Measurements C++ class.
73
+
74
+ Returns: the Measurements class
75
+ """
76
+ if self._use_mpi:
77
+ if not MPI_SUPPORT:
78
+ warn(str(mpi_error), UserWarning)
79
+
80
+ return MeasurementsMPIC64 if self.dtype == np.complex64 else MeasurementsMPIC128
81
+
82
+ # without MPI
83
+ return MeasurementsC64 if self.dtype == np.complex64 else MeasurementsC128
84
+
85
+ def _expval_pauli_sentence(self, measurementprocess: MeasurementProcess):
86
+ """Specialized method for computing the expectation value of a Pauli sentence.
87
+
88
+ Args:
89
+ measurementprocess (MeasurementProcess): Measurement process with pauli_rep.
90
+
91
+ Returns:
92
+ Expectation value.
93
+ """
94
+ pwords, coeffs = zip(*measurementprocess.obs.pauli_rep.items())
95
+ pauli_words = [qml.pauli.pauli_word_to_string(p) for p in pwords]
96
+ wires = [p.wires.tolist() for p in pwords]
97
+ return self._measurement_lightning.expval(pauli_words, wires, coeffs)
@@ -0,0 +1,343 @@
1
+ # Copyright 2018-2024 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
+ """
15
+ Class implementation for lightning_kokkos state-vector manipulation.
16
+ """
17
+ from warnings import warn
18
+
19
+ try:
20
+ from pennylane_lightning.lightning_kokkos_ops import (
21
+ InitializationSettings,
22
+ StateVectorC64,
23
+ StateVectorC128,
24
+ allocate_aligned_array,
25
+ print_configuration,
26
+ )
27
+
28
+ try:
29
+ from pennylane_lightning.lightning_kokkos_ops import (
30
+ MPIManagerKokkos,
31
+ StateVectorMPIC64,
32
+ StateVectorMPIC128,
33
+ )
34
+
35
+ mpi_error = None
36
+ MPI_SUPPORT = True
37
+ except ImportError as ex_mpi:
38
+ mpi_error = ex_mpi
39
+ MPI_SUPPORT = False
40
+
41
+ except ImportError as ex:
42
+ warn(str(ex), UserWarning)
43
+
44
+ from typing import Union
45
+
46
+ import numpy as np
47
+ import pennylane as qml
48
+ import scipy as sp
49
+ from numpy.random import Generator
50
+ from pennylane.measurements import MidMeasureMP
51
+ from pennylane.ops import Conditional
52
+ from pennylane.ops.op_math import Adjoint
53
+ from pennylane.wires import Wires
54
+
55
+ # pylint: disable=ungrouped-imports
56
+ from pennylane_lightning.lightning_base._state_vector import LightningBaseStateVector
57
+
58
+ from ._measurements import LightningKokkosMeasurements
59
+
60
+
61
+ class LightningKokkosStateVector(LightningBaseStateVector):
62
+ """Lightning Kokkos state-vector class.
63
+
64
+ Interfaces with C++ python binding methods for state-vector manipulation.
65
+
66
+ Args:
67
+ num_wires (int): the number of wires to initialize the device with
68
+ dtype: Datatypes for state-vector representation. Must be one of
69
+ ``np.complex64`` or ``np.complex128``. Default is ``np.complex128``
70
+ rng (Generator): random number generator to use for seeding sampling measurement.
71
+ kokkos_args (InitializationSettings): binding for Kokkos::InitializationSettings
72
+ (threading parameters).
73
+ mpi (bool): Use MPI for distributed state vector.
74
+
75
+ """
76
+
77
+ def __init__( # pylint: disable=too-many-arguments, too-many-positional-arguments
78
+ self,
79
+ num_wires: int,
80
+ dtype: Union[np.complex128, np.complex64] = np.complex128,
81
+ rng: Generator = None,
82
+ kokkos_args=None,
83
+ mpi: bool = None,
84
+ ):
85
+
86
+ super().__init__(num_wires, dtype, rng)
87
+
88
+ self._device_name = "lightning.kokkos"
89
+
90
+ self._kokkos_config = {}
91
+
92
+ self._mpi = mpi
93
+
94
+ # Initialize the state vector
95
+ sv_init_args = [self.num_wires]
96
+ if mpi:
97
+ self._mpi_manager = MPIManagerKokkos()
98
+ sv_init_args.insert(0, self._mpi_manager)
99
+
100
+ if kokkos_args is not None:
101
+ if not isinstance(kokkos_args, InitializationSettings):
102
+ raise TypeError(
103
+ f"Argument kokkos_args must be of type {type(InitializationSettings())} but it is of {type(kokkos_args)}."
104
+ )
105
+ sv_init_args.append(kokkos_args)
106
+
107
+ self._qubit_state = self._state_dtype()(*sv_init_args)
108
+
109
+ if not self._kokkos_config:
110
+ self._kokkos_config = self._kokkos_configuration()
111
+
112
+ @property
113
+ def state(self):
114
+ """Copy the state vector data from the device to the host.
115
+
116
+ A state vector Numpy array is explicitly allocated on the host to store and return
117
+ the data.
118
+
119
+ **Example**
120
+
121
+ >>> dev = qml.device('lightning.kokkos', wires=1)
122
+ >>> dev.apply([qml.PauliX(wires=[0])])
123
+ >>> print(dev.state)
124
+ [0.+0.j 1.+0.j]
125
+ """
126
+ if self._mpi:
127
+ self._qubit_state.reorderAllWires()
128
+ local_size = self._qubit_state.getLocalBlockSize()
129
+ state = np.zeros(local_size, dtype=self.dtype)
130
+ self.sync_d2h(state)
131
+ return state
132
+ state = np.zeros(2**self._num_wires, dtype=self.dtype)
133
+ self.sync_d2h(state)
134
+ return state
135
+
136
+ def _state_dtype(self):
137
+ """Binding to Lightning Managed state vector C++ class.
138
+
139
+ Returns: the state vector class
140
+ """
141
+ if self._mpi:
142
+ return StateVectorMPIC128 if self.dtype == np.complex128 else StateVectorMPIC64
143
+
144
+ return StateVectorC128 if self.dtype == np.complex128 else StateVectorC64
145
+
146
+ def sync_h2d(self, state_vector):
147
+ """Copy the state vector data on host provided by the user to the state
148
+ vector on the device
149
+
150
+ Args:
151
+ state_vector(array[complex]): the state vector array on host.
152
+
153
+
154
+ **Example**
155
+
156
+ >>> dev = qml.device('lightning.kokkos', wires=3)
157
+ >>> obs = qml.Identity(0) @ qml.PauliX(1) @ qml.PauliY(2)
158
+ >>> obs1 = qml.Identity(1)
159
+ >>> H = qml.Hamiltonian([1.0, 1.0], [obs1, obs])
160
+ >>> state_vector = np.array([0.0 + 0.0j, 0.0 + 0.1j, 0.1 + 0.1j, 0.1 + 0.2j, 0.2 + 0.2j, 0.3 + 0.3j, 0.3 + 0.4j, 0.4 + 0.5j,], dtype=np.complex64)
161
+ >>> dev.sync_h2d(state_vector)
162
+ >>> res = dev.expval(H)
163
+ >>> print(res)
164
+ 1.0
165
+ """
166
+ self._qubit_state.HostToDevice(state_vector.ravel(order="C"))
167
+
168
+ def sync_d2h(self, state_vector):
169
+ """Copy the state vector data on device to a state vector on the host provided
170
+ by the user
171
+
172
+ Args:
173
+ state_vector(array[complex]): the state vector array on device
174
+
175
+
176
+ **Example**
177
+
178
+ >>> dev = qml.device('lightning.kokkos', wires=1)
179
+ >>> dev.apply([qml.PauliX(wires=[0])])
180
+ >>> state_vector = np.zeros(2**dev.num_wires).astype(dev.c_dtype)
181
+ >>> dev.sync_d2h(state_vector)
182
+ >>> print(state_vector)
183
+ [0.+0.j 1.+0.j]
184
+ """
185
+ self._qubit_state.DeviceToHost(state_vector.ravel(order="C"))
186
+
187
+ def _kokkos_configuration(self):
188
+ """Get the default configuration of the kokkos device.
189
+
190
+ Returns: The `lightning.kokkos` device configuration
191
+ """
192
+ return print_configuration()
193
+
194
+ # pylint: disable=unused-argument
195
+ @staticmethod
196
+ def _operation_is_sparse(operation):
197
+ """Check if the operation is a sparse matrix operation.
198
+
199
+ Args:
200
+ operation (_): operation to check
201
+
202
+ Returns:
203
+ bool: True if the operation is a sparse matrix operation, False otherwise
204
+ """
205
+ # Currently there is not support for sparse matrices in the LightningKokkos device.
206
+ return False
207
+
208
+ def _apply_state_vector(self, state, device_wires: Wires):
209
+ """Initialize the internal state vector in a specified state.
210
+ Args:
211
+ state (Union[array[complex], scipy.SparseABC]): normalized input state of length ``2**len(wires)`` as a dense array or Scipy sparse array.
212
+ device_wires (Wires): wires that get initialized in the state.
213
+ """
214
+
215
+ if sp.sparse.issparse(state):
216
+ state = state.toarray().flatten()
217
+
218
+ if isinstance(state, self._qubit_state.__class__):
219
+ state_data = allocate_aligned_array(state.size(), np.dtype(self.dtype), True)
220
+ state.DeviceToHost(state_data)
221
+ state = state_data
222
+
223
+ # Convert PennyLane tensor to NumPy array if needed
224
+ if hasattr(state, "numpy"):
225
+ state = state.numpy()
226
+
227
+ if len(device_wires) == self._num_wires and Wires(sorted(device_wires)) == device_wires:
228
+ # Initialize the entire device state with the input state
229
+ if self._mpi:
230
+ self._qubit_state.resetIndices()
231
+ myrank = self._mpi_manager.getRank()
232
+ local_size = self._qubit_state.getLocalBlockSize()
233
+ local_state = state[myrank * local_size : (myrank + 1) * local_size]
234
+ self.sync_h2d(local_state)
235
+ return
236
+
237
+ output_shape = (2,) * self._num_wires
238
+ state = np.reshape(state, output_shape).ravel(order="C")
239
+ self.sync_h2d(np.reshape(state, output_shape))
240
+ return
241
+
242
+ # This operate on device
243
+ self._qubit_state.setStateVector(state, list(device_wires))
244
+
245
+ def _apply_lightning_controlled(self, operation, adjoint):
246
+ """Apply an arbitrary controlled operation to the state tensor.
247
+
248
+ Args:
249
+ operation (~pennylane.operation.Operation): controlled operation to apply
250
+ adjoint (bool): Apply the adjoint of the operation if True
251
+
252
+ Returns:
253
+ None
254
+ """
255
+ state = self.state_vector
256
+
257
+ if isinstance(operation.base, Adjoint):
258
+ base_operation = operation.base.base
259
+ adjoint = not adjoint
260
+ else:
261
+ base_operation = operation.base
262
+
263
+ method = getattr(state, f"{base_operation.name}", None)
264
+ control_wires = list(operation.control_wires)
265
+ control_values = operation.control_values
266
+ target_wires = list(operation.target_wires)
267
+ if method is not None: # apply n-controlled specialized gate
268
+ param = operation.parameters
269
+ method(control_wires, control_values, target_wires, adjoint, param)
270
+ else: # apply gate as an n-controlled matrix
271
+ method = getattr(state, "applyControlledMatrix")
272
+ method(
273
+ qml.matrix(base_operation),
274
+ control_wires,
275
+ control_values,
276
+ target_wires,
277
+ adjoint,
278
+ )
279
+
280
+ def _apply_lightning(
281
+ self, operations, mid_measurements: dict = None, postselect_mode: str = None
282
+ ):
283
+ """Apply a list of operations to the state tensor.
284
+
285
+ Args:
286
+ operations (list[~pennylane.operation.Operation]): operations to apply
287
+ mid_measurements (None, dict): Dictionary of mid-circuit measurements
288
+ postselect_mode (str): Configuration for handling shots with mid-circuit measurement
289
+ postselection. Use ``"hw-like"`` to discard invalid shots and ``"fill-shots"`` to
290
+ keep the same number of shots. Default is ``None``.
291
+
292
+ Returns:
293
+ None
294
+ """
295
+ state = self.state_vector
296
+
297
+ # Skip over identity operations instead of performing
298
+ # matrix multiplication with it.
299
+ for operation in operations:
300
+ if isinstance(operation, qml.Identity):
301
+ continue
302
+ if isinstance(operation, Adjoint):
303
+ op_adjoint_base = operation.base
304
+ invert_param = True
305
+ else:
306
+ op_adjoint_base = operation
307
+ invert_param = False
308
+ name = op_adjoint_base.name
309
+ method = getattr(state, name, None)
310
+ wires = list(operation.wires)
311
+
312
+ if isinstance(operation, Conditional):
313
+ if operation.meas_val.concretize(mid_measurements):
314
+ self._apply_lightning([operation.base])
315
+ elif isinstance(operation, MidMeasureMP):
316
+ self._apply_lightning_midmeasure(
317
+ LightningKokkosMeasurements(self).measure_final_state,
318
+ operation,
319
+ mid_measurements,
320
+ postselect_mode=postselect_mode,
321
+ )
322
+ elif isinstance(operation, qml.PauliRot):
323
+ method = getattr(state, "applyPauliRot")
324
+ paulis = operation._hyperparameters[ # pylint: disable=protected-access
325
+ "pauli_word"
326
+ ]
327
+ wires = [i for i, w in zip(wires, paulis) if w != "I"]
328
+ word = "".join(p for p in paulis if p != "I")
329
+ method(wires, invert_param, operation.parameters, word)
330
+ elif method is not None: # apply specialized gate
331
+ param = operation.parameters
332
+ method(wires, invert_param, param)
333
+ elif isinstance(op_adjoint_base, qml.ops.Controlled): # apply n-controlled gate
334
+ self._apply_lightning_controlled(op_adjoint_base, invert_param)
335
+ else: # apply gate as a matrix
336
+ # Inverse can be set to False since qml.matrix(operation) is already in
337
+ # inverted form
338
+ method = getattr(state, "applyMatrix")
339
+ try:
340
+ method(qml.matrix(operation), wires, False)
341
+ except AttributeError: # pragma: no cover
342
+ # To support older versions of PL
343
+ method(operation.matrix, wires, False)