File scipy-iprint.patch of Package python-scikit-learn
From 37edfee05f42a46f04573600a98f45c9902d5848 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lo=C3=AFc=20Est=C3=A8ve?= <loic.esteve@ymail.com>
Date: Tue, 24 Jun 2025 10:43:54 +0200
Subject: [PATCH 1/2] Remove deprecated iprint usage in scipy 1.15 LBFGS
---
sklearn/linear_model/_glm/_newton_solver.py | 3 ++-
sklearn/linear_model/_glm/glm.py | 3 ++-
sklearn/linear_model/_huber.py | 7 ++++++-
sklearn/linear_model/_logistic.py | 3 ++-
sklearn/neural_network/_multilayer_perceptron.py | 3 ++-
sklearn/utils/fixes.py | 10 ++++++++++
6 files changed, 24 insertions(+), 5 deletions(-)
diff --git a/sklearn/linear_model/_glm/_newton_solver.py b/sklearn/linear_model/_glm/_newton_solver.py
index c5c940bed6c39..7d89c4a5d76e1 100644
--- a/sklearn/linear_model/_glm/_newton_solver.py
+++ b/sklearn/linear_model/_glm/_newton_solver.py
@@ -14,6 +14,7 @@
from ..._loss.loss import HalfSquaredError
from ...exceptions import ConvergenceWarning
+from ...utils.fixes import _get_lbfgs_iprint_options_dict
from ...utils.optimize import _check_optimize_result
from .._linear_loss import LinearModelLoss
@@ -187,9 +188,9 @@ def fallback_lbfgs_solve(self, X, y, sample_weight):
options={
"maxiter": max_iter,
"maxls": 50, # default is 20
- "iprint": self.verbose - 1,
"gtol": self.tol,
"ftol": 64 * np.finfo(np.float64).eps,
+ **_get_lbfgs_iprint_options_dict(self.verbose - 1),
},
args=(X, y, sample_weight, self.l2_reg_strength, self.n_threads),
)
diff --git a/sklearn/linear_model/_glm/glm.py b/sklearn/linear_model/_glm/glm.py
index 7f138f420dc36..c2fc1a221a9e3 100644
--- a/sklearn/linear_model/_glm/glm.py
+++ b/sklearn/linear_model/_glm/glm.py
@@ -21,6 +21,7 @@
from ...utils import check_array
from ...utils._openmp_helpers import _openmp_effective_n_threads
from ...utils._param_validation import Hidden, Interval, StrOptions
+from ...utils.fixes import _get_lbfgs_iprint_options_dict
from ...utils.optimize import _check_optimize_result
from ...utils.validation import _check_sample_weight, check_is_fitted, validate_data
from .._linear_loss import LinearModelLoss
@@ -273,12 +274,12 @@ def fit(self, X, y, sample_weight=None):
options={
"maxiter": self.max_iter,
"maxls": 50, # default is 20
- "iprint": self.verbose - 1,
"gtol": self.tol,
# The constant 64 was found empirically to pass the test suite.
# The point is that ftol is very small, but a bit larger than
# machine precision for float64, which is the dtype used by lbfgs.
"ftol": 64 * np.finfo(float).eps,
+ **_get_lbfgs_iprint_options_dict(self.verbose - 1),
},
args=(X, y, sample_weight, l2_reg_strength, n_threads),
)
diff --git a/sklearn/linear_model/_huber.py b/sklearn/linear_model/_huber.py
index 51f24035a3c83..09d03520fe6e3 100644
--- a/sklearn/linear_model/_huber.py
+++ b/sklearn/linear_model/_huber.py
@@ -10,6 +10,7 @@
from ..utils._mask import axis0_safe_slice
from ..utils._param_validation import Interval
from ..utils.extmath import safe_sparse_dot
+from ..utils.fixes import _get_lbfgs_iprint_options_dict
from ..utils.optimize import _check_optimize_result
from ..utils.validation import _check_sample_weight, validate_data
from ._base import LinearModel
@@ -329,7 +330,11 @@ def fit(self, X, y, sample_weight=None):
method="L-BFGS-B",
jac=True,
args=(X, y, self.epsilon, self.alpha, sample_weight),
- options={"maxiter": self.max_iter, "gtol": self.tol, "iprint": -1},
+ options={
+ "maxiter": self.max_iter,
+ "gtol": self.tol,
+ **_get_lbfgs_iprint_options_dict(-1),
+ },
bounds=bounds,
)
diff --git a/sklearn/linear_model/_logistic.py b/sklearn/linear_model/_logistic.py
index b85c01ee69f9e..f2a9972500f65 100644
--- a/sklearn/linear_model/_logistic.py
+++ b/sklearn/linear_model/_logistic.py
@@ -30,6 +30,7 @@
)
from ..utils._param_validation import Hidden, Interval, StrOptions
from ..utils.extmath import row_norms, softmax
+from ..utils.fixes import _get_lbfgs_iprint_options_dict
from ..utils.metadata_routing import (
MetadataRouter,
MethodMapping,
@@ -464,9 +465,9 @@ def _logistic_regression_path(
options={
"maxiter": max_iter,
"maxls": 50, # default is 20
- "iprint": iprint,
"gtol": tol,
"ftol": 64 * np.finfo(float).eps,
+ **_get_lbfgs_iprint_options_dict(iprint),
},
)
n_iter_i = _check_optimize_result(
diff --git a/sklearn/neural_network/_multilayer_perceptron.py b/sklearn/neural_network/_multilayer_perceptron.py
index a8a00fe3b4ac5..42eecd23c88a5 100644
--- a/sklearn/neural_network/_multilayer_perceptron.py
+++ b/sklearn/neural_network/_multilayer_perceptron.py
@@ -31,6 +31,7 @@
)
from ..utils._param_validation import Interval, Options, StrOptions
from ..utils.extmath import safe_sparse_dot
+from ..utils.fixes import _get_lbfgs_iprint_options_dict
from ..utils.metaestimators import available_if
from ..utils.multiclass import (
_check_partial_fit_first_call,
@@ -585,8 +586,8 @@ def _fit_lbfgs(
options={
"maxfun": self.max_fun,
"maxiter": self.max_iter,
- "iprint": iprint,
"gtol": self.tol,
+ **_get_lbfgs_iprint_options_dict(iprint),
},
args=(
X,
diff --git a/sklearn/utils/fixes.py b/sklearn/utils/fixes.py
index d85ef82680bbb..cdd46c15b3272 100644
--- a/sklearn/utils/fixes.py
+++ b/sklearn/utils/fixes.py
@@ -392,3 +392,13 @@ def _in_unstable_openblas_configuration():
# See discussions in https://github.com/numpy/numpy/issues/19411
return True # pragma: no cover
return False
+
+
+# TODO: Remove when Scipy 1.15 is the minimum supported version
+# In scipy 1.15, wwhen LBFGS was converted from Fortran to C the internal info
+# details (via 'iprint' options key) were dropped, see
+# https://github.com/scipy/scipy/issues/23186#issuecomment-2987801035.
+# For scipy 1.15, iprint has no effect and for scipy >= 1.16 a
+# DeprecationWarning is emitted.
+def _get_lbfgs_iprint_options_dict(iprint_value):
+ return {} if sp_version >= parse_version("1.15") else {"iprint": iprint_value}
From c2317c697fcc39385e7085df0f99a603e14f3366 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lo=C3=AFc=20Est=C3=A8ve?= <loic.esteve@ymail.com>
Date: Tue, 24 Jun 2025 11:52:54 +0200
Subject: [PATCH 2/2] [azure parallel] [scipy-dev] more fixes
---
sklearn/linear_model/_glm/_newton_solver.py | 4 ++--
sklearn/linear_model/_glm/glm.py | 4 ++--
sklearn/linear_model/_huber.py | 4 ++--
sklearn/linear_model/_logistic.py | 4 ++--
sklearn/neighbors/_nca.py | 5 ++++-
sklearn/neural_network/_multilayer_perceptron.py | 4 ++--
sklearn/utils/fixes.py | 4 ++--
7 files changed, 16 insertions(+), 13 deletions(-)
diff --git a/sklearn/linear_model/_glm/_newton_solver.py b/sklearn/linear_model/_glm/_newton_solver.py
index 7d89c4a5d76e1..944789d5fec8a 100644
--- a/sklearn/linear_model/_glm/_newton_solver.py
+++ b/sklearn/linear_model/_glm/_newton_solver.py
@@ -14,7 +14,7 @@
from ..._loss.loss import HalfSquaredError
from ...exceptions import ConvergenceWarning
-from ...utils.fixes import _get_lbfgs_iprint_options_dict
+from ...utils.fixes import _get_lbfgs_options_dict
from ...utils.optimize import _check_optimize_result
from .._linear_loss import LinearModelLoss
@@ -190,7 +190,7 @@ def fallback_lbfgs_solve(self, X, y, sample_weight):
"maxls": 50, # default is 20
"gtol": self.tol,
"ftol": 64 * np.finfo(np.float64).eps,
- **_get_lbfgs_iprint_options_dict(self.verbose - 1),
+ **_get_lbfgs_options_dict("iprint", self.verbose - 1),
},
args=(X, y, sample_weight, self.l2_reg_strength, self.n_threads),
)
diff --git a/sklearn/linear_model/_glm/glm.py b/sklearn/linear_model/_glm/glm.py
index c2fc1a221a9e3..eba379f219350 100644
--- a/sklearn/linear_model/_glm/glm.py
+++ b/sklearn/linear_model/_glm/glm.py
@@ -21,7 +21,7 @@
from ...utils import check_array
from ...utils._openmp_helpers import _openmp_effective_n_threads
from ...utils._param_validation import Hidden, Interval, StrOptions
-from ...utils.fixes import _get_lbfgs_iprint_options_dict
+from ...utils.fixes import _get_lbfgs_options_dict
from ...utils.optimize import _check_optimize_result
from ...utils.validation import _check_sample_weight, check_is_fitted, validate_data
from .._linear_loss import LinearModelLoss
@@ -279,7 +279,7 @@ def fit(self, X, y, sample_weight=None):
# The point is that ftol is very small, but a bit larger than
# machine precision for float64, which is the dtype used by lbfgs.
"ftol": 64 * np.finfo(float).eps,
- **_get_lbfgs_iprint_options_dict(self.verbose - 1),
+ **_get_lbfgs_options_dict("iprint", self.verbose - 1),
},
args=(X, y, sample_weight, l2_reg_strength, n_threads),
)
diff --git a/sklearn/linear_model/_huber.py b/sklearn/linear_model/_huber.py
index 09d03520fe6e3..5528f282ff563 100644
--- a/sklearn/linear_model/_huber.py
+++ b/sklearn/linear_model/_huber.py
@@ -10,7 +10,7 @@
from ..utils._mask import axis0_safe_slice
from ..utils._param_validation import Interval
from ..utils.extmath import safe_sparse_dot
-from ..utils.fixes import _get_lbfgs_iprint_options_dict
+from ..utils.fixes import _get_lbfgs_options_dict
from ..utils.optimize import _check_optimize_result
from ..utils.validation import _check_sample_weight, validate_data
from ._base import LinearModel
@@ -333,7 +333,7 @@ def fit(self, X, y, sample_weight=None):
options={
"maxiter": self.max_iter,
"gtol": self.tol,
- **_get_lbfgs_iprint_options_dict(-1),
+ **_get_lbfgs_options_dict("iprint", -1),
},
bounds=bounds,
)
diff --git a/sklearn/linear_model/_logistic.py b/sklearn/linear_model/_logistic.py
index f2a9972500f65..a7137aa585982 100644
--- a/sklearn/linear_model/_logistic.py
+++ b/sklearn/linear_model/_logistic.py
@@ -30,7 +30,7 @@
)
from ..utils._param_validation import Hidden, Interval, StrOptions
from ..utils.extmath import row_norms, softmax
-from ..utils.fixes import _get_lbfgs_iprint_options_dict
+from ..utils.fixes import _get_lbfgs_options_dict
from ..utils.metadata_routing import (
MetadataRouter,
MethodMapping,
@@ -467,7 +467,7 @@ def _logistic_regression_path(
"maxls": 50, # default is 20
"gtol": tol,
"ftol": 64 * np.finfo(float).eps,
- **_get_lbfgs_iprint_options_dict(iprint),
+ **_get_lbfgs_options_dict("iprint", iprint),
},
)
n_iter_i = _check_optimize_result(
diff --git a/sklearn/neighbors/_nca.py b/sklearn/neighbors/_nca.py
index a4ef3c303b851..c89742edb115a 100644
--- a/sklearn/neighbors/_nca.py
+++ b/sklearn/neighbors/_nca.py
@@ -25,6 +25,7 @@
from ..preprocessing import LabelEncoder
from ..utils._param_validation import Interval, StrOptions
from ..utils.extmath import softmax
+from ..utils.fixes import _get_lbfgs_options_dict
from ..utils.multiclass import check_classification_targets
from ..utils.random import check_random_state
from ..utils.validation import check_array, check_is_fitted, validate_data
@@ -312,7 +313,9 @@ def fit(self, X, y):
"jac": True,
"x0": transformation,
"tol": self.tol,
- "options": dict(maxiter=self.max_iter, disp=disp),
+ "options": dict(
+ maxiter=self.max_iter, **_get_lbfgs_options_dict("disp", disp)
+ ),
"callback": self._callback,
}
diff --git a/sklearn/neural_network/_multilayer_perceptron.py b/sklearn/neural_network/_multilayer_perceptron.py
index 42eecd23c88a5..7b8dc5d7e2395 100644
--- a/sklearn/neural_network/_multilayer_perceptron.py
+++ b/sklearn/neural_network/_multilayer_perceptron.py
@@ -31,7 +31,7 @@
)
from ..utils._param_validation import Interval, Options, StrOptions
from ..utils.extmath import safe_sparse_dot
-from ..utils.fixes import _get_lbfgs_iprint_options_dict
+from ..utils.fixes import _get_lbfgs_options_dict
from ..utils.metaestimators import available_if
from ..utils.multiclass import (
_check_partial_fit_first_call,
@@ -587,7 +587,7 @@ def _fit_lbfgs(
"maxfun": self.max_fun,
"maxiter": self.max_iter,
"gtol": self.tol,
- **_get_lbfgs_iprint_options_dict(iprint),
+ **_get_lbfgs_options_dict("iprint", iprint),
},
args=(
X,
diff --git a/sklearn/utils/fixes.py b/sklearn/utils/fixes.py
index cdd46c15b3272..da010600c7649 100644
--- a/sklearn/utils/fixes.py
+++ b/sklearn/utils/fixes.py
@@ -400,5 +400,5 @@ def _in_unstable_openblas_configuration():
# https://github.com/scipy/scipy/issues/23186#issuecomment-2987801035.
# For scipy 1.15, iprint has no effect and for scipy >= 1.16 a
# DeprecationWarning is emitted.
-def _get_lbfgs_iprint_options_dict(iprint_value):
- return {} if sp_version >= parse_version("1.15") else {"iprint": iprint_value}
+def _get_lbfgs_options_dict(key, iprint_value):
+ return {} if sp_version >= parse_version("1.15") else {key: iprint_value}