BUG "array-like" in parameter validation treats sparse containers as valid inputs

### Describe the bug

In parameter validation there are many places where we use `["array-like", "sparse matrix"]` so I think at least the former should not be a superset of the latter, but it is the case now. Looking at the class `_ArrayLikes`, it treats the input as valid as long as the input has `__len__`, `shape`, or `__array__` and is not a scaler. Clearly both sparse matrices and sparse arrays satisfy this condition, though I think they should be excluded. I propose adding the constraint `not sp.issparse(array)` to `"array-like"`.

For more context please see #27950 which tries to extend parameter validation to the new sparse arrays.

<details>
<summary>Also quoting the <a href="https://scikit-learn.org/stable/glossary.html#term-array-like">glossary page</a></summary>
<p></p>

<img src="https://github.com/scikit-learn/scikit-learn/assets/108576690/d6393a0e-6ae5-4d71-b550-7b139a4edb1e" width="80%" />

</details>

### Steps/Code to Reproduce

```python
>>> from sklearn.utils._param_validation import validate_params
>>> @validate_params({"X": ["array-like"]}, prefer_skip_nested_validation=False)
... def func(X):
...     return X
...
>>> import scipy.sparse as sp
>>> func(sp.csr_array((3, 4)))
<3x4 sparse array of type '<class 'numpy.float64'>'
        with 0 stored elements in Compressed Sparse Row format>
>>> func(sp.csr_matrix((3, 4)))
<3x4 sparse matrix of type '<class 'numpy.float64'>'
        with 0 stored elements in Compressed Sparse Row format>
```

Another example can be `AgglomerativeClustering`, where the validation for `connectivity` does not include `"sparse matrix"` but tests such as `sklearn/cluster/tests/test_hierarchical.py::test_agglomerative_clustering` are passing even though `connectivity` is sparse.

### Expected Results

Both should raise `sklearn.utils._param_validation.InvalidParameterError: The 'X' parameter of func must be an array-like`.

### Actual Results

No error.

### Versions

```shell
System:
    python: 3.9.18 | packaged by conda-forge | (main, Aug 30 2023, 03:40:31) [MSC v.1929 64 bit (AMD64)]
executable: D:\Downloads\mambaforge\envs\sklearn-env\python.exe
   machine: Windows-10-10.0.19045-SP0

Python dependencies:
      sklearn: 1.5.dev0
          pip: 23.2.1
   setuptools: 68.2.2
        numpy: 1.26.0
        scipy: 1.11.2
       Cython: 3.0.2
       pandas: 2.1.4
   matplotlib: 3.8.2
       joblib: 1.3.2
threadpoolctl: 3.2.0

Built with OpenMP: True

threadpoolctl info:
       user_api: blas
   internal_api: mkl
    num_threads: 6
         prefix: libblas
       filepath: D:\Downloads\mambaforge\envs\sklearn-env\Library\bin\libblas.dll
        version: 2022.1-Product
threading_layer: intel

       user_api: openmp
   internal_api: openmp
    num_threads: 12
         prefix: vcomp
       filepath: D:\Downloads\mambaforge\envs\sklearn-env\vcomp140.dll
        version: None
```

COMMENT:
@glemaitre Thanks for your reply! I agree with you that sparse arrays should be different from sparse matrices.


I now realize that the original path I intended to follow might not be feasible, but since you may have a meeting I think putting my initial thoughts here might still be useful:

- Make array-like reject sparse containers. This would allow us to find where parameter validation does not contain the sparse matrix option but sparse matrices is actually tested, so we can fix docstrings correspondingly. This may have a side effect though. There can be classes/functions where parameter validation does not contain sparse matrix, it is not tested, but it actually works with sparse matrices and users are using it happily. We can find these cases out by looking for those that only have array-like but not sparse matrix in the parameter validation, add the sparse matrix constraint if needed, and add tests if needed.

- Restrict sparse matrix constraint to only accept sparse matrices but not sparse arrays. Create the new constraint called sparse container that accepts both. Then now all functions/classes that have been tested with sparse arrays will complain and we can change their constraints to sparse container and update docstrings correspondingly. Then it is also clear, if there exists any, which classes/functions are only supporting sparse matrices but not sparse arrays (at least not tested), then we can either add tests or fix bugs related to sparse array inputs.

- When no function/class is using the sparse matrix constraint we can remove it and only use the sparse container constraint for the future. Nothing special needs to be done upon removal of sparse matrices.

Labels: Bug