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

Support Sparse Matrix representation for Observables and Arithmetic Ops (1/3) #2964

Merged
merged 17 commits into from Aug 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion doc/releases/changelog-dev.md
Expand Up @@ -112,6 +112,9 @@
>>> qml.simplify(adj_op)
RX(-1, wires=[0])
```

* Added `sparse_matrix()` support for single qubit observables
[(#2964)](https://github.com/PennyLaneAI/pennylane/pull/2964)

* Added the `qml.is_hermitian` and `qml.is_unitary` function checks.
[(#2960)](https://github.com/PennyLaneAI/pennylane/pull/2960)
Expand Down Expand Up @@ -152,6 +155,7 @@ Albert Mitjans Coma,
Rashid N H M,
Zeyue Niu,
Mudit Pandey,
Antal Száva,
Jay Soni,
Antal Száva
Cody Wang,
David Wierichs
5 changes: 5 additions & 0 deletions pennylane/ops/identity.py
Expand Up @@ -16,6 +16,7 @@
cv and qubit computing paradigms in PennyLane.
"""
import numpy as np
from scipy import sparse

from pennylane.operation import CVObservable, Operation

Expand Down Expand Up @@ -91,6 +92,10 @@ def compute_matrix(): # pylint: disable=arguments-differ
"""
return np.eye(2)

@staticmethod
def compute_sparse_matrix(*params, **hyperparams):
return sparse.csr_matrix([[1, 0], [0, 1]])

@staticmethod
def _heisenberg_rep(p):
return np.array([1, 0, 0])
Expand Down
19 changes: 19 additions & 0 deletions pennylane/ops/qubit/non_parametric_ops.py
Expand Up @@ -21,6 +21,8 @@
from copy import copy

import numpy as np

from scipy import sparse
from scipy.linalg import block_diag

import pennylane as qml
Expand Down Expand Up @@ -76,6 +78,11 @@ def compute_matrix(): # pylint: disable=arguments-differ
"""
return np.array([[INV_SQRT2, INV_SQRT2], [INV_SQRT2, -INV_SQRT2]])

@staticmethod
def compute_sparse_matrix(*params, **hyperparams):
"""Compute the sparse matrix representation"""
return sparse.csr_matrix([[INV_SQRT2, INV_SQRT2], [INV_SQRT2, -INV_SQRT2]])

@staticmethod
def compute_eigvals(): # pylint: disable=arguments-differ
r"""Eigenvalues of the operator in the computational basis (static method).
Expand Down Expand Up @@ -214,6 +221,10 @@ def compute_matrix(): # pylint: disable=arguments-differ
"""
return np.array([[0, 1], [1, 0]])

@staticmethod
def compute_sparse_matrix(*params, **hyperparams):
return sparse.csr_matrix([[0, 1], [1, 0]])

@staticmethod
def compute_eigvals(): # pylint: disable=arguments-differ
r"""Eigenvalues of the operator in the computational basis (static method).
Expand Down Expand Up @@ -358,6 +369,10 @@ def compute_matrix(): # pylint: disable=arguments-differ
"""
return np.array([[0, -1j], [1j, 0]])

@staticmethod
def compute_sparse_matrix(*params, **hyperparams):
return sparse.csr_matrix([[0, -1j], [1j, 0]])

@staticmethod
def compute_eigvals(): # pylint: disable=arguments-differ
r"""Eigenvalues of the operator in the computational basis (static method).
Expand Down Expand Up @@ -500,6 +515,10 @@ def compute_matrix(): # pylint: disable=arguments-differ
"""
return np.array([[1, 0], [0, -1]])

@staticmethod
def compute_sparse_matrix(*params, **hyperparams):
return sparse.csr_matrix([[1, 0], [0, -1]])

@staticmethod
def compute_eigvals(): # pylint: disable=arguments-differ
r"""Eigenvalues of the operator in the computational basis (static method).
Expand Down
21 changes: 16 additions & 5 deletions tests/ops/op_math/test_sprod.py
Expand Up @@ -299,18 +299,29 @@ def test_sprod_qubit_unitary(self):
true_mat = 42 * U
assert np.allclose(mat, true_mat)

# TODO[Jay]: remove xfail once there is support for sparse matrices for most operations
@pytest.mark.xfail
@pytest.mark.parametrize("scalar, op", ops)
def test_sparse_matrix(self, op, scalar):
sparse_ops = (
qml.Identity(wires=0),
qml.PauliX(wires=0),
qml.PauliY(wires=0),
qml.PauliZ(wires=0),
qml.Hadamard(wires=0),
)

@pytest.mark.parametrize("op", sparse_ops)
def test_sparse_matrix(self, op):
"""Test the sparse_matrix representation of scaled ops."""
scalar = 1 + 2j
sprod_op = SProd(scalar, op)
sparse_matrix = sprod_op.sparse_matrix()
sparse_matrix.sort_indices()

expected_sparse_matrix = scalar * op.matrix()
expected_sparse_matrix = csr_matrix(expected_sparse_matrix)
expected_sparse_matrix.sort_indices()

assert np.allclose(sparse_matrix.todense(), expected_sparse_matrix.todense())
assert type(sparse_matrix) == type(expected_sparse_matrix)
assert all(sparse_matrix.data == expected_sparse_matrix.data)
assert all(sparse_matrix.indices == expected_sparse_matrix.indices)

def test_sparse_matrix_sparse_hamiltonian(self):
"""Test the sparse_matrix representation of scaled ops."""
Expand Down
22 changes: 21 additions & 1 deletion tests/ops/qubit/test_non_parametric_ops.py
Expand Up @@ -15,13 +15,14 @@
Unit tests for the available non-parametric qubit operations
"""
import itertools
import re
import pytest
import copy
import numpy as np
from scipy.sparse import csr_matrix
from scipy.stats import unitary_group

import pennylane as qml
from pennylane import math
from pennylane.wires import Wires

from gate_data import I, X, Y, Z, H, CNOT, SWAP, ISWAP, SISWAP, CZ, S, T, CSWAP, Toffoli, ECR
Expand All @@ -42,6 +43,14 @@
(qml.ECR, ECR),
]

SPARSE_MATRIX_SUPPORTED_OPERATIONS = (
(qml.Identity(wires=0), I),
(qml.Hadamard(wires=0), H),
(qml.PauliZ(wires=0), Z),
(qml.PauliX(wires=0), X),
(qml.PauliY(wires=0), Y),
)


class TestOperations:
@pytest.mark.parametrize("op_cls, mat", NON_PARAMETRIZED_OPERATIONS)
Expand Down Expand Up @@ -1160,6 +1169,17 @@ def test_pow_independent_ops(self, op, n):
assert op.pow(n)[0].__class__ is op.__class__


class TestSparseMatrix:
@pytest.mark.parametrize("op, mat", SPARSE_MATRIX_SUPPORTED_OPERATIONS)
def test_sparse_matrix(self, op, mat):
expected_sparse_mat = csr_matrix(mat)
sparse_mat = op.sparse_matrix()

assert type(sparse_mat) == type(expected_sparse_mat)
assert all(sparse_mat.data == expected_sparse_mat.data)
assert all(sparse_mat.indices == expected_sparse_mat.indices)


label_data = [
(qml.Identity(0), "I", "I"),
(qml.Hadamard(0), "H", "H"),
Expand Down