surrogates_interface 4.0.1__py3-none-any.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.
- surrogates_interface/__init__.py +3 -0
- surrogates_interface/domains.py +107 -0
- surrogates_interface/openmdao.py +389 -0
- surrogates_interface/surrogates.py +1027 -0
- surrogates_interface/test/__init__.py +0 -0
- surrogates_interface/test/test_domains.py +44 -0
- surrogates_interface/test/test_init.py +12 -0
- surrogates_interface/test/test_pca_jacobian.py +137 -0
- surrogates_interface/test/test_smt.py +258 -0
- surrogates_interface/test/test_surrogates.py +22 -0
- surrogates_interface/test/test_tensorflow_and_openmdao.py +1220 -0
- surrogates_interface/test/test_transformers.py +617 -0
- surrogates_interface/transformers.py +816 -0
- surrogates_interface-4.0.1.dist-info/LICENSE +25 -0
- surrogates_interface-4.0.1.dist-info/METADATA +66 -0
- surrogates_interface-4.0.1.dist-info/RECORD +17 -0
- surrogates_interface-4.0.1.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import re
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
5
|
+
import h5py
|
|
6
|
+
import numpy as np
|
|
7
|
+
import numpy.testing as npt
|
|
8
|
+
import pytest
|
|
9
|
+
from sklearn.decomposition import PCA
|
|
10
|
+
from sklearn.preprocessing import MinMaxScaler, PowerTransformer, StandardScaler
|
|
11
|
+
from sklearn.preprocessing._data import QuantileTransformer
|
|
12
|
+
|
|
13
|
+
from surrogates_interface.transformers import PCA as PCASI
|
|
14
|
+
from surrogates_interface.transformers import MinMaxScaler as MinMaxScalerSI
|
|
15
|
+
from surrogates_interface.transformers import PowerTransformer as PowerTransformerSI
|
|
16
|
+
from surrogates_interface.transformers import PowerTransformerYeoJohnson
|
|
17
|
+
from surrogates_interface.transformers import StandardScaler as StandardScalerSI
|
|
18
|
+
from surrogates_interface.transformers import Transformer
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TestMinMaxScaler:
|
|
22
|
+
@staticmethod
|
|
23
|
+
@pytest.fixture(scope="class")
|
|
24
|
+
def make_data():
|
|
25
|
+
rng = np.random.default_rng(seed=1234)
|
|
26
|
+
n_samples = 10
|
|
27
|
+
x = np.column_stack(
|
|
28
|
+
(
|
|
29
|
+
rng.uniform(low=-5.0, high=20.0, size=(n_samples,)),
|
|
30
|
+
rng.uniform(low=1.0, high=12.0, size=(n_samples,)),
|
|
31
|
+
rng.uniform(low=-23.0, high=+101.0, size=(n_samples,)),
|
|
32
|
+
)
|
|
33
|
+
)
|
|
34
|
+
# n_features = x.shape[1]
|
|
35
|
+
|
|
36
|
+
sklearn_scaler = MinMaxScaler(feature_range=(-1.0, +1.0), copy=True, clip=False)
|
|
37
|
+
x_transformed = sklearn_scaler.fit_transform(x)
|
|
38
|
+
return sklearn_scaler, x, x_transformed
|
|
39
|
+
|
|
40
|
+
@pytest.fixture(scope="class")
|
|
41
|
+
def si_scaler(self, make_data):
|
|
42
|
+
# test_from_sklearn
|
|
43
|
+
sklearn_scaler, _, _ = make_data
|
|
44
|
+
si_scaler = Transformer.from_sklearn(sklearn_scaler)
|
|
45
|
+
npt.assert_array_equal(si_scaler.scale_, sklearn_scaler.scale_)
|
|
46
|
+
npt.assert_array_equal(si_scaler.min_, sklearn_scaler.min_)
|
|
47
|
+
return si_scaler
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def test_clip():
|
|
51
|
+
scaler_with_clip = MinMaxScaler(clip=True)
|
|
52
|
+
with pytest.raises(
|
|
53
|
+
ValueError,
|
|
54
|
+
match=re.escape(
|
|
55
|
+
"clip is not supported, because it cannot be differentiated."
|
|
56
|
+
),
|
|
57
|
+
):
|
|
58
|
+
MinMaxScalerSI.from_sklearn(scaler_with_clip)
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def test_transform_outofplace(make_data, si_scaler):
|
|
62
|
+
_, x, x_transformed = make_data
|
|
63
|
+
y_si = si_scaler.transform(x, inplace=False)
|
|
64
|
+
npt.assert_array_almost_equal_nulp(y_si, x_transformed)
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def test_transform_inplace(make_data, si_scaler):
|
|
68
|
+
_, x, x_transformed = make_data
|
|
69
|
+
y_si = x.copy()
|
|
70
|
+
si_scaler.transform(y_si, inplace=True)
|
|
71
|
+
npt.assert_array_almost_equal_nulp(y_si, x_transformed)
|
|
72
|
+
|
|
73
|
+
@staticmethod
|
|
74
|
+
def test_inverse_transform_outofplace(make_data, si_scaler):
|
|
75
|
+
_, x, x_transformed = make_data
|
|
76
|
+
y_si = si_scaler.inverse_transform(x_transformed, inplace=False)
|
|
77
|
+
npt.assert_array_almost_equal_nulp(y_si, x, nulp=2)
|
|
78
|
+
|
|
79
|
+
@staticmethod
|
|
80
|
+
def test_inverse_transform_inplace(make_data, si_scaler):
|
|
81
|
+
_, x, x_transformed = make_data
|
|
82
|
+
y_si = x_transformed.copy()
|
|
83
|
+
si_scaler.inverse_transform(y_si, inplace=True)
|
|
84
|
+
npt.assert_array_almost_equal_nulp(y_si, x, nulp=2)
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def test_transform_gradient(make_data, si_scaler):
|
|
88
|
+
_, x, _ = make_data
|
|
89
|
+
y_exact = si_scaler.transform_gradient(x)
|
|
90
|
+
h = 1e-6
|
|
91
|
+
y_fd = (
|
|
92
|
+
si_scaler.transform(x + h, inplace=False)
|
|
93
|
+
- si_scaler.transform(x, inplace=False)
|
|
94
|
+
) / h
|
|
95
|
+
npt.assert_allclose(y_exact, y_fd)
|
|
96
|
+
|
|
97
|
+
@staticmethod
|
|
98
|
+
def test_inverse_transform_gradient(make_data, si_scaler):
|
|
99
|
+
_, x, x_transformed = make_data
|
|
100
|
+
y_exact = si_scaler.inverse_transform_gradient(x_transformed)
|
|
101
|
+
h = 1e-6
|
|
102
|
+
y_fd = (
|
|
103
|
+
si_scaler.inverse_transform(x_transformed + h, inplace=False)
|
|
104
|
+
- si_scaler.inverse_transform(x_transformed, inplace=False)
|
|
105
|
+
) / h
|
|
106
|
+
npt.assert_allclose(y_exact, y_fd)
|
|
107
|
+
|
|
108
|
+
@staticmethod
|
|
109
|
+
def test_eq():
|
|
110
|
+
obj_1 = MinMaxScalerSI(
|
|
111
|
+
np.array([1.1, 10.4, 3.7]), np.array([-3.0, -50.0, +21.0])
|
|
112
|
+
)
|
|
113
|
+
obj_2 = MinMaxScalerSI(
|
|
114
|
+
np.array([1.1, 10.4, 3.7]), np.array([-3.0, -50.0, +21.0])
|
|
115
|
+
)
|
|
116
|
+
assert obj_1 == obj_2
|
|
117
|
+
|
|
118
|
+
@staticmethod
|
|
119
|
+
def test_eq_wrong_type():
|
|
120
|
+
obj_1 = MinMaxScalerSI(
|
|
121
|
+
np.array([1.1, 10.4, 3.7]), np.array([-3.0, -50.0, +21.0])
|
|
122
|
+
)
|
|
123
|
+
obj_2 = StandardScalerSI(np.array([1.1, 10.4]), np.array([-3.0, -50.0]))
|
|
124
|
+
assert not (obj_1 == obj_2)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class TestStandardScaler:
|
|
128
|
+
@staticmethod
|
|
129
|
+
@pytest.fixture(scope="class")
|
|
130
|
+
def make_data():
|
|
131
|
+
rng = np.random.default_rng(seed=1234)
|
|
132
|
+
n_samples = 10
|
|
133
|
+
x = np.column_stack(
|
|
134
|
+
(
|
|
135
|
+
rng.uniform(low=-5.0, high=20.0, size=(n_samples,)),
|
|
136
|
+
rng.uniform(low=1.0, high=12.0, size=(n_samples,)),
|
|
137
|
+
rng.uniform(low=-23.0, high=+101.0, size=(n_samples,)),
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
# n_features = x.shape[1]
|
|
141
|
+
|
|
142
|
+
sklearn_scaler = StandardScaler(copy=True, with_mean=True, with_std=True)
|
|
143
|
+
x_transformed = sklearn_scaler.fit_transform(x)
|
|
144
|
+
return sklearn_scaler, x, x_transformed
|
|
145
|
+
|
|
146
|
+
@staticmethod
|
|
147
|
+
@pytest.fixture(scope="class")
|
|
148
|
+
def si_scaler(make_data):
|
|
149
|
+
# test_from_sklearn
|
|
150
|
+
sklearn_scaler, _, _ = make_data
|
|
151
|
+
si_scaler = Transformer.from_sklearn(sklearn_scaler)
|
|
152
|
+
npt.assert_array_almost_equal_nulp(si_scaler.scale_, sklearn_scaler.scale_)
|
|
153
|
+
npt.assert_array_equal(si_scaler.mean_, sklearn_scaler.mean_)
|
|
154
|
+
return si_scaler
|
|
155
|
+
|
|
156
|
+
@staticmethod
|
|
157
|
+
def test_transform_outofplace(make_data, si_scaler):
|
|
158
|
+
_, x, x_transformed = make_data
|
|
159
|
+
y_si = si_scaler.transform(x, inplace=False)
|
|
160
|
+
npt.assert_array_almost_equal_nulp(y_si, x_transformed, nulp=180)
|
|
161
|
+
|
|
162
|
+
@staticmethod
|
|
163
|
+
def test_transform_inplace(make_data, si_scaler):
|
|
164
|
+
_, x, x_transformed = make_data
|
|
165
|
+
y_si = x.copy()
|
|
166
|
+
si_scaler.transform(y_si, inplace=True)
|
|
167
|
+
npt.assert_array_almost_equal_nulp(y_si, x_transformed, nulp=180)
|
|
168
|
+
|
|
169
|
+
@staticmethod
|
|
170
|
+
def test_inverse_transform_outofplace(make_data, si_scaler):
|
|
171
|
+
_, x, x_transformed = make_data
|
|
172
|
+
y_si = si_scaler.inverse_transform(x_transformed, inplace=False)
|
|
173
|
+
npt.assert_array_almost_equal_nulp(y_si, x, nulp=2)
|
|
174
|
+
|
|
175
|
+
@staticmethod
|
|
176
|
+
def test_inverse_transform_inplace(make_data, si_scaler):
|
|
177
|
+
_, x, x_transformed = make_data
|
|
178
|
+
y_si = x_transformed.copy()
|
|
179
|
+
si_scaler.inverse_transform(y_si, inplace=True)
|
|
180
|
+
npt.assert_array_almost_equal_nulp(y_si, x, nulp=2)
|
|
181
|
+
|
|
182
|
+
@staticmethod
|
|
183
|
+
def test_transform_gradient(make_data, si_scaler):
|
|
184
|
+
_, x, _ = make_data
|
|
185
|
+
y_exact = si_scaler.transform_gradient(x)
|
|
186
|
+
h = 1e-6
|
|
187
|
+
y_fd = (
|
|
188
|
+
si_scaler.transform(x + h, inplace=False)
|
|
189
|
+
- si_scaler.transform(x, inplace=False)
|
|
190
|
+
) / h
|
|
191
|
+
npt.assert_allclose(y_exact, y_fd)
|
|
192
|
+
|
|
193
|
+
@staticmethod
|
|
194
|
+
def test_inverse_transform_gradient(make_data, si_scaler):
|
|
195
|
+
_, x, x_transformed = make_data
|
|
196
|
+
y_exact = si_scaler.inverse_transform_gradient(x_transformed)
|
|
197
|
+
h = 1e-6
|
|
198
|
+
y_fd = (
|
|
199
|
+
si_scaler.inverse_transform(x_transformed + h, inplace=False)
|
|
200
|
+
- si_scaler.inverse_transform(x_transformed, inplace=False)
|
|
201
|
+
) / h
|
|
202
|
+
npt.assert_allclose(y_exact, y_fd)
|
|
203
|
+
|
|
204
|
+
@staticmethod
|
|
205
|
+
def test_eq():
|
|
206
|
+
obj_1 = StandardScalerSI(np.array([1.1, 10.4]), np.array([-3.0, -50.0]))
|
|
207
|
+
obj_2 = StandardScalerSI(np.array([1.1, 10.4]), np.array([-3.0, -50.0]))
|
|
208
|
+
assert obj_1 == obj_2
|
|
209
|
+
|
|
210
|
+
@staticmethod
|
|
211
|
+
def test_eq_wrong_type():
|
|
212
|
+
obj_1 = StandardScalerSI(np.array([1.1, 10.4]), np.array([-3.0, -50.0]))
|
|
213
|
+
obj_2 = MinMaxScalerSI(
|
|
214
|
+
np.array([1.1, 10.4, 3.7]), np.array([-3.0, -50.0, +21.0])
|
|
215
|
+
)
|
|
216
|
+
assert not (obj_1 == obj_2)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class TestPCA:
|
|
220
|
+
@staticmethod
|
|
221
|
+
@pytest.fixture(scope="class")
|
|
222
|
+
def make_data_with_whitening():
|
|
223
|
+
rng = np.random.default_rng(seed=1234)
|
|
224
|
+
n_samples = 100
|
|
225
|
+
n_features_original = 5
|
|
226
|
+
x = rng.normal(
|
|
227
|
+
loc=np.arange(n_features_original),
|
|
228
|
+
scale=np.array([1.0, 5.0, 2.0, 20.0, 6.0]),
|
|
229
|
+
size=(n_samples, n_features_original),
|
|
230
|
+
)
|
|
231
|
+
# Introduce linear correlation between some components.
|
|
232
|
+
x[:, 1] = 2.0 * x[:, 0]
|
|
233
|
+
x[:, 2] = 3.0 * x[:, 0] - 5.0
|
|
234
|
+
sklearn_pca = PCA(n_components=3, whiten=True)
|
|
235
|
+
x_transformed = sklearn_pca.fit_transform(x)
|
|
236
|
+
# Test: with n_components=n_features_original,
|
|
237
|
+
# the last 2 elements of sklearn_pca.singular_values_ are small.
|
|
238
|
+
return sklearn_pca, x, x_transformed
|
|
239
|
+
|
|
240
|
+
@staticmethod
|
|
241
|
+
@pytest.fixture(scope="class")
|
|
242
|
+
def make_data_without_whitening():
|
|
243
|
+
rng = np.random.default_rng(seed=1234)
|
|
244
|
+
n_samples = 100
|
|
245
|
+
n_features_original = 5
|
|
246
|
+
x = rng.normal(
|
|
247
|
+
loc=np.arange(n_features_original),
|
|
248
|
+
scale=np.array([1.0, 5.0, 2.0, 20.0, 6.0]),
|
|
249
|
+
size=(n_samples, n_features_original),
|
|
250
|
+
)
|
|
251
|
+
# Introduce linear correlation between some components.
|
|
252
|
+
x[:, 1] = 2.0 * x[:, 0]
|
|
253
|
+
x[:, 2] = 3.0 * x[:, 0] - 5.0
|
|
254
|
+
sklearn_pca = PCA(n_components=3, whiten=False)
|
|
255
|
+
x_transformed = sklearn_pca.fit_transform(x)
|
|
256
|
+
# Test: with n_components=n_features_original,
|
|
257
|
+
# the last 2 elements of sklearn_pca.singular_values_ are small.
|
|
258
|
+
return sklearn_pca, x, x_transformed
|
|
259
|
+
|
|
260
|
+
@staticmethod
|
|
261
|
+
@pytest.fixture(scope="class")
|
|
262
|
+
def si_pca_with_whitening(make_data_with_whitening):
|
|
263
|
+
# test_from_sklearn
|
|
264
|
+
sklearn_pca, _, _ = make_data_with_whitening
|
|
265
|
+
si_pca = Transformer.from_sklearn(sklearn_pca)
|
|
266
|
+
npt.assert_array_almost_equal_nulp(
|
|
267
|
+
np.sqrt(sklearn_pca.explained_variance_), si_pca.explained_std_
|
|
268
|
+
)
|
|
269
|
+
return si_pca
|
|
270
|
+
|
|
271
|
+
@staticmethod
|
|
272
|
+
@pytest.fixture(scope="class")
|
|
273
|
+
def si_pca_without_whitening(make_data_without_whitening):
|
|
274
|
+
# test_from_sklearn
|
|
275
|
+
sklearn_pca, _, _ = make_data_without_whitening
|
|
276
|
+
si_pca = Transformer.from_sklearn(sklearn_pca)
|
|
277
|
+
npt.assert_array_almost_equal_nulp(
|
|
278
|
+
np.sqrt(sklearn_pca.explained_variance_), si_pca.explained_std_
|
|
279
|
+
)
|
|
280
|
+
return si_pca
|
|
281
|
+
|
|
282
|
+
@staticmethod
|
|
283
|
+
def test_transform_outofplace_with_whitening(
|
|
284
|
+
make_data_with_whitening, si_pca_with_whitening
|
|
285
|
+
):
|
|
286
|
+
_, x, x_transformed = make_data_with_whitening
|
|
287
|
+
y_si = si_pca_with_whitening.transform(x, inplace=False)
|
|
288
|
+
npt.assert_array_almost_equal_nulp(y_si, x_transformed, nulp=2500)
|
|
289
|
+
|
|
290
|
+
@staticmethod
|
|
291
|
+
def test_transform_inplace_with_whitening(
|
|
292
|
+
make_data_with_whitening, si_pca_with_whitening
|
|
293
|
+
):
|
|
294
|
+
_, x, _ = make_data_with_whitening
|
|
295
|
+
with pytest.raises(
|
|
296
|
+
ValueError,
|
|
297
|
+
match=re.escape("The PCA transformation cannot be done inplace."),
|
|
298
|
+
):
|
|
299
|
+
si_pca_with_whitening.transform(x, inplace=True)
|
|
300
|
+
|
|
301
|
+
@staticmethod
|
|
302
|
+
def test_inverse_transform_outofplace_with_whitening(
|
|
303
|
+
make_data_with_whitening, si_pca_with_whitening
|
|
304
|
+
):
|
|
305
|
+
_, x, x_transformed = make_data_with_whitening
|
|
306
|
+
y_si = si_pca_with_whitening.inverse_transform(x_transformed, inplace=False)
|
|
307
|
+
npt.assert_array_almost_equal_nulp(y_si, x, nulp=5900)
|
|
308
|
+
|
|
309
|
+
@staticmethod
|
|
310
|
+
def test_inverse_transform_inplace(make_data_with_whitening, si_pca_with_whitening):
|
|
311
|
+
_, _, x_transformed = make_data_with_whitening
|
|
312
|
+
with pytest.raises(
|
|
313
|
+
ValueError,
|
|
314
|
+
match=re.escape("The PCA inverse transformation cannot be done inplace."),
|
|
315
|
+
):
|
|
316
|
+
si_pca_with_whitening.inverse_transform(x_transformed, inplace=True)
|
|
317
|
+
|
|
318
|
+
@staticmethod
|
|
319
|
+
def test_transform_outofplace_without_whitening(
|
|
320
|
+
make_data_without_whitening, si_pca_without_whitening
|
|
321
|
+
):
|
|
322
|
+
_, x, x_transformed = make_data_without_whitening
|
|
323
|
+
y_si = si_pca_without_whitening.transform(x, inplace=False)
|
|
324
|
+
npt.assert_array_almost_equal_nulp(y_si, x_transformed, nulp=2500)
|
|
325
|
+
|
|
326
|
+
@staticmethod
|
|
327
|
+
def test_transform_inplace_without_whitening(
|
|
328
|
+
make_data_without_whitening, si_pca_without_whitening
|
|
329
|
+
):
|
|
330
|
+
_, x, _ = make_data_without_whitening
|
|
331
|
+
with pytest.raises(
|
|
332
|
+
ValueError,
|
|
333
|
+
match=re.escape("The PCA transformation cannot be done inplace."),
|
|
334
|
+
):
|
|
335
|
+
si_pca_without_whitening.transform(x, inplace=True)
|
|
336
|
+
|
|
337
|
+
@staticmethod
|
|
338
|
+
def test_inverse_transform_outofplace_without_whitening(
|
|
339
|
+
make_data_without_whitening, si_pca_without_whitening
|
|
340
|
+
):
|
|
341
|
+
_, x, x_transformed = make_data_without_whitening
|
|
342
|
+
y_si = si_pca_without_whitening.inverse_transform(x_transformed, inplace=False)
|
|
343
|
+
npt.assert_array_almost_equal_nulp(y_si, x, nulp=5900)
|
|
344
|
+
|
|
345
|
+
@staticmethod
|
|
346
|
+
def test_transform_gradient_with_whitening(
|
|
347
|
+
make_data_with_whitening, si_pca_with_whitening
|
|
348
|
+
):
|
|
349
|
+
_, x, _ = make_data_with_whitening
|
|
350
|
+
# Compute exact gradient.
|
|
351
|
+
y_exact = si_pca_with_whitening.transform_gradient(x)
|
|
352
|
+
# Compute gradient with central finite differences.
|
|
353
|
+
y_fd = np.zeros(
|
|
354
|
+
(
|
|
355
|
+
x.shape[0],
|
|
356
|
+
si_pca_with_whitening.n_components,
|
|
357
|
+
si_pca_with_whitening.n_features,
|
|
358
|
+
)
|
|
359
|
+
)
|
|
360
|
+
h = 1e-5
|
|
361
|
+
for i in range(x.shape[1]):
|
|
362
|
+
xm = x.copy()
|
|
363
|
+
xp = x.copy()
|
|
364
|
+
xm[:, i] -= h
|
|
365
|
+
xp[:, i] += h
|
|
366
|
+
y_fd[:, :, i] = si_pca_with_whitening.transform(
|
|
367
|
+
xp, inplace=False
|
|
368
|
+
) - si_pca_with_whitening.transform(xm, inplace=False)
|
|
369
|
+
y_fd /= 2.0 * h
|
|
370
|
+
npt.assert_allclose(y_exact, y_fd)
|
|
371
|
+
|
|
372
|
+
@staticmethod
|
|
373
|
+
def test_transform_gradient_without_whitening(
|
|
374
|
+
make_data_without_whitening, si_pca_without_whitening
|
|
375
|
+
):
|
|
376
|
+
_, x, _ = make_data_without_whitening
|
|
377
|
+
# Compute exact gradient.
|
|
378
|
+
y_exact = si_pca_without_whitening.transform_gradient(x)
|
|
379
|
+
# Compute gradient with central finite differences.
|
|
380
|
+
y_fd = np.zeros(
|
|
381
|
+
(
|
|
382
|
+
x.shape[0],
|
|
383
|
+
si_pca_without_whitening.n_components,
|
|
384
|
+
si_pca_without_whitening.n_features,
|
|
385
|
+
)
|
|
386
|
+
)
|
|
387
|
+
h = 1e-5
|
|
388
|
+
for i in range(x.shape[1]):
|
|
389
|
+
xm = x.copy()
|
|
390
|
+
xp = x.copy()
|
|
391
|
+
xm[:, i] -= h
|
|
392
|
+
xp[:, i] += h
|
|
393
|
+
y_fd[:, :, i] = si_pca_without_whitening.transform(
|
|
394
|
+
xp, inplace=False
|
|
395
|
+
) - si_pca_without_whitening.transform(xm, inplace=False)
|
|
396
|
+
y_fd /= 2.0 * h
|
|
397
|
+
npt.assert_allclose(y_exact, y_fd)
|
|
398
|
+
|
|
399
|
+
@staticmethod
|
|
400
|
+
def test_inverse_transform_gradient_with_whitening(
|
|
401
|
+
make_data_with_whitening, si_pca_with_whitening
|
|
402
|
+
):
|
|
403
|
+
_, _, x_transformed = make_data_with_whitening
|
|
404
|
+
# Compute exact gradient.
|
|
405
|
+
y_exact = si_pca_with_whitening.inverse_transform_gradient(x_transformed)
|
|
406
|
+
# Compute gradient with central finite differences.
|
|
407
|
+
y_fd = np.zeros(
|
|
408
|
+
(
|
|
409
|
+
x_transformed.shape[0],
|
|
410
|
+
si_pca_with_whitening.n_features,
|
|
411
|
+
si_pca_with_whitening.n_components,
|
|
412
|
+
)
|
|
413
|
+
)
|
|
414
|
+
h = 1e-6
|
|
415
|
+
for i in range(x_transformed.shape[1]):
|
|
416
|
+
xm = x_transformed.copy()
|
|
417
|
+
xp = x_transformed.copy()
|
|
418
|
+
xm[:, i] -= h
|
|
419
|
+
xp[:, i] += h
|
|
420
|
+
y_fd[:, :, i] = si_pca_with_whitening.inverse_transform(
|
|
421
|
+
xp, inplace=False
|
|
422
|
+
) - si_pca_with_whitening.inverse_transform(xm, inplace=False)
|
|
423
|
+
y_fd /= 2.0 * h
|
|
424
|
+
npt.assert_allclose(y_exact, y_fd)
|
|
425
|
+
|
|
426
|
+
@staticmethod
|
|
427
|
+
def test_inverse_transform_gradient_without_whitening(
|
|
428
|
+
make_data_without_whitening, si_pca_without_whitening
|
|
429
|
+
):
|
|
430
|
+
_, _, x_transformed = make_data_without_whitening
|
|
431
|
+
# Compute exact gradient.
|
|
432
|
+
y_exact = si_pca_without_whitening.inverse_transform_gradient(x_transformed)
|
|
433
|
+
# Compute gradient with central finite differences.
|
|
434
|
+
y_fd = np.zeros(
|
|
435
|
+
(
|
|
436
|
+
x_transformed.shape[0],
|
|
437
|
+
si_pca_without_whitening.n_features,
|
|
438
|
+
si_pca_without_whitening.n_components,
|
|
439
|
+
)
|
|
440
|
+
)
|
|
441
|
+
h = 1e-5
|
|
442
|
+
for i in range(x_transformed.shape[1]):
|
|
443
|
+
xm = x_transformed.copy()
|
|
444
|
+
xp = x_transformed.copy()
|
|
445
|
+
xm[:, i] -= h
|
|
446
|
+
xp[:, i] += h
|
|
447
|
+
y_fd[:, :, i] = si_pca_without_whitening.inverse_transform(
|
|
448
|
+
xp, inplace=False
|
|
449
|
+
) - si_pca_without_whitening.inverse_transform(xm, inplace=False)
|
|
450
|
+
y_fd /= 2.0 * h
|
|
451
|
+
npt.assert_allclose(y_exact, y_fd)
|
|
452
|
+
|
|
453
|
+
@staticmethod
|
|
454
|
+
def test_eq_different_type(si_pca_with_whitening):
|
|
455
|
+
obj_2 = StandardScalerSI(np.array([1.0]), np.array([0.5]))
|
|
456
|
+
assert not si_pca_with_whitening == obj_2
|
|
457
|
+
|
|
458
|
+
@staticmethod
|
|
459
|
+
def test_save_load(si_pca_with_whitening):
|
|
460
|
+
path = f"./{str(uuid.uuid4())}.h5"
|
|
461
|
+
with h5py.File(path, "w") as fid:
|
|
462
|
+
si_pca_with_whitening._save_h5(fid)
|
|
463
|
+
with h5py.File(path, "r") as fid:
|
|
464
|
+
si_pca_saved = PCASI._load_h5(fid)
|
|
465
|
+
assert si_pca_with_whitening == si_pca_saved
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
class TestPowerTransformerYeoJohnson:
|
|
469
|
+
@staticmethod
|
|
470
|
+
@pytest.fixture(scope="class")
|
|
471
|
+
def make_data():
|
|
472
|
+
rng = np.random.default_rng(seed=1234)
|
|
473
|
+
n_samples = 100
|
|
474
|
+
# We need to cover 4 cases, 2 with positive values and 2 with negative ones.
|
|
475
|
+
x = rng.uniform(low=-5.0, high=5.0, size=(n_samples, 4))
|
|
476
|
+
sklearn_scaler = PowerTransformer(
|
|
477
|
+
method="yeo-johnson", standardize=False, copy=True
|
|
478
|
+
)
|
|
479
|
+
sklearn_scaler.fit(x)
|
|
480
|
+
# Overwrite lambdas_, so that we can test everything.
|
|
481
|
+
sklearn_scaler.lambdas_ = np.array([0.5, 0.0, 3.5, 2.0])
|
|
482
|
+
x_transformed = sklearn_scaler.transform(x)
|
|
483
|
+
return sklearn_scaler, x, x_transformed
|
|
484
|
+
|
|
485
|
+
@pytest.fixture(scope="class")
|
|
486
|
+
def si_scaler(self, make_data):
|
|
487
|
+
# test_from_sklearn
|
|
488
|
+
sklearn_scaler, _, _ = make_data
|
|
489
|
+
si_scaler = Transformer.from_sklearn(sklearn_scaler)
|
|
490
|
+
npt.assert_array_almost_equal_nulp(si_scaler.lambdas_, sklearn_scaler.lambdas_)
|
|
491
|
+
return si_scaler
|
|
492
|
+
|
|
493
|
+
@staticmethod
|
|
494
|
+
def test_standardize():
|
|
495
|
+
with pytest.raises(
|
|
496
|
+
NotImplementedError,
|
|
497
|
+
match=re.escape(
|
|
498
|
+
"The standardize option is not supported. Please add a StandardScaler."
|
|
499
|
+
),
|
|
500
|
+
):
|
|
501
|
+
PowerTransformerSI(np.array([]), standardize=True)
|
|
502
|
+
|
|
503
|
+
@staticmethod
|
|
504
|
+
def test_transform_outofplace(make_data, si_scaler):
|
|
505
|
+
_, x, x_transformed = make_data
|
|
506
|
+
y_si = si_scaler.transform(x, inplace=False)
|
|
507
|
+
npt.assert_array_almost_equal_nulp(y_si, x_transformed)
|
|
508
|
+
|
|
509
|
+
@staticmethod
|
|
510
|
+
def test_transform_inplace(make_data, si_scaler):
|
|
511
|
+
_, x, x_transformed = make_data
|
|
512
|
+
y_si = x.copy()
|
|
513
|
+
si_scaler.transform(y_si, inplace=True)
|
|
514
|
+
npt.assert_array_almost_equal_nulp(y_si, x_transformed)
|
|
515
|
+
|
|
516
|
+
@staticmethod
|
|
517
|
+
def test_inverse_transform_outofplace(make_data, si_scaler):
|
|
518
|
+
_, x, x_transformed = make_data
|
|
519
|
+
y_si = si_scaler.inverse_transform(x_transformed, inplace=False)
|
|
520
|
+
npt.assert_array_almost_equal_nulp(y_si, x, nulp=35)
|
|
521
|
+
|
|
522
|
+
@staticmethod
|
|
523
|
+
def test_inverse_transform_inplace(make_data, si_scaler):
|
|
524
|
+
_, x, x_transformed = make_data
|
|
525
|
+
y_si = x_transformed.copy()
|
|
526
|
+
si_scaler.inverse_transform(y_si, inplace=True)
|
|
527
|
+
npt.assert_array_almost_equal_nulp(y_si, x, nulp=35)
|
|
528
|
+
|
|
529
|
+
@staticmethod
|
|
530
|
+
def test_transform_gradient(make_data, si_scaler):
|
|
531
|
+
_, x, x_transformed = make_data
|
|
532
|
+
# Compute exact gradient.
|
|
533
|
+
y_exact = si_scaler.transform_gradient(x)
|
|
534
|
+
# Compute gradient with central finite differences.
|
|
535
|
+
h = 1e-6
|
|
536
|
+
y_fd = (
|
|
537
|
+
si_scaler.transform(x + h, inplace=False)
|
|
538
|
+
- si_scaler.transform(x - h, inplace=False)
|
|
539
|
+
) / (2 * h)
|
|
540
|
+
npt.assert_allclose(y_exact, y_fd)
|
|
541
|
+
|
|
542
|
+
@staticmethod
|
|
543
|
+
def test_inverse_transform_gradient(make_data, si_scaler):
|
|
544
|
+
_, x, x_transformed = make_data
|
|
545
|
+
y_exact = si_scaler.inverse_transform_gradient(x_transformed)
|
|
546
|
+
h = 1e-6
|
|
547
|
+
y_fd = (
|
|
548
|
+
si_scaler.inverse_transform(x_transformed + h, inplace=False)
|
|
549
|
+
- si_scaler.inverse_transform(x_transformed - h, inplace=False)
|
|
550
|
+
) / (2 * h)
|
|
551
|
+
npt.assert_allclose(y_exact, y_fd, rtol=5e-5)
|
|
552
|
+
|
|
553
|
+
@staticmethod
|
|
554
|
+
def test_eq_same_type():
|
|
555
|
+
obj_1 = PowerTransformerSI(np.array([0.0, 3.5]), method="yeo-johnson")
|
|
556
|
+
obj_2 = PowerTransformerSI(np.array([0.0, 3.5]), method="yeo-johnson")
|
|
557
|
+
assert obj_1 == obj_2
|
|
558
|
+
|
|
559
|
+
@staticmethod
|
|
560
|
+
def test_eq_different_type():
|
|
561
|
+
obj_1 = PowerTransformerSI(np.array([0.0, 3.5]), method="yeo-johnson")
|
|
562
|
+
# obj_2 = PowerTransformerSI(np.array([0.0, 3.5]), method="box-cox")
|
|
563
|
+
obj_2 = StandardScalerSI(np.array([1.0]), np.array([0.5]))
|
|
564
|
+
assert not obj_1 == obj_2
|
|
565
|
+
|
|
566
|
+
@staticmethod
|
|
567
|
+
def test_save_load(si_scaler):
|
|
568
|
+
path = f"./{str(uuid.uuid4())}.h5"
|
|
569
|
+
with h5py.File(path, "w") as fid:
|
|
570
|
+
si_scaler._save_h5(fid)
|
|
571
|
+
with h5py.File(path, "r") as fid:
|
|
572
|
+
scaler_saved = PowerTransformerYeoJohnson._load_h5(fid)
|
|
573
|
+
assert si_scaler == scaler_saved
|
|
574
|
+
|
|
575
|
+
@staticmethod
|
|
576
|
+
def test_box_cox():
|
|
577
|
+
with pytest.raises(NotImplementedError):
|
|
578
|
+
PowerTransformerSI(np.array([0.0, 3.5]), method="box-cox")
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
def test_not_implemented_transformer():
|
|
582
|
+
scaler = QuantileTransformer()
|
|
583
|
+
with pytest.raises(NotImplementedError, match="QuantileTransformer not supported"):
|
|
584
|
+
Transformer.from_sklearn(scaler)
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
@pytest.mark.parametrize(
|
|
588
|
+
"scaler_cls,data",
|
|
589
|
+
[
|
|
590
|
+
(MinMaxScaler, np.reshape([4, 5, 6], (-1, 1))),
|
|
591
|
+
(MinMaxScaler, [[4, 5, 6], [6, 8, 10]]),
|
|
592
|
+
(StandardScaler, np.reshape([4, 5, 6], (-1, 1))),
|
|
593
|
+
(StandardScaler, [[4, 5, 6], [6, 8, 10]]),
|
|
594
|
+
],
|
|
595
|
+
)
|
|
596
|
+
def test_save_load(scaler_cls, data):
|
|
597
|
+
# test save and load with 1 and more features
|
|
598
|
+
scaler_sk = scaler_cls()
|
|
599
|
+
scaler_sk.fit_transform(data)
|
|
600
|
+
scaler_si = Transformer.from_sklearn(scaler_sk)
|
|
601
|
+
|
|
602
|
+
with h5py.File("tmp.hdf5", "w") as f:
|
|
603
|
+
scaler_si._save_h5(f)
|
|
604
|
+
saved_scaler_cls = {
|
|
605
|
+
MinMaxScaler: MinMaxScalerSI,
|
|
606
|
+
StandardScaler: StandardScalerSI,
|
|
607
|
+
}[scaler_cls]
|
|
608
|
+
scaler_saved = saved_scaler_cls._load_h5(f)
|
|
609
|
+
|
|
610
|
+
npt.assert_array_equal(
|
|
611
|
+
scaler_si.transform(data, inplace=False),
|
|
612
|
+
scaler_si.transform(data, inplace=False),
|
|
613
|
+
)
|
|
614
|
+
npt.assert_array_equal(
|
|
615
|
+
scaler_si.transform(data, inplace=False),
|
|
616
|
+
scaler_saved.transform(data, inplace=False),
|
|
617
|
+
)
|