Feature request: an additional config context for forcing conversion toward a specific Array API-compatible array library.

### Describe the workflow you want to enable

Per https://github.com/scikit-learn/scikit-learn/pull/22554 such workflow is possible:

```python
from sklearn.datasets import make_classification
from sklearn import config_context
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
import cupy.array_api as xp

X_np, y_np = make_classification(random_state=0)
X_cu = xp.asarray(X_np)
y_cu = xp.asarray(y_np)
X_cu.device


with config_context(array_api_dispatch=True):
    lda = LinearDiscriminantAnalysis()
    X_trans = lda.fit_transform(X_cu, y_cu)
```

(see https://scikit-learn.org/dev/modules/array_api.html )

I would like to additonally enable following workflow:

```python
X_np, y_np = make_classification(random_state=0)

with config_context(array_api_dispatch=True, array_api_namespace=xp):
    lda = LinearDiscriminantAnalysis()
    X_trans = lda.fit_transform(X_np, y_np)
```

where `X_trans` are the same in both examples, and in both examples compute has been done with `cupy`.  The difference is that we pass numpy inputs and rely on the estimator to convert using the appropriate array namespace.

The added value here for the user is that the `check_array` function could be used to load arbitrary inputs to the array library relying on its `xp.asarray` method, in a way that is consistent to `sklearn.utils.validation.check_array` requirements accross all array libraries.

Currently, a user that would have data formated as, let's say, a list of lists, has two choices:

- either pass the list of list directly to `lda.fit`, but then since the list of list does not have a `__array_namespace__` attribute it will fallback to numpy array namespace
- either convert first the input to `cupy`, but then, unless the user re-implements methodically `check_array` with `cupy` conversion, the behavior of the conversion might differ from what `check_array` would do when converting to `numpy`, and anyway reimplementing `check_array` is not very efficient to begin with.

**Edit:** following discussions, adding examples that highlights the added value I can see to memory management with such an interface:

```python
X_np, y_np = my_data_loader()  # let's assume that X_np, y_np are typed `np.int64`...

# as a consequence, here the cupy array are also typed `int64`
X_cu = xp.asarray(X_np)
y_cu = xp.asarray(y_np)
X_cu.device

with config_context(array_api_dispatch=True):
    lda = LinearDiscriminantAnalysis()
    # But `lda` requires `floats` to work
    # (see https://github.com/scikit-learn/scikit-learn/blob/f3f51f9b6/sklearn/discriminant_analysis.py#L551)
    # so implicitly, an additional copy of X_cu and y_cu in `float` is going to be created
    # resulting on twice the memory allocation for data.
    X_trans = lda.fit_transform(X_cu, y_cu)
```

for the unwary user, this workflow is safer:

```python
X_np, y_np = my_data_loader()  # let's assume that X_np, y_np are typed `np.int64`...

with config_context(array_api_dispatch=True, array_api_namespace=xp):
    lda = LinearDiscriminantAnalysis()
    # The X_np, y_np are yet again implicitly transformed as cupy float arrays. But
    # the intermediate copy that the users created is skipped (or at worst,
    # deleted right after creation), resulting in better memory management.
    X_trans = lda.fit_transform(X_np, y_np)
``` 

### Describe your proposed solution

The `get_namespace` function can query the `config_context` and force the use of the `__array_namespace__` that is set by the config rather than the `__array_namespace__` that attached to the input (if any). (rather simple solution)

COMMENT:
To come back to the original issue, I think we need a story to deal with the following situation:

- original data is on a CSV, parquet file or SQL database and pandas reads it as a regular dataframe;
- then the dataframe is fed to a pipeline with:
    - a preprocessing step that users a column transformer that extracts numerical features (e.g. one hot encoding, splines, whatever);
    - possibly a few transformers that maybe support the Array API spec to work on GPU and as a result would return transformed data with the matching container namespace;
    - a final supervised model like LDA that supports the Array API spec to work on GPU.

Then we want to run cross-validate / grid-search on this pipeline and want to make sure that the latest GPU-capable stages on the pipeline run on the GPU as much as possible. The grid search should be able tweak the hyperparameters if the column transformer.

Right now this is not possible because:

- a. the column transform always generates a numpy array in main memory
- b. the target variable `y_train` could be manually either a CuPy or a NumPy array. And different stages in the pipeline might not be CuPy aware.

To solve a. we could introduce a dedicated transformer in charge of converting a numpy array or a numerical-valued dataframe into an Array API container for the user requested namespace (`ArrayAPIConverter(namespace="cupy.array_api", dtype="float32")`) to insert in the pipeline just before the estimators such as `LDA` that are expected to work better with float32 cupy containers.

Solving b. is more tricky because unfortunately our pipeline / transformer API does not allow transforming `y_train` (unless we rely on meta-estimators).

What @fcharras proposes could potentially be a solution to tackle both a. and b.

Alternatively, we might add a new constructor argument or public setter method on estimators capable of Array API spec support to request them to handle the conversion of any compatible inputs to a give namespace.

I do not have a converged yet to express my opinion on this matter. But I just recognize that the current state of things is not satisfying.

Labels: New Feature, Needs Decision - Include Feature, Array API