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