8000 Failing CI for check_sample_weight_equivalence_on_dense_data with LinearRegerssion on debian_32bit · Issue #31098 · scikit-learn/scikit-learn · GitHub
[go: up one dir, main page]

Skip to content

Failing CI for check_sample_weight_equivalence_on_dense_data with LinearRegerssion on debian_32bit #31098

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ogrisel opened this issue Mar 28, 2025 · 21 comments · Fixed by #31188

Comments

@ogrisel
Copy link
Member
ogrisel commented Mar 28, 2025

Here is the last scheduled run (from 1 day ago) that passed:

https://dev.azure.com/scikit-learn/scikit-learn/_build/results?buildId=75127&view=logs&j=86340c1f-3d76-5202-0821-7817a0f52092&t=a73eff7b-829e-5a65-7648-23ff8e83ea2d

and here is a more recent run that failed (all CI is failing today):

https://dev.azure.com/scikit-learn/scikit-learn/_build/results?buildId=75179&view=logs&j=86340c1f-3d76-5202-0821-7817a0f52092&t=a73eff7b-829e-5a65-7648-23ff8e83ea2d

FAILED tests/test_common.py::test_estimators[LinearRegression(positive=True)-check_sample_weight_equivalence_on_dense_data] - AssertionError: 
FAILED utils/tests/test_estimator_checks.py::test_check_estimator_clones - AssertionError: 
= 2 failed, 34214 passed, 4182 skipped, 174 xfailed, 66 xpassed, 4252 warnings in 1489.21s (0:24:49) =

Full failure log:

2025-03-28T06:36:32.3433619Z =================================== FAILURES ===================================
2025-03-28T06:36:32.3434358Z �[31m�[1m_ test_estimators[LinearRegression(positive=True)-check_sample_weight_equivalence_on_dense_data] _�[0m
2025-03-28T06:36:32.3434613Z 
2025-03-28T06:36:32.3434838Z estimator = LinearRegression(positive=True)
2025-03-28T06:36:32.3435117Z check = functools.partial(<function check_sample_weight_equivalence_on_dense_data at 0xd8591e88>, 'LinearRegression')
2025-03-28T06:36:32.3435705Z request = <FixtureRequest for <Function test_estimators[LinearRegression(positive=True)-check_sample_weight_equivalence_on_dense_data]>>
2025-03-28T06:36:32.3435878Z 
2025-03-28T06:36:32.3436047Z     @parametrize_with_checks(
2025-03-28T06:36:32.3436274Z         list(_tested_estimators()), expected_failed_checks=_get_expected_failed_checks
2025-03-28T06:36:32.3436498Z     )
2025-03-28T06:36:32.3436684Z     def test_estimators(estimator, check, request):
2025-03-28T06:36:32.3436909Z         # Common tests for estimator instances
2025-03-28T06:36:32.3437101Z         with ignore_warnings(
2025-03-28T06:36:32.3437316Z             category=(FutureWarning, ConvergenceWarning, UserWarning, LinAlgWarning)
2025-03-28T06:36:32.3437521Z         ):
2025-03-28T06:36:32.3437708Z >           check(estimator)
2025-03-28T06:36:32.3437793Z 
2025-03-28T06:36:32.3438019Z check      = functools.partial(<function check_sample_weight_equivalence_on_dense_data at 0xd8591e88>, 'LinearRegression')
2025-03-28T06:36:32.3438293Z estimator  = LinearRegression(positive=True)
2025-03-28T06:36:32.3438559Z request    = <FixtureRequest for <Function test_estimators[LinearRegression(positive=True)-check_sample_weight_equivalence_on_dense_data]>>
2025-03-28T06:36:32.3438707Z 
2025-03-28T06:36:32.3439155Z �[1m�[31m/io/sklearn/tests/test_common.py�[0m:122: 
2025-03-28T06:36:32.3439405Z _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
2025-03-28T06:36:32.3439768Z �[1m�[31m/io/sklearn/utils/estimator_checks.py�[0m:1570: in check_sample_weight_equivalence_on_dense_data
2025-03-28T06:36:32.3440046Z     _check_sample_weight_equivalence(name, estimator_orig, sparse_container=None)
2025-03-28T06:36:32.3440296Z         estimator_orig = LinearRegression(positive=True)
2025-03-28T06:36:32.3440498Z         name       = 'LinearRegression'
2025-03-28T06:36:32.3440774Z �[1m�[31m/io/sklearn/utils/_testing.py�[0m:145: in wrapper
2025-03-28T06:36:32.3440988Z     return fn(*args, **kwargs)
2025-03-28T06:36:32.3441218Z         args       = ('LinearRegression', LinearRegression(positive=True))
2025-03-28T06:36:32.3441452Z         fn         = <function _check_sample_weight_equivalence at 0xd8591de8>
2025-03-28T06:36:32.3441744Z         kwargs     = {'sparse_container': None}
2025-03-28T06:36:32.3441952Z         self       = _IgnoreWarnings(record=True)
2025-03-28T06:36:32.3442307Z �[1m�[31m/io/sklearn/utils/estimator_checks.py�[0m:1566: in _check_sample_weight_equivalence
2025-03-28T06:36:32.3442654Z     assert_allclose_dense_sparse(X_pred1, X_pred2, err_msg=err_msg)
2025-03-28T06:36:32.3442905Z         X          = array([[0.37454012, 0.95071431, 0.73199394, 0.59865848, 0.15601864,
2025-03-28T06:36:32.3443158Z         0.15599452, 0.05808361, 0.86617615, 0.6011..., 0.98663958, 0.3742708 , 0.37064215, 0.81279957,
2025-03-28T06:36:32.3443423Z         0.94724858, 0.98600106, 0.75337819, 0.37625959, 0.08350072]])
2025-03-28T06:36:32.3443662Z         X_pred1    = array([ 8.88178420e-16,  1.00000000e+00,  2.00000000e+00,  1.18549798e+00,
2025-03-28T06:36:32.3443929Z         4.06241761e+00,  1.00000000e+00,  2...5767e+00,  2.00000000e+00, -2.79936287e-02, -8.90642835e-01,
2025-03-28T06:36:32.3444177Z        -8.00809991e-01,  1.00000000e+00,  1.00000000e+00])
2025-03-28T06:36:32.3444412Z         X_pred2    = array([0.        , 1.        , 2.        , 0.94186541, 1.72670876,
2025-03-28T06:36:32.3444772Z        1.        , 2.        , 2.        , 1.8723887 , 2.        ,
2025-03-28T06:36:32.3444987Z        1.50877777, 0.76107365, 1.70933257, 1.        , 1.        ])
2025-03-28T06:36:32.3445218Z         X_repeated = array([[0.37454012, 0.95071431, 0.73199394, 0.59865848, 0.15601864,
2025-03-28T06:36:32.3445494Z         0.15599452, 0.05808361, 0.86617615, 0.6011..., 0.98663958, 0.3742708 , 0.37064215, 0.81279957,
2025-03-28T06:36:32.3445740Z         0.94724858, 0.98600106, 0.75337819, 0.37625959, 0.08350072]])
2025-03-28T06:36:32.3445976Z         X_weighted = array([[0.60754485, 0.17052412, 0.06505159, 0.94888554, 0.96563203,
2025-03-28T06:36:32.3446499Z         0.80839735, 0.30461377, 0.09767211, 0.6842..., 0.69673717, 0.62894285, 0.87747201, 0.73507104,
2025-03-28T06:36:32.3446765Z         0.80348093, 0.28203457, 0.17743954, 0.75061475, 0.80683474]])
2025-03-28T06:36:32.3447241Z         err_msg    = 'Comparing the output of LinearRegression.predict revealed that fitting with `sample_weight` is not equivalent to fitting with removed or repeated data points.'
2025-03-28T06:36:32.3447553Z         estimator_orig = LinearRegression(positive=True)
2025-03-28T06:36:32.3448930Z         estimator_repeated = LinearRegression(positive=True)
2025-03-28T06:36:32.3449323Z         estimator_weighted = LinearRegression(positive=True)
2025-03-28T06:36:32.3449572Z         method     = 'predict'
2025-03-28T06:36:32.3449847Z         n_samples  = 15
2025-03-28T06:36:32.3450076Z         name       = 'LinearRegression'
2025-03-28T06:36:32.3450329Z         rng        = RandomState(MT19937) at 0xCB6EECE8
2025-03-28T06:36:32.3450580Z         sparse_container = None
2025-03-28T06:36:32.3450850Z         sw         = array([3, 4, 0, 3, 1, 0, 4, 4, 0, 3, 0, 0, 3, 2, 0])
2025-03-28T06:36:32.3451124Z         y          = array([0, 1, 2, 2, 1, 1, 2, 2, 1, 2, 0, 0, 1, 1, 1])
2025-03-28T06:36:32.3451409Z         y_repeated = array([0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2025-03-28T06:36:32.3451823Z        1, 1, 1, 1, 1])
2025-03-28T06:36:32.3452390Z         y_weighted = array([1, 2, 1, 2, 1, 1, 2, 1, 0, 2, 0, 2, 0, 1, 1])
2025-03-28T06:36:32.3467337Z �[1m�[31m/io/sklearn/utils/_testing.py�[0m:283: in assert_allclose_dense_sparse
2025-03-28T06:36:32.3468147Z     assert_allclose(x, y, rtol=rtol, atol=atol, err_msg=err_msg)
2025-03-28T06:36:32.3468478Z         atol       = 1e-09
2025-03-28T06:36:32.3468965Z         err_msg    = 'Comparing the output of LinearRegression.predict revealed that fitting with `sample_weight` is not equivalent to fitting with removed or repeated data points.'
2025-03-28T06:36:32.3469366Z         rtol       = 1e-07
2025-03-28T06:36:32.3469659Z         x          = array([ 8.88178420e-16,  1.00000000e+00,  2.00000000e+00,  1.18549798e+00,
2025-03-28T06:36:32.3470005Z         4.06241761e+00,  1.00000000e+00,  2...5767e+00,  2.00000000e+00, -2.79936287e-02, -8.90642835e-01,
2025-03-28T06:36:32.3470354Z        -8.00809991e-01,  1.00000000e+00,  1.00000000e+00])
2025-03-28T06:36:32.3470648Z         y          = array([0.        , 1.        , 2.        , 0.94186541, 1.72670876,
2025-03-28T06:36:32.3470947Z        1.        , 2.        , 2.        , 1.8723887 , 2.        ,
2025-03-28T06:36:32.3471226Z        1.50877777, 0.76107365, 1.70933257, 1.        , 1.        ])
2025-03-28T06:36:32.3471533Z _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
2025-03-28T06:36:32.3471702Z 
2025-03-28T06:36:32.3472165Z actual = array([ 8.88178420e-16,  1.00000000e+00,  2.00000000e+00,  1.18549798e+00,
2025-03-28T06:36:32.3472656Z         4.06241761e+00,  1.00000000e+00,  2...5767e+00,  2.00000000e+00, -2.79936287e-02, -8.90642835e-01,
2025-03-28T06:36:32.3473239Z        -8.00809991e-01,  1.00000000e+00,  1.00000000e+00])
2025-03-28T06:36:32.3473561Z desired = array([0.        , 1.        , 2.        , 0.94186541, 1.72670876,
2025-03-28T06:36:32.3473863Z        1.        , 2.        , 2.        , 1.8723887 , 2.        ,
2025-03-28T06:36:32.3474355Z        1.50877777, 0.76107365, 1.70933257, 1.        , 1.        ])
2025-03-28T06:36:32.3474896Z rtol = 1e-07, atol = 1e-09, equal_nan = True
2025-03-28T06:36:32.3475293Z err_msg = 'Comparing the output of LinearRegression.predict revealed that fitting with `sample_weight` is not equivalent to fitting with removed or repeated data points.'
2025-03-28T06:36:32.3475668Z verbose = True
2025-03-28T06:36:32.3475847Z 
2025-03-28T06:36:32.3476109Z     def assert_allclose(
2025-03-28T06:36:32.3476425Z         actual, desired, rtol=None, atol=0.0, equal_nan=True, err_msg="", verbose=True
2025-03-28T06:36:32.3476851Z     ):
2025-03-28T06:36:32.3477457Z         """dtype-aware variant of numpy.testing.assert_allclose
2025-03-28T06:36:32.3477743Z     
2025-03-28T06:36:32.3478184Z         This variant introspects the least precise floating point dtype
2025-03-28T06:36:32.3478502Z         in the input argument and automatically sets the relative tolerance
2025-03-28T06:36:32.3478839Z         parameter to 1e-4 float32 and use 1e-7 otherwise (typically float64
2025-03-28T06:36:32.3479134Z         in scikit-learn).
2025-03-28T06:36:32.3479381Z     
2025-03-28T06:36:32.3479837Z         `atol` is always left to 0. by default. It should be adjusted manually
2025-03-28T06:36:32.3480176Z         to an assertion-specific value in case there are null values expected
2025-03-28T06:36:32.3480467Z         in `desired`.
2025-03-28T06:36:32.3480718Z     
2025-03-28T06:36:32.3480995Z         The aggregate tolerance is `atol + rtol * abs(desired)`.
2025-03-28T06:36:32.3481285Z     
2025-03-28T06:36:32.3481704Z         Parameters
2025-03-28T06:36:32.3481965Z         ----------
2025-03-28T06:36:32.3482420Z         actual : array_like
2025-03-28T06:36:32.3483027Z             Array obtained.
2025-03-28T06:36:32.3483323Z         desired : array_like
2025-03-28T06:36:32.3483598Z             Array desired.
2025-03-28T06:36:32.3483881Z         rtol : float, optional, default=None
2025-03-28T06:36:32.3484165Z             Relative tolerance.
2025-03-28T06:36:32.3484462Z             If None, it is set based on the provided arrays' dtypes.
2025-03-28T06:36:32.3484791Z         atol : float, optional, default=0.
2025-03-28T06:36:32.3485072Z             Absolute tolerance.
2025-03-28T06:36:32.3485449Z         equal_nan : bool, optional, default=True
2025-03-28T06:36:32.3485916Z             If True, NaNs will compare equal.
2025-03-28T06:36:32.3486379Z         err_msg : str, optional, default=''
2025-03-28T06:36:32.3486669Z             The error message to be printed in case of failure.
2025-03-28T06:36:32.3486959Z         verbose : bool, optional, default=True
2025-03-28T06:36:32.3487445Z             If True, the conflicting values are appended to the error message.
2025-03-28T06:36:32.3487721Z     
2025-03-28T06:36:32.3487982Z         Raises
2025-03-28T06:36:32.3488229Z         ------
2025-03-28T06:36:32.3488486Z         AssertionError
2025-03-28T06:36:32.3490170Z             If actual and desired are not equal 
8000
up to specified precision.
2025-03-28T06:36:32.3490710Z     
2025-03-28T06:36:32.3491058Z         See Also
2025-03-28T06:36:32.3491386Z         --------
2025-03-28T06:36:32.3491708Z         numpy.testing.assert_allclose
2025-03-28T06:36:32.3492016Z     
2025-03-28T06:36:32.3492308Z         Examples
2025-03-28T06:36:32.3493161Z         --------
2025-03-28T06:36:32.3493425Z         >>> import numpy as np
2025-03-28T06:36:32.3493712Z         >>> from sklearn.utils._testing import assert_allclose
2025-03-28T06:36:32.3493988Z         >>> x = [1e-5, 1e-3, 1e-1]
2025-03-28T06:36:32.3494270Z         >>> y = np.arccos(np.cos(x))
2025-03-28T06:36:32.3494725Z         >>> assert_allclose(x, y, rtol=1e-5, atol=0)
2025-03-28T06:36:32.3495044Z         >>> a = np.full(shape=10, fill_value=1e-5, dtype=np.float32)
2025-03-28T06:36:32.3495339Z         >>> assert_allclose(a, 1e-5)
2025-03-28T06:36:32.3495595Z         """
2025-03-28T06:36:32.3496358Z         dtypes = []
2025-03-28T06:36:32.3496662Z     
2025-03-28T06:36:32.3496955Z         actual, desired = np.asanyarray(actual), np.asanyarray(desired)
2025-03-28T06:36:32.3497629Z         dtypes = [actual.dtype, desired.dtype]
2025-03-28T06:36:32.3497899Z     
2025-03-28T06:36:32.3498178Z         if rtol is None:
2025-03-28T06:36:32.3498471Z             rtols = [1e-4 if dtype == np.float32 else 1e-7 for dtype in dtypes]
2025-03-28T06:36:32.3498764Z             rtol = max(rtols)
2025-03-28T06:36:32.3499011Z     
2025-03-28T06:36:32.3604475Z >       np_assert_allclose(
2025-03-28T06:36:32.3607071Z             actual,
2025-03-28T06:36:32.3608138Z             desired,
2025-03-28T06:36:32.3608886Z             rtol=rtol,
2025-03-28T06:36:32.3609217Z             atol=atol,
2025-03-28T06:36:32.3625173Z             equal_nan=equal_nan,
2025-03-28T06:36:32.3639658Z             err_msg=err_msg,
2025-03-28T06:36:32.3640151Z             verbose=verbose,
2025-03-28T06:36:32.3640425Z         )
2025-03-28T06:36:32.3640827Z �[1m�[31mE       AssertionError: �[0m
2025-03-28T06:36:32.3641237Z �[1m�[31mE       Not equal to tolerance rtol=1e-07, atol=1e-09�[0m
2025-03-28T06:36:32.3641778Z �[1m�[31mE       Comparing the output of LinearRegression.predict revealed that fitting with `sample_weight` is not equivalent to fitting with removed or repeated data points.�[0m
2025-03-28T06:36:32.3642255Z �[1m�[31mE       Mismatched elements: 6 / 15 (40%)�[0m
2025-03-28T06:36:32.3642821Z �[1m�[31mE       Max absolute difference among violations: 2.51014256�[0m
2025-03-28T06:36:32.3643306Z �[1m�[31mE       Max relative difference among violations: 2.17024526�[0m
2025-03-28T06:36:32.3643773Z �[1m�[31mE        ACTUAL: array([ 8.881784e-16,  1.000000e+00,  2.000000e+00,  1.185498e+00,�[0m
2025-03-28T06:36:32.3644416Z �[1m�[31mE               4.062418e+00,  1.000000e+00,  2.000000e+00,  2.000000e+00,�[0m
2025-03-28T06:36:32.3644878Z �[1m�[31mE               4.105658e+00,  2.000000e+00, -2.799363e-02, -8.906428e-01,�[0m
2025-03-28T06:36:32.3645289Z �[1m�[31mE              -8.008100e-01,  1.000000e+00,  1.000000e+00])�[0m
2025-03-28T06:36:32.3645729Z �[1m�[31mE        DESIRED: array([0.      , 1.      , 2.      , 0.941865, 1.726709, 1.      ,�[0m
2025-03-28T06:36:32.3646166Z �[1m�[31mE              2.      , 2.      , 1.872389, 2.      , 1.508778, 0.761074,�[0m
2025-03-28T06:36:32.3646542Z �[1m�[31mE              1.709333, 1.      , 1.      ])�[0m
2025-03-28T06:36:32.3646727Z 
2025-03-28T06:36:32.3647013Z actual     = array([ 8.88178420e-16,  1.00000000e+00,  2.00000000e+00,  1.18549798e+00,
2025-03-28T06:36:32.3647353Z         4.06241761e+00,  1.00000000e+00,  2...5767e+00,  2.00000000e+00, -2.79936287e-02, -8.90642835e-01,
2025-03-28T06:36:32.3647675Z        -8.00809991e-01,  1.00000000e+00,  1.00000000e+00])
2025-03-28T06:36:32.3647970Z atol       = 1e-09
2025-03-28T06:36:32.3648247Z desired    = array([0.        , 1.        , 2.        , 0.94186541, 1.72670876,
2025-03-28T06:36:32.3648536Z        1.        , 2.        , 2.        , 1.8723887 , 2.        ,
2025-03-28T06:36:32.3648814Z        1.50877777, 0.76107365, 1.70933257, 1.        , 1.        ])
2025-03-28T06:36:32.3649119Z dtypes     = [dtype('float64'), dtype('float64')]
2025-03-28T06:36:32.3650674Z equal_nan  = True
2025-03-28T06:36:32.3664015Z err_msg    = 'Comparing the output of LinearRegression.predict revealed that fitting with `sample_weight` is not equivalent to fitting with removed or repeated data points.'
2025-03-28T06:36:32.3664453Z rtol       = 1e-07
2025-03-28T06:36:32.3664740Z verbose    = True
2025-03-28T06:36:32.3664894Z 
2025-03-28T06:36:32.3665331Z �[1m�[31m/io/sklearn/utils/_testing.py�[0m:237: AssertionError
2025-03-28T06:36:32.3665827Z �[31m�[1m_________________________ test_check_estimator_clones __________________________�[0m
2025-03-28T06:36:32.3666186Z 
2025-03-28T06:36:32.3666476Z     def test_check_estimator_clones():
2025-03-28T06:36:32.3666804Z         # check that check_estimator doesn't modify the estimator it receives
2025-03-28T06:36:32.3667086Z     
2025-03-28T06:36:32.3667500Z         iris = load_iris()
2025-03-28T06:36:32.3667744Z     
2025-03-28T06:36:32.3667984Z         for Estimator in [
2025-03-28T06:36:32.3668245Z             GaussianMixture,
2025-03-28T06:36:32.3668775Z             LinearRegression,
2025-03-28T06:36:32.3669031Z             SGDClassifier,
2025-03-28T06:36:32.3669267Z             PCA,
2025-03-28T06:36:32.3669511Z             MiniBatchKMeans,
2025-03-28T06:36:32.3669766Z         ]:
2025-03-28T06:36:32.3670005Z             # without fitting
2025-03-28T06:36:32.3670276Z             with ignore_warnings(category=ConvergenceWarning):
2025-03-28T06:36:32.3670550Z                 est = Estimator()
2025-03-28T06:36:32.3670803Z                 set_random_state(est)
2025-03-28T06:36:32.3671081Z                 old_hash = joblib.hash(est)
2025-03-28T06:36:32.3671475Z >               check_estimator(
2025-03-28T06:36:32.3671766Z                     est, expected_failed_checks=_get_expected_failed_checks(est)
2025-03-28T06:36:32.3672037Z                 )
2025-03-28T06:36:32.3672196Z 
2025-03-28T06:36:32.3672459Z Estimator  = <class 'sklearn.linear_model._base.LinearRegression'>
2025-03-28T06:36:32.3672931Z est        = LinearRegression()
2025-03-28T06:36:32.3673224Z iris       = {'data': array([[5.1, 3.5, 1.4, 0.2],
2025-03-28T06:36:32.3673509Z        [4.9, 3. , 1.4, 0.2],
2025-03-28T06:36:32.3673763Z        [4.7, 3.2, 1.3, 0.2],
2025-03-28T06:36:32.3674269Z        [4.6, 3.1, 1.5,... width (cm)', 'petal length (cm)', 'petal width (cm)'], 'filename': 'iris.csv', 'data_module': 'sklearn.datasets.data'}
2025-03-28T06:36:32.3674627Z old_hash   = 'fdcbee8ed611695d1e19a9bdabd615ac'
2025-03-28T06:36:32.3674814Z 
2025-03-28T06:36:32.3675204Z �[1m�[31m/io/sklearn/utils/tests/test_estimator_checks.py�[0m:919: 
2025-03-28T06:36:32.3675540Z _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
2025-03-28T06:36:32.3675945Z �[1m�[31m/io/sklearn/utils/_param_validation.py�[0m:218: in wrapper
2025-03-28T06:36:32.3676248Z     return func(*args, **kwargs)
2025-03-28T06:36:32.3676516Z         args       = (LinearRegression(),)
2025-03-28T06:36:32.3676790Z         func       = <function check_estimator at 0xd8591668>
2025-03-28T06:36:32.3677211Z         func_sig   = <Signature (estimator=None, generate_only=False, *, legacy: 'bool' = True, expected_failed_checks: 'dict[str, str] | N...al['warn'] | None" = 'warn', on_fail: "Literal['raise', 'warn'] | None" = 'raise', callback: 'Callable | None' = None)>
2025-03-28T06:36:32.3677607Z         global_skip_validation = False
2025-03-28T06:36:32.3677881Z         kwargs     = {'expected_failed_checks': {}}
2025-03-28T06:36:32.3678234Z         parameter_constraints = {'callback': [<built-in function callable>, None], 'expected_failed_checks': [<class 'dict'>, None], 'generate_only': ['boolean'], 'legacy': ['boolean'], ...}
2025-03-28T06:36:32.3678659Z         params     = {'callback': None, 'estimator': LinearRegression(), 'expected_failed_checks': {}, 'generate_only': False, ...}
2025-03-28T06:36:32.3678985Z         prefer_skip_nested_validation = False
2025-03-28T06:36:32.3679252Z         to_ignore  = ['self', 'cls']
2025-03-28T06:36:32.3679647Z �[1m�[31m/io/sklearn/utils/estimator_checks.py�[0m:856: in check_estimator
2025-03-28T06:36:32.3679964Z     check(estimator)
2025-03-28T06:36:32.3680221Z         callback   = None
2025-03-28T06:36:32.3680536Z         check      = functools.partial(<function check_sample_weight_equivalence_on_dense_data at 0xd8591e88>, 'LinearRegression')
2025-03-28T06:36:32.3680953Z         check_result = {'check_name': 'check_sample_weight_equivalence_on_dense_data', 'estimator': LinearRegression(), 'exception': None, 'expected_to_fail': False, ...}
2025-03-28T06:36:32.3681402Z         estimator  = LinearRegression(positive=True)
2025-03-28T06:36:32.3681697Z         expected_failed_checks = {}
2025-03-28T06:36:32.3681899Z         generate_only = False
2025-03-28T06:36:32.3682090Z         legacy     = True
2025-03-28T06:36:32.3682266Z         name       = 'LinearRegression'
2025-03-28T06:36:32.3682442Z         on_fail    = 'raise'
2025-03-28T06:36:32.3682732Z         on_skip    = 'warn'
2025-03-28T06:36:32.3682922Z         reason     = 'Check is not expected to fail'
2025-03-28T06:36:32.3683274Z         test_can_fail = False
2025-03-28T06:36:32.3683596Z         test_results = [{'check_name': 'check_estimator_cloneable', 'estimator': LinearRegression(), 'exception': None, 'expected_to_fail': F...k_no_attributes_set_in_init', 'estimator': LinearRegression(), 'exception': None, 'expected_to_fail': False, ...}, ...]
2025-03-28T06:36:32.3684106Z �[1m�[31m/io/sklearn/utils/estimator_checks.py�[0m:1570: in check_sample_weight_equivalence_on_dense_data
2025-03-28T06:36:32.3684400Z     _check_sample_weight_equivalence(name, estimator_orig, sparse_container=None)
2025-03-28T06:36:32.3684731Z         estimator_orig = LinearRegression(positive=True)
2025-03-28T06:36:32.3684930Z         name       = 'LinearRegression'
2025-03-28T06:36:32.3685217Z �[1m�[31m/io/sklearn/utils/_testing.py�[0m:145: in wrapper
2025-03-28T06:36:32.3685439Z     return fn(*args, **kwargs)
2025-03-28T06:36:32.3685650Z         args       = ('LinearRegression', LinearRegression(positive=True))
2025-03-28T06:36:32.3685891Z         fn         = <function _check_sample_weight_equivalence at 0xd8591de8>
2025-03-28T06:36:32.3686105Z         kwargs     = {'sparse_container': None}
2025-03-28T06:36:32.3686316Z         self       = _IgnoreWarnings(record=True)
2025-03-28T06:36:32.3686643Z �[1m�[31m/io/sklearn/utils/estimator_checks.py�[0m:1566: in _check_sample_weight_equivalence
2025-03-28T06:36:32.3686902Z     assert_allclose_dense_sparse(X_pred1, X_pred2, err_msg=err_msg)
2025-03-28T06:36:32.3687138Z         X          = array([[0.37454012, 0.95071431, 0.73199394, 0.59865848, 0.15601864,
2025-03-28T06:36:32.3687418Z         0.15599452, 0.05808361, 0.86617615, 0.6011..., 0.98663958, 0.3742708 , 0.37064215, 0.81279957,
2025-03-28T06:36:32.3687667Z         0.94724858, 0.98600106, 0.75337819, 0.37625959, 0.08350072]])
2025-03-28T06:36:32.3687906Z         X_pred1    = array([ 8.88178420e-16,  1.00000000e+00,  2.00000000e+00,  1.18549798e+00,
2025-03-28T06:36:32.3688173Z         4.06241761e+00,  1.00000000e+00,  2...5767e+00,  2.00000000e+00, -2.79936287e-02, -8.90642835e-01,
2025-03-28T06:36:32.3688439Z        -8.00809991e-01,  1.00000000e+00,  1.00000000e+00])
2025-03-28T06:36:32.3688661Z         X_pred2    = array([0.        , 1.        , 2.        , 0.94186541, 1.72670876,
2025-03-28T06:36:32.3688871Z        1.        , 2.        , 2.        , 1.8723887 , 2.        ,
2025-03-28T06:36:32.3689077Z        1.50877777, 0.76107365, 1.70933257, 1.        , 1.        ])
2025-03-28T06:36:32.3689323Z         X_repeated = array([[0.37454012, 0.95071431, 0.73199394, 0.59865848, 0.15601864,
2025-03-28T06:36:32.3689588Z         0.15599452, 0.05808361, 0.86617615, 0.6011..., 0.98663958, 0.3742708 , 0.37064215, 0.81279957,
2025-03-28T06:36:32.3689834Z         0.94724858, 0.98600106, 0.75337819, 0.37625959, 0.08350072]])
2025-03-28T06:36:32.3690073Z         X_weighted = array([[0.60754485, 0.17052412, 0.06505159, 0.94888554, 0.96563203,
2025-03-28T06:36:32.3690355Z         0.80839735, 0.30461377, 0.09767211, 0.6842..., 0.69673717, 0.62894285, 0.87747201, 0.73507104,
2025-03-28T06:36:32.3690606Z         0.80348093, 0.28203457, 0.17743954, 0.75061475, 0.80683474]])
2025-03-28T06:36:32.3690894Z         err_msg    = 'Comparing the output of LinearRegression.predict revealed that fitting with `sample_weight` is not equivalent to fitting with removed or repeated data points.'
2025-03-28T06:36:32.3691216Z         estimator_orig = LinearRegression(positive=True)
2025-03-28T06:36:32.3691435Z         estimator_repeated = LinearRegression(positive=True)
2025-03-28T06:36:32.3691653Z         estimator_weighted = LinearRegression(positive=True)
2025-03-28T06:36:32.3691853Z         method     = 'predict'
2025-03-28T06:36:32.3692029Z         n_samples  = 15
2025-03-28T06:36:32.3692223Z         name       = 'LinearRegression'
2025-03-28T06:36:32.3692420Z         rng        = RandomState(MT19937) at 0xCD5304A8
2025-03-28T06:36:32.3692720Z         sparse_container = None
2025-03-28T06:36:32.3692923Z         sw         = array([3, 4, 0, 3, 1, 0, 4, 4, 0, 3, 0, 0, 3, 2, 0])
2025-03-28T06:36:32.3693289Z         y          = array([0, 1, 2, 2, 1, 1, 2, 2, 1, 2, 0, 0, 1, 1, 1])
2025-03-28T06:36:32.3693514Z         y_repeated = array([0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2025-03-28T06:36:32.4389210Z        1, 1, 1, 1, 1])
2025-03-28T06:36:32.4392227Z         y_weighted = array([1, 2, 1, 2, 1, 1, 2, 1, 0, 2, 0, 2, 0, 1, 1])
2025-03-28T06:36:32.4394205Z �[1m�[31m/io/sklearn/utils/_testing.py�[0m:283: in assert_allclose_dense_sparse
2025-03-28T06:36:32.4395241Z     assert_allclose(x, y, rtol=rtol, atol=atol, err_msg=err_msg)
2025-03-28T06:36:32.4395958Z         atol       = 1e-09
2025-03-28T06:36:32.4396339Z         err_msg    = 'Comparing the output of LinearRegression.predict revealed that fitting with `sample_weight` is not equivalent to fitting with removed or repeated data points.'
2025-03-28T06:36:32.4396710Z         rtol       = 1e-07
2025-03-28T06:36:32.4397020Z         x          = array([ 8.88178420e-16,  1.00000000e+00,  2.00000000e+00,  1.18549798e+00,
2025-03-28T06:36:32.4397420Z         4.06241761e+00,  1.00000000e+00,  2...5767e+00,  2.00000000e+00, -2.79936287e-02, -8.90642835e-01,
2025-03-28T06:36:32.4397769Z        -8.00809991e-01,  1.00000000e+00,  1.00000000e+00])
2025-03-28T06:36:32.4398083Z         y          = array([0.        , 1.        , 2.        , 0.94186541, 1.72670876,
2025-03-28T06:36:32.4398404Z        1.        , 2.        , 2.        , 1.8723887 , 2.        ,
2025-03-28T06:36:32.4398711Z        1.50877777, 0.76107365, 1.70933257, 1.        , 1.        ])
2025-03-28T06:36:32.4399020Z _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
2025-03-28T06:36:32.4399360Z 
2025-03-28T06:36:32.4399684Z actual = array([ 8.88178420e-16,  1.00000000e+00,  2.00000000e+00,  1.18549798e+00,
2025-03-28T06:36:32.4400047Z         4.06241761e+00,  1.00000000e+00,  2...5767e+00,  2.00000000e+00, -2.79936287e-02, -8.90642835e-01,
2025-03-28T06:36:32.4400383Z        -8.00809991e-01,  1.00000000e+00,  1.00000000e+00])
2025-03-28T06:36:32.4400717Z desired = array([0.        , 1.        , 2.        , 0.94186541, 1.72670876,
2025-03-28T06:36:32.4401016Z        1.        , 2.        , 2.        , 1.8723887 , 2.        ,
2025-03-28T06:36:32.4401305Z        1.50877777, 0.76107365, 1.70933257, 1.        , 1.        ])
2025-03-28T06:36:32.4401599Z rtol = 1e-07, atol = 1e-09, equal_nan = True
2025-03-28T06:36:32.4401994Z err_msg = 'Comparing the output of LinearRegression.predict revealed that fitting with `sample_weight` is not equivalent to fitting with removed or repeated data points.'
2025-03-28T06:36:32.4402357Z verbose = True
2025-03-28T06:36:32.4402649Z 
2025-03-28T06:36:32.4403197Z     def assert_allclose(
2025-03-28T06:36:32.4403533Z         actual, desired, rtol=None, atol=0.0, equal_nan=True, err_msg="", verbose=True
2025-03-28T06:36:32.4403835Z     ):
2025-03-28T06:36:32.4404125Z         """dtype-aware variant of numpy.testing.assert_allclose
2025-03-28T06:36:32.4404412Z     
2025-03-28T06:36:32.4404713Z         This variant introspects the least precise floating point dtype
2025-03-28T06:36:32.4405098Z         in the input argument and automatically sets the relative tolerance
2025-03-28T06:36:32.4405430Z         parameter to 1e-4 float32 and use 1e-7 otherwise (typically float64
2025-03-28T06:36:32.4405731Z         in scikit-learn).
2025-03-28T06:36:32.4406156Z     
2025-03-28T06:36:32.4406440Z         `atol` is always left to 0. by default. It should be adjusted manually
2025-03-28T06:36:32.4406762Z         to an assertion-specific value in case there are null values expected
2025-03-28T06:36:32.4407464Z         in `desired`.
2025-03-28T06:36:32.4407766Z     
2025-03-28T06:36:32.4408043Z         The aggregate tolerance is `atol + rtol * abs(desired)`.
2025-03-28T06:36:32.4408313Z     
2025-03-28T06:36:32.4408563Z         Parameters
2025-03-28T06:36:32.4408818Z         ----------
2025-03-28T06:36:32.4409095Z         actual : array_like
2025-03-28T06:36:32.4409358Z             Array obtained.
2025-03-28T06:36:32.4409820Z         desired : array_like
2025-03-28T06:36:32.4410093Z             Array desired.
2025-03-28T06:36:32.4410364Z         rtol : float, optional, default=None
2025-03-28T06:36:32.4410653Z             Relative tolerance.
2025-03-28T06:36:32.4410943Z             If None, it is set based on the provided arrays' dtypes.
2025-03-28T06:36:32.4411236Z         atol : float, optional, default=0.
2025-03-28T06:36:32.4411512Z             Absolute tolerance.
2025-03-28T06:36:32.4411809Z         equal_nan : bool, optional, default=True
2025-03-28T06:36:32.4412101Z             If True, NaNs will compare equal.
2025-03-28T06:36:32.4412731Z         err_msg : str, optional, default=''
2025-03-28T06:36:32.4413109Z             The error message to be printed in case of failure.
2025-03-28T06:36:32.4413412Z         verbose : bool, optional, default=True
2025-03-28T06:36:32.4413734Z             If True, the conflicting values are appended to the error message.
2025-03-28T06:36:32.4414013Z     
2025-03-28T06:36:32.4414256Z         Raises
2025-03-28T06:36:32.4414514Z         ------
2025-03-28T06:36:32.4414771Z         AssertionError
2025-03-28T06:36:32.4415083Z             If actual and desired are not equal up to specified precision.
2025-03-28T06:36:32.4415491Z     
2025-03-28T06:36:32.4415753Z         See Also
2025-03-28T06:36:32.4416185Z         --------
2025-03-28T06:36:32.4416466Z         numpy.testing.assert_allclose
2025-03-28T06:36:32.4416900Z     
2025-03-28T06:36:32.4417154Z         Examples
2025-03-28T06:36:32.4417419Z         --------
2025-03-28T06:36:32.4417685Z         >>> import numpy as np
2025-03-28T06:36:32.4418011Z         >>> from sklearn.utils._testing import assert_allclose
2025-03-28T06:36:32.4418314Z         >>> x = [1e-5, 1e-3, 1e-1]
2025-03-28T06:36:32.4418599Z         >>> y = np.arccos(np.cos(x))
2025-03-28T06:36:32.4418899Z         >>> assert_allclose(x, y, rtol=1e-5, atol=0)
2025-03-28T06:36:32.4419379Z         >>> a = np.full(shape=10, fill_value=1e-5, dtype=np.float32)
2025-03-28T06:36:32.4420007Z         >>> assert_allclose(a, 1e-5)
2025-03-28T06:36:32.4420262Z         """
2025-03-28T06:36:32.4420502Z         dtypes = []
2025-03-28T06:36:32.4420743Z     
2025-03-28T06:36:32.4421010Z         actual, desired = np.asanyarray(actual), np.asanyarray(desired)
2025-03-28T06:36:32.4421324Z         dtypes = [actual.dtype, desired.dtype]
2025-03-28T06:36:32.4421576Z     
2025-03-28T06:36:32.4421816Z         if rtol is None:
2025-03-28T06:36:32.4422102Z             rtols = [1e-4 if dtype == np.float32 else 1e-7 for dtype in dtypes]
2025-03-28T06:36:32.4422404Z             rtol = max(rtols)
2025-03-28T06:36:32.4422867Z     
2025-03-28T06:36:32.4423151Z >       np_assert_allclose(
2025-03-28T06:36:32.4423412Z             actual,
2025-03-28T06:36:32.4423660Z             desired,
2025-03-28T06:36:32.4423929Z             rtol=rtol,
2025-03-28T06:36:32.4424278Z             atol=atol,
2025-03-28T06:36:32.4424545Z             equal_nan=equal_nan,
2025-03-28T06:36:32.4424804Z             err_msg=err_msg,
2025-03-28T06:36:32.4425069Z             verbose=verbose,
2025-03-28T06:36:32.4425335Z         )
2025-03-28T06:36:32.4425695Z �[1m�[31mE       AssertionError: �[0m
2025-03-28T06:36:32.4426095Z �[1m�[31mE       Not equal to tolerance rtol=1e-07, atol=1e-09�[0m
2025-03-28T06:36:32.4426645Z �[1m�[31mE       Comparing the output of LinearRegression.predict revealed that fitting with `sample_weight` is not equivalent to fitting with removed or repeated data points.�[0m
2025-03-28T06:36:32.4427542Z �[1m�[31mE       Mismatched elements: 6 / 15 (40%)�[0m
2025-03-28T06:36:32.4427985Z �[1m�[31mE       Max absolute difference among violations: 2.51014256�[0m
2025-03-28T06:36:32.4428409Z �[1m�[31mE       Max relative difference among violations: 2.17024526�[0m
2025-03-28T06:36:32.4428858Z �[1m�[31mE        ACTUAL: array([ 8.881784e-16,  1.000000e+00,  2.000000e+00,  1.185498e+00,�[0m
2025-03-28T06:36:32.4429327Z �[1m�[31mE               4.062418e+00,  1.000000e+00,  2.000000e+00,  2.000000e+00,�[0m
2025-03-28T06:36:32.4429765Z �[1m�[31mE               4.105658e+00,  2.000000e+00, -2.799363e-02, -8.906428e-01,�[0m
2025-03-28T06:36:32.4430365Z �[1m�[31mE              -8.008100e-01,  1.000000e+00,  1.000000e+00])�[0m
2025-03-28T06:36:32.4430805Z �[1m�[31mE        DESIRED: array([0.      , 1.      , 2.      , 0.941865, 1.726709, 1.      ,�[0m
2025-03-28T06:36:32.4431250Z �[1m�[31mE              2.      , 2.      , 1.872389, 2.      , 1.508778, 0.761074,�[0m
2025-03-28T06:36:32.4431622Z �[1m�[31mE              1.709333, 1.      , 1.      ])�[0m
2025-03-28T06:36:32.4431810Z 
2025-03-28T06:36:32.4432416Z actual     = array([ 8.88178420e-16,  1.00000000e+00,  2.00000000e+00,  1.18549798e+00,
2025-03-28T06:36:32.4433326Z         4.06241761e+00,  1.00000000e+00,  2...5767e+00,  2.00000000e+00, -2.79936287e-02, -8.90642835e-01,
2025-03-28T06:36:32.4433865Z        -8.00809991e-01,  1.00000000e+00,  1.00000000e+00])
2025-03-28T06:36:32.4434473Z atol       = 1e-09
2025-03-28T06:36:32.4434795Z desired    = array([0.        , 1.        , 2.        , 0.94186541, 1.72670876,
2025-03-28T06:36:32.4435122Z        1.        , 2.        , 2.        , 1.8723887 , 2.        ,
2025-03-28T06:36:32.4435605Z        1.50877777, 0.76107365, 1.70933257, 1.        , 1.        ])
2025-03-28T06:36:32.4435932Z dtypes     = [dtype('float64'), dtype('float64')]
2025-03-28T06:36:32.4436253Z equal_nan  = True
2025-03-28T06:36:32.4436639Z err_msg    = 'Comparing the output of LinearRegression.predict revealed that fitting with `sample_weight` is not equivalent to fitting with removed or repeated data points.'
2025-03-28T06:36:32.4437185Z rtol       = 1e-07
2025-03-28T06:36:32.4439658Z verbose    = True
2025-03-28T06:36:32.4439971Z 
2025-03-28T06:36:32.4440407Z �[1m�[31m/io/sklearn/utils/_testing.py�[0m:237: AssertionError

Looking at the software runtime info of each I only see two differences:

  • the pip version;
  • the CPU model.

All other dependencies seem to match, including the openblas version inspected by threadpoolctl.

EDIT: this is wrong, the scipy version is not the same and I missed it.

@github-actions github-actions bot added the Needs Triage Issue requires triage label Mar 28, 2025
@ogrisel ogrisel added Build / CI Bug and removed Needs Triage Issue requires triage labels Mar 28, 2025
@ogrisel
Copy link
Member Author
ogrisel commented Mar 28, 2025

The pip version is probably not the culprit. Maybe this is an openblas problem when running in 32 bit only on some CPU models? However, both detect the BLAS kernels optimized for the Nehalem architecture.

@ogrisel
Copy link
Member Author
ogrisel commented Mar 28, 2025

Also, the magnitude of the violations in the failing assert_allclose statement on the predicted values are definitely outside the range of machine precision level rounding errors:

Max absolute difference among violations: 2.51014256
Max relative difference among violations: 2.17024526

@ogrisel
Copy link
Member Author
ogrisel commented Mar 28, 2025

The two failures involve the same checks on LinearRegression, one with positive=True and the other with the default parameters.

@thedanindanger
Copy link

Confirming the same issue on PR #31031 This PR only modifies the positive implementation of Ridge _lbfgs solver. It does not touch the base linear regression class.

@antoinebaker
Copy link
Contributor
antoinebaker commented Mar 28, 2025

As a possible culprit, LinearRegression(positive=True) uses scipy.optimize.nnls as a solver.
The passing CI uses scipy: 1.14.1 while the failing one uses scipy: 1.15.2

@antoinebaker
Copy link
Contributor

There was a reimplementation of nnls in cython between the two versions
https://docs.scipy.org/doc/scipy-1.15.1/release/1.15.0-notes.html#scipy-optimize-improvements

@antoinebaker
Copy link
Contributor

I'm not familiar with the handling of float32 vs float64 arrays in scipy, but it seems that the cython nnls is written for float64 arrays ?
https://github.com/scipy/scipy/blob/0f1fd4a7268b813fa2b844ca6038e4dfdf90084a/scipy/optimize/_cython_nnls.pyx#L16-L18

@ogrisel
Copy link
Member Author
ogrisel commented Mar 28, 2025

How could I miss the change in scipy versions...

@ogrisel
Copy link
Member Author
ogrisel commented Mar 28, 2025

Thanks very much for spotting the root cause @antoinebaker. Could you please try to craft a minimal reproducer for scipy that does not rely on scikit-learn?

Meanwhile I will change our test suite to mark this case as XFAIL.

@ogrisel
Copy link
Member Author
ogrisel commented Mar 28, 2025

I'm not familiar with the handling of float32 vs float64 arrays in scipy, but it seems that the cython nnls is written for float64 arrays ?

That should not be a problem. The debian_32bit CI code is running a 32 bit Python runtime but code handling float64 bit inputs should work the same. 32 bit runtimes only limit the size of the allocatable memory to 2 GB per allocation. We should not run into that limit in our tests. And the message would not be the same if we did.

@ogrisel
Copy link
Member Author
ogrisel commented Mar 28, 2025

To reproduce locally, you can

docker run -v `pwd`:/io --platform linux/386 -it debian

then you can access your host folder under the /io mount point with the 32 bit linux container.

Off course you have to reinstall all the build dependencies (gcc and co) using apt within the container (apt update && apt install build-essential python3-dev python3-scipy).

@ogrisel
Copy link
Member Author
ogrisel commented Mar 28, 2025

I wonder why this did not fail in the lockfile update PR though...

EDIT: this is because we install python3-scipy using APT on this build and our lockfile system does not control those.

@ogrisel ogrisel changed the title Failing CI Failing CI for check_sample_weight_equivalence_on_dense_data with LinearRegerssion on debian_32bit Mar 28, 2025
@ogrisel
Copy link
Member Author
ogrisel commented Mar 31, 2025

@lesteve @antoinebaker actually the pyodide build is running on an older scipy release (scipy 1.14.1) than the debian_32bit build (scipy 1.15.2):

https://github.com/scikit-learn/scikit-learn/actions/runs/14163691264/job/39673230652#step:3:1863

So it's very likely that this will crash once the pyodide distribution gets updated.

So performing a root cause analysis / minimal reproducer to report upstream is likely useful.

@antoinebaker
Copy link
Contributor

I confirm that we can reproduce the bug on a container with 32bit debian and scipy 1.15.2, no bug for scipy 1.14.1.

Currently the test is close to our sample weight test

import numpy as np
from scipy.optimize import nnls

def preprocess(X, y, sw):
    if sw is None:
        sw = np.ones(len(y))
    X_mean = np.average(X, weights=sw, axis=0)
    y_mean = np.average(y, weights=sw)
    X_scaled = np.sqrt(sw)[:, np.newaxis] * (X - X_mean)
    y_scaled = np.sqrt(sw) * (y - y_mean)
    return X_scaled, y_scaled

def linreg(X, y, sw):
    X_scaled, y_scaled = preprocess(X, y, sw)
    return nnls(X_scaled, y_scaled)[0]

def test_linreg_sample_weight_equivalence():
    rng = np.random.RandomState(42)
    n_samples = 15
    X = rng.rand(n_samples, n_samples * 2)
    y = rng.randint(0, 3, size=n_samples)
    sw = rng.randint(0, 5, size=n_samples)
    # repeated
    X_repeated = X.repeat(repeats=sw, axis=0)
    y_repeated = y.repeat(repeats=sw)
    # compare
    coef_weighted = linreg(X, y, sw)
    coef_repeated = linreg(X_repeated, y_repeated, None)
    assert np.allclose(coef_weighted, coef_repeated)

Should I try to come up with a simpler test for the reproducer ?

@lesteve
Copy link
Member
lesteve commented Apr 2, 2025

Nice!

Should I try to come up with a simpler test for the reproducer ?

Ideally yes? Maybe there is a way to show that the output of nnls in the 32bit Docker image and the output of nnls is very different than the one on more "normal" machine? Wild-guess maybe even using dtype=np.float64 in the 32bit docker image already shows a big difference?

Out of curiosity: I guess you are compiling scipy from source to get 1.14.1 inside the Docker 32 bit image?

@lesteve
Copy link
Member
lesteve commented Apr 2, 2025

It also looks like NNLS has been rewritten in C very recently scipy/scipy#22524 so I guess you may want to build scipy from the main branch as well to see whether the problem is still there ...

@antoinebaker
Copy link
Contributor

Out of curiosity: I guess you are compiling scipy from source to get 1.14.1 inside the Docker 32 bit image?

I used venv to pip install specific versions of scipy

# Use a Debian base image
FROM debian:trixie
# Update the package list
RUN apt update
# Install scipy dependencies
RUN apt install -y build-essential gcc g++ gfortran libopenblas-dev liblapack-dev pkg-config python3-pip python3-dev python3-venv
# Install specific version of scipy
RUN python3 -m venv opt/scipy1.14.1
RUN opt/scipy1.14.1/bin/pip install scipy==1.14.1
RUN python3 -m venv opt/scipy1.15.2
RUN opt/scipy1.15.2/bin/pip install scipy==1.15.2

@antoinebaker
Copy link
Contributor

It also looks like NNLS has been rewritten in C very recently scipy/scipy#22524 so I guess you may want to build scipy from the main branch as well to see whether the problem is still there ...

Unfortunately the problem remains on scipy main.

@ilayn
Copy link
ilayn commented Apr 4, 2025

As you know, I have been rewriting a lot of F77 SciPy code in C (essentially from one evil to a more familiar evil) and calling up-to-date LAPACK routines instead of the ancient hand-rolled versions of these routines. However an immediate downside of this is that the problems that have been hiding in the dark is now coming out as I call more routines from C directly.

One thing, I am noticing is that the openblas-pthreads is being pulled instead of the vanilla version or somehow OPENBLAS_NUM_THREADS is set something other than 1 and causing strange issues. On the other hand, MacOS with MKL is having a different type of problem that I cannot see. More strangely we are not able to catch these issues with our SciPy test suite although there are approx. 16 different architectures are being tested. And none of them trips up including our own 32-bit CI env.

So I am willing to bet on the LAPACK backend that is the culprit. But I can't figure out how to prove this claim. In the meantime I'll add your example to our test suite and see how it fares in our 32bit run.

@ilayn
Copy link
ilayn commented Apr 4, 2025

Nevermind we got hit too. So this is on me. Sorry for the noise.

@ilayn
Copy link
ilayn commented Apr 6, 2025

Hi again, just merged a fix for the reproducer and hoping that it also fixes the actual issue in the test suite. Could you please retry with the new main and let me know?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
5 participants
0