Skip to content
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

Add Simulate and update Serialize for LightningQubit2 class #613

Merged
merged 97 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
9fdd4c4
lightning interface for new device api
albi3ro Feb 2, 2024
50c3bae
Auto update version
github-actions[bot] Feb 2, 2024
6ac4b89
Merge Master
AmintorDusko Feb 13, 2024
af41a63
update version
AmintorDusko Feb 13, 2024
17a4fb0
update version
AmintorDusko Feb 13, 2024
7d9c048
update lightning_qubit_2
AmintorDusko Feb 13, 2024
67b11c5
add LightningQubit2 to init
AmintorDusko Feb 13, 2024
6e82a1a
add LightningStateVector class
AmintorDusko Feb 13, 2024
0df4601
add LightningMeasurements class
AmintorDusko Feb 13, 2024
79e4d64
add new QuantumScriptSerializer class
AmintorDusko Feb 13, 2024
194696a
allow lightning.qubit2 to be tested within our suite
AmintorDusko Feb 13, 2024
ecf1bc6
add tests and CI workflow for lightning_qubit_2
AmintorDusko Feb 13, 2024
0460ae2
update CI
AmintorDusko Feb 13, 2024
1358706
update CI
AmintorDusko Feb 13, 2024
ba94149
Merge branch 'master' into lightning-qubit2-interface
albi3ro Feb 13, 2024
b975d03
Merge branch 'lightning-qubit2-interface' into lightning-qubit2-add-s…
albi3ro Feb 13, 2024
8c414db
add wire mapping, black
albi3ro Feb 13, 2024
24e4fa1
Merge branch 'lightning-qubit2-interface' into lightning-qubit2-add-s…
albi3ro Feb 13, 2024
695f4b6
add tests for custom wires
AmintorDusko Feb 13, 2024
91777b3
add tests for custom wires
AmintorDusko Feb 13, 2024
6d98910
add review suggestions
AmintorDusko Feb 13, 2024
a4147e9
format
AmintorDusko Feb 13, 2024
d784b5f
remove python class to reverse order of PRs
albi3ro Feb 16, 2024
c6f83ec
Merge branch 'master' into lightning-qubit2-add-simulate
AmintorDusko Feb 16, 2024
87779ea
update simulate to get a LightningStateVector
AmintorDusko Feb 16, 2024
9bbff30
add reset state
AmintorDusko Feb 16, 2024
b4ed92b
update simulate
AmintorDusko Feb 16, 2024
16cd1f8
update docs
AmintorDusko Feb 16, 2024
cd2828a
add Result import
AmintorDusko Feb 16, 2024
a4350b5
Update pennylane_lightning/lightning_qubit/_measurements.py
AmintorDusko Feb 21, 2024
80de995
Update pennylane_lightning/lightning_qubit/_measurements.py
AmintorDusko Feb 21, 2024
dc3149f
fix reset state
AmintorDusko Feb 21, 2024
378e376
Update pennylane_lightning/lightning_qubit/_state_vector.py
AmintorDusko Feb 21, 2024
7a5c010
Update pennylane_lightning/lightning_qubit/_state_vector.py
AmintorDusko Feb 21, 2024
b23eabf
Update pennylane_lightning/lightning_qubit/_state_vector.py
AmintorDusko Feb 21, 2024
112d2ff
remove LightningQubit2 references
AmintorDusko Feb 21, 2024
1a8fe19
remove unnecessary modules
AmintorDusko Feb 21, 2024
9ded798
merging Serializer classes
AmintorDusko Feb 21, 2024
2e43fd6
update serialize tests
AmintorDusko Feb 21, 2024
f80c363
update measurements with new serialize class
AmintorDusko Feb 21, 2024
4f4b29e
remove outdated test
AmintorDusko Feb 21, 2024
1f78bef
remove obsolete tests
AmintorDusko Feb 22, 2024
b7ccb84
remove unused dtype input from simulate
AmintorDusko Feb 22, 2024
58032ef
update measurements
AmintorDusko Feb 22, 2024
a3b696e
update state_vector
AmintorDusko Feb 22, 2024
cd7c67c
update lightning_qubit2
AmintorDusko Feb 22, 2024
910246b
format
AmintorDusko Feb 22, 2024
af6cfef
pylint
AmintorDusko Feb 22, 2024
dd31965
Update pennylane_lightning/lightning_qubit/_state_vector.py
AmintorDusko Feb 22, 2024
44bc987
remove old comment
AmintorDusko Feb 22, 2024
5a5b20a
some review suggestions
AmintorDusko Feb 22, 2024
dcdd9ff
Merge branch 'master' into lightning-qubit2-add-simulate
AmintorDusko Feb 22, 2024
a64cf78
Auto update version
github-actions[bot] Feb 22, 2024
ad1e3a4
remove print
AmintorDusko Feb 22, 2024
aec5c07
update state vector class
AmintorDusko Feb 22, 2024
286fd4c
add state vector class tests
AmintorDusko Feb 22, 2024
4ee6609
adding measurement tests
albi3ro Feb 22, 2024
c262650
Merge branch 'lightning-qubit2-add-simulate' of https://github.com/Pe…
albi3ro Feb 22, 2024
e8b606e
update state vector and tests
AmintorDusko Feb 23, 2024
7ab3ade
move and rename test files, and format
AmintorDusko Feb 23, 2024
36c6b79
Merge branch 'master' into lightning-qubit2-add-simulate
AmintorDusko Feb 23, 2024
9aa3e35
Auto update version
github-actions[bot] Feb 23, 2024
b9fa21d
skip measurements class for other devices and in the absence of binaries
AmintorDusko Feb 23, 2024
48c6724
format
AmintorDusko Feb 23, 2024
a716611
update measurements class
AmintorDusko Feb 23, 2024
261cb6d
expand measurement class testing
AmintorDusko Feb 23, 2024
aca8468
garbage collection
AmintorDusko Feb 23, 2024
80e3494
typo
AmintorDusko Feb 23, 2024
3aa71fb
Merge branch 'master' into lightning-qubit2-add-simulate
AmintorDusko Feb 26, 2024
a284d78
update coverage and StateVector class
AmintorDusko Feb 26, 2024
43f72f6
expand measurements class coverage
AmintorDusko Feb 26, 2024
2a973d9
Auto update version
github-actions[bot] Feb 26, 2024
f42f298
add coverage for n-controlled operations
AmintorDusko Feb 27, 2024
9cd5471
add map to standard wires to get_final_state for safety
AmintorDusko Feb 27, 2024
cd3096c
update jax config import
AmintorDusko Feb 27, 2024
099e180
Merge branch 'master' into lightning-qubit2-add-simulate
AmintorDusko Feb 27, 2024
98db9db
Auto update version
github-actions[bot] Feb 27, 2024
4f746d0
trigger CI
AmintorDusko Feb 27, 2024
0395945
update state vector class and tests for improved coverage
AmintorDusko Feb 28, 2024
4a863ba
update measurement class tests
AmintorDusko Feb 28, 2024
4374680
Merge branch 'master' into lightning-qubit2-add-simulate
AmintorDusko Feb 28, 2024
4b44e4f
update dev version
AmintorDusko Feb 28, 2024
62c43f0
remove device definition
AmintorDusko Feb 28, 2024
7956c16
Merge branch 'master' into lightning-qubit2-add-simulate
AmintorDusko Feb 28, 2024
2d26288
update dev version
AmintorDusko Feb 28, 2024
8a9b9a5
clean test_measurements_class.py
AmintorDusko Feb 29, 2024
36d810e
isort+black
AmintorDusko Feb 29, 2024
816955f
review suggestion
AmintorDusko Feb 29, 2024
940644c
fix docs
AmintorDusko Feb 29, 2024
140654a
Merge branch 'master' into lightning-qubit2-add-simulate
AmintorDusko Mar 4, 2024
b54d3ea
increase tolerance
AmintorDusko Mar 5, 2024
e1e9c05
Merge branch 'master' into lightning-qubit2-add-simulate
AmintorDusko Mar 5, 2024
3703b94
Auto update version
github-actions[bot] Mar 5, 2024
8519c1c
isort
AmintorDusko Mar 5, 2024
bdc28f8
review suggestions
AmintorDusko Mar 5, 2024
45b5448
remove unused imports
AmintorDusko Mar 5, 2024
5e80afb
remove diagonalization gate application from state vector
AmintorDusko Mar 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/tests_linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ jobs:
lcov --directory . -b ../pennylane_lightning/core/src --capture --output-file coverage.info
lcov --remove coverage.info '/usr/*' --output-file coverage.info
mv coverage.info coverage-${{ github.job }}-${{ matrix.pl_backend }}-Lapack.info

- name: Upload test results
uses: actions/upload-artifact@v3
if: always()
Expand Down Expand Up @@ -642,7 +642,7 @@ jobs:

- name: Combine coverage files
run: |
python -m pip install coverage
python -m pip install coverage
python -m coverage combine .coverage-python*
python -m coverage xml -o coverage-${{ github.job }}.xml

Expand Down
48 changes: 27 additions & 21 deletions pennylane_lightning/core/_serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,26 +172,26 @@ def sparse_hamiltonian_obs(self):
)
return self.sparse_hamiltonian_c64 if self.use_csingle else self.sparse_hamiltonian_c128

def _named_obs(self, observable, wires_map: dict):
def _named_obs(self, observable, wires_map: dict = None):
"""Serializes a Named observable"""
wires = [wires_map[w] for w in observable.wires]
wires = [wires_map[w] for w in observable.wires] if wires_map else observable.wires.tolist()
if observable.name == "Identity":
wires = wires[:1]
return self.named_obs(observable.name, wires)

def _hermitian_ob(self, observable, wires_map: dict):
def _hermitian_ob(self, observable, wires_map: dict = None):
"""Serializes a Hermitian observable"""
assert not isinstance(observable, Tensor)

wires = [wires_map[w] for w in observable.wires]
wires = [wires_map[w] for w in observable.wires] if wires_map else observable.wires.tolist()
return self.hermitian_obs(matrix(observable).ravel().astype(self.ctype), wires)

def _tensor_ob(self, observable, wires_map: dict):
def _tensor_ob(self, observable, wires_map: dict = None):
"""Serialize a tensor observable"""
assert isinstance(observable, Tensor)
return self.tensor_obs([self._ob(obs, wires_map) for obs in observable.obs])

def _hamiltonian(self, observable, wires_map: dict):
def _hamiltonian(self, observable, wires_map: dict = None):
coeffs = np.array(unwrap(observable.coeffs)).astype(self.rtype)
terms = [self._ob(t, wires_map) for t in observable.ops]

Expand All @@ -200,7 +200,7 @@ def _hamiltonian(self, observable, wires_map: dict):

return self.hamiltonian_obs(coeffs, terms)

def _sparse_hamiltonian(self, observable, wires_map: dict):
def _sparse_hamiltonian(self, observable, wires_map: dict = None):
"""Serialize an observable (Sparse Hamiltonian)

Args:
Expand All @@ -215,7 +215,7 @@ def _sparse_hamiltonian(self, observable, wires_map: dict):
Hmat = Hamiltonian([1.0], [Identity(0)]).sparse_matrix()
H_sparse = SparseHamiltonian(Hmat, wires=range(1))
spm = H_sparse.sparse_matrix()
# Only root 0 needs the overall sparsematrix data
# Only root 0 needs the overall sparse matrix data
if self._mpi_manager().getRank() == 0:
spm = observable.sparse_matrix()
self._mpi_manager().Barrier()
Expand All @@ -225,26 +225,28 @@ def _sparse_hamiltonian(self, observable, wires_map: dict):
indices = np.array(spm.indices).astype(np.int64)
offsets = np.array(spm.indptr).astype(np.int64)

wires = []
wires_list = observable.wires.tolist()
wires.extend([wires_map[w] for w in wires_list])
wires = [wires_map[w] for w in observable.wires] if wires_map else observable.wires.tolist()

return self.sparse_hamiltonian_obs(data, indices, offsets, wires)

def _pauli_word(self, observable, wires_map: dict):
def _pauli_word(self, observable, wires_map: dict = None):
"""Serialize a :class:`pennylane.pauli.PauliWord` into a Named or Tensor observable."""

def map_wire(wire: int):
return wires_map[wire] if wires_map else wire

if len(observable) == 1:
wire, pauli = list(observable.items())[0]
return self.named_obs(pauli_name_map[pauli], [wires_map[wire]])
return self.named_obs(pauli_name_map[pauli], [map_wire(wire)])

return self.tensor_obs(
[
self.named_obs(pauli_name_map[pauli], [wires_map[wire]])
self.named_obs(pauli_name_map[pauli], [map_wire(wire)])
for wire, pauli in observable.items()
]
)

def _pauli_sentence(self, observable, wires_map: dict):
def _pauli_sentence(self, observable, wires_map: dict = None):
"""Serialize a :class:`pennylane.pauli.PauliSentence` into a Hamiltonian."""
pwords, coeffs = zip(*observable.items())
terms = [self._pauli_word(pw, wires_map) for pw in pwords]
Expand All @@ -255,7 +257,7 @@ def _pauli_sentence(self, observable, wires_map: dict):
return self.hamiltonian_obs(coeffs, terms)

# pylint: disable=protected-access
def _ob(self, observable, wires_map):
def _ob(self, observable, wires_map: dict = None):
"""Serialize a :class:`pennylane.operation.Observable` into an Observable."""
if isinstance(observable, Tensor):
return self._tensor_ob(observable, wires_map)
Expand All @@ -269,7 +271,7 @@ def _ob(self, observable, wires_map):
return self._pauli_sentence(observable._pauli_rep, wires_map)
return self._hermitian_ob(observable, wires_map)

def serialize_observables(self, tape: QuantumTape, wires_map: dict) -> List:
def serialize_observables(self, tape: QuantumTape, wires_map: dict = None) -> List:
"""Serializes the observables of an input tape.

Args:
Expand All @@ -295,7 +297,7 @@ def serialize_observables(self, tape: QuantumTape, wires_map: dict) -> List:
return serialized_obs, offset_indices

def serialize_ops(
self, tape: QuantumTape, wires_map: dict
self, tape: QuantumTape, wires_map: dict = None
) -> Tuple[
List[List[str]],
List[np.ndarray],
Expand Down Expand Up @@ -349,7 +351,7 @@ def get_wires(operation, single_op):
wires_list = single_op.wires.tolist()
controlled_wires_list = []
control_values_list = []
return single_op, name, wires_list, controlled_wires_list, control_values_list
return single_op, name, list(wires_list), controlled_wires_list, control_values_list

for operation in tape.operations:
if isinstance(operation, (BasisState, StatePrep)):
Expand Down Expand Up @@ -382,8 +384,12 @@ def get_wires(operation, single_op):
mats.append([])

controlled_values.append(controlled_values_list)
controlled_wires.append([wires_map[w] for w in controlled_wires_list])
wires.append([wires_map[w] for w in wires_list])
controlled_wires.append(
[wires_map[w] for w in controlled_wires_list]
if wires_map
else list(controlled_wires_list)
)
wires.append([wires_map[w] for w in wires_list] if wires_map else wires_list)

inverses = [False] * len(names)
return (
Expand Down
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.36.0-dev1"
__version__ = "0.36.0-dev2"
191 changes: 191 additions & 0 deletions pennylane_lightning/lightning_qubit/_measurements.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
# Copyright 2018-2024 Xanadu Quantum Technologies Inc.

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Class implementation for state vector measurements.
"""

# pylint: disable=import-error, no-name-in-module, ungrouped-imports
try:
from pennylane_lightning.lightning_qubit_ops import (
MeasurementsC64,
MeasurementsC128,
)
except ImportError:
pass

from typing import Callable, List

import numpy as np
import pennylane as qml
AmintorDusko marked this conversation as resolved.
Show resolved Hide resolved
from pennylane.measurements import ExpectationMP, MeasurementProcess, StateMeasurement
from pennylane.tape import QuantumScript
from pennylane.typing import Result, TensorLike
from pennylane.wires import Wires

from pennylane_lightning.core._serialize import QuantumScriptSerializer

from ._state_vector import LightningStateVector


class LightningMeasurements:
"""Lightning Measurements class

Measures the state provided by the LightningStateVector class.

Args:
qubit_state(LightningStateVector): Lightning state-vector class containing the state vector to be measured.
"""

def __init__(self, qubit_state: LightningStateVector) -> None:
self._qubit_state = qubit_state
self._state = qubit_state.state_vector
self._dtype = qubit_state.dtype
self._measurement_lightning = self._measurement_dtype()(self.state)

@property
def qubit_state(self):
"""Returns a handle to the LightningStateVector class."""
return self._qubit_state

@property
def state(self):
"""Returns a handle to the Lightning internal data class."""
return self._state

@property
def dtype(self):
"""Returns the simulation data type."""
return self._dtype

def _measurement_dtype(self):
"""Binding to Lightning Measurements C++ class.

Returns: the Measurements class
"""
return MeasurementsC64 if self.dtype == np.complex64 else MeasurementsC128
AmintorDusko marked this conversation as resolved.
Show resolved Hide resolved

def state_diagonalizing_gates(self, measurementprocess: StateMeasurement) -> TensorLike:
"""Apply a measurement to state when the measurement process has an observable with diagonalizing gates.
This method is bypassing the measurement process to default.qubit implementation.

Args:
measurementprocess (StateMeasurement): measurement to apply to the state

Returns:
TensorLike: the result of the measurement
"""
diagonalizing_gates = measurementprocess.diagonalizing_gates()
self._qubit_state.apply_operations(measurementprocess.diagonalizing_gates())
AmintorDusko marked this conversation as resolved.
Show resolved Hide resolved

state_array = self._qubit_state.state
wires = Wires(range(self._qubit_state.num_wires))

result = measurementprocess.process_state(state_array, wires)

self._qubit_state.apply_operations([qml.adjoint(g) for g in reversed(diagonalizing_gates)])

return result

# pylint: disable=protected-access
def expval(self, measurementprocess: MeasurementProcess):
AmintorDusko marked this conversation as resolved.
Show resolved Hide resolved
"""Expectation value of the supplied observable contained in the MeasurementProcess.

Args:
measurementprocess (StateMeasurement): measurement to apply to the state

Returns:
Expectation value of the observable
"""

if measurementprocess.obs.name == "SparseHamiltonian":
# ensuring CSR sparse representation.
CSR_SparseHamiltonian = measurementprocess.obs.sparse_matrix(
wire_order=list(range(self._qubit_state.num_wires))
).tocsr(copy=False)
return self._measurement_lightning.expval(
CSR_SparseHamiltonian.indptr,
CSR_SparseHamiltonian.indices,
CSR_SparseHamiltonian.data,
)

if (
measurementprocess.obs.name in ["Hamiltonian", "Hermitian"]
or (measurementprocess.obs.arithmetic_depth > 0)
or isinstance(measurementprocess.obs.name, List)
):
ob_serialized = QuantumScriptSerializer(
self._qubit_state.device_name, self.dtype == np.complex64
)._ob(measurementprocess.obs)
return self._measurement_lightning.expval(ob_serialized)

return self._measurement_lightning.expval(
measurementprocess.obs.name, measurementprocess.obs.wires
)

def get_measurement_function(
self, measurementprocess: MeasurementProcess
) -> Callable[[MeasurementProcess, TensorLike], TensorLike]:
"""Get the appropriate method for performing a measurement.

Args:
measurementprocess (MeasurementProcess): measurement process to apply to the state

Returns:
Callable: function that returns the measurement result
"""
if isinstance(measurementprocess, StateMeasurement):
if isinstance(measurementprocess, ExpectationMP):
if measurementprocess.obs.name in [
"Identity",
"Projector",
]:
return self.state_diagonalizing_gates
return self.expval

if measurementprocess.obs is None or measurementprocess.obs.has_diagonalizing_gates:
return self.state_diagonalizing_gates

raise NotImplementedError

def measurement(self, measurementprocess: MeasurementProcess) -> TensorLike:
"""Apply a measurement process to a state.

Args:
measurementprocess (MeasurementProcess): measurement process to apply to the state

Returns:
TensorLike: the result of the measurement
"""
return self.get_measurement_function(measurementprocess)(measurementprocess)

def measure_final_state(self, circuit: QuantumScript) -> Result:
"""
Perform the measurements required by the circuit on the provided state.

This is an internal function that will be called by the successor to ``lightning.qubit``.

Args:
circuit (QuantumScript): The single circuit to simulate

Returns:
Tuple[TensorLike]: The measurement results
"""

if circuit.shots:
raise NotImplementedError
# analytic case
if len(circuit.measurements) == 1:
return self.measurement(circuit.measurements[0])

return tuple(self.measurement(mp) for mp in circuit.measurements)
Loading
Loading