From a9e4087b67c5196867f33e0052074aed839094e4 Mon Sep 17 00:00:00 2001 From: Rashid N H M <95639609+rashidnhm@users.noreply.github.com> Date: Tue, 1 Aug 2023 17:12:31 -0400 Subject: [PATCH 1/3] [sc-36527]: Add new robots.txt to doc build to hide latest build from search engine (#4423) --- doc/conf.py | 2 +- doc/robots.txt | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 doc/robots.txt diff --git a/doc/conf.py b/doc/conf.py index 153ca25757a..ac11bfacb8e 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -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. diff --git a/doc/robots.txt b/doc/robots.txt new file mode 100644 index 00000000000..bdf20e33b80 --- /dev/null +++ b/doc/robots.txt @@ -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 From 1baad70ecbf2e632de93af43597109c6af095276 Mon Sep 17 00:00:00 2001 From: BorjaRequena <59647767+BorjaRequena@users.noreply.github.com> Date: Wed, 2 Aug 2023 06:18:42 +0200 Subject: [PATCH 2/3] Adding a `wire_order` kwarg to `Tensor.sparse_matrix()` (#4424) * `wire_order` kwarg for `Tensor.sparse_matrix()` * update changelog * swap kwarg order Co-authored-by: Matthew Silverman * adapt docstring to the kwarg order swap * add comment about using over --------- Co-authored-by: Matthew Silverman --- doc/releases/changelog-dev.md | 4 +++ pennylane/devices/default_qubit.py | 2 +- pennylane/operation.py | 19 ++++++++++--- tests/test_operation.py | 44 ++++++++++++++++++++++++------ 4 files changed, 55 insertions(+), 14 deletions(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 3aaf6183a07..b6c600dabd4 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -181,6 +181,10 @@ [(#4391)](https://github.com/PennyLaneAI/pennylane/pull/4391)

Bug fixes 🐛

+ +* 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) diff --git a/pennylane/devices/default_qubit.py b/pennylane/devices/default_qubit.py index ed33195fbae..0a7d739a879 100644 --- a/pennylane/devices/default_qubit.py +++ b/pennylane/devices/default_qubit.py @@ -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 = ( diff --git a/pennylane/operation.py b/pennylane/operation.py index 16920b5213c..94fba8417a2 100644 --- a/pennylane/operation.py +++ b/pennylane/operation.py @@ -2404,7 +2404,7 @@ 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. @@ -2412,10 +2412,15 @@ def sparse_matrix( 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 @@ -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 @@ -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) diff --git a/tests/test_operation.py b/tests/test_operation.py index 38941929c8c..13c93332bc9 100644 --- a/tests/test_operation.py +++ b/tests/test_operation.py @@ -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)) From 52ccdba73a5b02103b05d7827e7e09af18e3e961 Mon Sep 17 00:00:00 2001 From: Edward Jiang <34989448+eddddddy@users.noreply.github.com> Date: Wed, 2 Aug 2023 11:22:15 -0400 Subject: [PATCH 3/3] Fix `split_non_commuting` when tape contains both `expval` and `var` measurements (#4426) * fix split non commuting * changelog * pylint --- doc/releases/changelog-dev.md | 4 +++ pennylane/transforms/split_non_commuting.py | 4 +-- tests/transforms/test_split_non_commuting.py | 30 ++++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index b6c600dabd4..6b9301ca6b7 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -226,6 +226,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) +

Contributors ✍️

This release contains contributions from (in alphabetical order): diff --git a/pennylane/transforms/split_non_commuting.py b/pennylane/transforms/split_non_commuting.py index 463fd90a073..1e05efdde28 100644 --- a/pennylane/transforms/split_non_commuting.py +++ b/pennylane/transforms/split_non_commuting.py @@ -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, ) diff --git a/tests/transforms/test_split_non_commuting.py b/tests/transforms/test_split_non_commuting.py index fec09baa760..3cbc08fc197 100644 --- a/tests/transforms/test_split_non_commuting.py +++ b/tests/transforms/test_split_non_commuting.py @@ -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: