Repository: qutip/qutip

Issue:
Groundstate bug in QuTiP 5
### Bug Description

The `groundstate()` function seems to have a bug in the current version of `dev.major`. 
When calling `groundstate()` on any Qobj, an error occurs. 

### Code to Reproduce the Bug

```shell
from qutip import destroy
H = destroy(2).dag() * destroy(2)
H.groundstate()
```


### Code Output

```shell
File "/home/chris/git/qutip-dev-major/qutip/core/qobj.py", line 1632, in groundstate
    grndvec = Qobj(evecs[0], dims=new_dims)
TypeError: 'qutip.core.data.dense.Dense' object is not subscriptable
```


### Expected Behaviour

The groundstate of H should be returned.

### Your Environment

```shell
QuTiP Version:      5.0.0.dev0+98a8e7d
Numpy Version:      1.22.4
Scipy Version:      1.8.1
Cython Version:     0.29.30
Matplotlib Version: 3.5.2
Python Version:     3.8.10
Number of CPUs:     4
BLAS Info:          OPENBLAS
INTEL MKL Ext:      False
Platform Info:      Linux (x86_64)
```


### Additional Context

_No response_


Discussion:

Resolving patch:
diff --git a/qutip/core/qobj.py b/qutip/core/qobj.py
--- a/qutip/core/qobj.py
+++ b/qutip/core/qobj.py
@@ -9,6 +9,7 @@
 
 import functools
 import numbers
+import warnings
 
 import numpy as np
 import scipy.sparse
@@ -1615,29 +1616,16 @@ def groundstate(self, sparse=False, tol=0, maxiter=100000, safe=True):
         Use sparse only if memory requirements demand it.
         """
         eigvals = 2 if safe else 1
-        if isinstance(self.data, _data.CSR) and sparse:
-            evals, evecs = _data.eigs_csr(self.data,
-                                          isherm=self._isherm,
-                                          eigvals=eigvals, tol=tol,
-                                          maxiter=maxiter)
-        elif isinstance(self.data, _data.CSR):
-            evals, evecs = _data.eigs(_data.to(_data.Dense, self.data),
-                                      isherm=self._isherm,
-                                      eigvals=eigvals)
-        else:
-            evals, evecs = _data.eigs(self.data,
-                                      isherm=self._isherm,
-                                      eigvals=eigvals)
+        evals, evecs = self.eigenstates(sparse=sparse, eigvals=eigvals,
+                                        tol=tol, maxiter=maxiter)
 
         if safe:
             tol = tol or settings.core['atol']
-            if (evals[1]-evals[0]) <= 10*tol:
-                print("WARNING: Ground state may be degenerate. "
-                        "Use Q.eigenstates()")
-        new_dims = [self.dims[0], [1] * len(self.dims[0])]
-        grndvec = Qobj(evecs[0], dims=new_dims)
-        grndvec = grndvec / grndvec.norm()
-        return evals[0], grndvec
+            # This tol should be less strick than the tol for the eigensolver
+            # so it's numerical errors are not seens as degenerate states.
+            if (evals[1]-evals[0]) <= 10 * tol:
+                warnings.warn("Ground state may be degenerate.", UserWarning)
+        return evals[0], evecs[0]
 
     def dnorm(self, B=None):
         """Calculates the diamond norm, or the diamond distance to another


Test patch:
diff --git a/qutip/tests/core/test_qobj.py b/qutip/tests/core/test_qobj.py
--- a/qutip/tests/core/test_qobj.py
+++ b/qutip/tests/core/test_qobj.py
@@ -1110,3 +1110,17 @@ def test_sum_buildin(shape):
     data = np.random.rand(*shape) + 1j*np.random.rand(*shape)
     qobj = qutip.Qobj(data)
     assert sum([qobj, 2 * qobj, -qobj]) == 2 * qobj
+
+
+def test_groundstate():
+    eigenvals = np.sort(np.random.rand(10))
+    eigenvals[1:] += 0.1  # Ensure no degenerate groundstate
+    qobj = qutip.rand_herm(eigenvals)
+    groundenergy, groundstate = qobj.groundstate()
+    assert groundenergy == pytest.approx(eigenvals[0])
+    assert qutip.expect(qobj, groundstate) == pytest.approx(eigenvals[0])
+    assert groundstate.overlap(groundstate) == pytest.approx(1)
+
+    with pytest.warns(UserWarning) as warning:
+        qutip.qeye(5).groundstate()
+    assert "degenerate" in warning[0].message.args[0]