Implement native Array API support for LinearRegression (feat. custom NNLS solver)

### Describe the workflow you want to enable

LinearRegression is the "Hello World" of machine learning, yet it currently lacks native Array API support, forcing users to move data to the CPU for training. This creates a significant gap in scikit-learn's interoperability story with the PyTorch, JAX, and CuPy ecosystems.

Currently, the fit method in sklearn/linear_model/_base.py relies on:

scipy.optimize.nnls when positive=True.

scipy.linalg.lstsq for standard cases.

While lstsq has potential dispatch paths via SCIPY_ARRAY_API, the nnls dependency effectively blocks GPU support for constrained regression because scipy.optimize does not support Array API inputs.

This proposal aims to enable the following workflow without any CPU roundtrips:
```import torch
from sklearn.linear_model import LinearRegression

X_gpu = torch.rand(100, 10, device='cuda')
y_gpu = torch.rand(100, device='cuda')

# Should run entirely on GPU
reg = LinearRegression(positive=True)
reg.fit(X_gpu, y_gpu)
```

### Describe your proposed solution

**Implement _array_api_nnls**: Create a custom Non-Negative Least Squares solver in sklearn/utils/_array_api.py using strictly xp namespace primitives (e.g., a Projected Gradient Descent or Coordinate Descent approach). This removes the hard dependency on SciPy's Fortran implementation for the positive=True case.

**Refactor LinearRegression.fit**:

Detect Array API inputs.

Route positive=True cases to the new _array_api_nnls solver.

Route standard cases to xp.linalg.lstsq (if available) or leverage the SCIPY_ARRAY_API dispatch mechanism for lstsq, ensuring no implicit host memory transfers.

**Technical Debt Cleanup**: Refactor _preprocess_data in sklearn/linear_model/_base.py to remove the X_scale variable (marked as a "historic artifact" in the source code) to prevent unnecessary device memory allocation.

**Tagging & Testing:**

Update __sklearn_tags__ to set tags.array_api_support = True.

Add comprehensive tests in sklearn/linear_model/tests/test_base.py validating execution on PyTorch (CPU/MPS/CUDA) and CuPy devices.

### Describe alternatives you've considered, if relevant

Wait for SciPy nnls support: We could wait for scipy.optimize.nnls to support the Array API standard. However, this timeline is uncertain, and LinearRegression is too fundamental to leave unsupported.

Partial Support: We could support positive=False only (via lstsq) and raise an error for positive=True on Array API inputs. This was rejected as it creates a confusing user experience and fragment feature support.

CPU Fallback: Silently converting inputs to NumPy, fitting on CPU, and converting back. This was rejected as it defeats the primary purpose of Array API support (performance and keeping data on-device).

### Additional context

Requires implementing a numerical optimization solver restricted to the Array API surface (no NumPy fallbacks allowed).

Relevant Files: sklearn/linear_model/_base.py, sklearn/utils/_array_api.py.

Prior Art: Estimators like Ridge and LinearDiscriminantAnalysis have already received Array API support, providing a pattern for the lstsq components of this work.

COMMENT:
Thanks for opening this issue, @pankajbaid567, and for helping improve scikit-learn.

You may find the discussion in https://github.com/data-apis/array-api/issues/227 helpful, as it explains why array API support for `linalg.lstsq` was dropped.

With that said, I’m closing this issue.

Labels: Array API