Repository: PennyLaneAI/pennylane
Pull request #1522 (2021-08-12 16:13:48)

V0.17.0 deprecations
* Deprecate the tape methods `get_resources` and `get_depth` as they are superseded by specs;
* Fully deprecate `qml.sample` when `shots=None`: raise an error in `QubitDevice` instead of a warning

github-actions[bot]: Hello. You may have forgotten to update the changelog!
Please edit [.github/CHANGELOG.md](/PennyLaneAI/pennylane/blob/master/.github/CHANGELOG.md) with:
* A one-to-two sentence description of the change. You may include a small working example for new features.
* A link back to this PR.
* Your name (or GitHub username) in the contributors section.
antalszava: [ch8061]
codecov[bot]: # [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1522?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
> Merging [#1522](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1522?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (4e5c1b9) into [master](https://codecov.io/gh/PennyLaneAI/pennylane/commit/5c0adbc0960d65094708bd41b6ce9d0ec514496f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (5c0adbc) will **decrease** coverage by `0.00%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1522/graphs/tree.svg?width=650&height=150&src=pr&token=9S3YzlUgGk&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1522?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)

```diff
@@            Coverage Diff             @@
##           master    #1522      +/-   ##
==========================================
- Coverage   98.21%   98.21%   -0.01%     
==========================================
  Files         184      184              
  Lines       13218    13209       -9     
==========================================
- Hits        12982    12973       -9     
  Misses        236      236              
```


| [Impacted Files](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1522?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) | Coverage Δ | |
|---|---|---|
| [pennylane/tape/tape.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1522/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL3RhcGUvdGFwZS5weQ==) | `98.56% <ø> (-0.04%)` | :arrow_down: |
| [pennylane/\_qubit\_device.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1522/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL19xdWJpdF9kZXZpY2UucHk=) | `98.64% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1522?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1522?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Last update [5c0adbc...4e5c1b9](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1522?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).

antalszava: Thank you Josh!

1. Indeed, two tests were using `qml.sample` without specifying the number of shots, adjusted these.
2. Good question!

Changed to have `master` as the base, so that we can make sure we're good by running CI.

Manually the following demos seem to be the ones with `qml.sample`, all of which also specify the number of shots:

* `tutorial_vqls.py`
```python
    dev_x = qml.device("default.qubit", wires=n_qubits, shots=n_shots)

    @qml.qnode(dev_x)
    def prepare_and_sample(weights):

        # Variational circuit generating a guess for the solution vector |x>
        variational_block(weights)

        # We assume that the system is measured in the computational basis.
        # If we label each basis state with a decimal integer j = 0, 1, ... 2 ** n_qubits - 1,
        # this is equivalent to a measurement of the following diagonal observable.
        basis_obs = qml.Hermitian(np.diag(range(2 ** n_qubits)), wires=range(n_qubits))

        return qml.sample(basis_obs)
```
* `tutorial_jax_transformations.py`
```python
        dev = qml.device("default.qubit.jax", wires=2, shots=10, prng_key=key)

        # Now we can create our qnode within the circuit function.
        @qml.qnode(dev, interface="jax")
        def my_circuit():
            qml.RX(param, wires=0)
            qml.CNOT(wires=[0, 1])
            return qml.sample(qml.PauliZ(0))
```
* `tutorial_qaoa_maxcut.py`
```python
    dev = qml.device("default.qubit", wires=n_wires, shots=1)

    @qml.qnode(dev)
    def circuit(gammas, betas, edge=None, n_layers=1):
        # apply Hadamards to get the n qubit |+> state
        for wire in range(n_wires):
            qml.Hadamard(wires=wire)
        # p instances of unitary operators
        for i in range(n_layers):
            U_C(gammas[i])
            U_B(betas[i])
        if edge is None:
            # measurement phase
            return qml.sample(comp_basis_measurement(range(n_wires)))
```
* `qsim_beyond_classical.py`
```python
dev = qml.device('cirq.qsim', wires=wires, qubits=qubits, shots=shots)

@qml.qnode(dev)
def circuit(seed=42, return_probs=False):
    np.random.seed(seed)
    gate_idx = generate_single_qubit_gate_list()

    # m full cycles - single-qubit gates & two-qubit gate
    for i, gs in enumerate(gate_sequence):
        for w in range(wires):
            single_qubit_gates[gate_idx[i][w]](wires=w)

        for qb_1, qb_2 in gate_order[gs]:
            ops.ISWAP(wires=(qb_1, qb_2))
            ops.CPhase(-np.pi/6, wires=(qb_1, qb_2))

    # one half-cycle - single-qubit gates only
    for w in range(wires):
        single_qubit_gates[gate_idx[-1][w]](wires=w)

    if return_probs:
        return qml.probs(wires=range(wires))
    else:
        return [qml.sample(qml.PauliZ(i)) for i in range(wires)]

dev_x = qml.device("default.qubit", wires=n_qubits, shots=n_shots)
```
* `tutorial_coherent_vqls.py`
```python
dev_x = qml.device("default.qubit", wires=n_qubits, shots=n_shots)

@qml.qnode(dev_x)
def prepare_and_sample(weights):

    # Variational circuit generating a guess for the solution vector |x>
    variational_block(weights)

    # We assume that the system is measured in the computational basis.
    # If we label each basis state with a decimal integer j = 0, 1, ... 2 ** n_qubits - 1,
    # this is equivalent to a measurement of the following diagonal observable.
    basis_obs = qml.Hermitian(np.diag(range(2 ** n_qubits)), wires=range(n_qubits))

    return qml.sample(basis_obs)
```
antalszava: Thank you for the review @thisac! My plan would be to merge first into `master` and check that the demos are passing well in the `qml` repo just for extra safety. Then will create an identical PR to add this into the rc branch.

Patch:
diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md
index c5bf87e98..3ecfa9c79 100644
--- a/.github/CHANGELOG.md
+++ b/.github/CHANGELOG.md
@@ -627,6 +627,15 @@ Maria Schuld.
   
 <h3>Breaking changes</h3>
 
+* Removed the deprecated tape methods `get_resources` and `get_depth` as they are
+  superseded by the `specs` tape attribute.
+  [(#1522)](https://github.com/PennyLaneAI/pennylane/pull/1522)
+
+* Specifying `shots=None` with `qml.sample` was previously deprecated.
+  From this release onwards, setting `shots=None` when sampling will
+  raise an error.
+  [(#1522)](https://github.com/PennyLaneAI/pennylane/pull/1522)
+
 * The existing `pennylane.collections.apply` function is no longer accessible
   via `qml.apply`, and needs to be imported directly from the ``collections``
   package.
diff --git a/Makefile b/Makefile
index 3e842efb1..4bfb182ef 100644
--- a/Makefile
+++ b/Makefile
@@ -12,6 +12,7 @@ help:
 	@echo "  wheel              to build the PennyLane wheel"
 	@echo "  dist               to package the source distribution"
 	@echo "  clean              to delete all temporary, cache, and build files"
+	@echo "  docs               to build the PennyLane documentation"
 	@echo "  clean-docs         to delete all built documentation"
 	@echo "  test               to run the test suite"
 	@echo "  coverage           to generate a coverage report"
diff --git a/pennylane/_qubit_device.py b/pennylane/_qubit_device.py
index 5561d9cca..48b328a6f 100644
--- a/pennylane/_qubit_device.py
+++ b/pennylane/_qubit_device.py
@@ -489,14 +489,13 @@ class QubitDevice(Device):
             array[int]: the sampled basis states
         """
         if self.shots is None:
-            warnings.warn(
+
+            raise qml.QuantumFunctionError(
                 "The number of shots has to be explicitly set on the device "
-                "when using sample-based measurements. Since no shots are specified, "
-                "a default of 1000 shots is used.",
-                UserWarning,
+                "when using sample-based measurements."
             )
 
-        shots = self.shots or 1000
+        shots = self.shots
 
         basis_states = np.arange(number_of_states)
         return np.random.choice(basis_states, shots, p=state_probability)
diff --git a/pennylane/tape/tape.py b/pennylane/tape/tape.py
index 6d6a82d1e..50593afbb 100644
--- a/pennylane/tape/tape.py
+++ b/pennylane/tape/tape.py
@@ -19,7 +19,6 @@ from collections import Counter, deque, defaultdict
 import contextlib
 import copy
 from threading import RLock
-import warnings
 
 import numpy as np
 
@@ -1003,77 +1002,6 @@ class QuantumTape(AnnotatedQueue):
 
         return self._graph
 
-    def get_resources(self):
-        """Resource requirements of a quantum circuit.
-
-        Returns:
-            dict[str, int]: how many times constituent operations are applied
-
-        **Example**
-
-        .. code-block:: python3
-
-            with qml.tape.QuantumTape() as tape:
-                qml.Hadamard(wires=0)
-                qml.RZ(0.26, wires=1)
-                qml.CNOT(wires=[1, 0])
-                qml.Rot(1.8, -2.7, 0.2, wires=0)
-                qml.Hadamard(wires=1)
-                qml.CNOT(wires=[0, 1])
-                qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))
-
-        Asking for the resources produces a dictionary as shown below:
-
-        >>> tape.get_resources()
-        {'Hadamard': 2, 'RZ': 1, 'CNOT': 2, 'Rot': 1}
-
-        """
-
-        warnings.warn(
-            "``tape.get_resources``is now deprecated and will be removed in v0.17. "
-            "Please use the more general ``tape.specs`` instead.",
-            UserWarning,
-        )
-
-        return self.specs["gate_types"]
-
-    def get_depth(self):
-        """Depth of the quantum circuit.
-
-        Returns:
-            int: Circuit depth, computed as the longest path in the
-            circuit's directed acyclic graph representation.
-
-        **Example**
-
-        .. code-block:: python3
-
-            with QuantumTape() as tape:
-                qml.Hadamard(wires=0)
-                qml.PauliX(wires=1)
-                qml.CRX(2.3, wires=[0, 1])
-                qml.Rot(1.2, 3.2, 0.7, wires=[1])
-                qml.CRX(-2.3, wires=[0, 1])
-                qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))
-
-        The depth can be obtained like so:
-
-        >>> tape.get_depth()
-        4
-
-        """
-
-        warnings.warn(
-            "``tape.get_depth`` is now deprecated and will be removed in v0.17. "
-            "Please use the more general ``tape.specs`` instead.",
-            UserWarning,
-        )
-
-        if self._depth is None:
-            self._depth = self.graph.get_depth()
-
-        return self._depth
-
     @property
     def specs(self):
         """Resource information about a quantum circuit.


Test patch:
diff --git a/tests/tape/test_tape.py b/tests/tape/test_tape.py
index 632f19adb..36b1d938f 100644
--- a/tests/tape/test_tape.py
+++ b/tests/tape/test_tape.py
@@ -401,25 +401,18 @@ class TestResourceEstimation:
         """Test that empty tapes return empty resource counts."""
         tape = make_empty_tape
 
-        with pytest.warns(UserWarning, match=r"``tape.get_resources``is now deprecated"):
-            info = tape.get_resources()
-        assert len(info) == 0
-
-        with pytest.warns(UserWarning, match=r"``tape.get_depth`` is now deprecated"):
-            depth = tape.get_depth()
-        assert depth == 0
+        assert len(tape.specs["gate_types"]) == 0
+        assert tape.specs["depth"] == 0
 
     def test_resources_tape(self, make_tape):
         """Test that regular tapes return correct number of resources."""
         tape = make_tape
 
-        with pytest.warns(UserWarning, match=r"``tape.get_depth`` is now deprecated"):
-            depth = tape.get_depth()
+        depth = tape.specs["depth"]
         assert depth == 3
 
         # Verify resource counts
-        with pytest.warns(UserWarning, match=r"``tape.get_resources``is now deprecated"):
-            resources = tape.get_resources()
+        resources = tape.specs["gate_types"]
         assert len(resources) == 3
         assert resources["RX"] == 2
         assert resources["Rot"] == 1
@@ -429,12 +422,10 @@ class TestResourceEstimation:
         """Test that tapes return correct number of resources after adding to them."""
         tape = make_extendible_tape
 
-        with pytest.warns(UserWarning, match=r"``tape.get_depth`` is now deprecated"):
-            depth = tape.get_depth()
+        depth = tape.specs["depth"]
         assert depth == 3
 
-        with pytest.warns(UserWarning, match=r"``tape.get_resources``is now deprecated"):
-            resources = tape.get_resources()
+        resources = tape.specs["gate_types"]
         assert len(resources) == 3
         assert resources["RX"] == 2
         assert resources["Rot"] == 1
@@ -446,11 +437,9 @@ class TestResourceEstimation:
             qml.expval(qml.PauliX(wires="a"))
             qml.probs(wires=[0, "a"])
 
-        with pytest.warns(UserWarning, match=r"``tape.get_depth`` is now deprecated"):
-            assert tape.get_depth() == 4
+        assert tape.specs["depth"] == 4
 
-        with pytest.warns(UserWarning, match=r"``tape.get_resources``is now deprecated"):
-            resources = tape.get_resources()
+        resources = tape.specs["gate_types"]
         assert len(resources) == 4
         assert resources["RX"] == 2
         assert resources["Rot"] == 1
diff --git a/tests/templates/test_state_preparations/test_mottonen_state_prep.py b/tests/templates/test_state_preparations/test_mottonen_state_prep.py
index 22e93ad5f..8d56bda5c 100644
--- a/tests/templates/test_state_preparations/test_mottonen_state_prep.py
+++ b/tests/templates/test_state_preparations/test_mottonen_state_prep.py
@@ -252,7 +252,7 @@ class TestDecomposition:
         # when the RZ cascade is skipped, CNOT gates should only be those required for RY cascade
         circuit(state_vector)
 
-        assert circuit.qtape.get_resources()["CNOT"] == n_CNOT
+        assert circuit.qtape.specs["gate_types"]["CNOT"] == n_CNOT
 
     def test_custom_wire_labels(self, tol):
         """Test that template can deal with non-numeric, nonconsecutive wire labels."""
diff --git a/tests/test_measure.py b/tests/test_measure.py
index 9cf6d7190..46d433668 100644
--- a/tests/test_measure.py
+++ b/tests/test_measure.py
@@ -273,7 +273,7 @@ class TestSample:
 
     def test_providing_no_observable_and_no_wires(self):
         """Test that we can provide no observable and no wires to sample function"""
-        dev = qml.device("default.qubit", wires=2)
+        dev = qml.device("default.qubit", wires=2, shots=1000)
 
         @qml.qnode(dev)
         def circuit():
@@ -289,7 +289,7 @@ class TestSample:
         """Test that we can provide no observable but specify wires to the sample function"""
         wires = [0, 2]
         wires_obj = qml.wires.Wires(wires)
-        dev = qml.device("default.qubit", wires=3)
+        dev = qml.device("default.qubit", wires=3, shots=1000)
 
         @qml.qnode(dev)
         def circuit():
diff --git a/tests/test_qubit_device.py b/tests/test_qubit_device.py
index fcaf8d614..854103d67 100644
--- a/tests/test_qubit_device.py
+++ b/tests/test_qubit_device.py
@@ -413,8 +413,9 @@ class TestSampleBasisStates:
         dev.shots = None
         state_probs = [0.1, 0.2, 0.3, 0.4]
 
-        with pytest.warns(
-            UserWarning, match="The number of shots has to be explicitly set on the device"
+        with pytest.raises(
+            qml.QuantumFunctionError,
+            match="The number of shots has to be explicitly set on the device",
         ):
             dev.sample_basis_states(number_of_states, state_probs)