Talip7/scikit-learn-issues-multilabel huggingface dataset:
Are there any pitfalls by combining `n_jobs` and `random_state`?

### Discussed in https://github.com/scikit-learn/scikit-learn/discussions/30809

<div type='discussions-op-text'>

<sup>Originally posted by **adosar** February 11, 2025</sup>
In [Controlling randomness](https://scikit-learn.org/stable/common_pitfalls.html#common-pitfalls-and-recommended-practices), the guide is discussing how to properly control randomness either for an estimator or CV or when using both. However, there is no mention if `random_state` and `n_jobs > 1` interact in any unexpected way.

Lets consider a typical use case where a user cross validates a `RandomForestClassifier` with `KFold`:
```python
estimator = RandomForestClassifer(random_state=np.random.RandomState(1))  # Recommended to pass RandomState instance.
kfold = KFold(shuffle=True, random_state=42)  # Recommended to pass int.
cross_val_score(estimator, n_jobs=-1, ..., cv=kfold)
```
Since `n_jobs=-1` this means that multiple cores will be used for cross validation (e.g. 1 core per fold).

Would the same state be used for the different folds, since during multiprocessing the estimator and hence the `rng` passed to it, is copied via fork?

</div>

COMMENT:
Probably this [joblib example](https://joblib.readthedocs.io/en/stable/auto_examples/parallel_random_state.html#sphx-glr-auto-examples-parallel-random-state-py) is relevant as well.

Labels: Needs Triage
Add metadata routing params support in the predict method of `BaggingClassifier/Regressor`

### Describe the workflow you want to enable

Hello! I'm trying to use metadata routing with `BaggingClassifier` and `BaggingRegressor` however it is implemented for the `fit` method, not the `predict` one. I am wondering if there is a particular reason for not doing it on the predict function or if this is a feature that could be added. This would enable situations like the following, which currently gives an error:

```python
import numpy as np
import sklearn
from sklearn import ensemble
from sklearn.base import BaseEstimator

sklearn.set_config(enable_metadata_routing=True)


class CustomEstimator(BaseEstimator):
    def fit(self, X, y, foo):
        return self

    def predict(self, X, bar):
        return np.zeros(X.shape[0])


estimator = CustomEstimator()
estimator.set_fit_request(foo=True)
estimator.set_predict_request(bar=True)
model = ensemble.BaggingRegressor(estimator)

n, p = 10, 2
rng = np.random.default_rng(0)
x = rng.random((n, p))
y = rng.integers(0, 2, n)

model.fit(x, y, foo=True)
model.predict(x, bar=True). # TypeError: BaggingRegressor.predict() got an unexpected keyword argument 'bar'

```

### Describe your proposed solution

Similar to the fit method, something like:
```python
if _routing_enabled():
    routed_params = process_routing(self, "predict", **predict_params)
```

However, I don't have enough understanding of the metadata routing implementation to know exactly what should be done.

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

_No response_

### Additional context

I tried to have a look at the history of PRs/Issues to find a discussion around this point, but could not find it in the PR introducing the metadata routing to these estimators (#28432).

COMMENT:
In the first round of implementation, we focused on `fit`, happy to have a PR implementing this for the `predict` method.

Labels: New Feature, Metadata Routing
Add metadata routing params support in the predict method of `BaggingClassifier/Regressor`

### Describe the workflow you want to enable

Hello! I'm trying to use metadata routing with `BaggingClassifier` and `BaggingRegressor` however it is implemented for the `fit` method, not the `predict` one. I am wondering if there is a particular reason for not doing it on the predict function or if this is a feature that could be added. This would enable situations like the following, which currently gives an error:

```python
import numpy as np
import sklearn
from sklearn import ensemble
from sklearn.base import BaseEstimator

sklearn.set_config(enable_metadata_routing=True)


class CustomEstimator(BaseEstimator):
    def fit(self, X, y, foo):
        return self

    def predict(self, X, bar):
        return np.zeros(X.shape[0])


estimator = CustomEstimator()
estimator.set_fit_request(foo=True)
estimator.set_predict_request(bar=True)
model = ensemble.BaggingRegressor(estimator)

n, p = 10, 2
rng = np.random.default_rng(0)
x = rng.random((n, p))
y = rng.integers(0, 2, n)

model.fit(x, y, foo=True)
model.predict(x, bar=True). # TypeError: BaggingRegressor.predict() got an unexpected keyword argument 'bar'

```

### Describe your proposed solution

Similar to the fit method, something like:
```python
if _routing_enabled():
    routed_params = process_routing(self, "predict", **predict_params)
```

However, I don't have enough understanding of the metadata routing implementation to know exactly what should be done.

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

_No response_

### Additional context

I tried to have a look at the history of PRs/Issues to find a discussion around this point, but could not find it in the PR introducing the metadata routing to these estimators (#28432).

COMMENT:
Thank you for the feedback!

Labels: New Feature, Metadata Routing
Add metadata routing params support in the predict method of `BaggingClassifier/Regressor`

### Describe the workflow you want to enable

Hello! I'm trying to use metadata routing with `BaggingClassifier` and `BaggingRegressor` however it is implemented for the `fit` method, not the `predict` one. I am wondering if there is a particular reason for not doing it on the predict function or if this is a feature that could be added. This would enable situations like the following, which currently gives an error:

```python
import numpy as np
import sklearn
from sklearn import ensemble
from sklearn.base import BaseEstimator

sklearn.set_config(enable_metadata_routing=True)


class CustomEstimator(BaseEstimator):
    def fit(self, X, y, foo):
        return self

    def predict(self, X, bar):
        return np.zeros(X.shape[0])


estimator = CustomEstimator()
estimator.set_fit_request(foo=True)
estimator.set_predict_request(bar=True)
model = ensemble.BaggingRegressor(estimator)

n, p = 10, 2
rng = np.random.default_rng(0)
x = rng.random((n, p))
y = rng.integers(0, 2, n)

model.fit(x, y, foo=True)
model.predict(x, bar=True). # TypeError: BaggingRegressor.predict() got an unexpected keyword argument 'bar'

```

### Describe your proposed solution

Similar to the fit method, something like:
```python
if _routing_enabled():
    routed_params = process_routing(self, "predict", **predict_params)
```

However, I don't have enough understanding of the metadata routing implementation to know exactly what should be done.

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

_No response_

### Additional context

I tried to have a look at the history of PRs/Issues to find a discussion around this point, but could not find it in the PR introducing the metadata routing to these estimators (#28432).

COMMENT:
cc @StefanieSenger in case you fancy taking on this one.

Labels: New Feature, Metadata Routing
Add metadata routing params support in the predict method of `BaggingClassifier/Regressor`

### Describe the workflow you want to enable

Hello! I'm trying to use metadata routing with `BaggingClassifier` and `BaggingRegressor` however it is implemented for the `fit` method, not the `predict` one. I am wondering if there is a particular reason for not doing it on the predict function or if this is a feature that could be added. This would enable situations like the following, which currently gives an error:

```python
import numpy as np
import sklearn
from sklearn import ensemble
from sklearn.base import BaseEstimator

sklearn.set_config(enable_metadata_routing=True)


class CustomEstimator(BaseEstimator):
    def fit(self, X, y, foo):
        return self

    def predict(self, X, bar):
        return np.zeros(X.shape[0])


estimator = CustomEstimator()
estimator.set_fit_request(foo=True)
estimator.set_predict_request(bar=True)
model = ensemble.BaggingRegressor(estimator)

n, p = 10, 2
rng = np.random.default_rng(0)
x = rng.random((n, p))
y = rng.integers(0, 2, n)

model.fit(x, y, foo=True)
model.predict(x, bar=True). # TypeError: BaggingRegressor.predict() got an unexpected keyword argument 'bar'

```

### Describe your proposed solution

Similar to the fit method, something like:
```python
if _routing_enabled():
    routed_params = process_routing(self, "predict", **predict_params)
```

However, I don't have enough understanding of the metadata routing implementation to know exactly what should be done.

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

_No response_

### Additional context

I tried to have a look at the history of PRs/Issues to find a discussion around this point, but could not find it in the PR introducing the metadata routing to these estimators (#28432).

COMMENT:
Yes, I'm happy to take care of it.

Labels: New Feature, Metadata Routing
Add metadata routing params support in the predict method of `BaggingClassifier/Regressor`

### Describe the workflow you want to enable

Hello! I'm trying to use metadata routing with `BaggingClassifier` and `BaggingRegressor` however it is implemented for the `fit` method, not the `predict` one. I am wondering if there is a particular reason for not doing it on the predict function or if this is a feature that could be added. This would enable situations like the following, which currently gives an error:

```python
import numpy as np
import sklearn
from sklearn import ensemble
from sklearn.base import BaseEstimator

sklearn.set_config(enable_metadata_routing=True)


class CustomEstimator(BaseEstimator):
    def fit(self, X, y, foo):
        return self

    def predict(self, X, bar):
        return np.zeros(X.shape[0])


estimator = CustomEstimator()
estimator.set_fit_request(foo=True)
estimator.set_predict_request(bar=True)
model = ensemble.BaggingRegressor(estimator)

n, p = 10, 2
rng = np.random.default_rng(0)
x = rng.random((n, p))
y = rng.integers(0, 2, n)

model.fit(x, y, foo=True)
model.predict(x, bar=True). # TypeError: BaggingRegressor.predict() got an unexpected keyword argument 'bar'

```

### Describe your proposed solution

Similar to the fit method, something like:
```python
if _routing_enabled():
    routed_params = process_routing(self, "predict", **predict_params)
```

However, I don't have enough understanding of the metadata routing implementation to know exactly what should be done.

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

_No response_

### Additional context

I tried to have a look at the history of PRs/Issues to find a discussion around this point, but could not find it in the PR introducing the metadata routing to these estimators (#28432).

COMMENT:
Out of curiosity, what is your use case that needs routing metadata in predict methods, @aperezlebel?

Labels: New Feature, Metadata Routing
Add metadata routing params support in the predict method of `BaggingClassifier/Regressor`

### Describe the workflow you want to enable

Hello! I'm trying to use metadata routing with `BaggingClassifier` and `BaggingRegressor` however it is implemented for the `fit` method, not the `predict` one. I am wondering if there is a particular reason for not doing it on the predict function or if this is a feature that could be added. This would enable situations like the following, which currently gives an error:

```python
import numpy as np
import sklearn
from sklearn import ensemble
from sklearn.base import BaseEstimator

sklearn.set_config(enable_metadata_routing=True)


class CustomEstimator(BaseEstimator):
    def fit(self, X, y, foo):
        return self

    def predict(self, X, bar):
        return np.zeros(X.shape[0])


estimator = CustomEstimator()
estimator.set_fit_request(foo=True)
estimator.set_predict_request(bar=True)
model = ensemble.BaggingRegressor(estimator)

n, p = 10, 2
rng = np.random.default_rng(0)
x = rng.random((n, p))
y = rng.integers(0, 2, n)

model.fit(x, y, foo=True)
model.predict(x, bar=True). # TypeError: BaggingRegressor.predict() got an unexpected keyword argument 'bar'

```

### Describe your proposed solution

Similar to the fit method, something like:
```python
if _routing_enabled():
    routed_params = process_routing(self, "predict", **predict_params)
```

However, I don't have enough understanding of the metadata routing implementation to know exactly what should be done.

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

_No response_

### Additional context

I tried to have a look at the history of PRs/Issues to find a discussion around this point, but could not find it in the PR introducing the metadata routing to these estimators (#28432).

COMMENT:
I'm working with an estimator that uses feature names in addition to the feature values. When I wrap the estimator inside a BaggingClassifier or BaggingRegressor, I can't pass the feature names to the predict method, even when using a pandas dataframe because the bagging estimator converts it to a numpy array before passing it to the base estimator.

Thank you @StefanieSenger for taking care of this PR!

Labels: New Feature, Metadata Routing
Add metadata routing params support in the predict method of `BaggingClassifier/Regressor`

### Describe the workflow you want to enable

Hello! I'm trying to use metadata routing with `BaggingClassifier` and `BaggingRegressor` however it is implemented for the `fit` method, not the `predict` one. I am wondering if there is a particular reason for not doing it on the predict function or if this is a feature that could be added. This would enable situations like the following, which currently gives an error:

```python
import numpy as np
import sklearn
from sklearn import ensemble
from sklearn.base import BaseEstimator

sklearn.set_config(enable_metadata_routing=True)


class CustomEstimator(BaseEstimator):
    def fit(self, X, y, foo):
        return self

    def predict(self, X, bar):
        return np.zeros(X.shape[0])


estimator = CustomEstimator()
estimator.set_fit_request(foo=True)
estimator.set_predict_request(bar=True)
model = ensemble.BaggingRegressor(estimator)

n, p = 10, 2
rng = np.random.default_rng(0)
x = rng.random((n, p))
y = rng.integers(0, 2, n)

model.fit(x, y, foo=True)
model.predict(x, bar=True). # TypeError: BaggingRegressor.predict() got an unexpected keyword argument 'bar'

```

### Describe your proposed solution

Similar to the fit method, something like:
```python
if _routing_enabled():
    routed_params = process_routing(self, "predict", **predict_params)
```

However, I don't have enough understanding of the metadata routing implementation to know exactly what should be done.

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

_No response_

### Additional context

I tried to have a look at the history of PRs/Issues to find a discussion around this point, but could not find it in the PR introducing the metadata routing to these estimators (#28432).

COMMENT:
Thank you @aperezlebel, I feel what you describe is something scikit-learn would offer without metadata routing. I didn't check how to do that precisely though. Anyways, seems the routing to predict will be in version 1.7.

Labels: New Feature, Metadata Routing
⚠️ CI failed on Wheel builder (last failure: Feb 10, 2025) ⚠️

**CI failed on [Wheel builder](https://github.com/scikit-learn/scikit-learn/actions/runs/13233133978)** (Feb 10, 2025)

COMMENT:
## CI is no longer failing! ✅

[Successful run](https://github.com/scikit-learn/scikit-learn/actions/runs/13255448684) on Feb 11, 2025

Labels: Needs Triage
_py_sort() returns ValueError on windows with numpy 1.26.4 but works correctly with numpy 2.x

### Describe the bug

_py_sort() returns ValueError with numpy 1.26.4 but works correctly with numpy 2.x. I have created 2 different conda envs with different numpy versions from conda-forge:
```
conda create -n numpy_1.26.4 numpy=1.26.4 scikit-learn=1.6.1 -c conda-forge --override-channels
```
and
```
conda create -n numpy_2 numpy=2 scikit-learn=1.6.1 -c conda-forge --override-channel
```
In each of the envs, I essentially reproduced https://github.com/scikit-learn/scikit-learn/blob/main/sklearn/tree/tests/test_tree.py#L2820 test_sort_log2_build test that shows different behavior. This works correctly with numpy 2, but with numpy 1.26.4 it returns: 
```
ValueError: Buffer dtype mismatch, expected 'intp_t' but got 'long'
```

### Steps/Code to Reproduce

In fact, this is just a copy of test_sort_log2_build test:
```
>>> import numpy as np
>>> print(np.__version__)
1.26.4
>>> import sklearn
>>> print(sklearn.__version__)
1.6.1
>>> from sklearn.tree._partitioner import _py_sort
>>> rng = np.random.default_rng(75)
>>> some = rng.normal(loc=0.0, scale=10.0, size=10).astype(np.float32)
>>> feature_values = np.concatenate([some] * 5)
>>> samples = np.arange(50)
>>> _py_sort(feature_values, samples, 50)
```

### Expected Results

```
>>> _py_sort(feature_values, samples, 50)
>>>
```
This is the normal behavior of the test in case numpy 2:
```
>>> import numpy as np
>>> print(np.__version__)
2.1.2
```

### Actual Results

```
>>> _py_sort(feature_values, samples, 50)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "_partitioner.pyx", line 705, in sklearn.tree._partitioner._py_sort
ValueError: Buffer dtype mismatch, expected 'intp_t' but got 'long'
```
This behavior is reproduced if the test is run with numpy 1.26.4

### Versions

```shell
>>> import sklearn
>>> print(sklearn.__version__)
1.6.1
```

COMMENT:
@ekomarova Could you give us information regarding the operating system in which you could trigger the error. #30793 proposes a change related to the fact that the default `int` in Windows was historically 32-bits with NumPy < 2 and it became 64-bits NumPy 2.X.

So if the OS match then, it would make a lot of sense.

Labels: Bug
_py_sort() returns ValueError on windows with numpy 1.26.4 but works correctly with numpy 2.x

### Describe the bug

_py_sort() returns ValueError with numpy 1.26.4 but works correctly with numpy 2.x. I have created 2 different conda envs with different numpy versions from conda-forge:
```
conda create -n numpy_1.26.4 numpy=1.26.4 scikit-learn=1.6.1 -c conda-forge --override-channels
```
and
```
conda create -n numpy_2 numpy=2 scikit-learn=1.6.1 -c conda-forge --override-channel
```
In each of the envs, I essentially reproduced https://github.com/scikit-learn/scikit-learn/blob/main/sklearn/tree/tests/test_tree.py#L2820 test_sort_log2_build test that shows different behavior. This works correctly with numpy 2, but with numpy 1.26.4 it returns: 
```
ValueError: Buffer dtype mismatch, expected 'intp_t' but got 'long'
```

### Steps/Code to Reproduce

In fact, this is just a copy of test_sort_log2_build test:
```
>>> import numpy as np
>>> print(np.__version__)
1.26.4
>>> import sklearn
>>> print(sklearn.__version__)
1.6.1
>>> from sklearn.tree._partitioner import _py_sort
>>> rng = np.random.default_rng(75)
>>> some = rng.normal(loc=0.0, scale=10.0, size=10).astype(np.float32)
>>> feature_values = np.concatenate([some] * 5)
>>> samples = np.arange(50)
>>> _py_sort(feature_values, samples, 50)
```

### Expected Results

```
>>> _py_sort(feature_values, samples, 50)
>>>
```
This is the normal behavior of the test in case numpy 2:
```
>>> import numpy as np
>>> print(np.__version__)
2.1.2
```

### Actual Results

```
>>> _py_sort(feature_values, samples, 50)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "_partitioner.pyx", line 705, in sklearn.tree._partitioner._py_sort
ValueError: Buffer dtype mismatch, expected 'intp_t' but got 'long'
```
This behavior is reproduced if the test is run with numpy 1.26.4

### Versions

```shell
>>> import sklearn
>>> print(sklearn.__version__)
1.6.1
```

COMMENT:
@glemaitre Windows is mentioned in the issue title, so I guess this is Windows indeed 😉. 

@ekomarova it would be super useful if you could give us a snippet using scikit-learn public functions/classes that trigger this issue, see [self-contained example code](https://scikit-learn.org/dev/developers/minimal_reproducer.html). I am guessing that you are not using `sklearn.tree._partitioner._py_sort` directly.

I thought "surely we are testing Windows with our minimum dependencies in our CI" but no 😓, we have a single Windows build with our min Python (3.9) but numpy>=2 ...

Labels: Bug
_py_sort() returns ValueError on windows with numpy 1.26.4 but works correctly with numpy 2.x

### Describe the bug

_py_sort() returns ValueError with numpy 1.26.4 but works correctly with numpy 2.x. I have created 2 different conda envs with different numpy versions from conda-forge:
```
conda create -n numpy_1.26.4 numpy=1.26.4 scikit-learn=1.6.1 -c conda-forge --override-channels
```
and
```
conda create -n numpy_2 numpy=2 scikit-learn=1.6.1 -c conda-forge --override-channel
```
In each of the envs, I essentially reproduced https://github.com/scikit-learn/scikit-learn/blob/main/sklearn/tree/tests/test_tree.py#L2820 test_sort_log2_build test that shows different behavior. This works correctly with numpy 2, but with numpy 1.26.4 it returns: 
```
ValueError: Buffer dtype mismatch, expected 'intp_t' but got 'long'
```

### Steps/Code to Reproduce

In fact, this is just a copy of test_sort_log2_build test:
```
>>> import numpy as np
>>> print(np.__version__)
1.26.4
>>> import sklearn
>>> print(sklearn.__version__)
1.6.1
>>> from sklearn.tree._partitioner import _py_sort
>>> rng = np.random.default_rng(75)
>>> some = rng.normal(loc=0.0, scale=10.0, size=10).astype(np.float32)
>>> feature_values = np.concatenate([some] * 5)
>>> samples = np.arange(50)
>>> _py_sort(feature_values, samples, 50)
```

### Expected Results

```
>>> _py_sort(feature_values, samples, 50)
>>>
```
This is the normal behavior of the test in case numpy 2:
```
>>> import numpy as np
>>> print(np.__version__)
2.1.2
```

### Actual Results

```
>>> _py_sort(feature_values, samples, 50)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "_partitioner.pyx", line 705, in sklearn.tree._partitioner._py_sort
ValueError: Buffer dtype mismatch, expected 'intp_t' but got 'long'
```
This behavior is reproduced if the test is run with numpy 1.26.4

### Versions

```shell
>>> import sklearn
>>> print(sklearn.__version__)
1.6.1
```

COMMENT:
@lesteve Yes, you are absolutely right in saying that we do not use `sklearn.tree._partitioner._py_sort` directly. All we do is essentially include sklearn as a dependency in our product and run multiple sklearn tests, one of which fails with numpy 1.26. But I still think it could be a user case for those who want to use sklearn with numpy 1.26 anyway instead of numpy 2.x. But in any case, since we are facing a problem in the test, fixing the test itself could also be a solution if you think that there is no need to fix the function itself.

Labels: Bug