diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 1ac970e098..317d06f9d6 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -2,6 +2,9 @@ ### New features since last release +* Add `Projector` observable support via diagonalization to Lightning-GPU. + [(#894)](https://github.com/PennyLaneAI/pennylane-lightning/pull/894) + * Add 1-target wire controlled gate support to `lightning.tensor`. Note that `cutensornet` only supports 1-target wire controlled gate as of `v24.08`. A controlled gate with more than 1 target wire should be converted to dense matrix. [(#880)](https://github.com/PennyLaneAI/pennylane-lightning/pull/880) diff --git a/doc/lightning_gpu/device.rst b/doc/lightning_gpu/device.rst index 4623d911b9..a5162c7579 100644 --- a/doc/lightning_gpu/device.rst +++ b/doc/lightning_gpu/device.rst @@ -104,6 +104,7 @@ Supported operations and observables ~pennylane.PauliX ~pennylane.PauliY ~pennylane.PauliZ + ~pennylane.Projector ~pennylane.Hermitian ~pennylane.Hamiltonian ~pennylane.SparseHamiltonian diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index bc9075b0d4..7a3513576d 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.39.0-dev10" +__version__ = "0.39.0-dev11" diff --git a/pennylane_lightning/lightning_gpu/lightning_gpu.py b/pennylane_lightning/lightning_gpu/lightning_gpu.py index 0e55c5c9e1..f99dc6f095 100644 --- a/pennylane_lightning/lightning_gpu/lightning_gpu.py +++ b/pennylane_lightning/lightning_gpu/lightning_gpu.py @@ -187,6 +187,7 @@ def _mebibytesToBytes(mebibytes): "LinearCombination", "Hermitian", "Identity", + "Projector", "Sum", "Prod", "SProd", @@ -804,6 +805,15 @@ def expval(self, observable, shot_range=None, bin_size=None): Returns: Expectation value of the observable """ + if isinstance(observable, qml.Projector): + diagonalizing_gates = observable.diagonalizing_gates() + if self.shots is None and diagonalizing_gates: + self.apply(diagonalizing_gates) + results = super().expval(observable, shot_range=shot_range, bin_size=bin_size) + if self.shots is None and diagonalizing_gates: + self.apply([qml.adjoint(g, lazy=False) for g in reversed(diagonalizing_gates)]) + return results + if self.shots is not None: # estimate the expectation value samples = self.sample(observable, shot_range=shot_range, bin_size=bin_size) @@ -887,6 +897,15 @@ def var(self, observable, shot_range=None, bin_size=None): Returns: Variance of the observable """ + if isinstance(observable, qml.Projector): + diagonalizing_gates = observable.diagonalizing_gates() + if self.shots is None and diagonalizing_gates: + self.apply(diagonalizing_gates) + results = super().var(observable, shot_range=shot_range, bin_size=bin_size) + if self.shots is None and diagonalizing_gates: + self.apply([qml.adjoint(g, lazy=False) for g in reversed(diagonalizing_gates)]) + return results + if self.shots is not None: # estimate the var # Lightning doesn't support sampling yet diff --git a/tests/test_expval.py b/tests/test_expval.py index b07ac4a8e2..617ed4e07a 100644 --- a/tests/test_expval.py +++ b/tests/test_expval.py @@ -106,9 +106,6 @@ def test_projector_expectation(self, theta, phi, qubit_device, tol): dev_def = qml.device("default.qubit", wires=n_qubits) dev = qubit_device(wires=n_qubits) - if "Projector" not in dev.observables: - pytest.skip("Device does not support the Projector observable.") - init_state = np.random.rand(2**n_qubits) + 1j * np.random.rand(2**n_qubits) init_state /= np.linalg.norm(init_state) obs = qml.Projector(np.array([0, 1, 0, 0]) / np.sqrt(2), wires=[0, 1]) diff --git a/tests/test_var.py b/tests/test_var.py index f2675fa2dc..4b4e8561fa 100644 --- a/tests/test_var.py +++ b/tests/test_var.py @@ -50,19 +50,15 @@ def test_var(self, theta, phi, qubit_device, tol): assert np.allclose(var, expected, tol) - pytest.mark.skipif( + @pytest.mark.skipif( device_name == "lightning.tensor", reason="lightning.tensor doesn't support projector." ) - def test_projector_var(self, theta, phi, qubit_device, tol): """Test that Projector variance value is correct""" n_qubits = 2 dev_def = qml.device("default.qubit", wires=n_qubits) dev = qubit_device(wires=n_qubits) - if "Projector" not in dev.observables: - pytest.skip("Device does not support the Projector observable.") - init_state = np.random.rand(2**n_qubits) + 1j * np.random.rand(2**n_qubits) init_state /= np.linalg.norm(init_state) obs = qml.Projector(np.array([0, 1, 0, 0]) / np.sqrt(2), wires=[0, 1])