scikit-learn-intelex 2024.1.0__py38-none-manylinux1_x86_64.whl → 2024.3.0__py38-none-manylinux1_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of scikit-learn-intelex might be problematic. Click here for more details.

Files changed (51) hide show
  1. {scikit_learn_intelex-2024.1.0.dist-info → scikit_learn_intelex-2024.3.0.dist-info}/METADATA +2 -2
  2. {scikit_learn_intelex-2024.1.0.dist-info → scikit_learn_intelex-2024.3.0.dist-info}/RECORD +45 -44
  3. sklearnex/__init__.py +9 -7
  4. sklearnex/cluster/dbscan.py +6 -4
  5. sklearnex/conftest.py +63 -0
  6. sklearnex/{preview/decomposition → covariance}/__init__.py +19 -19
  7. sklearnex/covariance/incremental_covariance.py +130 -0
  8. sklearnex/covariance/tests/test_incremental_covariance.py +143 -0
  9. sklearnex/decomposition/pca.py +322 -1
  10. sklearnex/decomposition/tests/test_pca.py +34 -5
  11. sklearnex/dispatcher.py +91 -59
  12. sklearnex/ensemble/_forest.py +15 -24
  13. sklearnex/ensemble/tests/test_forest.py +15 -19
  14. sklearnex/linear_model/__init__.py +1 -2
  15. sklearnex/linear_model/linear.py +3 -10
  16. sklearnex/{preview/linear_model → linear_model}/logistic_regression.py +32 -40
  17. sklearnex/linear_model/tests/test_logreg.py +70 -7
  18. sklearnex/neighbors/__init__.py +1 -1
  19. sklearnex/neighbors/_lof.py +204 -0
  20. sklearnex/neighbors/knn_classification.py +13 -18
  21. sklearnex/neighbors/knn_regression.py +12 -17
  22. sklearnex/neighbors/knn_unsupervised.py +10 -15
  23. sklearnex/neighbors/tests/test_neighbors.py +12 -16
  24. sklearnex/preview/__init__.py +1 -1
  25. sklearnex/preview/cluster/k_means.py +3 -8
  26. sklearnex/preview/covariance/covariance.py +46 -12
  27. sklearnex/spmd/__init__.py +1 -0
  28. sklearnex/{preview/linear_model → spmd/covariance}/__init__.py +5 -5
  29. sklearnex/spmd/covariance/covariance.py +21 -0
  30. sklearnex/spmd/ensemble/forest.py +4 -12
  31. sklearnex/spmd/linear_model/__init__.py +2 -1
  32. sklearnex/spmd/linear_model/logistic_regression.py +21 -0
  33. sklearnex/svm/nusvc.py +9 -6
  34. sklearnex/svm/nusvr.py +6 -7
  35. sklearnex/svm/svc.py +9 -6
  36. sklearnex/svm/svr.py +3 -4
  37. sklearnex/tests/_utils.py +155 -0
  38. sklearnex/tests/test_memory_usage.py +9 -7
  39. sklearnex/tests/test_monkeypatch.py +179 -138
  40. sklearnex/tests/test_n_jobs_support.py +71 -9
  41. sklearnex/tests/test_parallel.py +6 -8
  42. sklearnex/tests/test_patching.py +321 -82
  43. sklearnex/neighbors/lof.py +0 -436
  44. sklearnex/preview/decomposition/pca.py +0 -376
  45. sklearnex/preview/decomposition/tests/test_preview_pca.py +0 -42
  46. sklearnex/preview/linear_model/tests/test_preview_logistic_regression.py +0 -59
  47. sklearnex/tests/_models_info.py +0 -170
  48. sklearnex/tests/utils/_launch_algorithms.py +0 -118
  49. {scikit_learn_intelex-2024.1.0.dist-info → scikit_learn_intelex-2024.3.0.dist-info}/LICENSE.txt +0 -0
  50. {scikit_learn_intelex-2024.1.0.dist-info → scikit_learn_intelex-2024.3.0.dist-info}/WHEEL +0 -0
  51. {scikit_learn_intelex-2024.1.0.dist-info → scikit_learn_intelex-2024.3.0.dist-info}/top_level.txt +0 -0
@@ -14,109 +14,348 @@
14
14
  # limitations under the License.
15
15
  # ==============================================================================
16
16
 
17
+
18
+ import importlib
19
+ import inspect
20
+ import logging
17
21
  import os
18
- import pathlib
19
22
  import re
20
- import subprocess
21
23
  import sys
22
- from inspect import isclass
24
+ from inspect import signature
23
25
 
26
+ import numpy as np
27
+ import numpy.random as nprnd
24
28
  import pytest
25
- from _models_info import TO_SKIP
26
- from sklearn.base import BaseEstimator
27
-
28
- from sklearnex import get_patch_map, is_patched_instance, patch_sklearn, unpatch_sklearn
29
-
30
-
31
- def get_branch(s):
32
- if len(s) == 0:
33
- return "NO INFO"
34
- for i in s:
35
- if "failed to run accelerated version, fallback to original Scikit-learn" in i:
36
- return "was in OPT, but go in Scikit"
37
- for i in s:
38
- if "running accelerated version" in i:
39
- return "OPT"
40
- return "Scikit"
41
-
42
-
43
- def run_parse(mas, result):
44
- name, dtype = mas[0].split()
45
- temp = []
46
- INFO_POS = 16
47
- for i in range(1, len(mas)):
48
- mas[i] = mas[i][INFO_POS:] # remove 'SKLEARNEX INFO: '
49
- if not mas[i].startswith("sklearn"):
50
- ind = name + " " + dtype + " " + mas[i]
51
- result[ind] = get_branch(temp)
52
- temp.clear()
53
- else:
54
- temp.append(mas[i])
29
+ from _utils import (
30
+ DTYPES,
31
+ PATCHED_FUNCTIONS,
32
+ PATCHED_MODELS,
33
+ SPECIAL_INSTANCES,
34
+ UNPATCHED_FUNCTIONS,
35
+ UNPATCHED_MODELS,
36
+ gen_dataset,
37
+ gen_models_info,
38
+ )
39
+ from sklearn.base import (
40
+ BaseEstimator,
41
+ ClassifierMixin,
42
+ ClusterMixin,
43
+ OutlierMixin,
44
+ RegressorMixin,
45
+ TransformerMixin,
46
+ )
47
+
48
+ from daal4py.sklearn._utils import sklearn_check_version
49
+ from onedal.tests.utils._dataframes_support import (
50
+ _convert_to_dataframe,
51
+ get_dataframes_and_queues,
52
+ )
53
+ from sklearnex import is_patched_instance
54
+ from sklearnex.dispatcher import _is_preview_enabled
55
+ from sklearnex.metrics import pairwise_distances, roc_auc_score
55
56
 
56
57
 
57
- def get_result_log():
58
- os.environ["SKLEARNEX_VERBOSE"] = "INFO"
59
- absolute_path = str(pathlib.Path(__file__).parent.absolute())
60
- try:
61
- process = subprocess.check_output(
62
- [sys.executable, absolute_path + "/utils/_launch_algorithms.py"]
58
+ @pytest.mark.parametrize("dtype", DTYPES)
59
+ @pytest.mark.parametrize(
60
+ "dataframe, queue", get_dataframes_and_queues(dataframe_filter_="numpy")
61
+ )
62
+ @pytest.mark.parametrize("metric", ["cosine", "correlation"])
63
+ def test_pairwise_distances_patching(caplog, dataframe, queue, dtype, metric):
64
+ with caplog.at_level(logging.WARNING, logger="sklearnex"):
65
+ rng = nprnd.default_rng()
66
+ X = _convert_to_dataframe(
67
+ rng.random(size=1000), sycl_queue=queue, target_df=dataframe, dtype=dtype
63
68
  )
64
- except subprocess.CalledProcessError as e:
65
- print(e)
66
- exit(1)
67
- mas = []
68
- result = {}
69
- for i in process.decode().split("\n"):
70
- if i.startswith("SKLEARNEX WARNING"):
71
- continue
72
- if not i.startswith("SKLEARNEX INFO") and len(mas) != 0:
73
- run_parse(mas, result)
74
- mas.clear()
75
- mas.append(i.strip())
76
- else:
77
- mas.append(i.strip())
78
- del os.environ["SKLEARNEX_VERBOSE"]
79
- return result
80
69
 
70
+ _ = pairwise_distances(X.reshape(1, -1), metric=metric)
71
+ assert all(
72
+ [
73
+ "running accelerated version" in i.message
74
+ or "fallback to original Scikit-learn" in i.message
75
+ for i in caplog.records
76
+ ]
77
+ ), f"sklearnex patching issue in pairwise_distances with log: \n{caplog.text}"
81
78
 
82
- result_log = get_result_log()
83
79
 
80
+ @pytest.mark.parametrize(
81
+ "dtype", [i for i in DTYPES if "32" in i.__name__ or "64" in i.__name__]
82
+ )
83
+ @pytest.mark.parametrize(
84
+ "dataframe, queue", get_dataframes_and_queues(dataframe_filter_="numpy")
85
+ )
86
+ def test_roc_auc_score_patching(caplog, dataframe, queue, dtype):
87
+ if dtype in [np.uint32, np.uint64] and sys.platform == "win32":
88
+ pytest.skip("Windows issue with unsigned ints")
89
+ with caplog.at_level(logging.WARNING, logger="sklearnex"):
90
+ rng = nprnd.default_rng()
91
+ X = _convert_to_dataframe(
92
+ rng.integers(2, size=1000),
93
+ sycl_queue=queue,
94
+ target_df=dataframe,
95
+ dtype=dtype,
96
+ )
97
+ y = _convert_to_dataframe(
98
+ rng.integers(2, size=1000),
99
+ sycl_queue=queue,
100
+ target_df=dataframe,
101
+ dtype=dtype,
102
+ )
84
103
 
85
- @pytest.mark.parametrize("configuration", result_log)
86
- def test_patching(configuration):
87
- if "OPT" in result_log[configuration]:
88
- return
89
- for skip in TO_SKIP:
90
- if re.search(skip, configuration) is not None:
91
- pytest.skip("SKIPPED", allow_module_level=False)
92
- raise ValueError("Test patching failed: " + configuration)
104
+ _ = roc_auc_score(X, y)
105
+ assert all(
106
+ [
107
+ "running accelerated version" in i.message
108
+ or "fallback to original Scikit-learn" in i.message
109
+ for i in caplog.records
110
+ ]
111
+ ), f"sklearnex patching issue in roc_auc_score with log: \n{caplog.text}"
93
112
 
94
113
 
95
- def _load_all_models(patched):
96
- if patched:
97
- patch_sklearn()
114
+ @pytest.mark.parametrize("dtype", DTYPES)
115
+ @pytest.mark.parametrize(
116
+ "dataframe, queue", get_dataframes_and_queues(dataframe_filter_="numpy")
117
+ )
118
+ @pytest.mark.parametrize("estimator, method", gen_models_info(PATCHED_MODELS))
119
+ def test_standard_estimator_patching(caplog, dataframe, queue, dtype, estimator, method):
120
+ with caplog.at_level(logging.WARNING, logger="sklearnex"):
121
+ est = PATCHED_MODELS[estimator]()
98
122
 
99
- models = []
100
- for patch_infos in get_patch_map().values():
101
- maybe_class = getattr(patch_infos[0][0][0], patch_infos[0][0][1])
102
- if (
103
- maybe_class is not None
104
- and isclass(maybe_class)
105
- and issubclass(maybe_class, BaseEstimator)
123
+ if estimator == "TSNE" and method == "fit_transform":
124
+ pytest.skip("TSNE.fit_transform is too slow for common testing")
125
+ elif (
126
+ estimator == "Ridge"
127
+ and method in ["predict", "score"]
128
+ and sys.platform == "win32"
129
+ and dtype in [np.uint32, np.uint64]
106
130
  ):
107
- models.append(maybe_class())
131
+ pytest.skip("Windows segmentation fault for Ridge.predict for unsigned ints")
132
+ elif not hasattr(est, method):
133
+ pytest.skip(f"sklearn available_if prevents testing {estimator}.{method}")
134
+ X, y = gen_dataset(est, queue=queue, target_df=dataframe, dtype=dtype)
135
+ est.fit(X, y)
136
+
137
+ if method != "score":
138
+ getattr(est, method)(X)
139
+ else:
140
+ est.score(X, y)
141
+ assert all(
142
+ [
143
+ "running accelerated version" in i.message
144
+ or "fallback to original Scikit-learn" in i.message
145
+ for i in caplog.records
146
+ ]
147
+ ), f"sklearnex patching issue in {estimator}.{method} with log: \n{caplog.text}"
148
+
149
+
150
+ @pytest.mark.parametrize("dtype", DTYPES)
151
+ @pytest.mark.parametrize(
152
+ "dataframe, queue", get_dataframes_and_queues(dataframe_filter_="numpy")
153
+ )
154
+ @pytest.mark.parametrize("estimator, method", gen_models_info(SPECIAL_INSTANCES))
155
+ def test_special_estimator_patching(caplog, dataframe, queue, dtype, estimator, method):
156
+ # prepare logging
157
+
158
+ with caplog.at_level(logging.WARNING, logger="sklearnex"):
159
+ est = SPECIAL_INSTANCES[estimator]
160
+
161
+ X, y = gen_dataset(est, queue=queue, target_df=dataframe, dtype=dtype)
162
+ est.fit(X, y)
163
+
164
+ if not hasattr(est, method):
165
+ pytest.skip(f"sklearn available_if prevents testing {estimator}.{method}")
166
+ if method != "score":
167
+ getattr(est, method)(X)
168
+ else:
169
+ est.score(X, y)
170
+ assert all(
171
+ [
172
+ "running accelerated version" in i.message
173
+ or "fallback to original Scikit-learn" in i.message
174
+ for i in caplog.records
175
+ ]
176
+ ), f"sklearnex patching issue in {estimator}.{method} with log: \n{caplog.text}"
177
+
178
+
179
+ @pytest.mark.parametrize("estimator", UNPATCHED_MODELS.keys())
180
+ def test_standard_estimator_signatures(estimator):
181
+ est = PATCHED_MODELS[estimator]()
182
+ unpatched_est = UNPATCHED_MODELS[estimator]()
183
+
184
+ # all public sklearn methods should have signature matches in sklearnex
185
+
186
+ unpatched_est_methods = [
187
+ i
188
+ for i in dir(unpatched_est)
189
+ if not i.startswith("_") and not i.endswith("_") and hasattr(unpatched_est, i)
190
+ ]
191
+ for method in unpatched_est_methods:
192
+ est_method = getattr(est, method)
193
+ unpatched_est_method = getattr(unpatched_est, method)
194
+ if callable(unpatched_est_method):
195
+ regex = rf"(?:sklearn|daal4py)\S*{estimator}" # needed due to differences in module structure
196
+ patched_sig = re.sub(regex, estimator, str(signature(est_method)))
197
+ unpatched_sig = re.sub(regex, estimator, str(signature(unpatched_est_method)))
198
+ assert (
199
+ patched_sig == unpatched_sig
200
+ ), f"Signature of {estimator}.{method} does not match sklearn"
201
+
202
+
203
+ @pytest.mark.parametrize("estimator", UNPATCHED_MODELS.keys())
204
+ def test_standard_estimator_init_signatures(estimator):
205
+ # Several estimators have additional parameters that are user-accessible
206
+ # which are sklearnex-specific. They will fail and are removed from tests.
207
+ # remove n_jobs due to estimator patching for sklearnex (known deviation)
208
+ patched_sig = str(signature(PATCHED_MODELS[estimator].__init__))
209
+ unpatched_sig = str(signature(UNPATCHED_MODELS[estimator].__init__))
210
+
211
+ # Sklearnex allows for positional kwargs and n_jobs, when sklearn doesn't
212
+ for kwarg in ["n_jobs=None", "*"]:
213
+ patched_sig = patched_sig.replace(", " + kwarg, "")
214
+ unpatched_sig = unpatched_sig.replace(", " + kwarg, "")
108
215
 
109
- if patched:
110
- unpatch_sklearn()
216
+ # Special sklearnex-specific kwargs are removed from signatures here
217
+ if estimator in [
218
+ "RandomForestRegressor",
219
+ "RandomForestClassifier",
220
+ "ExtraTreesRegressor",
221
+ "ExtraTreesClassifier",
222
+ ]:
223
+ for kwarg in ["min_bin_size=1", "max_bins=256"]:
224
+ patched_sig = patched_sig.replace(", " + kwarg, "")
111
225
 
112
- return models
226
+ assert (
227
+ patched_sig == unpatched_sig
228
+ ), f"Signature of {estimator}.__init__ does not match sklearn"
113
229
 
114
230
 
115
- PATCHED_MODELS = _load_all_models(patched=True)
116
- UNPATCHED_MODELS = _load_all_models(patched=False)
231
+ @pytest.mark.parametrize(
232
+ "function",
233
+ [
234
+ i
235
+ for i in UNPATCHED_FUNCTIONS.keys()
236
+ if i not in ["train_test_split", "set_config", "config_context"]
237
+ ],
238
+ )
239
+ def test_patched_function_signatures(function):
240
+ # certain functions are dropped from the test
241
+ # as they add functionality to the underlying sklearn function
242
+ if not sklearn_check_version("1.1") and function == "_assert_all_finite":
243
+ pytest.skip("Sklearn versioning not added to _assert_all_finite")
244
+ func = PATCHED_FUNCTIONS[function]
245
+ unpatched_func = UNPATCHED_FUNCTIONS[function]
117
246
 
247
+ if callable(unpatched_func):
248
+ assert str(signature(func)) == str(
249
+ signature(unpatched_func)
250
+ ), f"Signature of {func} does not match sklearn"
118
251
 
119
- @pytest.mark.parametrize(("patched", "unpatched"), zip(PATCHED_MODELS, UNPATCHED_MODELS))
120
- def test_is_patched_instance(patched, unpatched):
252
+
253
+ def test_patch_map_match():
254
+ # This rule applies to functions and classes which are out of preview.
255
+ # Items listed in a matching submodule's __all__ attribute should be
256
+ # in get_patch_map. There should not be any missing or additional elements.
257
+
258
+ def list_all_attr(string):
259
+ try:
260
+ modules = set(importlib.import_module(string).__all__)
261
+ except ModuleNotFoundError:
262
+ modules = set([None])
263
+ return modules
264
+
265
+ if _is_preview_enabled():
266
+ pytest.skip("preview sklearnex has been activated")
267
+ patched = {**PATCHED_MODELS, **PATCHED_FUNCTIONS}
268
+
269
+ sklearnex__all__ = list_all_attr("sklearnex")
270
+ sklearn__all__ = list_all_attr("sklearn")
271
+
272
+ module_map = {i: i for i in sklearnex__all__.intersection(sklearn__all__)}
273
+
274
+ # _assert_all_finite patches an internal sklearn function which isn't
275
+ # exposed via __all__ in sklearn. It is a special case where this rule
276
+ # is not applied (e.g. it is grandfathered in).
277
+ del patched["_assert_all_finite"]
278
+
279
+ # remove all scikit-learn-intelex-only estimators
280
+ for i in patched.copy():
281
+ if i not in UNPATCHED_MODELS and i not in UNPATCHED_FUNCTIONS:
282
+ del patched[i]
283
+
284
+ for module in module_map:
285
+ sklearn_module__all__ = list_all_attr("sklearn." + module_map[module])
286
+ sklearnex_module__all__ = list_all_attr("sklearnex." + module)
287
+ intersect = sklearnex_module__all__.intersection(sklearn_module__all__)
288
+
289
+ for i in intersect:
290
+ if i:
291
+ del patched[i]
292
+ else:
293
+ del patched[module]
294
+ assert patched == {}, f"{patched.keys()} were not properly patched"
295
+
296
+
297
+ @pytest.mark.parametrize("estimator", UNPATCHED_MODELS.keys())
298
+ def test_is_patched_instance(estimator):
299
+ patched = PATCHED_MODELS[estimator]
300
+ unpatched = UNPATCHED_MODELS[estimator]
121
301
  assert is_patched_instance(patched), f"{patched} is a patched instance"
122
302
  assert not is_patched_instance(unpatched), f"{unpatched} is an unpatched instance"
303
+
304
+
305
+ @pytest.mark.parametrize("estimator", PATCHED_MODELS.keys())
306
+ def test_if_estimator_inherits_sklearn(estimator):
307
+ est = PATCHED_MODELS[estimator]
308
+ if estimator in UNPATCHED_MODELS:
309
+ assert issubclass(
310
+ est, UNPATCHED_MODELS[estimator]
311
+ ), f"{estimator} does not inherit from the patched sklearn estimator"
312
+ else:
313
+ assert issubclass(est, BaseEstimator)
314
+ assert any(
315
+ [
316
+ issubclass(est, i)
317
+ for i in [
318
+ ClassifierMixin,
319
+ ClusterMixin,
320
+ OutlierMixin,
321
+ RegressorMixin,
322
+ TransformerMixin,
323
+ ]
324
+ ]
325
+ ), f"{estimator} does not inherit a sklearn Mixin"
326
+
327
+
328
+ @pytest.mark.parametrize("estimator", UNPATCHED_MODELS.keys())
329
+ def test_docstring_patching_match(estimator):
330
+ patched = PATCHED_MODELS[estimator]
331
+ unpatched = UNPATCHED_MODELS[estimator]
332
+ patched_docstrings = {
333
+ i: getattr(patched, i).__doc__
334
+ for i in dir(patched)
335
+ if not i.startswith("_") and not i.endswith("_") and hasattr(patched, i)
336
+ }
337
+ unpatched_docstrings = {
338
+ i: getattr(unpatched, i).__doc__
339
+ for i in dir(unpatched)
340
+ if not i.startswith("_") and not i.endswith("_") and hasattr(unpatched, i)
341
+ }
342
+
343
+ # check class docstring match if a docstring is available
344
+
345
+ assert (patched.__doc__ is None) == (unpatched.__doc__ is None)
346
+
347
+ # check class attribute docstrings
348
+
349
+ for i in unpatched_docstrings:
350
+ assert (patched_docstrings[i] is None) == (unpatched_docstrings[i] is None)
351
+
352
+
353
+ @pytest.mark.parametrize("member", ["_onedal_cpu_supported", "_onedal_gpu_supported"])
354
+ @pytest.mark.parametrize(
355
+ "name",
356
+ [i for i in PATCHED_MODELS.keys() if "sklearnex" in PATCHED_MODELS[i].__module__],
357
+ )
358
+ def test_onedal_supported_member(name, member):
359
+ patched = PATCHED_MODELS[name]
360
+ sig = str(inspect.signature(getattr(patched, member)))
361
+ assert "(self, method_name, *data)" == sig