Skip to content

Commit 675a5e1

Browse files
TST improve test_ridge_regression_unpenalized_hstacked_X (scikit-learn#34334)
Co-authored-by: Virgil Chan <virchan.math@gmail.com>
1 parent e8b70c0 commit 675a5e1

4 files changed

Lines changed: 62 additions & 20 deletions

File tree

sklearn/ensemble/tests/test_gradient_boosting.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ def test_regression_synthetic(global_random_seed):
233233
clf = GradientBoostingRegressor(**regression_params)
234234
clf.fit(X_train, y_train)
235235
mse = mean_squared_error(y_test, clf.predict(X_test))
236-
assert mse < 2500.0
236+
assert mse < 2550.0
237237

238238
# Friedman3
239239
X, y = datasets.make_friedman3(n_samples=1200, random_state=random_state)

sklearn/linear_model/tests/test_coordinate_descent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1340,7 +1340,7 @@ def test_enet_sample_weight_consistency(
13401340
check_sample_weight_equivalence alone and also tests sparse X.
13411341
"""
13421342
rng = np.random.RandomState(global_random_seed)
1343-
n_samples, n_features = 10, 5
1343+
n_samples, n_features = 20, 8
13441344
X = rng.rand(n_samples, n_features)
13451345
y = rng.rand(n_samples)
13461346

sklearn/linear_model/tests/test_logistic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1112,7 +1112,7 @@ def test_logistic_cv_sparse(global_random_seed, solver, csr_container):
11121112
)
11131113
clfs.fit(csr_container(X), y)
11141114

1115-
rtol = 6e-2 if solver in ("sag", "saga") else 1e-5
1115+
rtol = 0.075 if solver in ("sag", "saga") else 1e-5
11161116
assert_allclose(clfs.coef_, clf.coef_, rtol=rtol)
11171117
assert_allclose(clfs.intercept_, clf.intercept_, rtol=rtol)
11181118
assert clfs.C_ == clf.C_

sklearn/linear_model/tests/test_ridge.py

Lines changed: 59 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -375,31 +375,73 @@ def test_ridge_regression_unpenalized_hstacked_X(
375375
X = X[:, :-1] # remove intercept
376376
intercept = coef[-1]
377377
coef = coef[:-1]
378+
# Put one intercept term into the new X, the other one is the "fit_intercept".
379+
X = np.concatenate((X, X, np.ones((n_samples, 1))), axis=1)
380+
# We omit the factor of 1/2 such that both intercept terms have the same
381+
# effective design matrix column (all ones).
382+
# We will need to correct for this factor of 2 later.
378383
else:
379384
intercept = 0
380-
X = 0.5 * np.concatenate((X, X), axis=1)
385+
X = 0.5 * np.concatenate((X, X), axis=1)
381386
assert np.linalg.matrix_rank(X) <= min(n_samples, n_features)
382-
model.fit(X, y)
383387

384-
if n_samples > n_features or not fit_intercept:
385-
assert model.intercept_ == pytest.approx(intercept)
388+
with warnings.catch_warnings():
386389
if solver == "cholesky":
387-
# Cholesky is a bad choice for singular X.
388-
pytest.skip()
389-
assert_allclose(model.coef_, np.r_[coef, coef])
390+
# Cause is np.linalg.LinAlgError
391+
warnings.filterwarnings("ignore", category=UserWarning)
392+
model.fit(X, y)
393+
394+
norm_solution = np.linalg.norm(np.r_[intercept, intercept, coef, coef])
395+
if fit_intercept:
396+
# Here we meed the factor of 2 because we did not divide X by 1/2.
397+
norm_model = np.linalg.norm(2 * np.r_[model.intercept_, model.coef_])
398+
model_coef = 2 * model.coef_[:-1] # remove the intercept
390399
else:
391-
# FIXME: Same as in test_ridge_regression_unpenalized.
392-
# As it is an underdetermined problem, residuals = 0. This shows that we get
393-
# a solution to X w = y ....
400+
norm_model = np.linalg.norm(np.r_[model.coef_])
401+
model_coef = model.coef_
402+
403+
if n_samples > n_features: # long
404+
if not fit_intercept and solver == "cholesky":
405+
# It always fails for some platforms like Windows, and it fails for some
406+
# global_random_seed. So we skip instead of xfail.
407+
pytest.skip(
408+
reason="Solver cholesky with fit_intercept=False fails from time to "
409+
"time for unknown reasons."
410+
)
411+
assert_allclose(model_coef, np.r_[coef, coef])
412+
if fit_intercept:
413+
# We should have model.intercept_ == model.coef[-1] == 0.5 * intercept.
414+
# But Ridge does center X to obtain model.intercept_, so X[-1] gets
415+
# centered to zero and model.coef_[-1] stays zero.
416+
# We therefore test model_intercept + model.coef_[-1] == intercept.
417+
# FIXME: Note that this is NOT the minimum norm solution.
418+
assert model.intercept_ == pytest.approx(intercept)
419+
assert model.coef_[-1] == 0
420+
assert norm_model > (1 + 1e-12) * norm_solution
421+
else:
422+
assert model.intercept_ == pytest.approx(0.5 * intercept)
423+
assert norm_model == pytest.approx(norm_solution, rel=5e-11)
424+
else: # wide
425+
# As it is an underdetermined problem, residuals = 0. The following shows that
426+
# we get a solution, i.e. a (non-unique) minimum of the objective function ...
394427
assert_allclose(model.predict(X), y)
395-
# But it is not the minimum norm solution. (This should be equal.)
396-
assert np.linalg.norm(np.r_[model.intercept_, model.coef_]) > np.linalg.norm(
397-
np.r_[intercept, coef, coef]
398-
)
399428

400-
pytest.xfail(reason="Ridge does not provide the minimum norm solution.")
401-
assert model.intercept_ == pytest.approx(intercept)
402-
assert_allclose(model.coef_, np.r_[coef, coef])
429+
if fit_intercept:
430+
# FIXME: Same as in test_ridge_regression_unpenalized.
431+
# As it is an underdetermined problem, residuals = 0. This shows that we get
432+
# a solution to X w = y ....
433+
# But it is not the minimum norm solution. Otherwise the norms would be
434+
# equal.
435+
pytest.xfail(reason="Ridge does not provide the minimum norm solution.")
436+
assert norm_model == pytest.approx(norm_solution, rel=1e-12)
437+
# This time it is not only related to the intercept, as above, but also to
438+
# the coefficients.
439+
assert_allclose(model_coef, np.r_[coef, coef])
440+
else:
441+
# Here, we find the minimum norm solution.
442+
assert norm_model == pytest.approx(norm_solution, rel=1e-12)
443+
assert model.intercept_ == pytest.approx(intercept)
444+
assert_allclose(model_coef, np.r_[coef, coef])
403445

404446

405447
@pytest.mark.parametrize("solver", SOLVERS)

0 commit comments

Comments
 (0)