Skip to content

Commit

Permalink
Add Projector Obs to LGPU (#894)
Browse files Browse the repository at this point in the history
### 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 <[email protected]>
  • Loading branch information
maliasadi and ringo-but-quantum authored Sep 10, 2024
1 parent a36fe22 commit 75dfecb
Show file tree
Hide file tree
Showing 6 changed files with 25 additions and 9 deletions.
3 changes: 3 additions & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
1 change: 1 addition & 0 deletions doc/lightning_gpu/device.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ Supported operations and observables
~pennylane.PauliX
~pennylane.PauliY
~pennylane.PauliZ
~pennylane.Projector
~pennylane.Hermitian
~pennylane.Hamiltonian
~pennylane.SparseHamiltonian
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.39.0-dev10"
__version__ = "0.39.0-dev11"
19 changes: 19 additions & 0 deletions pennylane_lightning/lightning_gpu/lightning_gpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ def _mebibytesToBytes(mebibytes):
"LinearCombination",
"Hermitian",
"Identity",
"Projector",
"Sum",
"Prod",
"SProd",
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
3 changes: 0 additions & 3 deletions tests/test_expval.py
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down
6 changes: 1 addition & 5 deletions tests/test_var.py
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down

0 comments on commit 75dfecb

Please sign in to comment.