Repository: PennyLaneAI/pennylane

Issue:
[BUG] `qml.probs()` (no wires) fails with `shots=None`
### Expected behavior

`qml.probs` without passing any wires (i.e., `wires=None`) works when `shots=None`.

### Actual behavior

An error is raised.

### Additional information

_No response_

### Source code

```shell
import pennylane as qml

num_wires = 3

dev = qml.device('default.qubit', wires=num_wires, shots=None)

@qml.qnode(dev)
def circuit_probs():
    return qml.probs()

circuit_probs()
```


### Tracebacks

```shell
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-11-165e18c351bf> in <module>
      9     return qml.probs()
     10 
---> 11 circuit_probs()

~/pennylane/pennylane/qnode.py in __call__(self, *args, **kwargs)
    580         self._tape_cached = using_custom_cache and self.tape.hash in cache
    581 
--> 582         res = qml.execute(
    583             [self.tape],
    584             device=self.device,

~/pennylane/pennylane/interfaces/execution.py in execute(tapes, device, gradient_fn, interface, mode, gradient_kwargs, cache, cachesize, max_diff, override_shots, expand_fn, max_expansion, device_batch_transform)
    304     if gradient_fn == "backprop" or interface is None:
    305         return batch_fn(
--> 306             qml.interfaces.cache_execute(
    307                 batch_execute, cache, return_tuple=False, expand_fn=expand_fn
    308             )(tapes)

~/pennylane/pennylane/interfaces/execution.py in wrapper(tapes, **kwargs)
    133         else:
    134             # execute all unique tapes that do not exist in the cache
--> 135             res = fn(execution_tapes.values(), **kwargs)
    136 
    137         final_res = []

~/pennylane/pennylane/interfaces/execution.py in fn(tapes, **kwargs)
     85         def fn(tapes, **kwargs):  # pylint: disable=function-redefined
     86             tapes = [expand_fn(tape) for tape in tapes]
---> 87             return original_fn(tapes, **kwargs)
     88 
     89     @wraps(fn)

~anaconda3/lib/python3.8/contextlib.py in inner(*args, **kwds)
     73         def inner(*args, **kwds):
     74             with self._recreate_cm():
---> 75                 return func(*args, **kwds)
     76         return inner
     77 

~/pennylane/pennylane/_qubit_device.py in batch_execute(self, circuits)
    366             self.reset()
    367 
--> 368             res = self.execute(circuit)
    369             results.append(res)
    370 

~/pennylane/pennylane/_qubit_device.py in execute(self, circuit, **kwargs)
    293 
    294         else:
--> 295             results = self.statistics(circuit.observables)
    296 
    297         if not circuit.is_sampled:

~/pennylane/pennylane/_qubit_device.py in statistics(self, observables, shot_range, bin_size)
    492             elif obs.return_type is Probability:
    493                 results.append(
--> 494                     self.probability(wires=obs.wires, shot_range=shot_range, bin_size=bin_size)
    495                 )
    496 

~/pennylane/pennylane/_qubit_device.py in probability(self, wires, shot_range, bin_size)
    767 
    768         if self.shots is None:
--> 769             return self.analytic_probability(wires=wires)
    770 
    771         return self.estimate_probability(wires=wires, shot_range=shot_range, bin_size=bin_size)

~/pennylane/pennylane/devices/default_qubit.py in analytic_probability(self, wires)
    805         real_state = self._real(flat_state)
    806         imag_state = self._imag(flat_state)
--> 807         prob = self.marginal_prob(real_state**2 + imag_state**2, wires)
    808         return prob

~/pennylane/pennylane/_qubit_device.py in marginal_prob(self, prob, wires)
    833         # it corresponds to the orders of the wires passed.
    834         num_wires = len(device_wires)
--> 835         basis_states = self.generate_basis_states(num_wires)
    836         basis_states = basis_states[:, np.argsort(np.argsort(device_wires))]
    837 

~/pennylane/pennylane/_qubit_device.py in generate_basis_states(num_wires, dtype)
    624         # A slower, but less memory intensive method
    625         basis_states_generator = itertools.product((0, 1), repeat=num_wires)
--> 626         return np.fromiter(itertools.chain(*basis_states_generator), dtype=int).reshape(
    627             -1, num_wires
    628         )

ValueError: cannot reshape array of size 0 into shape (0)
```


### System information

```shell
Name: PennyLane
Version: 0.23.0.dev0
Summary: PennyLane is a Python quantum machine learning library by Xanadu Inc.
Home-page: https://github.com/XanaduAI/pennylane
Author: 
Author-email: 
License: Apache License 2.0
Location: /pennylane
Requires: numpy, scipy, networkx, retworkx, autograd, toml, appdirs, semantic_version, autoray, cachetools, pennylane-lightning
Required-by: PennyLane-Orquestra, pennylane-qulacs, PennyLane-Honeywell, PennyLane-AQT, PennyLane-PQ, PennyLane-Forest, PennyLane-qsharp, PennyLane-Qchem, PennyLane-IonQ, PennyLane-Cirq, PennyLane-qiskit, amazon-braket-pennylane-plugin, PennyLane-SF, PennyLane-Lightning
Platform info:           Linux-5.13.0-39-generic-x86_64-with-glibc2.10
Python version:          3.8.5
Numpy version:           1.19.5
Scipy version:           1.7.3
Installed devices:
- orquestra.forest (PennyLane-Orquestra-0.15.0)
- orquestra.ibmq (PennyLane-Orquestra-0.15.0)
- orquestra.qiskit (PennyLane-Orquestra-0.15.0)
- orquestra.qulacs (PennyLane-Orquestra-0.15.0)
- qulacs.simulator (pennylane-qulacs-0.17.0.dev0)
- honeywell.hqs (PennyLane-Honeywell-0.16.0.dev0)
- aqt.noisy_sim (PennyLane-AQT-0.18.0)
- aqt.sim (PennyLane-AQT-0.18.0)
- projectq.classical (PennyLane-PQ-0.18.0.dev0)
- projectq.ibm (PennyLane-PQ-0.18.0.dev0)
- projectq.simulator (PennyLane-PQ-0.18.0.dev0)
- forest.numpy_wavefunction (PennyLane-Forest-0.18.0.dev0)
- forest.qvm (PennyLane-Forest-0.18.0.dev0)
- forest.wavefunction (PennyLane-Forest-0.18.0.dev0)
- microsoft.QuantumSimulator (PennyLane-qsharp-0.19.0)
- ionq.qpu (PennyLane-IonQ-0.17.0.dev0)
- ionq.simulator (PennyLane-IonQ-0.17.0.dev0)
- cirq.mixedsimulator (PennyLane-Cirq-0.22.0)
- cirq.pasqal (PennyLane-Cirq-0.22.0)
- cirq.qsim (PennyLane-Cirq-0.22.0)
- cirq.qsimh (PennyLane-Cirq-0.22.0)
- cirq.simulator (PennyLane-Cirq-0.22.0)
- qiskit.aer (PennyLane-qiskit-0.22.0.dev0)
- qiskit.basicaer (PennyLane-qiskit-0.22.0.dev0)
- qiskit.ibmq (PennyLane-qiskit-0.22.0.dev0)
- qiskit.ibmq.circuit_runner (PennyLane-qiskit-0.22.0.dev0)
- qiskit.ibmq.sampler (PennyLane-qiskit-0.22.0.dev0)
- braket.aws.qubit (amazon-braket-pennylane-plugin-1.5.7.dev0)
- braket.local.qubit (amazon-braket-pennylane-plugin-1.5.7.dev0)
- strawberryfields.fock (PennyLane-SF-0.21.0.dev0)
- strawberryfields.gaussian (PennyLane-SF-0.21.0.dev0)
- strawberryfields.gbs (PennyLane-SF-0.21.0.dev0)
- strawberryfields.remote (PennyLane-SF-0.21.0.dev0)
- strawberryfields.tf (PennyLane-SF-0.21.0.dev0)
- lightning.qubit (PennyLane-Lightning-0.23.0.dev2)
- default.gaussian (PennyLane-0.23.0.dev0)
- default.mixed (PennyLane-0.23.0.dev0)
- default.qubit (PennyLane-0.23.0.dev0)
- default.qubit.autograd (PennyLane-0.23.0.dev0)
- default.qubit.jax (PennyLane-0.23.0.dev0)
- default.qubit.tf (PennyLane-0.23.0.dev0)
- default.qubit.torch (PennyLane-0.23.0.dev0)
```


### Existing GitHub issues

- [X] I have searched existing GitHub issues to make sure the issue does not already exist.


Discussion:

Resolving patch:
diff --git a/pennylane/measurements.py b/pennylane/measurements.py
--- a/pennylane/measurements.py
+++ b/pennylane/measurements.py
@@ -133,6 +133,7 @@ def diagonalizing_gates(self):
             List[.Operation]: the operations that diagonalize the observables
         """
         try:
+            # pylint: disable=no-member
             return self.expand().operations
         except qml.operation.DecompositionUndefinedError:
             return []
@@ -517,6 +518,12 @@ def circuit():
          the computational basis
     """
     # pylint: disable=protected-access
+
+    if wires is None and op is None:
+        raise qml.QuantumFunctionError(
+            "qml.probs requires either the wires or the observable to be passed."
+        )
+
     if isinstance(op, qml.Hamiltonian):
         raise qml.QuantumFunctionError("Hamiltonians are not supported for rotating probabilities.")
 


Test patch:
diff --git a/tests/test_prob.py b/tests/test_prob.py
--- a/tests/test_prob.py
+++ b/tests/test_prob.py
@@ -437,6 +437,25 @@ def circuit():
         circuit()
 
 
+def test_probs_no_wires_obs_raises():
+    """Test that an informative error is raised when no wires or observables
+    are passed to qml.probs."""
+    num_wires = 1
+
+    dev = qml.device("default.qubit", wires=num_wires, shots=None)
+
+    @qml.qnode(dev)
+    def circuit_probs():
+        qml.RY(0.34, wires=0)
+        return qml.probs()
+
+    with pytest.raises(
+        qml.QuantumFunctionError,
+        match="qml.probs requires either the wires or the observable to be passed.",
+    ):
+        circuit_probs()
+
+
 @pytest.mark.parametrize(
     "operation", [qml.SingleExcitation, qml.SingleExcitationPlus, qml.SingleExcitationMinus]
 )