Repository: qutip/qutip

Issue:
enr_destroy throws a ValueError in the Qobj creation step
### Bug Description

In the "return" statement of enr_destroy, we have `return [Qobj(a, dims=[dims, dims]) for a in a_ops]`. Then, when attempting to create the Qobjs in this return statement, we have the following conditional statement:

```
if (self._data.shape[0] > np.prod(np.hstack(self.dims[0])) or
           self._data.shape[1] > np.prod(np.hstack(self.dims[1]))) and \
           self.type != 'super':

            raise ValueError(f"Qobj has smaller dims {self.dims} " +
                             f"than underlying shape {self._data.shape}")
```
                            
Because np.prod assumes that the input numbers are 32 bit integers, there is a possibility for an overflow, causing the result to be negative and hence this ValueError will always be raised.

### Code to Reproduce the Bug

```shell
from qutip import enr_destroy

enr_destroy([12]*12,2)
```


### Code Output

```shell
ValueError: Qobj has smaller dims [[12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12], [12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12]] than underlying shape (91, 91)
```


### Expected Behaviour

enr_destroy would work

### Your Environment

```shell
QuTiP Version:      4.7.3
Numpy Version:      1.20.1
Scipy Version:      1.9.3
Cython Version:     0.29.21
Matplotlib Version: 3.6.2
Python Version:     3.8.6
Number of CPUs:     16
BLAS Info:          OPENBLAS
OPENMP Installed:   False
INTEL MKL Ext:      False
Platform Info:      Windows (AMD64)
```


### Additional Context

This can be easily fixed by doing np.prod(np.hstack(self.dims[0])**, dtype = np.int64**)  instead of what is currently being done.


Discussion:

Resolving patch:
diff --git a/qutip/qobj.py b/qutip/qobj.py
--- a/qutip/qobj.py
+++ b/qutip/qobj.py
@@ -28,6 +28,7 @@
 from qutip.fastsparse import fast_csr_matrix, fast_identity
 from qutip.cy.ptrace import _ptrace
 from qutip.permute import _permute
+from qutip.dimensions import flatten
 from qutip.sparse import (
     sp_eigs, sp_expm, sp_fro_norm, sp_max_norm, sp_one_norm, sp_L2_norm,
 )
@@ -332,10 +333,15 @@ def __init__(self, inpt=None, dims=None, shape=None,
         # use cases such as enr_fock and other enr_ functions.
         # This does leave open the possibility of data still being
         # misused such as Qobj(complex[n**2][1], dims = [[n],[n]])
-        if (self._data.shape[0] > np.prod(np.hstack(self.dims[0])) or
-           self._data.shape[1] > np.prod(np.hstack(self.dims[1]))) and \
-           self.type != 'super':
-
+        dims_size = (
+            np.prod(flatten(self.dims[0]), dtype=np.int64),
+            np.prod(flatten(self.dims[1]), dtype=np.int64)
+        )
+        shape = self._data.shape
+        if (
+            (shape[0] > dims_size[0] or shape[1] > dims_size[1])
+            and self.type != 'super'
+        ):
             raise ValueError(f"Qobj has smaller dims {self.dims} " +
                              f"than underlying shape {self._data.shape}")
 


Test patch:
diff --git a/qutip/tests/test_enr_state_operator.py b/qutip/tests/test_enr_state_operator.py
--- a/qutip/tests/test_enr_state_operator.py
+++ b/qutip/tests/test_enr_state_operator.py
@@ -113,3 +113,8 @@ def test_thermal_dm(dimensions, n_excitations, nbar_type):
     test_dm = qutip.enr_thermal_dm(dimensions, n_excitations, nbars)
     expect_dm = _reference_dm(dimensions, n_excitations, nbars)
     np.testing.assert_allclose(test_dm.full(), expect_dm.full(), atol=1e-12)
+
+
+def test_large_space():
+    # Check for #2249
+    qutip.enr_destroy([12]*12, 2)