@@ -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