From 75dfecb1116c94edfc11291091e286b5eb907f16 Mon Sep 17 00:00:00 2001 From: Ali Asadi <10773383+maliasadi@users.noreply.github.com> Date: Mon, 9 Sep 2024 20:48:07 -0400 Subject: [PATCH] Add Projector Obs to LGPU (#894) ### Before submitting Please complete the following checklist when submitting a PR: - [ ] All new features must include a unit test. If you've fixed a bug or added code that should be tested, add a test to the [`tests`](../tests) directory! - [ ] All new functions and code must be clearly commented and documented. If you do make documentation changes, make sure that the docs build and render correctly by running `make docs`. - [ ] Ensure that the test suite passes, by running `make test`. - [ ] Add a new entry to the `.github/CHANGELOG.md` file, summarizing the change, and including a link back to the PR. - [ ] Ensure that code is properly formatted by running `make format`. When all the above are checked, delete everything above the dashed line and fill in the pull request template. ------------------------------------------------------------------------------------------------------------ **Context:** Add support for `Projector` observable via diagonalization to LGPU. **Description of the Change:** **Benefits:** **Possible Drawbacks:** **Related GitHub Issues:** Fixes #883 [sc-72652] --------- Co-authored-by: ringo-but-quantum --- .github/CHANGELOG.md | 3 +++ doc/lightning_gpu/device.rst | 1 + pennylane_lightning/core/_version.py | 2 +- .../lightning_gpu/lightning_gpu.py | 19 +++++++++++++++++++ tests/test_expval.py | 3 --- tests/test_var.py | 6 +----- 6 files changed, 25 insertions(+), 9 deletions(-) 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])