From 802074c2c4a9b1191267850709695aa24e8ba5c8 Mon Sep 17 00:00:00 2001 From: dherrera1911 Date: Tue, 10 Mar 2026 00:03:40 -0400 Subject: [PATCH 1/3] Handle sklearn force_all_finite rename via local wrappers --- metric_learn/_util.py | 111 +++++++++++++++++++++++++++++------------- 1 file changed, 78 insertions(+), 33 deletions(-) diff --git a/metric_learn/_util.py b/metric_learn/_util.py index 868ececa..03fa8b09 100644 --- a/metric_learn/_util.py +++ b/metric_learn/_util.py @@ -1,25 +1,70 @@ -import numpy as np -from numpy.linalg import LinAlgError -from sklearn.datasets import make_spd_matrix -from sklearn.decomposition import PCA -from sklearn.utils import check_array -from sklearn.utils.validation import check_X_y, check_random_state -from .exceptions import PreprocessorError, NonPSDError -from sklearn.discriminant_analysis import LinearDiscriminantAnalysis -from scipy.linalg import pinvh, eigh -import sys -import time -import warnings - -# hack around lack of axis kwarg in older numpy versions -try: - np.linalg.norm([[4]], axis=1) -except TypeError: - def vector_norm(X): - return np.apply_along_axis(np.linalg.norm, 1, X) -else: - def vector_norm(X): - return np.linalg.norm(X, axis=1) +import numpy as np +from numpy.linalg import LinAlgError +from inspect import signature +from sklearn.datasets import make_spd_matrix +from sklearn.decomposition import PCA +from sklearn.utils import check_array +from sklearn.utils.validation import check_X_y, check_random_state +from .exceptions import PreprocessorError, NonPSDError +from sklearn.discriminant_analysis import LinearDiscriminantAnalysis +from scipy.linalg import pinvh, eigh +import sys +import time +import warnings + +# hack around lack of axis kwarg in older numpy versions +try: + np.linalg.norm([[4]], axis=1) +except TypeError: + def vector_norm(X): + return np.apply_along_axis(np.linalg.norm, 1, X) +else: + def vector_norm(X): + return np.linalg.norm(X, axis=1) + + +_CHECK_ARRAY_SUPPORTS_FORCE_ALL_FINITE = ( + 'force_all_finite' in signature(check_array).parameters) +_CHECK_X_Y_SUPPORTS_FORCE_ALL_FINITE = ( + 'force_all_finite' in signature(check_X_y).parameters) +_MISSING = object() # sentinel value to check if an argument is given or not + +def _normalize_force_all_finite_arg(kwargs, supports_force_all_finite): + """Take a dictionary of arguments, and make sure that the argument that + controls finite values check in `check_array` and `check_X_y` is named + correctly for the version of scikit-learn used (`force_all_finite` for + older version, `ensure_all_finite` for newer ones). + """ + kwargs = kwargs.copy() + force_all_finite = kwargs.pop('force_all_finite', _MISSING) + ensure_all_finite = kwargs.pop('ensure_all_finite', _MISSING) + if supports_force_all_finite: + if ensure_all_finite is not _MISSING: + kwargs['force_all_finite'] = ensure_all_finite + elif force_all_finite is not _MISSING: + kwargs['force_all_finite'] = force_all_finite + else: + if force_all_finite is not _MISSING: + kwargs['ensure_all_finite'] = force_all_finite + elif ensure_all_finite is not _MISSING: + kwargs['ensure_all_finite'] = ensure_all_finite + return kwargs + + +def _check_array(*args, **kwargs): + """Local wrapper around `sklearn.utils.check_array` to deal with the change + from `force_all_finite` to `ensure_all_finite` in scikit-learn.""" + kwargs = _normalize_force_all_finite_arg( + kwargs, _CHECK_ARRAY_SUPPORTS_FORCE_ALL_FINITE) + return check_array(*args, **kwargs) + + +def _check_X_y(*args, **kwargs): + """Local wrapper around `sklearn.utils.check_X_y` to deal with the change + from `force_all_finite` to `ensure_all_finite` in scikit-learn.""" + kwargs = _normalize_force_all_finite_arg( + kwargs, _CHECK_X_Y_SUPPORTS_FORCE_ALL_FINITE) + return check_X_y(*args, **kwargs) def check_input(input_data, y=None, preprocessor=None, @@ -115,14 +160,14 @@ def check_input(input_data, y=None, preprocessor=None, # We need to convert input_data into a numpy.ndarray if possible, before # any further checks or conversions, and deal with y if needed. Therefore - # we use check_array/check_X_y with fixed permissive arguments. + # we use the wrappers _check_array/_check_X_y with fixed permissive arguments. if y is None: - input_data = check_array(input_data, ensure_2d=False, allow_nd=True, + input_data = _check_array(input_data, ensure_2d=False, allow_nd=True, copy=False, force_all_finite=False, accept_sparse=True, dtype=None, ensure_min_features=0, ensure_min_samples=0) else: - input_data, y = check_X_y(input_data, y, ensure_2d=False, allow_nd=True, + input_data, y = _check_X_y(input_data, y, ensure_2d=False, allow_nd=True, copy=False, force_all_finite=False, accept_sparse=True, dtype=None, ensure_min_features=0, ensure_min_samples=0, @@ -165,9 +210,9 @@ def check_input_tuples(input_data, context, preprocessor, args_for_sk_checks, make_error_input(420, input_data, context) else: make_error_input(200, input_data, context) - input_data = check_array(input_data, allow_nd=True, ensure_2d=False, + input_data = _check_array(input_data, allow_nd=True, ensure_2d=False, **args_for_sk_checks) - # we need to check num_features because check_array does not check it + # we need to check num_features because _check_array does not check it # for 3D inputs: if args_for_sk_checks['ensure_min_features'] > 0: n_features = input_data.shape[2] @@ -180,7 +225,7 @@ def check_input_tuples(input_data, context, preprocessor, args_for_sk_checks, # normally we don't need to check_tuple_size too because tuple_size # shouldn't be able to be modified by any preprocessor if input_data.ndim != 3: - # we have to ensure this because check_array above does not + # we have to ensure this because _check_array above does not if preprocessor_has_been_applied: make_error_input(211, input_data, context) else: @@ -205,10 +250,10 @@ def check_input_classic(input_data, context, preprocessor, args_for_sk_checks): else: make_error_input(100, input_data, context) - input_data = check_array(input_data, allow_nd=True, ensure_2d=False, + input_data = _check_array(input_data, allow_nd=True, ensure_2d=False, **args_for_sk_checks) if input_data.ndim != 2: - # we have to ensure this because check_array above does not + # we have to ensure this because _check_array above does not if preprocessor_has_been_applied: make_error_input(111, input_data, context) else: @@ -317,7 +362,7 @@ def __init__(self, X): # format with arguments in check_input, and only this latter function # should return the appropriate errors). We do this only to have a numpy # array object which can be indexed by another numpy array object. - X = check_array(X, + X = _check_array(X, accept_sparse=True, dtype=None, force_all_finite=False, ensure_2d=False, allow_nd=True, @@ -514,7 +559,7 @@ def _initialize_components(n_components, input, y=None, init='auto', if isinstance(init, np.ndarray): # we copy the array, so that if we update the metric, we don't want to # update the init - init = check_array(init, copy=True) + init = _check_array(init, copy=True) # Assert that init.shape[1] = X.shape[1] if init.shape[1] != n_features: @@ -656,7 +701,7 @@ def _initialize_metric_mahalanobis(input, init='identity', random_state=None, if isinstance(init, np.ndarray): # we copy the array, so that if we update the metric, we don't want to # update the init - init = check_array(init, copy=True) + init = _check_array(init, copy=True) # Assert that init.shape[1] = n_features if init.shape != (n_features,) * 2: From 67759540f1eb8e82689e92fd4feb20a10661bcdd Mon Sep 17 00:00:00 2001 From: dherrera1911 Date: Tue, 10 Mar 2026 00:44:45 -0400 Subject: [PATCH 2/3] Simplify wrappers --- metric_learn/_util.py | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/metric_learn/_util.py b/metric_learn/_util.py index 03fa8b09..5db7a764 100644 --- a/metric_learn/_util.py +++ b/metric_learn/_util.py @@ -27,43 +27,22 @@ def vector_norm(X): 'force_all_finite' in signature(check_array).parameters) _CHECK_X_Y_SUPPORTS_FORCE_ALL_FINITE = ( 'force_all_finite' in signature(check_X_y).parameters) -_MISSING = object() # sentinel value to check if an argument is given or not - -def _normalize_force_all_finite_arg(kwargs, supports_force_all_finite): - """Take a dictionary of arguments, and make sure that the argument that - controls finite values check in `check_array` and `check_X_y` is named - correctly for the version of scikit-learn used (`force_all_finite` for - older version, `ensure_all_finite` for newer ones). - """ - kwargs = kwargs.copy() - force_all_finite = kwargs.pop('force_all_finite', _MISSING) - ensure_all_finite = kwargs.pop('ensure_all_finite', _MISSING) - if supports_force_all_finite: - if ensure_all_finite is not _MISSING: - kwargs['force_all_finite'] = ensure_all_finite - elif force_all_finite is not _MISSING: - kwargs['force_all_finite'] = force_all_finite - else: - if force_all_finite is not _MISSING: - kwargs['ensure_all_finite'] = force_all_finite - elif ensure_all_finite is not _MISSING: - kwargs['ensure_all_finite'] = ensure_all_finite - return kwargs def _check_array(*args, **kwargs): """Local wrapper around `sklearn.utils.check_array` to deal with the change from `force_all_finite` to `ensure_all_finite` in scikit-learn.""" - kwargs = _normalize_force_all_finite_arg( - kwargs, _CHECK_ARRAY_SUPPORTS_FORCE_ALL_FINITE) + if not _CHECK_ARRAY_SUPPORTS_FORCE_ALL_FINITE and "force_all_finite" in kwargs: + kwargs = kwargs.copy() + kwargs["ensure_all_finite"] = kwargs.pop("force_all_finite") return check_array(*args, **kwargs) - def _check_X_y(*args, **kwargs): """Local wrapper around `sklearn.utils.check_X_y` to deal with the change from `force_all_finite` to `ensure_all_finite` in scikit-learn.""" - kwargs = _normalize_force_all_finite_arg( - kwargs, _CHECK_X_Y_SUPPORTS_FORCE_ALL_FINITE) + if not _CHECK_X_Y_SUPPORTS_FORCE_ALL_FINITE and "force_all_finite" in kwargs: + kwargs = kwargs.copy() + kwargs["ensure_all_finite"] = kwargs.pop("force_all_finite") return check_X_y(*args, **kwargs) From c691bc0384b8c49be4f60795819064d93438d26d Mon Sep 17 00:00:00 2001 From: dherrera1911 Date: Sat, 14 Mar 2026 08:52:03 -0400 Subject: [PATCH 3/3] Change EOL to CRLF --- metric_learn/_util.py | 92 +++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/metric_learn/_util.py b/metric_learn/_util.py index 5db7a764..de608218 100644 --- a/metric_learn/_util.py +++ b/metric_learn/_util.py @@ -1,49 +1,49 @@ -import numpy as np -from numpy.linalg import LinAlgError -from inspect import signature -from sklearn.datasets import make_spd_matrix -from sklearn.decomposition import PCA -from sklearn.utils import check_array -from sklearn.utils.validation import check_X_y, check_random_state -from .exceptions import PreprocessorError, NonPSDError -from sklearn.discriminant_analysis import LinearDiscriminantAnalysis -from scipy.linalg import pinvh, eigh -import sys -import time -import warnings - -# hack around lack of axis kwarg in older numpy versions -try: - np.linalg.norm([[4]], axis=1) -except TypeError: - def vector_norm(X): - return np.apply_along_axis(np.linalg.norm, 1, X) -else: - def vector_norm(X): - return np.linalg.norm(X, axis=1) - - -_CHECK_ARRAY_SUPPORTS_FORCE_ALL_FINITE = ( - 'force_all_finite' in signature(check_array).parameters) -_CHECK_X_Y_SUPPORTS_FORCE_ALL_FINITE = ( - 'force_all_finite' in signature(check_X_y).parameters) - - -def _check_array(*args, **kwargs): - """Local wrapper around `sklearn.utils.check_array` to deal with the change - from `force_all_finite` to `ensure_all_finite` in scikit-learn.""" - if not _CHECK_ARRAY_SUPPORTS_FORCE_ALL_FINITE and "force_all_finite" in kwargs: - kwargs = kwargs.copy() - kwargs["ensure_all_finite"] = kwargs.pop("force_all_finite") - return check_array(*args, **kwargs) - -def _check_X_y(*args, **kwargs): - """Local wrapper around `sklearn.utils.check_X_y` to deal with the change - from `force_all_finite` to `ensure_all_finite` in scikit-learn.""" - if not _CHECK_X_Y_SUPPORTS_FORCE_ALL_FINITE and "force_all_finite" in kwargs: - kwargs = kwargs.copy() - kwargs["ensure_all_finite"] = kwargs.pop("force_all_finite") - return check_X_y(*args, **kwargs) +import numpy as np +from numpy.linalg import LinAlgError +from inspect import signature +from sklearn.datasets import make_spd_matrix +from sklearn.decomposition import PCA +from sklearn.utils import check_array +from sklearn.utils.validation import check_X_y, check_random_state +from .exceptions import PreprocessorError, NonPSDError +from sklearn.discriminant_analysis import LinearDiscriminantAnalysis +from scipy.linalg import pinvh, eigh +import sys +import time +import warnings + +# hack around lack of axis kwarg in older numpy versions +try: + np.linalg.norm([[4]], axis=1) +except TypeError: + def vector_norm(X): + return np.apply_along_axis(np.linalg.norm, 1, X) +else: + def vector_norm(X): + return np.linalg.norm(X, axis=1) + + +_CHECK_ARRAY_SUPPORTS_FORCE_ALL_FINITE = ( + 'force_all_finite' in signature(check_array).parameters) +_CHECK_X_Y_SUPPORTS_FORCE_ALL_FINITE = ( + 'force_all_finite' in signature(check_X_y).parameters) + + +def _check_array(*args, **kwargs): + """Local wrapper around `sklearn.utils.check_array` to deal with the change + from `force_all_finite` to `ensure_all_finite` in scikit-learn.""" + if not _CHECK_ARRAY_SUPPORTS_FORCE_ALL_FINITE and "force_all_finite" in kwargs: + kwargs = kwargs.copy() + kwargs["ensure_all_finite"] = kwargs.pop("force_all_finite") + return check_array(*args, **kwargs) + +def _check_X_y(*args, **kwargs): + """Local wrapper around `sklearn.utils.check_X_y` to deal with the change + from `force_all_finite` to `ensure_all_finite` in scikit-learn.""" + if not _CHECK_X_Y_SUPPORTS_FORCE_ALL_FINITE and "force_all_finite" in kwargs: + kwargs = kwargs.copy() + kwargs["ensure_all_finite"] = kwargs.pop("force_all_finite") + return check_X_y(*args, **kwargs) def check_input(input_data, y=None, preprocessor=None,