ElasticNetCV alpha_grid does not take positive constraint into account

### Describe the bug

`ElasticNetCV` internally computes `alpha_max` that is just big enough to push all coef to 0.

The existing code (in `_alpha_grid()`) does not take `positive` as an input, and is only correct when `positive=False`.

As a result, `ElasticNetCV(positive=True)` may use the wrong `alpha_max` to define the `alphas_` search grid. Sometimes all the alphas in the search grid will push all coef to 0 💥

This is an easy fix. I'll try to submit a PR...

### Steps/Code to Reproduce

```python
import numpy as np
from sklearn.linear_model import ElasticNetCV, ElasticNet

X = np.array([[1, 1], [1, -1]])
beta = np.array([1, -1e3])
y = X @ beta

config = dict(fit_intercept=False, positive=True)

alpha_grid = ElasticNetCV(cv=2, **config).fit(X, y).alphas_

alpha_max = alpha_grid[0]
alpha_min = alpha_grid[-1]
print(f"{alpha_max=}, {alpha_min=}")

assert ElasticNet(alpha=alpha_max, **config).fit(X, y).coef_.max() == 0  # OK
assert ElasticNet(alpha=alpha_min, **config).fit(X, y).coef_.max() > 0, f"{alpha_min=} still gives all zero coef"
```

### Expected Results

```python
alpha_max=np.float64(2.0), alpha_min=np.float64(0.002)
```

### Actual Results

```python
alpha_max=np.float64(2000.0), alpha_min=np.float64(2.0)
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
Cell In[1], line 17
     14 print(f"{alpha_max=}, {alpha_min=}")
     16 assert ElasticNet(alpha=alpha_max, **config).fit(X, y).coef_.max() == 0  # OK
---> 17 assert ElasticNet(alpha=alpha_min, **config).fit(X, y).coef_.max() > 0, f"{alpha_min=} still gives all zero coef"

AssertionError: alpha_min=np.float64(2.0) still gives all zero coef
```

### Versions

```shell
System:
    python: 3.12.11 | packaged by conda-forge | (main, Jun  4 2025, 14:45:31) [GCC 13.3.0]
executable: /home/jli/miniforge3/envs/torch/bin/python3
   machine: Linux-6.6.87.2-microsoft-standard-WSL2-x86_64-with-glibc2.39

Python dependencies:
      sklearn: 1.7.2
          pip: 25.1.1
   setuptools: 80.9.0
        numpy: 2.3.5
        scipy: 1.16.3
       Cython: None
       pandas: 2.3.0
   matplotlib: 3.10.3
       joblib: 1.5.2
threadpoolctl: 3.6.0

Built with OpenMP: True

threadpoolctl info:
       user_api: blas
   internal_api: openblas
    num_threads: 12
         prefix: libscipy_openblas
       filepath: /home/jli/miniforge3/envs/torch/lib/python3.12/site-packages/numpy.libs/libscipy_openblas64_-fdde5778.so
        version: 0.3.30
threading_layer: pthreads
   architecture: Haswell

       user_api: blas
   internal_api: openblas
    num_threads: 12
         prefix: libscipy_openblas
       filepath: /home/jli/miniforge3/envs/torch/lib/python3.12/site-packages/scipy.libs/libscipy_openblas-b75cc656.so
        version: 0.3.29.dev
threading_layer: pthreads
   architecture: Haswell

       user_api: openmp
   internal_api: openmp
    num_threads: 12
         prefix: libgomp
       filepath: /home/jli/miniforge3/envs/torch/lib/python3.12/site-packages/scikit_learn.libs/libgomp-a34b3233.so.1.0.0
        version: None
```

COMMENT:
The intuition is that the additional w>=0 constraint changes the subgradient of abs(w) at w=0 from [-1, 1] to (-inf, 1]. This is by definition: the subgradient k of abs(w) at w=0 satisfies: kw <= abs(w) for any w>=0, so k<=1.

Then the same argument applies: (X.T @ y)_j in (-inf, \alpha].

Thus alpha_max = max(np.maximum(0, X.T @ y)).

The existing` _alpha_grid` code handles multi-target ElasticNet with sample weight, but the same logic applies. I had Gemini free tier prove the generic multi-target ElasticNet case (any decent 2025 LLM should be able to do it, so I’ll not paste the proof here).

Labels: Bug, module:linear_model