Duality gap computation in covariance.GraphicalLasso yields negative values.

### Describe the bug

The computation of the duality gap in `_dual_gap(emp_cov, precision_, alpha)` of `GraphicalLasso` uses the definition from `Duchi et al., 2012`. 
However, their duality gap is expressed given a _feasible_ dual variable. In the implementation, it is applied to a primal variable that doesn't necessarily satisfy the dual variable's feasibility constraints.
This results in potentially negative values for the duality gap.

The primal problem reads

$$P(\Theta) = \min_\Theta \varphi(\Theta) + \nu(\Theta)$$
with
$$\varphi(\Theta) =  -\log \det (\Theta)  + \langle S, \Theta \rangle \text{ and } \nu(\Theta) =  \Vert \Lambda \odot \Theta \Vert_1$$
The Fenchel-Rockafellar dual problem is then given by

$$D(W) = \min_{W}  \varphi^{\star} (W) + \nu^{\star} (-W)$$
$$= \min_{W} -\log \det(S - W) - p \quad \text{s.t. } \quad \vert W_{ij} \vert < \lambda_{ij}$$
We can then compute the duality gap as follows:
$$G(\Theta, W) = P(\Theta) + D(W)$$
Only for a *feasible* $W$ can we actually take $\theta = (S - W)^{-1}$ and thus simplify the duality gap expression and fall back on the implemented formula:
$$G(\Theta) = \langle S, \Theta \rangle + \Vert \Lambda \odot \Theta \Vert_1 - p$$ 
@mathurinm 

### Steps/Code to Reproduce

```python
import numpy as np
from sklearn.covariance import GraphicalLasso

np.random.seed(0)
X = np.random.randn(100, 50)
emp_cov = X.T @ X / len(X)

clf = GraphicalLasso(verbose=True, tol=1e-20).fit(emp_cov)
```

### Expected Results

The expected results would be positive values for the duality gap at all iterations.

### Actual Results

Here are the duality gaps for the first 4 iterations : 
```
[graphical_lasso] Iteration   0, cost  5.24e+01, dual gap 6.745e-01
[graphical_lasso] Iteration   1, cost  5.24e+01, dual gap -3.808e-05
[graphical_lasso] Iteration   2, cost  5.24e+01, dual gap -4.379e-09
[graphical_lasso] Iteration   3, cost  5.24e+01, dual gap 5.049e-09

```

### Versions

```shell
System:
    python: 3.10.6 (main, Oct 24 2022, 16:07:47) [GCC 11.2.0]
executable: /home/cpouliquen/anaconda3/envs/these-can/bin/python3.10
   machine: Linux-5.4.0-148-generic-x86_64-with-glibc2.27

Python dependencies:
          pip: 22.2.2
   setuptools: 64.0.2
      sklearn: 1.0.2
        numpy: 1.22.3
        scipy: 1.8.1
       Cython: 0.29.33
       pandas: 1.5.1
   matplotlib: 3.5.3
       joblib: 1.2.0
threadpoolctl: 3.1.0

Built with OpenMP: True
```
```

COMMENT:
Indeed that looks like a bug to print negative values for the dual gap but since the fit loop does not stop on such negative values, it does not actually causes a user facing bug in the end, right?

@Perceptronium do you have a specific suggestion to:
- either implement the correct dual gap in the general setting (if it's not to computationally expensive), if so, please feel free to open a PR.
- alternatively cheaply detect when W is not feasible and update the print statement accordingly, and then continue without triggering the stopping criterion. The latter point we already do, but not intentionally, so the fix would only to be in what we print when `verbose > 0`.

Labels: Bug, module:covariance