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

Fix stateprep casting rules. #501

Merged
merged 15 commits into from
Sep 19, 2023
9 changes: 6 additions & 3 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@

### Breaking changes

* Cast integral-valued arrays to the device's complex type on entry in `_preprocess_state_vector` to ensure the state is correctly represented with floating-point numbers.
[(#501)](https://github.com/PennyLaneAI/pennylane-lightning/pull/501)

* Update DefaultQubit to DefaultQubitLegacy on Lightning fallback.
[(#500)](https://github.com/PennyLaneAI/pennylane-lightning/pull/500)

* Enums defined in `GateOperation.hpp` start at `1` (previously `0`). `::BEGIN` is introduced in a few places where it was assumed `0` accordingly.
[(#485)](https://github.com/PennyLaneAI/pennylane-lightning/pull/485)

* Enable pre-commit hooks to format all Python files and linting of all Python source files.
[(#485)](https://github.com/PennyLaneAI/pennylane-lightning/pull/485)

* Update DefaultQubit to DefaultQubitLegacy on Lightning fallback.
[(#500)](https://github.com/PennyLaneAI/pennylane-lightning/pull/500)

### Improvements

* Refactor LKokkos `StateVectorKokkos` class to use Kokkos `RangePolicy` together with special functors in `applyMultiQubitOp` to apply 1- to 4-wire generic unitary gates. For more than 4 wires, the general implementation using Kokkos `TeamPolicy` is employed to yield the best all-around performance.
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.33.0-dev9"
__version__ = "0.33.0-dev10"
4 changes: 4 additions & 0 deletions pennylane_lightning/core/lightning_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ def accepts_obj(obj):

return qml.BooleanFn(accepts_obj)

# pylint: disable=missing-function-docstring
@classmethod
def capabilities(cls):
capabilities = super().capabilities().copy()
Expand Down Expand Up @@ -206,6 +207,9 @@ def _preprocess_state_vector(self, state, device_wires):
# translate to wire labels used by device
device_wires = self.map_wires(device_wires)

# special case for integral types
if state.dtype.kind == "i":
state = qml.numpy.array(state, dtype=self.C_DTYPE)
state = self._asarray(state, dtype=self.C_DTYPE)

if len(device_wires) == self.num_wires and Wires(sorted(device_wires)) == device_wires:
Expand Down
8 changes: 6 additions & 2 deletions pennylane_lightning/lightning_qubit/lightning_qubit.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ def __init__(
self._state = self._create_basis_state(0)
self._pre_rotated_state = self._state

self._batch_obs = batch_obs
self._mcmc = mcmc
if self._mcmc:
if kernel_name not in [
Expand Down Expand Up @@ -281,7 +282,7 @@ def measurements(self):

@property
def state(self):
# Flattening the state.
"""Returns the flattened state vector."""
shape = (1 << self.num_wires,)
return self._reshape(self._pre_rotated_state, shape)

Expand Down Expand Up @@ -368,7 +369,9 @@ def apply_lightning(self, state, operations):

return np.reshape(state_vector, state.shape)

# pylint: disable=unused-argument
def apply(self, operations, rotations=None, **kwargs):
"""Applies operations to the state vector."""
# State preparation is currently done in Python
if operations: # make sure operations[0] exists
if isinstance(operations[0], StatePrep):
Expand Down Expand Up @@ -629,6 +632,7 @@ def _init_process_jacobian_tape(self, tape, starting_state, use_device_state):
return StateVectorC64(ket) if self.use_csingle else StateVectorC128(ket)

def adjoint_jacobian(self, tape, starting_state=None, use_device_state=False):
"""Computes and returns the Jacobian with the adjoint method."""
if self.shots is not None:
warn(
"Requested adjoint differentiation to be computed with finite shots. "
Expand Down Expand Up @@ -812,7 +816,7 @@ def processing_fn_state(tape):
else:

class LightningQubit(LightningBaseFallBack): # pragma: no cover
# pylint: disable=missing-class-docstring
# pylint: disable=missing-class-docstring, too-few-public-methods
name = "Lightning qubit PennyLane plugin [No binaries found - Fallback: default.qubit]"
short_name = "lightning.qubit"

Expand Down
45 changes: 20 additions & 25 deletions tests/test_comparison.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,24 +73,23 @@ def test_one_qubit_circuit(

monkeypatch.setenv("OMP_NUM_THREADS", str(num_threads))

def circuit():
def circuit(measurement):
"""A combination of the one_qubit_block and a simple PauliZ measurement applied to a
basis state"""
qml.BasisState(np.array(basis_state), wires=0)
one_qubit_block(wires=0)
return qml.expval(qml.PauliZ(0))
return measurement() if callable(measurement) else measurement

dev_l = lightning_dev_version(wires)
dev_d = default_qubit_dev(wires)

lightning = qml.QNode(circuit, dev_l)
default = qml.QNode(circuit, dev_d)

lightning()
lightning(qml.expval(qml.PauliZ(0)))
lightning_state = dev_l.state

default()
default_state = dev_d.state
default_state = default(qml.state)

assert np.allclose(lightning_state, default_state)
assert os.getenv("OMP_NUM_THREADS") == str(num_threads)
Expand All @@ -108,7 +107,7 @@ def test_two_qubit_circuit(
"""Test a two-qubit circuit"""
monkeypatch.setenv("OMP_NUM_THREADS", str(num_threads))

def circuit():
def circuit(measurement):
"""A combination of two qubit gates with the one_qubit_block and a simple PauliZ
measurement applied to an input basis state"""
qml.BasisState(np.array(basis_state), wires=[0, 1])
Expand All @@ -123,19 +122,18 @@ def circuit():
one_qubit_block(wires=1)
qml.CRZ(0.02, wires=[0, 1])
qml.CRot(0.2, 0.3, 0.7, wires=[0, 1])
return qml.expval(qml.PauliZ(0))
return measurement() if callable(measurement) else measurement

dev_l = lightning_dev_version(wires)
dev_d = default_qubit_dev(wires)

lightning = qml.QNode(circuit, dev_l)
default = qml.QNode(circuit, dev_d)

lightning()
lightning(qml.expval(qml.PauliZ(0)))
lightning_state = dev_l.state

default()
default_state = dev_d.state
default_state = default(qml.state)

assert np.allclose(lightning_state, default_state)

Expand All @@ -152,7 +150,7 @@ def test_three_qubit_circuit(
"""Test a three-qubit circuit"""
monkeypatch.setenv("OMP_NUM_THREADS", str(num_threads))

def circuit():
def circuit(measurement):
"""A combination of two and three qubit gates with the one_qubit_block and a simple
PauliZ measurement applied to an input basis state"""
qml.BasisState(np.array(basis_state), wires=[0, 1, 2])
Expand All @@ -175,19 +173,18 @@ def circuit():
qml.CRot(0.2, 0.3, 0.7, wires=[2, 1])
qml.RZ(0.4, wires=0)
qml.Toffoli(wires=[2, 1, 0])
return qml.expval(qml.PauliZ(0))
return measurement() if callable(measurement) else measurement

dev_l = lightning_dev_version(wires)
dev_d = default_qubit_dev(wires)

lightning = qml.QNode(circuit, dev_l)
default = qml.QNode(circuit, dev_d)

lightning()
lightning(qml.expval(qml.PauliZ(0)))
lightning_state = dev_l.state

default()
default_state = dev_d.state
default_state = default(qml.state)

assert np.allclose(lightning_state, default_state)

Expand All @@ -204,7 +201,7 @@ def test_four_qubit_circuit(
"""Test a four-qubit circuit"""
monkeypatch.setenv("OMP_NUM_THREADS", str(num_threads))

def circuit():
def circuit(measurement):
"""A combination of two and three qubit gates with the one_qubit_block and a simple
PauliZ measurement, all acting on a four qubit input basis state"""
qml.BasisState(np.array(basis_state), wires=[0, 1, 2, 3])
Expand Down Expand Up @@ -232,19 +229,18 @@ def circuit():
qml.CRot(0.2, 0.3, 0.7, wires=[2, 1])
qml.RZ(0.4, wires=0)
qml.Toffoli(wires=[2, 1, 0])
return qml.expval(qml.PauliZ(0))
return measurement() if callable(measurement) else measurement

dev_l = lightning_dev_version(wires)
dev_d = default_qubit_dev(wires)

lightning = qml.QNode(circuit, dev_l)
default = qml.QNode(circuit, dev_d)

lightning()
lightning(qml.expval(qml.PauliZ(0)))
lightning_state = dev_l.state

default()
default_state = dev_d.state
default_state = default(qml.state)

assert np.allclose(lightning_state, default_state)

Expand All @@ -265,23 +261,22 @@ def test_n_qubit_circuit(
shape = qml.StronglyEntanglingLayers.shape(2, wires)
w = np.random.uniform(high=2 * np.pi, size=shape)

def circuit():
def circuit(measurement):
"""Prepares the equal superposition state and then applies StronglyEntanglingLayers
and concludes with a simple PauliZ measurement"""
stateprep(vec, wires=range(wires))
qml.StronglyEntanglingLayers(w, wires=range(wires))
return qml.expval(qml.PauliZ(0))
return measurement() if callable(measurement) else measurement

dev_l = lightning_dev_version(wires)
dev_d = default_qubit_dev(wires)

lightning = qml.QNode(circuit, dev_l)
default = qml.QNode(circuit, dev_d)

lightning()
lightning(qml.expval(qml.PauliZ(0)))
lightning_state = dev_l.state

default()
default_state = dev_d.state
default_state = default(qml.state)

assert np.allclose(lightning_state, default_state)