Skip to content

Commit

Permalink
Merge branch 'master' into shots-on-new-device
Browse files Browse the repository at this point in the history
  • Loading branch information
timmysilv authored Aug 2, 2023
2 parents 0bd6cf7 + 52ccdba commit 8b5f1b2
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 17 deletions.
2 changes: 1 addition & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
# html_extra_path = []
html_extra_path = ["robots.txt"]

# If not "", a "Last updated on:" timestamp is inserted at every page bottom,
# using the given strftime format.
Expand Down
8 changes: 8 additions & 0 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@
[(#4391)](https://github.com/PennyLaneAI/pennylane/pull/4391)

<h3>Bug fixes 🐛</h3>

* Allow sparse matrix calculation of `SProd`s containing a `Tensor`. When using
`Tensor.sparse_matrix()`, it is recommended to use the `wire_order` keyword argument over `wires`.
[(#4424)](https://github.com/PennyLaneAI/pennylane/pull/4424)

* Replace deprecated `jax.ad` by `jax.interpreters.ad`.
[(#4403)](https://github.com/PennyLaneAI/pennylane/pull/4403)
Expand Down Expand Up @@ -232,6 +236,10 @@
not really have a decomposition.
[(#4407)](https://github.com/PennyLaneAI/pennylane/pull/4407)

* `qml.transforms.split_non_commuting` now correctly works on tapes containing both `expval`
and `var` measurements.
[(#4426)](https://github.com/PennyLaneAI/pennylane/pull/4426)

<h3>Contributors ✍️</h3>

This release contains contributions from (in alphabetical order):
Expand Down
7 changes: 7 additions & 0 deletions doc/robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
User-agent: *

Disallow: /en/v0.30.0/ # Hidden version

Disallow: /en/latest/ # Not hidden, but disallow from search engine results

Sitemap: https://docs.pennylane.ai/sitemap.xml
2 changes: 1 addition & 1 deletion pennylane/devices/default_qubit.py
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@ def expval(self, observable, shot_range=None, bin_size=None):
# that the user provided.
for op, coeff in zip(observable.ops, observable.data):
# extract a scipy.sparse.coo_matrix representation of this Pauli word
coo = qml.operation.Tensor(op).sparse_matrix(wires=self.wires, format="coo")
coo = qml.operation.Tensor(op).sparse_matrix(wire_order=self.wires, format="coo")
Hmat = qml.math.cast(qml.math.convert_like(coo.data, self.state), self.C_DTYPE)

product = (
Expand Down
19 changes: 15 additions & 4 deletions pennylane/operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2404,18 +2404,23 @@ def check_wires_partial_overlap(self):
return 0

def sparse_matrix(
self, wires=None, format="csr"
self, wire_order=None, wires=None, format="csr"
): # pylint:disable=arguments-renamed, arguments-differ
r"""Computes, by default, a `scipy.sparse.csr_matrix` representation of this Tensor.
This is useful for larger qubit numbers, where the dense matrix becomes very large, while
consisting mostly of zero entries.
Args:
wires (Iterable): Wire labels that indicate the order of wires according to which the matrix
wire_order (Iterable): Wire labels that indicate the order of wires according to which the matrix
is constructed. If not provided, ``self.wires`` is used.
wires (Iterable): Same as ``wire_order`` to ensure compatibility with all the classes. Must only
provide one: either ``wire_order`` or ``wires``.
format: the output format for the sparse representation. All scipy sparse formats are accepted.
Raises:
ValueError: if both ``wire_order`` and ``wires`` are provided at the same time.
Returns:
:class:`scipy.sparse._csr.csr_matrix`: sparse matrix representation
Expand All @@ -2435,7 +2440,7 @@ def sparse_matrix(
If we define a custom wire ordering, the matrix representation changes
accordingly:
>>> print(t.sparse_matrix(wires=[1, 0]))
>>> print(t.sparse_matrix(wire_order=[1, 0]))
(0, 1) 1
(1, 0) 1
(2, 3) -1
Expand All @@ -2444,11 +2449,17 @@ def sparse_matrix(
We can also enforce implicit identities by passing wire labels that
are not present in the constituent operations:
>>> res = t.sparse_matrix(wires=[0, 1, 2])
>>> res = t.sparse_matrix(wire_order=[0, 1, 2])
>>> print(res.shape)
(8, 8)
"""
if wires is not None and wire_order is not None:
raise ValueError(
"Wire order has been specified twice. Provide only one of either "
"``wire_order`` or ``wires``, but not both."
)

wires = wires or wire_order
wires = self.wires if wires is None else Wires(wires)
list_of_sparse_ops = [eye(2, format="coo")] * len(wires)

Expand Down
4 changes: 2 additions & 2 deletions pennylane/transforms/split_non_commuting.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,10 @@ def circuit0(x):
if len(groups) > 1:
# make one tape per commuting group
tapes = []
for group in groups:
for group, indices in zip(groups, group_coeffs):
new_tape = tape.__class__(
tape._ops,
(m.__class__(obs=o) for m, o in zip(tape.measurements, group)),
(tape.measurements[i].__class__(obs=o) for o, i in zip(group, indices)),
tape._prep,
)

Expand Down
44 changes: 35 additions & 9 deletions tests/test_operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1856,32 +1856,58 @@ def test_sparse_matrix_swapped_wires(self):
when the custom wires swap the order."""

t = qml.PauliX(0) @ qml.PauliZ(1)
data = [1, 1, -1, -1]
indices = [1, 0, 3, 2]
indptr = [0, 1, 2, 3, 4]

s = t.sparse_matrix(wires=[1, 0])

assert np.allclose(s.data, [1, 1, -1, -1])
assert np.allclose(s.indices, [1, 0, 3, 2])
assert np.allclose(s.indptr, [0, 1, 2, 3, 4])
assert np.allclose(s.data, data)
assert np.allclose(s.indices, indices)
assert np.allclose(s.indptr, indptr)

s = t.sparse_matrix(wire_order=[1, 0])

assert np.allclose(s.data, data)
assert np.allclose(s.indices, indices)
assert np.allclose(s.indptr, indptr)

def test_sparse_matrix_extra_wire(self):
"""Tests that the correct sparse matrix representation is used
when the custom wires add an extra wire with an implied identity operation."""

t = qml.PauliX(0) @ qml.PauliZ(1)
data = [1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0]
indices = [4, 5, 6, 7, 0, 1, 2, 3]
indptr = [0, 1, 2, 3, 4, 5, 6, 7, 8]

s = t.sparse_matrix(wires=[0, 1, 2])

assert s.shape == (8, 8)
assert np.allclose(s.data, [1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0])
assert np.allclose(s.indices, [4, 5, 6, 7, 0, 1, 2, 3])
assert np.allclose(s.indptr, [0, 1, 2, 3, 4, 5, 6, 7, 8])
assert np.allclose(s.data, data)
assert np.allclose(s.indices, indices)
assert np.allclose(s.indptr, indptr)

def test_sparse_matrix_error(self):
"""Tests that an error is raised if the sparse matrix is computed for
a tensor whose constituent operations are not all single-qubit gates."""
s = t.sparse_matrix(wire_order=[0, 1, 2])

assert s.shape == (8, 8)
assert np.allclose(s.data, data)
assert np.allclose(s.indices, indices)
assert np.allclose(s.indptr, indptr)

def test_sparse_matrix_errors(self):
"""Tests that errors are raised when the sparse matrix is computed for a tensor
whose constituent operations are not all single-qubit gates, and when both ``wires``
and ``wire_order`` at specified at once."""

t = qml.PauliX(0) @ qml.Hermitian(np.eye(4), wires=[1, 2])
with pytest.raises(ValueError, match="Can only compute"):
t.sparse_matrix()

t = qml.PauliX(0) @ qml.PauliZ(1)
with pytest.raises(ValueError, match="Wire order has been specified twice"):
t.sparse_matrix(wires=[0, 1], wire_order=[0, 1])

def test_copy(self):
"""Test copying of a Tensor."""
tensor = Tensor(qml.PauliX(0), qml.PauliY(1), qml.PauliZ(2))
Expand Down
30 changes: 30 additions & 0 deletions tests/transforms/test_split_non_commuting.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,36 @@ def test_different_measurement_types(self, meas_type):
for meas in new_tape.measurements:
assert meas.return_type == the_return_type

def test_mixed_measurement_types(self):
"""Test that mixing expval and var works correctly."""

with qml.queuing.AnnotatedQueue() as q:
qml.Hadamard(0)
qml.Hadamard(1)
qml.expval(qml.PauliX(0))
qml.expval(qml.PauliZ(1))
qml.var(qml.PauliZ(0))

tape = qml.tape.QuantumScript.from_queue(q)
split, _ = split_non_commuting(tape)

assert len(split) == 2

with qml.queuing.AnnotatedQueue() as q:
qml.Hadamard(0)
qml.Hadamard(1)
qml.expval(qml.PauliX(0))
qml.var(qml.PauliZ(0))
qml.expval(qml.PauliZ(1))

tape = qml.tape.QuantumScript.from_queue(q)
split, _ = split_non_commuting(tape)

assert len(split) == 2
assert qml.equal(split[0].measurements[0], qml.expval(qml.PauliX(0)))
assert qml.equal(split[0].measurements[1], qml.expval(qml.PauliZ(1)))
assert qml.equal(split[1].measurements[0], qml.var(qml.PauliZ(0)))

def test_raise_not_supported(self):
"""Test that NotImplementedError is raised when probabilities or samples are called"""
with qml.queuing.AnnotatedQueue() as q:
Expand Down

0 comments on commit 8b5f1b2

Please sign in to comment.