surrogates_interface 4.1.0__tar.gz → 4.1.2__tar.gz
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-4.1.0 → surrogates_interface-4.1.2}/PKG-INFO +15 -16
- {surrogates_interface-4.1.0 → surrogates_interface-4.1.2}/pyproject.toml +21 -18
- {surrogates_interface-4.1.0 → surrogates_interface-4.1.2}/surrogates_interface/openmdao.py +10 -10
- {surrogates_interface-4.1.0 → surrogates_interface-4.1.2}/surrogates_interface/surrogates.py +74 -38
- {surrogates_interface-4.1.0 → surrogates_interface-4.1.2}/surrogates_interface/transformers.py +29 -14
- {surrogates_interface-4.1.0 → surrogates_interface-4.1.2}/LICENSE +0 -0
- {surrogates_interface-4.1.0 → surrogates_interface-4.1.2}/README.md +0 -0
- {surrogates_interface-4.1.0 → surrogates_interface-4.1.2}/surrogates_interface/__init__.py +0 -0
- {surrogates_interface-4.1.0 → surrogates_interface-4.1.2}/surrogates_interface/domains.py +0 -0
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: surrogates_interface
|
|
3
|
-
Version: 4.1.
|
|
3
|
+
Version: 4.1.2
|
|
4
4
|
Summary: Interface for surrogate models.
|
|
5
5
|
Home-page: https://gitlab.windenergy.dtu.dk/surrogate-models/surrogate-models-interface
|
|
6
6
|
License: MIT
|
|
7
7
|
Author: DTU Wind and Energy Systems
|
|
8
8
|
Maintainer: Riccardo Riva
|
|
9
9
|
Maintainer-email: ricriv@dtu.dk
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.10,<3.12
|
|
11
11
|
Classifier: License :: OSI Approved :: MIT License
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
15
|
Provides-Extra: code-style
|
|
@@ -20,24 +19,24 @@ Provides-Extra: smt
|
|
|
20
19
|
Provides-Extra: test
|
|
21
20
|
Provides-Extra: tf
|
|
22
21
|
Provides-Extra: torch
|
|
23
|
-
Requires-Dist: black ; extra == "code-style"
|
|
24
|
-
Requires-Dist: codespell ; extra == "code-style"
|
|
25
|
-
Requires-Dist: flake8 ; extra == "code-style"
|
|
26
|
-
Requires-Dist: h5py
|
|
27
|
-
Requires-Dist: isort ; extra == "code-style"
|
|
28
|
-
Requires-Dist:
|
|
22
|
+
Requires-Dist: black (>=24.0.0,<25.0.0) ; extra == "code-style"
|
|
23
|
+
Requires-Dist: codespell (>=2.0.0,<3.0.0) ; extra == "code-style"
|
|
24
|
+
Requires-Dist: flake8 (>=7.0.0,<8.0.0) ; extra == "code-style"
|
|
25
|
+
Requires-Dist: h5py (>=3.10.0,<4.0.0)
|
|
26
|
+
Requires-Dist: isort (>=5.0.0,<6.0.0) ; extra == "code-style"
|
|
27
|
+
Requires-Dist: keras (>=3.0.0,<4.0.0) ; extra == "tf" or extra == "torch"
|
|
28
|
+
Requires-Dist: matplotlib (>=3.8.0,<4.0.0) ; extra == "test" or extra == "smt"
|
|
29
29
|
Requires-Dist: numpy (>=1.10.0)
|
|
30
|
-
Requires-Dist: numpydoc ; extra == "docs"
|
|
30
|
+
Requires-Dist: numpydoc (>=1.8.0,<2.0.0) ; extra == "docs"
|
|
31
31
|
Requires-Dist: openmdao (>=3.25,<4.0) ; extra == "docs" or extra == "test" or extra == "om"
|
|
32
32
|
Requires-Dist: pydata-sphinx-theme ; extra == "docs"
|
|
33
|
-
Requires-Dist: pytest ; extra == "test"
|
|
34
|
-
Requires-Dist: pytest-cov ; extra == "test"
|
|
35
|
-
Requires-Dist:
|
|
36
|
-
Requires-Dist: scikit-learn ; extra == "test" or extra == "smt" or extra == "tf"
|
|
33
|
+
Requires-Dist: pytest (>=8.0.0,<9.0.0) ; extra == "test"
|
|
34
|
+
Requires-Dist: pytest-cov (>=6.0.0,<7.0.0) ; extra == "test"
|
|
35
|
+
Requires-Dist: scikit-learn (>=1.4.0,<2.0.0) ; extra == "test" or extra == "smt" or extra == "tf" or extra == "torch"
|
|
37
36
|
Requires-Dist: smt (>=2.0.1,<3.0.0) ; extra == "test" or extra == "smt"
|
|
38
|
-
Requires-Dist: sphinx ; extra == "docs"
|
|
37
|
+
Requires-Dist: sphinx (>=8.0.0,<9.0.0) ; extra == "docs"
|
|
39
38
|
Requires-Dist: sphinx-copybutton ; extra == "docs"
|
|
40
|
-
Requires-Dist: sphinx-sitemap ; extra == "docs"
|
|
39
|
+
Requires-Dist: sphinx-sitemap (>=2.0.0,<3.0.0) ; extra == "docs"
|
|
41
40
|
Requires-Dist: sphinx_design ; extra == "docs"
|
|
42
41
|
Requires-Dist: tensorflow (>=2.10.0,<3.0.0) ; extra == "test" or extra == "tf"
|
|
43
42
|
Requires-Dist: tensorflow-intel (>=2.10.0,<3.0.0) ; (sys_platform == "win32") and (extra == "test" or extra == "tf")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "surrogates_interface"
|
|
3
|
-
version = "4.1.
|
|
3
|
+
version = "4.1.2"
|
|
4
4
|
description = "Interface for surrogate models."
|
|
5
5
|
authors = ["DTU Wind and Energy Systems"]
|
|
6
6
|
maintainers = [
|
|
@@ -16,25 +16,24 @@ generate-setup-file = false
|
|
|
16
16
|
|
|
17
17
|
[tool.poetry.dependencies]
|
|
18
18
|
|
|
19
|
-
python = ">=3.
|
|
20
|
-
numpy = ">=1.10.0"
|
|
21
|
-
h5py = "
|
|
22
|
-
black = {version="
|
|
23
|
-
flake8 = {version="
|
|
24
|
-
isort = {version="
|
|
25
|
-
codespell = {version="
|
|
26
|
-
pytest = {version="
|
|
27
|
-
pytest-cov = {version="
|
|
28
|
-
pytest-testmon = {version="*", optional=true}
|
|
19
|
+
python = ">=3.10,<3.12"
|
|
20
|
+
numpy = {version=">=1.10.0"}
|
|
21
|
+
h5py = {version="^3.10.0"}
|
|
22
|
+
black = {version="^24.0.0", optional=true}
|
|
23
|
+
flake8 = {version="^7.0.0", optional=true}
|
|
24
|
+
isort = {version="^5.0.0", optional=true}
|
|
25
|
+
codespell = {version="^2.0.0", optional=true}
|
|
26
|
+
pytest = {version="^8.0.0", optional=true}
|
|
27
|
+
pytest-cov = {version="^6.0.0", optional=true}
|
|
29
28
|
tomli = {version="^2.0.1", python="<3.11", optional=true}
|
|
30
|
-
sphinx = {version="
|
|
29
|
+
sphinx = {version="^8.0.0", optional=true}
|
|
31
30
|
pydata-sphinx-theme = {version="*", optional=true}
|
|
32
31
|
sphinx-copybutton = {version="*", optional=true}
|
|
33
|
-
sphinx-sitemap = {version="
|
|
32
|
+
sphinx-sitemap = {version="^2.0.0", optional=true}
|
|
34
33
|
sphinx_design = {version="*", optional=true}
|
|
35
|
-
numpydoc = {version="
|
|
36
|
-
matplotlib = {version="
|
|
37
|
-
scikit-learn = {version="
|
|
34
|
+
numpydoc = {version="^1.8.0", optional=true}
|
|
35
|
+
matplotlib = {version="^3.8.0", optional=true}
|
|
36
|
+
scikit-learn = {version="^1.4.0", optional=true}
|
|
38
37
|
# The following configuration for TensorFlow seems to work on Windows and linux, as tested at
|
|
39
38
|
# https://gitlab.windenergy.dtu.dk/surrogate-models/test-poetry-and-tensorflow
|
|
40
39
|
tensorflow = {version = "^2.10.0", optional=true }
|
|
@@ -43,6 +42,7 @@ tensorflow-io-gcs-filesystem = [
|
|
|
43
42
|
{version = "< 0.32.0", markers = "platform_system == 'Windows'", python=">=3.9,<3.12", optional=true}
|
|
44
43
|
]
|
|
45
44
|
tensorflow-intel = {version="^2.10.0", platform = "win32", optional=true}
|
|
45
|
+
keras = {version="^3.0.0", optional=true}
|
|
46
46
|
smt = {version="^2.0.1", optional=true}
|
|
47
47
|
openmdao = {version="^3.25", optional=true}
|
|
48
48
|
torch = { version = "^2.0.0", optional=true }
|
|
@@ -67,7 +67,6 @@ code_style = ["black",
|
|
|
67
67
|
test = ["matplotlib", # Needed by SMT.
|
|
68
68
|
"pytest",
|
|
69
69
|
"pytest-cov",
|
|
70
|
-
"pytest-testmon",
|
|
71
70
|
"tomli",
|
|
72
71
|
"scikit-learn",
|
|
73
72
|
"smt",
|
|
@@ -87,11 +86,15 @@ tf = ["scikit-learn",
|
|
|
87
86
|
"tensorflow",
|
|
88
87
|
"tensorflow-io-gcs-filesystem",
|
|
89
88
|
"tensorflow-intel",
|
|
89
|
+
"keras",
|
|
90
90
|
]
|
|
91
91
|
|
|
92
92
|
om = ["openmdao"]
|
|
93
93
|
|
|
94
|
-
torch = ["torch"
|
|
94
|
+
torch = ["torch",
|
|
95
|
+
"scikit-learn",
|
|
96
|
+
"keras",
|
|
97
|
+
]
|
|
95
98
|
|
|
96
99
|
|
|
97
100
|
|
|
@@ -90,8 +90,8 @@ class SurrogateModelCompFD(om.ExplicitComponent):
|
|
|
90
90
|
self.options.declare(
|
|
91
91
|
"input_type",
|
|
92
92
|
desc="""Type of the input.
|
|
93
|
-
- If `InputOutputType.JOINED` there will be only 1 input named `input_name`.
|
|
94
|
-
- If `InputOutputType.SPLIT` there will be 1 input per
|
|
93
|
+
- If `InputOutputType.JOINED` there will be only 1 input named `options["input_name"]`.
|
|
94
|
+
- If `InputOutputType.SPLIT` there will be 1 input per channel. Their names will be taken from `model.input_names`.
|
|
95
95
|
""",
|
|
96
96
|
check_valid=self._check_in_out_type,
|
|
97
97
|
)
|
|
@@ -99,8 +99,8 @@ class SurrogateModelCompFD(om.ExplicitComponent):
|
|
|
99
99
|
self.options.declare(
|
|
100
100
|
"output_type",
|
|
101
101
|
desc="""Type of the output.
|
|
102
|
-
- If `InputOutputType.JOINED` there will be only 1 output named `output_name`.
|
|
103
|
-
- If `InputOutputType.SPLIT` there will be 1
|
|
102
|
+
- If `InputOutputType.JOINED` there will be only 1 output named `options["output_name"]`.
|
|
103
|
+
- If `InputOutputType.SPLIT` there will be 1 output per channel. Their names will be taken from `model.output_names`.
|
|
104
104
|
""",
|
|
105
105
|
check_valid=self._check_in_out_type,
|
|
106
106
|
)
|
|
@@ -150,13 +150,13 @@ class SurrogateModelCompFD(om.ExplicitComponent):
|
|
|
150
150
|
----------
|
|
151
151
|
inputs : dict
|
|
152
152
|
Inputs.
|
|
153
|
-
- If `input_type is InputOutputType.JOINED` we expect one input named `input_name` of shape `(n_points, n_inputs)`.
|
|
153
|
+
- If `input_type is InputOutputType.JOINED` we expect one input named `options["input_name"]` of shape `(n_points, n_inputs)`.
|
|
154
154
|
- If `input_type is InputOutputType.SPLIT` we expect one input per feature of shape `(n_points,)`.
|
|
155
|
-
The inputs will be stacked according to `input_names`.
|
|
155
|
+
The inputs will be stacked according to `model.input_names`.
|
|
156
156
|
outputs : dict
|
|
157
157
|
Outputs.
|
|
158
|
-
- If `output_type is InputOutputType.JOINED` we expect one output named `output_name` of shape `(n_points, n_outputs)`.
|
|
159
|
-
The outputs will be stacked according to `output_names`.
|
|
158
|
+
- If `output_type is InputOutputType.JOINED` we expect one output named `options["output_name"]` of shape `(n_points, n_outputs)`.
|
|
159
|
+
The outputs will be stacked according to `model.output_names`.
|
|
160
160
|
- If `output_type is InputOutputType.SPLIT` we expect one output per feature of shape `(n_points,)`.
|
|
161
161
|
|
|
162
162
|
Returns
|
|
@@ -240,9 +240,9 @@ class SurrogateModelComp(SurrogateModelCompFD):
|
|
|
240
240
|
----------
|
|
241
241
|
inputs : dict
|
|
242
242
|
Inputs.
|
|
243
|
-
- If `input_type is InputOutputType.JOINED` we expect one input named `input_name` of shape `(n_points, n_inputs)`.
|
|
243
|
+
- If `input_type is InputOutputType.JOINED` we expect one input named `options["input_name"]` of shape `(n_points, n_inputs)`.
|
|
244
244
|
- If `input_type is InputOutputType.SPLIT` we expect one input per feature of shape `(n_points,)`.
|
|
245
|
-
The inputs will be stacked according to `input_names`.
|
|
245
|
+
The inputs will be stacked according to `model.input_names`.
|
|
246
246
|
partials : dict
|
|
247
247
|
Jacobian.
|
|
248
248
|
- If `output_type is InputOutputType.JOINED` we expect one Jacobian of shape `(n_points, n_outputs)`.
|
{surrogates_interface-4.1.0 → surrogates_interface-4.1.2}/surrogates_interface/surrogates.py
RENAMED
|
@@ -364,6 +364,10 @@ class SurrogateModel:
|
|
|
364
364
|
.. math::
|
|
365
365
|
\hat{y} = g_1^{-1} \circ g_2^{-1} \circ \cdots \circ g_m^{-1} \circ \hat{\tilde{y}}
|
|
366
366
|
|
|
367
|
+
and by substitution we find
|
|
368
|
+
|
|
369
|
+
.. math::
|
|
370
|
+
\hat{y} = g_1^{-1}(g_2^{-1}(g_m^{-1}(s(f_n(f_2(f_1(x)))))))
|
|
367
371
|
|
|
368
372
|
Parameters
|
|
369
373
|
----------
|
|
@@ -419,9 +423,26 @@ class SurrogateModel:
|
|
|
419
423
|
raise NotImplementedError()
|
|
420
424
|
|
|
421
425
|
def predict_output_and_jacobian(self, x, *args, **kwargs):
|
|
422
|
-
"""
|
|
426
|
+
r"""
|
|
423
427
|
Predict the complete model output and Jacobian, including all input and output transformations.
|
|
424
428
|
|
|
429
|
+
Let's recall that the predicted output is given by
|
|
430
|
+
|
|
431
|
+
.. math::
|
|
432
|
+
\hat{y} = g_1^{-1}(g_2^{-1}(g_m^{-1}(s(f_n(f_2(f_1(x)))))))
|
|
433
|
+
|
|
434
|
+
By indicating with :math:`J_h(z)` the Jacobian of :math:`h` evaluated at :math:`z`, we obtain
|
|
435
|
+
the Jacobian of the predicted output by applying the chain rule
|
|
436
|
+
|
|
437
|
+
.. math::
|
|
438
|
+
J = &J_{g_1^{-1}}(g_2^{-1}(g_m^{-1}(s(f_n(f_2(f_1(x))))))) \\
|
|
439
|
+
&J_{g_2^{-1}}(g_m^{-1}(s(f_n(f_2(f_1(x)))))) \\
|
|
440
|
+
&J_{g_m^{-1}}(s(f_n(f_2(f_1(x))))) \\
|
|
441
|
+
&J_{s}(f_n(f_2(f_1(x)))) \\
|
|
442
|
+
&J_{f_n}(f_2(f_1(x)))
|
|
443
|
+
J_{f_2}(f_1(x))
|
|
444
|
+
J_{f_1}(x)
|
|
445
|
+
|
|
425
446
|
Parameters
|
|
426
447
|
----------
|
|
427
448
|
x : array-like of shape (n_points, n_inputs)
|
|
@@ -1030,48 +1051,51 @@ class SurrogateModelFamily:
|
|
|
1030
1051
|
|
|
1031
1052
|
|
|
1032
1053
|
class PyTorchModel(SurrogateModel):
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
import torch.func as torch_func # fmt: skip
|
|
1054
|
+
"""
|
|
1055
|
+
Class that stores and evaluates an existing PyTorch model.
|
|
1056
|
+
"""
|
|
1037
1057
|
|
|
1038
|
-
|
|
1058
|
+
def __init__(self, *args, dtype=None, max_batch=50_000, **kwargs):
|
|
1059
|
+
import torch
|
|
1060
|
+
import torch._dynamo
|
|
1039
1061
|
|
|
1040
|
-
|
|
1062
|
+
torch._dynamo.config.suppress_errors = True
|
|
1041
1063
|
super().__init__(*args, **kwargs)
|
|
1042
1064
|
self.model.eval()
|
|
1043
|
-
self.device =
|
|
1044
|
-
"cuda" if self.torch.cuda.is_available() else "cpu"
|
|
1045
|
-
)
|
|
1065
|
+
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
|
1046
1066
|
self.model.to(self.device)
|
|
1047
|
-
self.dtype = dtype
|
|
1048
|
-
self.model.to(dtype)
|
|
1067
|
+
self.dtype = torch.float16 if dtype is None else dtype
|
|
1068
|
+
self.model.to(self.dtype)
|
|
1049
1069
|
self.max_batch = max_batch
|
|
1050
1070
|
|
|
1051
1071
|
def __to_numpy(self, _x):
|
|
1072
|
+
import torch
|
|
1073
|
+
|
|
1052
1074
|
try:
|
|
1053
1075
|
import cupy as cp
|
|
1054
1076
|
except ImportError: # pragma: no cover
|
|
1055
1077
|
cp = np
|
|
1056
|
-
lalib = (
|
|
1057
|
-
cp if self.device == self.torch.device("cuda") else np
|
|
1058
|
-
) # pragma: no cover
|
|
1078
|
+
lalib = cp if self.device == torch.device("cuda") else np # pragma: no cover
|
|
1059
1079
|
return lalib.asarray(_x).astype(lalib.float32)
|
|
1060
1080
|
|
|
1061
1081
|
def __to_tensor(self, _x):
|
|
1082
|
+
import torch
|
|
1083
|
+
|
|
1062
1084
|
if len(_x.shape) < 2:
|
|
1063
1085
|
_x = _x.reshape(1, -1)
|
|
1064
|
-
return
|
|
1086
|
+
return torch.as_tensor(_x, device=self.device, dtype=self.dtype)
|
|
1065
1087
|
|
|
1066
1088
|
def __record_in_dtype(self, _x): # pragma: no cover
|
|
1067
1089
|
# TODO: could be used to retain the input/output dtype the same
|
|
1068
1090
|
self.in_dtype = _x.dtype
|
|
1069
1091
|
|
|
1070
1092
|
def _predict_model_output(self, x):
|
|
1093
|
+
import torch
|
|
1094
|
+
|
|
1071
1095
|
x = self.__to_tensor(x)
|
|
1072
1096
|
|
|
1073
|
-
with
|
|
1074
|
-
output =
|
|
1097
|
+
with torch.no_grad():
|
|
1098
|
+
output = torch.zeros(
|
|
1075
1099
|
(x.shape[0], self.n_outputs),
|
|
1076
1100
|
device=self.device,
|
|
1077
1101
|
dtype=self.dtype,
|
|
@@ -1100,21 +1124,28 @@ class PyTorchModel(SurrogateModel):
|
|
|
1100
1124
|
return self.__to_numpy(y)
|
|
1101
1125
|
|
|
1102
1126
|
def _save_model(self, filepath):
|
|
1103
|
-
|
|
1127
|
+
import torch
|
|
1128
|
+
|
|
1129
|
+
torch.save(self.model.state_dict(), filepath)
|
|
1104
1130
|
|
|
1105
1131
|
@staticmethod
|
|
1106
1132
|
def _load_model(filepath, model_cls):
|
|
1107
|
-
import torch
|
|
1133
|
+
import torch
|
|
1134
|
+
|
|
1108
1135
|
model_cls.load_state_dict(torch.load(filepath, weights_only=True))
|
|
1109
1136
|
return model_cls
|
|
1110
1137
|
|
|
1111
1138
|
def _predict_model_jvp(self, x, dx):
|
|
1139
|
+
import torch.func as torch_func
|
|
1140
|
+
|
|
1112
1141
|
if not hasattr(self, "batched_jvp_func"):
|
|
1113
1142
|
def g(_x): # fmt: skip
|
|
1114
1143
|
return self.model(_x)
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1144
|
+
|
|
1145
|
+
def single_jvp(_x, _v):
|
|
1146
|
+
return torch_func.jvp(g, (_x,), (_v,))
|
|
1147
|
+
|
|
1148
|
+
self.batched_jvp_func = torch_func.vmap(single_jvp)
|
|
1118
1149
|
|
|
1119
1150
|
x = self.__to_tensor(x)
|
|
1120
1151
|
dx = self.__to_tensor(dx)
|
|
@@ -1122,13 +1153,17 @@ class PyTorchModel(SurrogateModel):
|
|
|
1122
1153
|
return self.__to_numpy(output.detach()), self.__to_numpy(jvp_res.detach())
|
|
1123
1154
|
|
|
1124
1155
|
def _predict_model_vjp(self, x, dy):
|
|
1156
|
+
import torch.func as torch_func
|
|
1157
|
+
|
|
1125
1158
|
if not hasattr(self, "batched_vjp_func"):
|
|
1126
1159
|
def g(_x): # fmt: skip
|
|
1127
1160
|
return self.model(_x)
|
|
1128
|
-
|
|
1129
|
-
|
|
1161
|
+
|
|
1162
|
+
def single_vjp(_x, _v):
|
|
1163
|
+
out, f = torch_func.vjp(g, _x)
|
|
1130
1164
|
return out, f(_v)
|
|
1131
|
-
|
|
1165
|
+
|
|
1166
|
+
self.batched_vjp_func = torch_func.vmap(single_vjp)
|
|
1132
1167
|
|
|
1133
1168
|
x = self.__to_tensor(x)
|
|
1134
1169
|
dy = self.__to_tensor(dy)
|
|
@@ -1136,49 +1171,50 @@ class PyTorchModel(SurrogateModel):
|
|
|
1136
1171
|
return self.__to_numpy(output.detach()), self.__to_numpy(vjp_res[0].detach())
|
|
1137
1172
|
|
|
1138
1173
|
def _predict_model_output_and_jacobian_fwd(self, x):
|
|
1174
|
+
import torch.func as torch_func
|
|
1175
|
+
|
|
1139
1176
|
if not hasattr(self, "y_jac_fwd_func"):
|
|
1140
1177
|
def g(_x): # fmt: skip
|
|
1141
1178
|
result = self.model(_x)
|
|
1142
1179
|
return (result, result)
|
|
1143
|
-
self.y_jac_fwd_func =
|
|
1144
|
-
self.torch_func.jacfwd(g, has_aux=True)
|
|
1145
|
-
)
|
|
1180
|
+
self.y_jac_fwd_func = torch_func.vmap(torch_func.jacfwd(g, has_aux=True))
|
|
1146
1181
|
|
|
1147
1182
|
x = self.__to_tensor(x)
|
|
1148
1183
|
jac, output = self.y_jac_fwd_func(x)
|
|
1149
1184
|
return self.__to_numpy(output.detach()), self.__to_numpy(jac.detach())
|
|
1150
1185
|
|
|
1151
1186
|
def _predict_model_output_and_jacobian_rev(self, x):
|
|
1187
|
+
import torch.func as torch_func
|
|
1188
|
+
|
|
1152
1189
|
if not hasattr(self, "y_jac_rev_func"):
|
|
1153
1190
|
def g(_x): # fmt: skip
|
|
1154
1191
|
result = self.model(_x)
|
|
1155
1192
|
return (result, result)
|
|
1156
|
-
self.y_jac_rev_func =
|
|
1157
|
-
self.torch_func.jacrev(g, has_aux=True)
|
|
1158
|
-
)
|
|
1193
|
+
self.y_jac_rev_func = torch_func.vmap(torch_func.jacrev(g, has_aux=True))
|
|
1159
1194
|
|
|
1160
1195
|
x = self.__to_tensor(x)
|
|
1161
1196
|
jac, output = self.y_jac_rev_func(x)
|
|
1162
1197
|
return self.__to_numpy(output.detach()), self.__to_numpy(jac.detach())
|
|
1163
1198
|
|
|
1164
1199
|
def _predict_model_jacobian_sbatch(self, x): # pragma: no cover
|
|
1165
|
-
|
|
1200
|
+
# Not used for now, but left as a reference for future if GPU memory issues arise.
|
|
1201
|
+
import torch
|
|
1202
|
+
import torch.func as torch_func
|
|
1203
|
+
|
|
1166
1204
|
if not hasattr(self, "y_jac_func"):
|
|
1167
1205
|
def g(_x): # fmt: skip
|
|
1168
1206
|
result = self.model(_x)
|
|
1169
1207
|
return (result, result)
|
|
1170
|
-
self.y_jac_func =
|
|
1171
|
-
self.torch_func.jacrev(g, has_aux=True)
|
|
1172
|
-
)
|
|
1208
|
+
self.y_jac_func = torch_func.vmap(torch_func.jacrev(g, has_aux=True))
|
|
1173
1209
|
|
|
1174
1210
|
x = self.__to_tensor(x)
|
|
1175
1211
|
batch_size = x.shape[0]
|
|
1176
|
-
y =
|
|
1212
|
+
y = torch.zeros(
|
|
1177
1213
|
(batch_size, self.n_outputs),
|
|
1178
1214
|
device=self.device,
|
|
1179
1215
|
dtype=self.dtype,
|
|
1180
1216
|
)
|
|
1181
|
-
jac =
|
|
1217
|
+
jac = torch.zeros(
|
|
1182
1218
|
(batch_size, self.n_outputs, self.n_inputs),
|
|
1183
1219
|
device=self.device,
|
|
1184
1220
|
dtype=self.dtype,
|
{surrogates_interface-4.1.0 → surrogates_interface-4.1.2}/surrogates_interface/transformers.py
RENAMED
|
@@ -125,7 +125,7 @@ class GainOffsetScaler(Transformer):
|
|
|
125
125
|
x : array-like of shape (n_points, n_features)
|
|
126
126
|
Input data that will be transformed.
|
|
127
127
|
inplace : bool
|
|
128
|
-
`True` if the transformation must happen inplace
|
|
128
|
+
`True` if the transformation must happen inplace, which means that no additional memory is allocated.
|
|
129
129
|
|
|
130
130
|
Returns
|
|
131
131
|
-------
|
|
@@ -147,7 +147,7 @@ class GainOffsetScaler(Transformer):
|
|
|
147
147
|
x : array-like of shape (n_points, n_features)
|
|
148
148
|
Input data that will be transformed.
|
|
149
149
|
inplace : bool
|
|
150
|
-
True if the transformation must happen inplace
|
|
150
|
+
`True` if the transformation must happen inplace, which means that no additional memory is allocated.
|
|
151
151
|
|
|
152
152
|
Returns
|
|
153
153
|
-------
|
|
@@ -173,6 +173,7 @@ class GainOffsetScaler(Transformer):
|
|
|
173
173
|
-------
|
|
174
174
|
y : array-like of shape (n_points, n_features)
|
|
175
175
|
Gradient of the direct transformation evaluated on the input data.
|
|
176
|
+
Since all features are independent, the gradient is stored in compact form.
|
|
176
177
|
Warning: a read-only view is returned.
|
|
177
178
|
"""
|
|
178
179
|
y = np.broadcast_to(self.gain_, x.shape)
|
|
@@ -190,6 +191,7 @@ class GainOffsetScaler(Transformer):
|
|
|
190
191
|
-------
|
|
191
192
|
y : array-like of shape (n_points, n_features)
|
|
192
193
|
Gradient of the inverse transformation evaluated on the input data.
|
|
194
|
+
Since all features are independent, the gradient is stored in compact form.
|
|
193
195
|
Warning: a read-only view is returned.
|
|
194
196
|
"""
|
|
195
197
|
y = np.broadcast_to(self.inv_gain_, x.shape)
|
|
@@ -344,7 +346,7 @@ class PCA(Transformer):
|
|
|
344
346
|
x : ndarray of shape (n_points, n_features)
|
|
345
347
|
Input data that will be transformed.
|
|
346
348
|
inplace : bool, optional
|
|
347
|
-
Must be `False`. Although it would be possible to
|
|
349
|
+
Must be `False`. Although it would be possible to update the first `n_components` columns of `x`,
|
|
348
350
|
we choose to not do it, so that `predict_output()` can be supported more easily.
|
|
349
351
|
|
|
350
352
|
Returns
|
|
@@ -527,7 +529,7 @@ class PowerTransformerYeoJohnson(Transformer):
|
|
|
527
529
|
x : array-like of shape (n_points, n_features)
|
|
528
530
|
Input data that will be transformed.
|
|
529
531
|
inplace : bool
|
|
530
|
-
`True` if the transformation must happen inplace
|
|
532
|
+
`True` if the transformation must happen inplace, which means that no additional memory is allocated.
|
|
531
533
|
|
|
532
534
|
Returns
|
|
533
535
|
-------
|
|
@@ -627,7 +629,7 @@ class PowerTransformerYeoJohnson(Transformer):
|
|
|
627
629
|
x : array-like of shape (n_points, n_features)
|
|
628
630
|
Input data that will be transformed.
|
|
629
631
|
inplace : bool
|
|
630
|
-
True if the transformation must happen inplace
|
|
632
|
+
`True` if the transformation must happen inplace, which means that no additional memory is allocated.
|
|
631
633
|
|
|
632
634
|
Returns
|
|
633
635
|
-------
|
|
@@ -726,6 +728,7 @@ class PowerTransformerYeoJohnson(Transformer):
|
|
|
726
728
|
-------
|
|
727
729
|
y : array-like of shape (n_points, n_features)
|
|
728
730
|
Gradient of the transformation evaluated on the input data.
|
|
731
|
+
Since all features are independent, the gradient is stored in compact form.
|
|
729
732
|
"""
|
|
730
733
|
y = np.zeros_like(x)
|
|
731
734
|
|
|
@@ -780,6 +783,7 @@ class PowerTransformerYeoJohnson(Transformer):
|
|
|
780
783
|
-------
|
|
781
784
|
y : array-like of shape (n_points, n_features)
|
|
782
785
|
Gradient of the inverse transformation evaluated on the input data.
|
|
786
|
+
Since all features are independent, the gradient is stored in compact form.
|
|
783
787
|
"""
|
|
784
788
|
y = np.zeros_like(x)
|
|
785
789
|
|
|
@@ -817,39 +821,50 @@ class PowerTransformerYeoJohnson(Transformer):
|
|
|
817
821
|
|
|
818
822
|
|
|
819
823
|
class StandardScalerTorch(StandardScaler):
|
|
820
|
-
import torch
|
|
821
824
|
|
|
822
825
|
def __init__(self, scale, mean):
|
|
826
|
+
import torch
|
|
827
|
+
|
|
823
828
|
super().__init__(scale, mean)
|
|
824
|
-
device =
|
|
829
|
+
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
|
825
830
|
self.device = device
|
|
826
831
|
def __np2torch(v): # fmt: skip
|
|
827
|
-
return
|
|
832
|
+
return torch.as_tensor(v).to(device)
|
|
828
833
|
self.gain_ = __np2torch(self.gain_)
|
|
829
834
|
self.inv_gain_ = __np2torch(self.inv_gain_)
|
|
830
835
|
self.offset_ = __np2torch(self.offset_)
|
|
831
836
|
|
|
832
837
|
def transform(self, x, inplace=True):
|
|
838
|
+
import torch
|
|
839
|
+
|
|
833
840
|
if inplace:
|
|
834
841
|
raise ValueError("No inplace...")
|
|
835
|
-
x =
|
|
842
|
+
x = torch.as_tensor(x, device=self.device, dtype=torch.float32)
|
|
836
843
|
return super().transform(x, inplace)
|
|
837
844
|
|
|
838
845
|
def inverse_transform(self, x, inplace=True):
|
|
846
|
+
import torch
|
|
847
|
+
|
|
839
848
|
if inplace:
|
|
840
849
|
raise ValueError("No inplace...")
|
|
841
|
-
x =
|
|
850
|
+
x = torch.as_tensor(x, device=self.device, dtype=torch.float32)
|
|
842
851
|
return super().inverse_transform(x, inplace)
|
|
843
852
|
|
|
844
853
|
def transform_gradient(self, x):
|
|
845
|
-
|
|
854
|
+
import torch
|
|
855
|
+
|
|
856
|
+
return torch.broadcast_to(self.gain_, x.shape).numpy()
|
|
846
857
|
|
|
847
858
|
def inverse_transform_gradient(self, x):
|
|
848
|
-
|
|
859
|
+
import torch
|
|
860
|
+
|
|
861
|
+
return torch.broadcast_to(self.inv_gain_, x.shape).numpy()
|
|
849
862
|
|
|
850
863
|
def __eq__(self, other):
|
|
864
|
+
import torch
|
|
865
|
+
|
|
851
866
|
if isinstance(other, self.__class__):
|
|
852
|
-
test_gain =
|
|
853
|
-
test_offset =
|
|
867
|
+
test_gain = torch.equal(self.gain_, other.gain_)
|
|
868
|
+
test_offset = torch.equal(self.offset_, other.offset_)
|
|
854
869
|
return test_gain and test_offset
|
|
855
870
|
return False
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|