Skip to content
4 changes: 4 additions & 0 deletions doc/whats_new/v0.22.rst
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ random sampling procedures.
- :class:`linear_model.Ridge` when `X` is sparse. |Fix|
- :class:`model_selection.StratifiedKFold` and any use of `cv=int` with a
classifier. |Fix|
- :class:`cross_decomposition.CCA` when using scipy >= 1.3 |Fix|

Details are listed in the changelog below.

Expand Down Expand Up @@ -209,6 +210,9 @@ Changelog
``inverse_transform`` to transform data to the original space`.
:pr:`15304` by :user:`Jaime Ferrando Huertas <jiwidi>`.

- |Fix| :class:`cross_decomposition.CCA` now produces the same results with
scipy 1.3 and previous scipy versions. :pr:`15661` by `Thomas Fan`_.

:mod:`sklearn.datasets`
.......................

Expand Down
17 changes: 15 additions & 2 deletions sklearn/cross_decomposition/_pls.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,26 @@ def _nipals_twoblocks_inner_loop(X, Y, mode="A", max_iter=500, tol=1e-06,
ite = 1
X_pinv = Y_pinv = None
eps = np.finfo(X.dtype).eps

if mode == "B":
# Uses condition from scipy<1.3 in pinv2 which was changed in
# https://github.com/scipy/scipy/pull/10067. In scipy 1.3, the
# condition was changed to depend on the largest singular value
X_t = X.dtype.char.lower()
Y_t = Y.dtype.char.lower()
factor = {'f': 1E3, 'd': 1E6}

cond_X = factor[X_t] * eps
cond_Y = factor[Y_t] * eps

# Inner loop of the Wold algo.
while True:
# 1.1 Update u: the X weights
if mode == "B":
if X_pinv is None:
# We use slower pinv2 (same as np.linalg.pinv) for stability
# reasons
X_pinv = pinv2(X, check_finite=False)
X_pinv = pinv2(X, check_finite=False, cond=cond_X)
x_weights = np.dot(X_pinv, y_score)
else: # mode A
# Mode A regress each X column on y_score
Expand All @@ -64,7 +76,8 @@ def _nipals_twoblocks_inner_loop(X, Y, mode="A", max_iter=500, tol=1e-06,
# 2.1 Update y_weights
if mode == "B":
if Y_pinv is None:
Y_pinv = pinv2(Y, check_finite=False) # compute once pinv(Y)
# compute once pinv(Y)
Y_pinv = pinv2(Y, check_finite=False, cond=cond_Y)
y_weights = np.dot(Y_pinv, x_score)
else:
# Mode A regress each Y column on x_score
Expand Down