Skip to content

Commit 58a47d6

Browse files
authored
FIX Explicitly disable array API dispatching for reference computation (scikit-learn#34378)
1 parent dfaa3f6 commit 58a47d6

20 files changed

Lines changed: 171 additions & 108 deletions

File tree

sklearn/_loss/tests/test_loss.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1455,9 +1455,12 @@ def _assert_array_api_result(
14551455

14561456
method = getattr(loss_instance, method_name)
14571457
array_api_method = getattr(array_api_loss_instance, method_name)
1458-
result_np = method(
1459-
y_true=y_true, raw_prediction=raw_prediction, sample_weight=sample_weight_np
1460-
)
1458+
with config_context(array_api_dispatch=False):
1459+
result_np = method(
1460+
y_true=y_true,
1461+
raw_prediction=raw_prediction,
1462+
sample_weight=sample_weight_np,
1463+
)
14611464
with config_context(array_api_dispatch=True):
14621465
result_xp = array_api_method(
14631466
y_true=y_true_xp,

sklearn/covariance/tests/test_covariance.py

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,8 @@ def test_empirical_covariance_array_api(
395395
X_np = X.astype(dtype_name, copy=False)
396396
X_xp = xp.asarray(X_np, device=device)
397397

398-
result_np = empirical_covariance(X_np, assume_centered=assume_centered)
398+
with config_context(array_api_dispatch=False):
399+
result_np = empirical_covariance(X_np, assume_centered=assume_centered)
399400

400401
with config_context(array_api_dispatch=True):
401402
result_xp = empirical_covariance(X_xp, assume_centered=assume_centered)
@@ -418,7 +419,8 @@ def test_ledoit_wolf_shrinkage_array_api(
418419
X_np = X[:, :n_features].astype(dtype_name, copy=False)
419420
X_xp = xp.asarray(X_np, device=device)
420421

421-
shrinkage_np = ledoit_wolf_shrinkage(X_np)
422+
with config_context(array_api_dispatch=False):
423+
shrinkage_np = ledoit_wolf_shrinkage(X_np)
422424

423425
with config_context(array_api_dispatch=True):
424426
shrinkage_xp = ledoit_wolf_shrinkage(X_xp)
@@ -436,10 +438,11 @@ def test_log_likelihood_array_api(array_namespace, device_name, dtype_name):
436438
xp, device = _array_api_for_tests(array_namespace, device_name, dtype_name)
437439

438440
X_np = X.astype(dtype_name, copy=False)
439-
emp_cov_np = empirical_covariance(X_np)
440-
est = EmpiricalCovariance().fit(X_np)
441-
precision_np = est.get_precision()
442-
result_np = log_likelihood(emp_cov_np, precision_np)
441+
with config_context(array_api_dispatch=False):
442+
emp_cov_np = empirical_covariance(X_np)
443+
est = EmpiricalCovariance().fit(X_np)
444+
precision_np = est.get_precision()
445+
result_np = log_likelihood(emp_cov_np, precision_np)
443446

444447
# Downcast to target dtype before converting because `empirical_covariance` can
445448
# return float64 for numpy and MPS only supports float32
@@ -469,8 +472,9 @@ def test_score_array_api(array_namespace, device_name, dtype_name, store_precisi
469472
lw_xp = LedoitWolf(store_precision=store_precision).fit(X_xp)
470473
score_xp = lw_xp.score(X_xp)
471474

472-
lw_np = LedoitWolf(store_precision=store_precision).fit(X_np)
473-
score_np = lw_np.score(X_np)
475+
with config_context(array_api_dispatch=False):
476+
lw_np = LedoitWolf(store_precision=store_precision).fit(X_np)
477+
score_np = lw_np.score(X_np)
474478

475479
assert isinstance(score_xp, float)
476480
assert_allclose(score_np, score_xp, atol=_atol_for_type(dtype_name))
@@ -488,8 +492,9 @@ def test_error_norm_array_api(array_namespace, device_name, dtype_name, norm):
488492
X_np = X.astype(dtype_name, copy=False)
489493
X_xp = xp.asarray(X_np, device=device)
490494

491-
lw_np = LedoitWolf().fit(X_np)
492-
comp_cov_np = empirical_covariance(X_np)
495+
with config_context(array_api_dispatch=False):
496+
lw_np = LedoitWolf().fit(X_np)
497+
comp_cov_np = empirical_covariance(X_np)
493498

494499
with config_context(array_api_dispatch=True):
495500
lw_xp = LedoitWolf().fit(X_xp)
@@ -498,7 +503,8 @@ def test_error_norm_array_api(array_namespace, device_name, dtype_name, norm):
498503
comp_cov_xp = xp.asarray(comp_cov_np.astype(dtype_name), device=device)
499504
result_xp = lw_xp.error_norm(comp_cov_xp, norm=norm)
500505

501-
result_np = lw_np.error_norm(comp_cov_np, norm=norm)
506+
with config_context(array_api_dispatch=False):
507+
result_np = lw_np.error_norm(comp_cov_np, norm=norm)
502508
assert_allclose(result_np, float(result_xp), atol=_atol_for_type(dtype_name))
503509

504510

@@ -513,8 +519,9 @@ def test_mahalanobis_array_api(array_namespace, device_name, dtype_name):
513519
X_np = X.astype(dtype_name, copy=False)
514520
X_xp = xp.asarray(X_np, device=device)
515521

516-
lw_np = LedoitWolf().fit(X_np)
517-
dist_np = lw_np.mahalanobis(X_np)
522+
with config_context(array_api_dispatch=False):
523+
lw_np = LedoitWolf().fit(X_np)
524+
dist_np = lw_np.mahalanobis(X_np)
518525

519526
with config_context(array_api_dispatch=True):
520527
lw_xp = LedoitWolf().fit(X_xp)

sklearn/decomposition/tests/test_pca.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -942,9 +942,10 @@ def check_array_api_get_precision(
942942
iris_np = iris.data.astype(dtype_name)
943943
iris_xp = xp.asarray(iris_np, device=device)
944944

945-
estimator.fit(iris_np)
946-
precision_np = estimator.get_precision()
947-
covariance_np = estimator.get_covariance()
945+
with config_context(array_api_dispatch=False):
946+
estimator.fit(iris_np)
947+
precision_np = estimator.get_precision()
948+
covariance_np = estimator.get_covariance()
948949

949950
rtol = 2e-4 if iris_np.dtype == "float32" else 2e-7
950951
with config_context(array_api_dispatch=True):
@@ -1057,10 +1058,11 @@ def test_pca_mle_array_api_compliance(
10571058
X_xp = xp.asarray(X, device=device)
10581059
y_xp = xp.asarray(y, device=device)
10591060

1060-
est.fit(X, y)
1061+
with config_context(array_api_dispatch=False):
1062+
est.fit(X, y)
10611063

1062-
components_np = est.components_
1063-
explained_variance_np = est.explained_variance_
1064+
components_np = est.components_
1065+
explained_variance_np = est.explained_variance_
10641066

10651067
est_xp = clone(est)
10661068
with config_context(array_api_dispatch=True):

sklearn/linear_model/_glm/tests/test_glm.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,13 +1282,15 @@ def test_poisson_regressor_array_api_compliance(
12821282

12831283
params = dict(alpha=1, solver="lbfgs", max_iter=500)
12841284
params["tol"] = 3e-6 if dtype_name == "float32" else 1e-13
1285-
glm_np = PoissonRegressor(**params).fit(X_np, y_np, sample_weight=sample_weight)
1286-
assert glm_np.n_iter_ < glm_np.max_iter
1285+
with config_context(array_api_dispatch=False):
1286+
glm_np = PoissonRegressor(**params).fit(X_np, y_np, sample_weight=sample_weight)
1287+
assert glm_np.n_iter_ < glm_np.max_iter
12871288

1288-
# Test that alpha was not too large for meaningful testing.
1289-
assert np.abs(glm_np.coef_).max() > 0.1
1289+
# Test that alpha was not too large for meaningful testing.
1290+
assert np.abs(glm_np.coef_).max() > 0.1
1291+
1292+
predict_np = glm_np.predict(X_np)
12901293

1291-
predict_np = glm_np.predict(X_np)
12921294
atol = _atol_for_type(dtype_name)
12931295
rtol = 2e-3 if dtype_name == "float32" else 3e-7
12941296

sklearn/linear_model/tests/test_logistic.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2878,16 +2878,23 @@ def test_logistic_regression_array_api_compliance(
28782878
lr_params = dict(C=1e-2, solver=solver, max_iter=500, class_weight=class_weight)
28792879
lr_params["tol"] = 1e-6 if dtype_name == "float32" else 1e-12
28802880

2881-
lr_np = LogisticRegression(**lr_params).fit(X_np, y_np, sample_weight=sample_weight)
2882-
# Make sure that the reference fit converged.
2883-
assert lr_np.n_iter_ < lr_np.max_iter
2881+
# Compute the reference fit and predictions with array API dispatch explicitly
2882+
# disabled so that they always follow the NumPy code path, independently of
2883+
# the default value of the `array_api_dispatch` config flag.
2884+
with config_context(array_api_dispatch=False):
2885+
lr_np = LogisticRegression(**lr_params).fit(
2886+
X_np, y_np, sample_weight=sample_weight
2887+
)
2888+
# Make sure that the reference fit converged.
2889+
assert lr_np.n_iter_ < lr_np.max_iter
28842890

2885-
# Test that C was not too large for meaningful testing.
2886-
assert np.abs(lr_np.coef_).max() > 0.1
2891+
# Test that C was not too large for meaningful testing.
2892+
assert np.abs(lr_np.coef_).max() > 0.1
2893+
2894+
predict_proba_np = lr_np.predict_proba(X_np)
2895+
preditct_log_proba_np = lr_np.predict_log_proba(X_np)
2896+
prediction_np = lr_np.predict(X_np)
28872897

2888-
predict_proba_np = lr_np.predict_proba(X_np)
2889-
preditct_log_proba_np = lr_np.predict_log_proba(X_np)
2890-
prediction_np = lr_np.predict(X_np)
28912898
if solver == "lbfgs":
28922899
atol = _atol_for_type(dtype_name) * 10
28932900
rtol = 1e-3 if dtype_name == "float32" else 1e-7

sklearn/linear_model/tests/test_ridge.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,9 +1460,10 @@ def check_array_api_attributes(
14601460
X_iris_xp = xp.asarray(X_iris_np, device=device)
14611461
y_iris_xp = xp.asarray(y_iris_np, device=device)
14621462

1463-
estimator.fit(X_iris_np, y_iris_np)
1464-
coef_np = estimator.coef_
1465-
intercept_np = estimator.intercept_
1463+
with config_context(array_api_dispatch=False):
1464+
estimator.fit(X_iris_np, y_iris_np)
1465+
coef_np = estimator.coef_
1466+
intercept_np = estimator.intercept_
14661467

14671468
with config_context(array_api_dispatch=True):
14681469
estimator_xp = clone(estimator).fit(X_iris_xp, y_iris_xp)
@@ -1535,9 +1536,11 @@ def test_ridge_classifier_multilabel_array_api(
15351536
X, y = make_multilabel_classification(random_state=0)
15361537
X_np = X.astype(dtype_name)
15371538
y_np = y.astype(dtype_name)
1538-
ridge_np = estimator.fit(X_np, y_np)
1539-
pred_np = ridge_np.predict(X_np)
1540-
classes_np = ridge_np.classes_.copy()
1539+
with config_context(array_api_dispatch=False):
1540+
ridge_np = estimator.fit(X_np, y_np)
1541+
pred_np = ridge_np.predict(X_np)
1542+
classes_np = ridge_np.classes_.copy()
1543+
15411544
with config_context(array_api_dispatch=True):
15421545
X_xp, y_xp = xp.asarray(X_np, device=device), xp.asarray(y_np, device=device)
15431546
ridge_xp = estimator.fit(X_xp, y_xp)
@@ -1562,8 +1565,9 @@ def test_ridge_per_target_alpha_array_api(array_namespace, device_name, dtype_na
15621565
y_np = y.astype(dtype_name)
15631566
alphas = np.asarray([1e-2, 0.1, 1.0], dtype=dtype_name)
15641567

1565-
ridge_np = Ridge(alpha=alphas, solver="svd").fit(X_np, y_np)
1566-
pred_np = ridge_np.predict(X_np)
1568+
with config_context(array_api_dispatch=False):
1569+
ridge_np = Ridge(alpha=alphas, solver="svd").fit(X_np, y_np)
1570+
pred_np = ridge_np.predict(X_np)
15671571

15681572
with config_context(array_api_dispatch=True):
15691573
X_xp, y_xp = xp.asarray(X_np, device=device), xp.asarray(y_np, device=device)

sklearn/mixture/tests/test_gaussian_mixture.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,7 +1521,8 @@ def test_gaussian_mixture_array_api_compliance(
15211521
init_params=init_params,
15221522
**additional_kwargs,
15231523
)
1524-
gmm.fit(X)
1524+
with sklearn.config_context(array_api_dispatch=False):
1525+
gmm.fit(X)
15251526

15261527
X_xp = xp.asarray(X, device=device)
15271528

@@ -1588,26 +1589,33 @@ def test_gaussian_mixture_array_api_compliance(
15881589
)
15891590

15901591
# Check methods
1592+
with sklearn.config_context(array_api_dispatch=False):
1593+
predict_np = gmm.predict(X)
1594+
predict_proba_np = gmm.predict_proba(X)
1595+
score_samples_np = gmm.score_samples(X)
1596+
score_np = gmm.score(X)
1597+
aic_np = gmm.aic(X)
1598+
bic_np = gmm.bic(X)
1599+
sample_X, sample_y = gmm.sample(10)
1600+
15911601
assert (
1592-
adjusted_rand_score(gmm.predict(X), move_to(predict_xp, xp=np, device="cpu"))
1593-
> 0.95
1602+
adjusted_rand_score(predict_np, move_to(predict_xp, xp=np, device="cpu")) > 0.95
15941603
)
15951604
assert_allclose(
1596-
gmm.predict_proba(X),
1605+
predict_proba_np,
15971606
move_to(predict_proba_xp, xp=np, device="cpu"),
15981607
rtol=increased_rtol,
15991608
atol=increased_atol,
16001609
)
16011610
assert_allclose(
1602-
gmm.score_samples(X),
1611+
score_samples_np,
16031612
move_to(score_samples_xp, xp=np, device="cpu"),
16041613
rtol=increased_rtol,
16051614
)
16061615
# comparing Python float so need explicit rtol when X has dtype float32
1607-
assert_allclose(gmm.score(X), score_xp, rtol=default_rtol)
1608-
assert_allclose(gmm.aic(X), aic_xp, rtol=default_rtol)
1609-
assert_allclose(gmm.bic(X), bic_xp, rtol=default_rtol)
1610-
sample_X, sample_y = gmm.sample(10)
1616+
assert_allclose(score_np, score_xp, rtol=default_rtol)
1617+
assert_allclose(aic_np, aic_xp, rtol=default_rtol)
1618+
assert_allclose(bic_np, bic_xp, rtol=default_rtol)
16111619
# generated samples are float64 so need explicit rtol when X has dtype float32
16121620
assert_allclose(
16131621
sample_X, move_to(sample_X_xp, xp=np, device="cpu"), rtol=default_rtol

sklearn/model_selection/tests/test_split.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1365,9 +1365,11 @@ def test_array_api_train_test_split(
13651365
y_np = y.astype(dtype_name)
13661366
y_xp = xp.asarray(y_np, device=device)
13671367

1368-
X_train_np, X_test_np, y_train_np, y_test_np = train_test_split(
1369-
X_np, y, random_state=0, shuffle=shuffle, stratify=stratify
1370-
)
1368+
with config_context(array_api_dispatch=False):
1369+
X_train_np, X_test_np, y_train_np, y_test_np = train_test_split(
1370+
X_np, y, random_state=0, shuffle=shuffle, stratify=stratify
1371+
)
1372+
13711373
with config_context(array_api_dispatch=True):
13721374
if stratify is not None:
13731375
stratify_xp = xp.asarray(stratify)

sklearn/model_selection/tests/test_validation.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2729,7 +2729,8 @@ def test_cross_val_predict_array_api_compliance(
27292729
with config_context(array_api_dispatch=True):
27302730
pred_xp = cross_val_predict(estimator, X_xp, y_xp, cv=cv)
27312731

2732-
pred_np = cross_val_predict(estimator, X_np, y_np, cv=cv)
2732+
with config_context(array_api_dispatch=False):
2733+
pred_np = cross_val_predict(estimator, X_np, y_np, cv=cv)
27332734
assert_allclose(
27342735
move_to(pred_xp, xp=np, device="cpu"), pred_np, atol=_atol_for_type(dtype_name)
27352736
)

sklearn/preprocessing/tests/test_data.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,12 @@ def test_standard_scaler_sample_weight_array_api(
189189
yw = np.ones(Xw.shape[0]).astype(dtype_name, copy=False)
190190
X_test = np.array([[1.5, 2.5, 3.5], [3.5, 4.5, 5.5]]).astype(dtype_name, copy=False)
191191

192-
scaler = StandardScaler()
193-
scaler.fit(X, y)
192+
with config_context(array_api_dispatch=False):
193+
scaler = StandardScaler()
194+
scaler.fit(X, y)
194195

195-
scaler_w = StandardScaler()
196-
scaler_w.fit(Xw, yw, sample_weight=sample_weight)
196+
scaler_w = StandardScaler()
197+
scaler_w.fit(Xw, yw, sample_weight=sample_weight)
197198

198199
# Test array-api support and correctness.
199200
X_xp = xp.asarray(X, device=device)
@@ -2159,7 +2160,8 @@ def test_binarizer_array_api_int(array_namespace, device_name, dtype_name):
21592160
for dtype_name_ in [dtype_name, "int32", "int64"]:
21602161
X_np = np.reshape(np.asarray([0, 1, 2, 3, 4], dtype=dtype_name_), (-1, 1))
21612162
X_xp = xp.asarray(X_np, device=device)
2162-
binarized_np = Binarizer(threshold=2.5).fit_transform(X_np)
2163+
with config_context(array_api_dispatch=False):
2164+
binarized_np = Binarizer(threshold=2.5).fit_transform(X_np)
21632165
with config_context(array_api_dispatch=True):
21642166
binarized_xp = Binarizer(threshold=2.5).fit_transform(X_xp)
21652167
assert_array_equal(move_to(binarized_xp, xp=np, device="cpu"), binarized_np)

0 commit comments

Comments
 (0)