Prompt48 commited on
Commit
af25e42
·
verified ·
1 Parent(s): ff8fcd9

Upload edit\Qwen3-TTS-test\.venv\Lib\site-packages\sklearn\decomposition\tests\test_dict_learning.py with huggingface_hub

Browse files
edit//Qwen3-TTS-test//.venv//Lib//site-packages//sklearn//decomposition//tests//test_dict_learning.py ADDED
@@ -0,0 +1,1015 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import itertools
2
+ import warnings
3
+ from functools import partial
4
+
5
+ import numpy as np
6
+ import pytest
7
+
8
+ import sklearn
9
+ from sklearn.base import clone
10
+ from sklearn.decomposition import (
11
+ DictionaryLearning,
12
+ MiniBatchDictionaryLearning,
13
+ SparseCoder,
14
+ dict_learning,
15
+ dict_learning_online,
16
+ sparse_encode,
17
+ )
18
+ from sklearn.decomposition._dict_learning import _update_dict
19
+ from sklearn.exceptions import ConvergenceWarning
20
+ from sklearn.utils import check_array
21
+ from sklearn.utils._testing import (
22
+ TempMemmap,
23
+ assert_allclose,
24
+ assert_array_almost_equal,
25
+ assert_array_equal,
26
+ ignore_warnings,
27
+ )
28
+ from sklearn.utils.estimator_checks import (
29
+ check_transformer_data_not_an_array,
30
+ check_transformer_general,
31
+ check_transformers_unfitted,
32
+ )
33
+ from sklearn.utils.parallel import Parallel
34
+
35
+ rng_global = np.random.RandomState(0)
36
+ n_samples, n_features = 10, 8
37
+ X = rng_global.randn(n_samples, n_features)
38
+
39
+
40
+ # TODO: remove mark once loky bug is fixed:
41
+ # https://github.com/joblib/loky/issues/458
42
+ @pytest.mark.thread_unsafe
43
+ def test_sparse_encode_shapes_omp():
44
+ rng = np.random.RandomState(0)
45
+ algorithms = ["omp", "lasso_lars", "lasso_cd", "lars", "threshold"]
46
+ for n_components, n_samples in itertools.product([1, 5], [1, 9]):
47
+ X_ = rng.randn(n_samples, n_features)
48
+ dictionary = rng.randn(n_components, n_features)
49
+ for algorithm, n_jobs in itertools.product(algorithms, [1, 2]):
50
+ code = sparse_encode(X_, dictionary, algorithm=algorithm, n_jobs=n_jobs)
51
+ assert code.shape == (n_samples, n_components)
52
+
53
+
54
+ def test_dict_learning_shapes():
55
+ n_components = 5
56
+ dico = DictionaryLearning(n_components, random_state=0).fit(X)
57
+ assert dico.components_.shape == (n_components, n_features)
58
+
59
+ n_components = 1
60
+ dico = DictionaryLearning(n_components, random_state=0).fit(X)
61
+ assert dico.components_.shape == (n_components, n_features)
62
+ assert dico.transform(X).shape == (X.shape[0], n_components)
63
+
64
+
65
+ def test_dict_learning_overcomplete():
66
+ n_components = 12
67
+ dico = DictionaryLearning(n_components, random_state=0).fit(X)
68
+ assert dico.components_.shape == (n_components, n_features)
69
+
70
+
71
+ def test_max_iter():
72
+ def ricker_function(resolution, center, width):
73
+ """Discrete sub-sampled Ricker (Mexican hat) wavelet"""
74
+ x = np.linspace(0, resolution - 1, resolution)
75
+ x = (
76
+ (2 / (np.sqrt(3 * width) * np.pi**0.25))
77
+ * (1 - (x - center) ** 2 / width**2)
78
+ * np.exp(-((x - center) ** 2) / (2 * width**2))
79
+ )
80
+ return x
81
+
82
+ def ricker_matrix(width, resolution, n_components):
83
+ """Dictionary of Ricker (Mexican hat) wavelets"""
84
+ centers = np.linspace(0, resolution - 1, n_components)
85
+ D = np.empty((n_components, resolution))
86
+ for i, center in enumerate(centers):
87
+ D[i] = ricker_function(resolution, center, width)
88
+ D /= np.sqrt(np.sum(D**2, axis=1))[:, np.newaxis]
89
+ return D
90
+
91
+ transform_algorithm = "lasso_cd"
92
+ resolution = 256
93
+ subsampling = 3 # subsampling factor
94
+ n_components = resolution // subsampling
95
+
96
+ # Compute a wavelet dictionary
97
+ D_multi = np.r_[
98
+ tuple(
99
+ ricker_matrix(
100
+ width=w, resolution=resolution, n_components=n_components // 5
101
+ )
102
+ for w in (10, 50, 100, 500)
103
+ )
104
+ ]
105
+
106
+ X = np.linspace(0, resolution - 1, resolution)
107
+ first_quarter = X < resolution / 4
108
+ X[first_quarter] = 3.0
109
+ X[np.logical_not(first_quarter)] = -1.0
110
+ X = X.reshape(1, -1)
111
+
112
+ # check that the underlying model fails to converge
113
+ with pytest.warns(ConvergenceWarning):
114
+ model = SparseCoder(
115
+ D_multi, transform_algorithm=transform_algorithm, transform_max_iter=1
116
+ )
117
+ model.fit_transform(X)
118
+
119
+ # check that the underlying model converges w/o warnings
120
+ with warnings.catch_warnings():
121
+ warnings.simplefilter("error", ConvergenceWarning)
122
+ model = SparseCoder(
123
+ D_multi, transform_algorithm=transform_algorithm, transform_max_iter=500
124
+ )
125
+ model.fit_transform(X)
126
+
127
+
128
+ def test_dict_learning_lars_positive_parameter():
129
+ n_components = 5
130
+ alpha = 1
131
+ err_msg = "Positive constraint not supported for 'lars' coding method."
132
+ with pytest.raises(ValueError, match=err_msg):
133
+ dict_learning(X, n_components, alpha=alpha, positive_code=True)
134
+
135
+
136
+ @pytest.mark.parametrize(
137
+ "transform_algorithm",
138
+ [
139
+ "lasso_lars",
140
+ "lasso_cd",
141
+ "threshold",
142
+ ],
143
+ )
144
+ @pytest.mark.parametrize("positive_code", [False, True])
145
+ @pytest.mark.parametrize("positive_dict", [False, True])
146
+ def test_dict_learning_positivity(transform_algorithm, positive_code, positive_dict):
147
+ n_components = 5
148
+ dico = DictionaryLearning(
149
+ n_components,
150
+ transform_algorithm=transform_algorithm,
151
+ random_state=0,
152
+ positive_code=positive_code,
153
+ positive_dict=positive_dict,
154
+ fit_algorithm="cd",
155
+ ).fit(X)
156
+
157
+ code = dico.transform(X)
158
+ if positive_dict:
159
+ assert (dico.components_ >= 0).all()
160
+ else:
161
+ assert (dico.components_ < 0).any()
162
+ if positive_code:
163
+ assert (code >= 0).all()
164
+ else:
165
+ assert (code < 0).any()
166
+
167
+
168
+ @pytest.mark.parametrize("positive_dict", [False, True])
169
+ def test_dict_learning_lars_dict_positivity(positive_dict):
170
+ n_components = 5
171
+ dico = DictionaryLearning(
172
+ n_components,
173
+ transform_algorithm="lars",
174
+ random_state=0,
175
+ positive_dict=positive_dict,
176
+ fit_algorithm="cd",
177
+ ).fit(X)
178
+
179
+ if positive_dict:
180
+ assert (dico.components_ >= 0).all()
181
+ else:
182
+ assert (dico.components_ < 0).any()
183
+
184
+
185
+ def test_dict_learning_lars_code_positivity():
186
+ n_components = 5
187
+ dico = DictionaryLearning(
188
+ n_components,
189
+ transform_algorithm="lars",
190
+ random_state=0,
191
+ positive_code=True,
192
+ fit_algorithm="cd",
193
+ ).fit(X)
194
+
195
+ err_msg = "Positive constraint not supported for '{}' coding method."
196
+ err_msg = err_msg.format("lars")
197
+ with pytest.raises(ValueError, match=err_msg):
198
+ dico.transform(X)
199
+
200
+
201
+ def test_dict_learning_reconstruction():
202
+ n_components = 12
203
+ dico = DictionaryLearning(
204
+ n_components, transform_algorithm="omp", transform_alpha=0.001, random_state=0
205
+ )
206
+ code = dico.fit(X).transform(X)
207
+ assert_array_almost_equal(np.dot(code, dico.components_), X)
208
+ assert_array_almost_equal(dico.inverse_transform(code), X)
209
+
210
+ dico.set_params(transform_algorithm="lasso_lars")
211
+ code = dico.transform(X)
212
+ assert_array_almost_equal(np.dot(code, dico.components_), X, decimal=2)
213
+ assert_array_almost_equal(dico.inverse_transform(code), X, decimal=2)
214
+
215
+ # test error raised for wrong code size
216
+ with pytest.raises(ValueError, match="Expected 12, got 11."):
217
+ dico.inverse_transform(code[:, :-1])
218
+
219
+ # used to test lars here too, but there's no guarantee the number of
220
+ # nonzero atoms is right.
221
+
222
+
223
+ # TODO: remove mark once loky bug is fixed:
224
+ # https://github.com/joblib/loky/issues/458
225
+ @pytest.mark.thread_unsafe
226
+ def test_dict_learning_reconstruction_parallel():
227
+ # regression test that parallel reconstruction works with n_jobs>1
228
+ n_components = 12
229
+ dico = DictionaryLearning(
230
+ n_components,
231
+ transform_algorithm="omp",
232
+ transform_alpha=0.001,
233
+ random_state=0,
234
+ n_jobs=4,
235
+ )
236
+ code = dico.fit(X).transform(X)
237
+ assert_array_almost_equal(np.dot(code, dico.components_), X)
238
+
239
+ dico.set_params(transform_algorithm="lasso_lars")
240
+ code = dico.transform(X)
241
+ assert_array_almost_equal(np.dot(code, dico.components_), X, decimal=2)
242
+
243
+
244
+ # TODO: remove mark once loky bug is fixed:
245
+ # https://github.com/joblib/loky/issues/458
246
+ @pytest.mark.thread_unsafe
247
+ def test_dict_learning_lassocd_readonly_data():
248
+ n_components = 12
249
+ with TempMemmap(X) as X_read_only:
250
+ dico = DictionaryLearning(
251
+ n_components,
252
+ transform_algorithm="lasso_cd",
253
+ transform_alpha=0.001,
254
+ random_state=0,
255
+ n_jobs=4,
256
+ )
257
+ with ignore_warnings(category=ConvergenceWarning):
258
+ code = dico.fit(X_read_only).transform(X_read_only)
259
+ assert_array_almost_equal(
260
+ np.dot(code, dico.components_), X_read_only, decimal=2
261
+ )
262
+
263
+
264
+ def test_dict_learning_nonzero_coefs():
265
+ n_components = 4
266
+ dico = DictionaryLearning(
267
+ n_components,
268
+ transform_algorithm="lars",
269
+ transform_n_nonzero_coefs=3,
270
+ random_state=0,
271
+ )
272
+ code = dico.fit(X).transform(X[np.newaxis, 1])
273
+ assert len(np.flatnonzero(code)) == 3
274
+
275
+ dico.set_params(transform_algorithm="omp")
276
+ code = dico.transform(X[np.newaxis, 1])
277
+ assert len(np.flatnonzero(code)) == 3
278
+
279
+
280
+ def test_dict_learning_split():
281
+ n_components = 5
282
+ dico = DictionaryLearning(
283
+ n_components, transform_algorithm="threshold", random_state=0
284
+ )
285
+ code = dico.fit(X).transform(X)
286
+ Xr = dico.inverse_transform(code)
287
+
288
+ dico.split_sign = True
289
+ split_code = dico.transform(X)
290
+
291
+ assert_array_almost_equal(
292
+ split_code[:, :n_components] - split_code[:, n_components:], code
293
+ )
294
+
295
+ Xr2 = dico.inverse_transform(split_code)
296
+ assert_array_almost_equal(Xr, Xr2)
297
+
298
+
299
+ def test_dict_learning_online_shapes():
300
+ rng = np.random.RandomState(0)
301
+ n_components = 8
302
+
303
+ code, dictionary = dict_learning_online(
304
+ X,
305
+ n_components=n_components,
306
+ batch_size=4,
307
+ max_iter=10,
308
+ method="cd",
309
+ random_state=rng,
310
+ return_code=True,
311
+ )
312
+ assert code.shape == (n_samples, n_components)
313
+ assert dictionary.shape == (n_components, n_features)
314
+ assert np.dot(code, dictionary).shape == X.shape
315
+
316
+ dictionary = dict_learning_online(
317
+ X,
318
+ n_components=n_components,
319
+ batch_size=4,
320
+ max_iter=10,
321
+ method="cd",
322
+ random_state=rng,
323
+ return_code=False,
324
+ )
325
+ assert dictionary.shape == (n_components, n_features)
326
+
327
+
328
+ def test_dict_learning_online_lars_positive_parameter():
329
+ err_msg = "Positive constraint not supported for 'lars' coding method."
330
+ with pytest.raises(ValueError, match=err_msg):
331
+ dict_learning_online(X, batch_size=4, max_iter=10, positive_code=True)
332
+
333
+
334
+ @pytest.mark.parametrize(
335
+ "transform_algorithm",
336
+ [
337
+ "lasso_lars",
338
+ "lasso_cd",
339
+ "threshold",
340
+ ],
341
+ )
342
+ @pytest.mark.parametrize("positive_code", [False, True])
343
+ @pytest.mark.parametrize("positive_dict", [False, True])
344
+ def test_minibatch_dictionary_learning_positivity(
345
+ transform_algorithm, positive_code, positive_dict
346
+ ):
347
+ n_components = 8
348
+ dico = MiniBatchDictionaryLearning(
349
+ n_components,
350
+ batch_size=4,
351
+ max_iter=10,
352
+ transform_algorithm=transform_algorithm,
353
+ random_state=0,
354
+ positive_code=positive_code,
355
+ positive_dict=positive_dict,
356
+ fit_algorithm="cd",
357
+ ).fit(X)
358
+
359
+ code = dico.transform(X)
360
+ if positive_dict:
361
+ assert (dico.components_ >= 0).all()
362
+ else:
363
+ assert (dico.components_ < 0).any()
364
+ if positive_code:
365
+ assert (code >= 0).all()
366
+ else:
367
+ assert (code < 0).any()
368
+
369
+
370
+ @pytest.mark.parametrize("positive_dict", [False, True])
371
+ def test_minibatch_dictionary_learning_lars(positive_dict):
372
+ n_components = 8
373
+
374
+ dico = MiniBatchDictionaryLearning(
375
+ n_components,
376
+ batch_size=4,
377
+ max_iter=10,
378
+ transform_algorithm="lars",
379
+ random_state=0,
380
+ positive_dict=positive_dict,
381
+ fit_algorithm="cd",
382
+ ).fit(X)
383
+
384
+ if positive_dict:
385
+ assert (dico.components_ >= 0).all()
386
+ else:
387
+ assert (dico.components_ < 0).any()
388
+
389
+
390
+ @pytest.mark.parametrize("positive_code", [False, True])
391
+ @pytest.mark.parametrize("positive_dict", [False, True])
392
+ def test_dict_learning_online_positivity(positive_code, positive_dict):
393
+ rng = np.random.RandomState(0)
394
+ n_components = 8
395
+
396
+ code, dictionary = dict_learning_online(
397
+ X,
398
+ n_components=n_components,
399
+ batch_size=4,
400
+ method="cd",
401
+ alpha=1,
402
+ random_state=rng,
403
+ positive_dict=positive_dict,
404
+ positive_code=positive_code,
405
+ )
406
+ if positive_dict:
407
+ assert (dictionary >= 0).all()
408
+ else:
409
+ assert (dictionary < 0).any()
410
+ if positive_code:
411
+ assert (code >= 0).all()
412
+ else:
413
+ assert (code < 0).any()
414
+
415
+
416
+ def test_dict_learning_online_verbosity():
417
+ # test verbosity for better coverage
418
+ n_components = 5
419
+ import sys
420
+ from io import StringIO
421
+
422
+ old_stdout = sys.stdout
423
+ try:
424
+ sys.stdout = StringIO()
425
+
426
+ # convergence monitoring verbosity
427
+ dico = MiniBatchDictionaryLearning(
428
+ n_components, batch_size=4, max_iter=5, verbose=1, tol=0.1, random_state=0
429
+ )
430
+ dico.fit(X)
431
+ dico = MiniBatchDictionaryLearning(
432
+ n_components,
433
+ batch_size=4,
434
+ max_iter=5,
435
+ verbose=1,
436
+ max_no_improvement=2,
437
+ random_state=0,
438
+ )
439
+ dico.fit(X)
440
+ # higher verbosity level
441
+ dico = MiniBatchDictionaryLearning(
442
+ n_components, batch_size=4, max_iter=5, verbose=2, random_state=0
443
+ )
444
+ dico.fit(X)
445
+
446
+ # function API verbosity
447
+ dict_learning_online(
448
+ X,
449
+ n_components=n_components,
450
+ batch_size=4,
451
+ alpha=1,
452
+ verbose=1,
453
+ random_state=0,
454
+ )
455
+ dict_learning_online(
456
+ X,
457
+ n_components=n_components,
458
+ batch_size=4,
459
+ alpha=1,
460
+ verbose=2,
461
+ random_state=0,
462
+ )
463
+ finally:
464
+ sys.stdout = old_stdout
465
+
466
+ assert dico.components_.shape == (n_components, n_features)
467
+
468
+
469
+ def test_dict_learning_online_estimator_shapes():
470
+ n_components = 5
471
+ dico = MiniBatchDictionaryLearning(
472
+ n_components, batch_size=4, max_iter=5, random_state=0
473
+ )
474
+ dico.fit(X)
475
+ assert dico.components_.shape == (n_components, n_features)
476
+
477
+
478
+ def test_dict_learning_online_overcomplete():
479
+ n_components = 12
480
+ dico = MiniBatchDictionaryLearning(
481
+ n_components, batch_size=4, max_iter=5, random_state=0
482
+ ).fit(X)
483
+ assert dico.components_.shape == (n_components, n_features)
484
+
485
+
486
+ def test_dict_learning_online_initialization():
487
+ n_components = 12
488
+ rng = np.random.RandomState(0)
489
+ V = rng.randn(n_components, n_features)
490
+ dico = MiniBatchDictionaryLearning(
491
+ n_components, batch_size=4, max_iter=0, dict_init=V, random_state=0
492
+ ).fit(X)
493
+ assert_array_equal(dico.components_, V)
494
+
495
+
496
+ def test_dict_learning_online_readonly_initialization():
497
+ n_components = 12
498
+ rng = np.random.RandomState(0)
499
+ V = rng.randn(n_components, n_features)
500
+ V.setflags(write=False)
501
+ MiniBatchDictionaryLearning(
502
+ n_components,
503
+ batch_size=4,
504
+ max_iter=1,
505
+ dict_init=V,
506
+ random_state=0,
507
+ shuffle=False,
508
+ ).fit(X)
509
+
510
+
511
+ def test_dict_learning_online_partial_fit():
512
+ n_components = 12
513
+ rng = np.random.RandomState(0)
514
+ V = rng.randn(n_components, n_features) # random init
515
+ V /= np.sum(V**2, axis=1)[:, np.newaxis]
516
+ dict1 = MiniBatchDictionaryLearning(
517
+ n_components,
518
+ max_iter=10,
519
+ batch_size=1,
520
+ alpha=1,
521
+ shuffle=False,
522
+ dict_init=V,
523
+ max_no_improvement=None,
524
+ tol=0.0,
525
+ random_state=0,
526
+ ).fit(X)
527
+ dict2 = MiniBatchDictionaryLearning(
528
+ n_components, alpha=1, dict_init=V, random_state=0
529
+ )
530
+ for i in range(10):
531
+ for sample in X:
532
+ dict2.partial_fit(sample[np.newaxis, :])
533
+
534
+ assert not np.all(sparse_encode(X, dict1.components_, alpha=1) == 0)
535
+ assert_array_almost_equal(dict1.components_, dict2.components_, decimal=2)
536
+
537
+ # partial_fit should ignore max_iter (#17433)
538
+ assert dict1.n_steps_ == dict2.n_steps_ == 100
539
+
540
+
541
+ def test_sparse_encode_shapes():
542
+ n_components = 12
543
+ rng = np.random.RandomState(0)
544
+ V = rng.randn(n_components, n_features) # random init
545
+ V /= np.sum(V**2, axis=1)[:, np.newaxis]
546
+ for algo in ("lasso_lars", "lasso_cd", "lars", "omp", "threshold"):
547
+ code = sparse_encode(X, V, algorithm=algo)
548
+ assert code.shape == (n_samples, n_components)
549
+
550
+
551
+ @pytest.mark.parametrize("algo", ["lasso_lars", "lasso_cd", "threshold"])
552
+ @pytest.mark.parametrize("positive", [False, True])
553
+ def test_sparse_encode_positivity(algo, positive):
554
+ n_components = 12
555
+ rng = np.random.RandomState(0)
556
+ V = rng.randn(n_components, n_features) # random init
557
+ V /= np.sum(V**2, axis=1)[:, np.newaxis]
558
+ code = sparse_encode(X, V, algorithm=algo, positive=positive)
559
+ if positive:
560
+ assert (code >= 0).all()
561
+ else:
562
+ assert (code < 0).any()
563
+
564
+
565
+ @pytest.mark.parametrize("algo", ["lars", "omp"])
566
+ def test_sparse_encode_unavailable_positivity(algo):
567
+ n_components = 12
568
+ rng = np.random.RandomState(0)
569
+ V = rng.randn(n_components, n_features) # random init
570
+ V /= np.sum(V**2, axis=1)[:, np.newaxis]
571
+ err_msg = "Positive constraint not supported for '{}' coding method."
572
+ err_msg = err_msg.format(algo)
573
+ with pytest.raises(ValueError, match=err_msg):
574
+ sparse_encode(X, V, algorithm=algo, positive=True)
575
+
576
+
577
+ def test_sparse_encode_input():
578
+ n_components = 100
579
+ rng = np.random.RandomState(0)
580
+ V = rng.randn(n_components, n_features) # random init
581
+ V /= np.sum(V**2, axis=1)[:, np.newaxis]
582
+ Xf = check_array(X, order="F")
583
+ for algo in ("lasso_lars", "lasso_cd", "lars", "omp", "threshold"):
584
+ a = sparse_encode(X, V, algorithm=algo)
585
+ b = sparse_encode(Xf, V, algorithm=algo)
586
+ assert_array_almost_equal(a, b)
587
+
588
+
589
+ def test_sparse_encode_error():
590
+ n_components = 12
591
+ rng = np.random.RandomState(0)
592
+ V = rng.randn(n_components, n_features) # random init
593
+ V /= np.sum(V**2, axis=1)[:, np.newaxis]
594
+ code = sparse_encode(X, V, alpha=0.001)
595
+ assert not np.all(code == 0)
596
+ assert np.sqrt(np.sum((np.dot(code, V) - X) ** 2)) < 0.1
597
+
598
+
599
+ def test_sparse_encode_error_default_sparsity():
600
+ rng = np.random.RandomState(0)
601
+ X = rng.randn(100, 64)
602
+ D = rng.randn(2, 64)
603
+ code = ignore_warnings(sparse_encode)(X, D, algorithm="omp", n_nonzero_coefs=None)
604
+ assert code.shape == (100, 2)
605
+
606
+
607
+ def test_sparse_coder_estimator():
608
+ n_components = 12
609
+ rng = np.random.RandomState(0)
610
+ V = rng.randn(n_components, n_features) # random init
611
+ V /= np.sum(V**2, axis=1)[:, np.newaxis]
612
+ coder = SparseCoder(
613
+ dictionary=V, transform_algorithm="lasso_lars", transform_alpha=0.001
614
+ )
615
+ code = coder.fit_transform(X)
616
+ Xr = coder.inverse_transform(code)
617
+ assert not np.all(code == 0)
618
+ assert np.sqrt(np.sum((np.dot(code, V) - X) ** 2)) < 0.1
619
+ np.testing.assert_allclose(Xr, np.dot(code, V))
620
+
621
+
622
+ def test_sparse_coder_estimator_clone():
623
+ n_components = 12
624
+ rng = np.random.RandomState(0)
625
+ V = rng.normal(size=(n_components, n_features)) # random init
626
+ V /= np.sum(V**2, axis=1)[:, np.newaxis]
627
+ coder = SparseCoder(
628
+ dictionary=V, transform_algorithm="lasso_lars", transform_alpha=0.001
629
+ )
630
+ cloned = clone(coder)
631
+ assert id(cloned) != id(coder)
632
+ np.testing.assert_allclose(cloned.dictionary, coder.dictionary)
633
+ assert id(cloned.dictionary) != id(coder.dictionary)
634
+ data = np.random.rand(n_samples, n_features).astype(np.float32)
635
+ np.testing.assert_allclose(cloned.transform(data), coder.transform(data))
636
+
637
+
638
+ # TODO: remove mark once loky bug is fixed:
639
+ # https://github.com/joblib/loky/issues/458
640
+ @pytest.mark.thread_unsafe
641
+ def test_sparse_coder_parallel_mmap():
642
+ # Non-regression test for:
643
+ # https://github.com/scikit-learn/scikit-learn/issues/5956
644
+ # Test that SparseCoder does not error by passing reading only
645
+ # arrays to child processes
646
+
647
+ rng = np.random.RandomState(777)
648
+ n_components, n_features = 40, 64
649
+ init_dict = rng.rand(n_components, n_features)
650
+ # Ensure that `data` is >2M. Joblib memory maps arrays
651
+ # if they are larger than 1MB. The 4 accounts for float32
652
+ # data type
653
+ n_samples = int(2e6) // (4 * n_features)
654
+ data = np.random.rand(n_samples, n_features).astype(np.float32)
655
+
656
+ sc = SparseCoder(init_dict, transform_algorithm="omp", n_jobs=2)
657
+ sc.fit_transform(data)
658
+
659
+
660
+ def test_sparse_coder_common_transformer():
661
+ rng = np.random.RandomState(777)
662
+ n_components, n_features = 40, 3
663
+ init_dict = rng.rand(n_components, n_features)
664
+
665
+ sc = SparseCoder(init_dict)
666
+
667
+ check_transformer_data_not_an_array(sc.__class__.__name__, sc)
668
+ check_transformer_general(sc.__class__.__name__, sc)
669
+ check_transformer_general_memmap = partial(
670
+ check_transformer_general, readonly_memmap=True
671
+ )
672
+ check_transformer_general_memmap(sc.__class__.__name__, sc)
673
+ check_transformers_unfitted(sc.__class__.__name__, sc)
674
+
675
+
676
+ def test_sparse_coder_n_features_in():
677
+ d = np.array([[1, 2, 3], [1, 2, 3]])
678
+ X = np.array([[1, 2, 3]])
679
+ sc = SparseCoder(d)
680
+ sc.fit(X)
681
+ assert sc.n_features_in_ == d.shape[1]
682
+
683
+
684
+ def test_sparse_encoder_feature_number_error():
685
+ n_components = 10
686
+ rng = np.random.RandomState(0)
687
+ D = rng.uniform(size=(n_components, n_features))
688
+ X = rng.uniform(size=(n_samples, n_features + 1))
689
+ coder = SparseCoder(D)
690
+ with pytest.raises(
691
+ ValueError, match="Dictionary and X have different numbers of features"
692
+ ):
693
+ coder.fit(X)
694
+
695
+
696
+ def test_update_dict():
697
+ # Check the dict update in batch mode vs online mode
698
+ # Non-regression test for #4866
699
+ rng = np.random.RandomState(0)
700
+
701
+ code = np.array([[0.5, -0.5], [0.1, 0.9]])
702
+ dictionary = np.array([[1.0, 0.0], [0.6, 0.8]])
703
+
704
+ X = np.dot(code, dictionary) + rng.randn(2, 2)
705
+
706
+ # full batch update
707
+ newd_batch = dictionary.copy()
708
+ _update_dict(newd_batch, X, code)
709
+
710
+ # online update
711
+ A = np.dot(code.T, code)
712
+ B = np.dot(X.T, code)
713
+ newd_online = dictionary.copy()
714
+ _update_dict(newd_online, X, code, A, B)
715
+
716
+ assert_allclose(newd_batch, newd_online)
717
+
718
+
719
+ @pytest.mark.parametrize(
720
+ "algorithm", ("lasso_lars", "lasso_cd", "lars", "threshold", "omp")
721
+ )
722
+ @pytest.mark.parametrize("data_type", (np.float32, np.float64))
723
+ # Note: do not check integer input because `lasso_lars` and `lars` fail with
724
+ # `ValueError` in `_lars_path_solver`
725
+ def test_sparse_encode_dtype_match(data_type, algorithm):
726
+ n_components = 6
727
+ rng = np.random.RandomState(0)
728
+ dictionary = rng.randn(n_components, n_features)
729
+ code = sparse_encode(
730
+ X.astype(data_type), dictionary.astype(data_type), algorithm=algorithm
731
+ )
732
+ assert code.dtype == data_type
733
+
734
+
735
+ @pytest.mark.parametrize(
736
+ "algorithm", ("lasso_lars", "lasso_cd", "lars", "threshold", "omp")
737
+ )
738
+ def test_sparse_encode_numerical_consistency(algorithm):
739
+ # verify numerical consistency among np.float32 and np.float64
740
+ rtol = 1e-4
741
+ n_components = 6
742
+ rng = np.random.RandomState(0)
743
+ dictionary = rng.randn(n_components, n_features)
744
+ code_32 = sparse_encode(
745
+ X.astype(np.float32), dictionary.astype(np.float32), algorithm=algorithm
746
+ )
747
+ code_64 = sparse_encode(
748
+ X.astype(np.float64), dictionary.astype(np.float64), algorithm=algorithm
749
+ )
750
+ assert_allclose(code_32, code_64, rtol=rtol)
751
+
752
+
753
+ @pytest.mark.parametrize(
754
+ "transform_algorithm", ("lasso_lars", "lasso_cd", "lars", "threshold", "omp")
755
+ )
756
+ @pytest.mark.parametrize("data_type", (np.float32, np.float64))
757
+ # Note: do not check integer input because `lasso_lars` and `lars` fail with
758
+ # `ValueError` in `_lars_path_solver`
759
+ def test_sparse_coder_dtype_match(data_type, transform_algorithm):
760
+ # Verify preserving dtype for transform in sparse coder
761
+ n_components = 6
762
+ rng = np.random.RandomState(0)
763
+ dictionary = rng.randn(n_components, n_features)
764
+ coder = SparseCoder(
765
+ dictionary.astype(data_type), transform_algorithm=transform_algorithm
766
+ )
767
+ code = coder.transform(X.astype(data_type))
768
+ assert code.dtype == data_type
769
+
770
+
771
+ @pytest.mark.parametrize("fit_algorithm", ("lars", "cd"))
772
+ @pytest.mark.parametrize(
773
+ "transform_algorithm", ("lasso_lars", "lasso_cd", "lars", "threshold", "omp")
774
+ )
775
+ @pytest.mark.parametrize(
776
+ "data_type, expected_type",
777
+ (
778
+ (np.float32, np.float32),
779
+ (np.float64, np.float64),
780
+ (np.int32, np.float64),
781
+ (np.int64, np.float64),
782
+ ),
783
+ )
784
+ def test_dictionary_learning_dtype_match(
785
+ data_type,
786
+ expected_type,
787
+ fit_algorithm,
788
+ transform_algorithm,
789
+ ):
790
+ # Verify preserving dtype for fit and transform in dictionary learning class
791
+ dict_learner = DictionaryLearning(
792
+ n_components=8,
793
+ fit_algorithm=fit_algorithm,
794
+ transform_algorithm=transform_algorithm,
795
+ random_state=0,
796
+ )
797
+ dict_learner.fit(X.astype(data_type))
798
+ assert dict_learner.components_.dtype == expected_type
799
+ assert dict_learner.transform(X.astype(data_type)).dtype == expected_type
800
+
801
+
802
+ @pytest.mark.parametrize("fit_algorithm", ("lars", "cd"))
803
+ @pytest.mark.parametrize(
804
+ "transform_algorithm", ("lasso_lars", "lasso_cd", "lars", "threshold", "omp")
805
+ )
806
+ @pytest.mark.parametrize(
807
+ "data_type, expected_type",
808
+ (
809
+ (np.float32, np.float32),
810
+ (np.float64, np.float64),
811
+ (np.int32, np.float64),
812
+ (np.int64, np.float64),
813
+ ),
814
+ )
815
+ def test_minibatch_dictionary_learning_dtype_match(
816
+ data_type,
817
+ expected_type,
818
+ fit_algorithm,
819
+ transform_algorithm,
820
+ ):
821
+ # Verify preserving dtype for fit and transform in minibatch dictionary learning
822
+ dict_learner = MiniBatchDictionaryLearning(
823
+ n_components=8,
824
+ batch_size=10,
825
+ fit_algorithm=fit_algorithm,
826
+ transform_algorithm=transform_algorithm,
827
+ max_iter=100,
828
+ tol=1e-1,
829
+ random_state=0,
830
+ )
831
+ dict_learner.fit(X.astype(data_type))
832
+
833
+ assert dict_learner.components_.dtype == expected_type
834
+ assert dict_learner.transform(X.astype(data_type)).dtype == expected_type
835
+ assert dict_learner._A.dtype == expected_type
836
+ assert dict_learner._B.dtype == expected_type
837
+
838
+
839
+ @pytest.mark.parametrize("method", ("lars", "cd"))
840
+ @pytest.mark.parametrize(
841
+ "data_type, expected_type",
842
+ (
843
+ (np.float32, np.float32),
844
+ (np.float64, np.float64),
845
+ (np.int32, np.float64),
846
+ (np.int64, np.float64),
847
+ ),
848
+ )
849
+ def test_dict_learning_dtype_match(data_type, expected_type, method):
850
+ # Verify output matrix dtype
851
+ rng = np.random.RandomState(0)
852
+ n_components = 8
853
+ code, dictionary, _ = dict_learning(
854
+ X.astype(data_type),
855
+ n_components=n_components,
856
+ alpha=1,
857
+ random_state=rng,
858
+ method=method,
859
+ )
860
+ assert code.dtype == expected_type
861
+ assert dictionary.dtype == expected_type
862
+
863
+
864
+ @pytest.mark.parametrize("method", ("lars", "cd"))
865
+ def test_dict_learning_numerical_consistency(method):
866
+ # verify numerically consistent among np.float32 and np.float64
867
+ rtol = 1e-4
868
+ n_components = 4
869
+ alpha = 2
870
+
871
+ U_64, V_64, _ = dict_learning(
872
+ X.astype(np.float64),
873
+ n_components=n_components,
874
+ alpha=alpha,
875
+ random_state=0,
876
+ method=method,
877
+ )
878
+ U_32, V_32, _ = dict_learning(
879
+ X.astype(np.float32),
880
+ n_components=n_components,
881
+ alpha=alpha,
882
+ random_state=0,
883
+ method=method,
884
+ )
885
+
886
+ # Optimal solution (U*, V*) is not unique.
887
+ # If (U*, V*) is optimal solution, (-U*,-V*) is also optimal,
888
+ # and (column permutated U*, row permutated V*) are also optional
889
+ # as long as holding UV.
890
+ # So here UV, ||U||_1,1 and sum(||V_k||_2^2) are verified
891
+ # instead of comparing directly U and V.
892
+ assert_allclose(np.matmul(U_64, V_64), np.matmul(U_32, V_32), rtol=rtol)
893
+ assert_allclose(np.sum(np.abs(U_64)), np.sum(np.abs(U_32)), rtol=rtol)
894
+ assert_allclose(np.sum(V_64**2), np.sum(V_32**2), rtol=rtol)
895
+ # verify an obtained solution is not degenerate
896
+ assert np.mean(U_64 != 0.0) > 0.05
897
+ assert np.count_nonzero(U_64 != 0.0) == np.count_nonzero(U_32 != 0.0)
898
+
899
+
900
+ @pytest.mark.parametrize("method", ("lars", "cd"))
901
+ @pytest.mark.parametrize(
902
+ "data_type, expected_type",
903
+ (
904
+ (np.float32, np.float32),
905
+ (np.float64, np.float64),
906
+ (np.int32, np.float64),
907
+ (np.int64, np.float64),
908
+ ),
909
+ )
910
+ def test_dict_learning_online_dtype_match(data_type, expected_type, method):
911
+ # Verify output matrix dtype
912
+ rng = np.random.RandomState(0)
913
+ n_components = 8
914
+ code, dictionary = dict_learning_online(
915
+ X.astype(data_type),
916
+ n_components=n_components,
917
+ alpha=1,
918
+ batch_size=10,
919
+ random_state=rng,
920
+ method=method,
921
+ )
922
+ assert code.dtype == expected_type
923
+ assert dictionary.dtype == expected_type
924
+
925
+
926
+ @pytest.mark.parametrize("method", ("lars", "cd"))
927
+ def test_dict_learning_online_numerical_consistency(method):
928
+ # verify numerically consistent among np.float32 and np.float64
929
+ rtol = 1e-4
930
+ n_components = 4
931
+ alpha = 1
932
+
933
+ U_64, V_64 = dict_learning_online(
934
+ X.astype(np.float64),
935
+ n_components=n_components,
936
+ max_iter=1_000,
937
+ alpha=alpha,
938
+ batch_size=10,
939
+ random_state=0,
940
+ method=method,
941
+ tol=0.0,
942
+ max_no_improvement=None,
943
+ )
944
+ U_32, V_32 = dict_learning_online(
945
+ X.astype(np.float32),
946
+ n_components=n_components,
947
+ max_iter=1_000,
948
+ alpha=alpha,
949
+ batch_size=10,
950
+ random_state=0,
951
+ method=method,
952
+ tol=0.0,
953
+ max_no_improvement=None,
954
+ )
955
+
956
+ # Optimal solution (U*, V*) is not unique.
957
+ # If (U*, V*) is optimal solution, (-U*,-V*) is also optimal,
958
+ # and (column permutated U*, row permutated V*) are also optional
959
+ # as long as holding UV.
960
+ # So here UV, ||U||_1,1 and sum(||V_k||_2) are verified
961
+ # instead of comparing directly U and V.
962
+ assert_allclose(np.matmul(U_64, V_64), np.matmul(U_32, V_32), rtol=rtol)
963
+ assert_allclose(np.sum(np.abs(U_64)), np.sum(np.abs(U_32)), rtol=rtol)
964
+ assert_allclose(np.sum(V_64**2), np.sum(V_32**2), rtol=rtol)
965
+ # verify an obtained solution is not degenerate
966
+ assert np.mean(U_64 != 0.0) > 0.05
967
+ assert np.count_nonzero(U_64 != 0.0) == np.count_nonzero(U_32 != 0.0)
968
+
969
+
970
+ @pytest.mark.parametrize(
971
+ "estimator",
972
+ [
973
+ SparseCoder(rng_global.uniform(size=(n_features, n_features))),
974
+ DictionaryLearning(),
975
+ MiniBatchDictionaryLearning(batch_size=4, max_iter=10),
976
+ ],
977
+ ids=lambda x: x.__class__.__name__,
978
+ )
979
+ def test_get_feature_names_out(estimator):
980
+ """Check feature names for dict learning estimators."""
981
+ estimator.fit(X)
982
+ n_components = X.shape[1]
983
+
984
+ feature_names_out = estimator.get_feature_names_out()
985
+ estimator_name = estimator.__class__.__name__.lower()
986
+ assert_array_equal(
987
+ feature_names_out,
988
+ [f"{estimator_name}{i}" for i in range(n_components)],
989
+ )
990
+
991
+
992
+ # TODO: remove mark once loky bug is fixed:
993
+ # https://github.com/joblib/loky/issues/458
994
+ @pytest.mark.thread_unsafe
995
+ def test_cd_work_on_joblib_memmapped_data(monkeypatch):
996
+ monkeypatch.setattr(
997
+ sklearn.decomposition._dict_learning,
998
+ "Parallel",
999
+ partial(Parallel, max_nbytes=100),
1000
+ )
1001
+
1002
+ rng = np.random.RandomState(0)
1003
+ X_train = rng.randn(10, 10)
1004
+
1005
+ dict_learner = DictionaryLearning(
1006
+ n_components=5,
1007
+ random_state=0,
1008
+ n_jobs=2,
1009
+ fit_algorithm="cd",
1010
+ max_iter=50,
1011
+ verbose=True,
1012
+ )
1013
+
1014
+ # This must run and complete without error.
1015
+ dict_learner.fit(X_train)