gemseo-multi-fidelity 0.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.
- gemseo_multi_fidelity/__init__.py +17 -0
- gemseo_multi_fidelity/core/MFMapperAdapter_input.json +22 -0
- gemseo_multi_fidelity/core/MFMapperAdapter_output.json +22 -0
- gemseo_multi_fidelity/core/MFMapperLinker_input.json +22 -0
- gemseo_multi_fidelity/core/MFMapperLinker_output.json +22 -0
- gemseo_multi_fidelity/core/MFScenarioAdapter_input.json +39 -0
- gemseo_multi_fidelity/core/MFScenarioAdapter_output.json +23 -0
- gemseo_multi_fidelity/core/__init__.py +16 -0
- gemseo_multi_fidelity/core/boxed_domain.py +242 -0
- gemseo_multi_fidelity/core/corr_function.py +411 -0
- gemseo_multi_fidelity/core/criticality.py +124 -0
- gemseo_multi_fidelity/core/ds_mapper.py +307 -0
- gemseo_multi_fidelity/core/errors.py +42 -0
- gemseo_multi_fidelity/core/eval_mapper.py +188 -0
- gemseo_multi_fidelity/core/id_mapper_adapter.py +61 -0
- gemseo_multi_fidelity/core/mapper_adapter.py +126 -0
- gemseo_multi_fidelity/core/mapper_linker.py +72 -0
- gemseo_multi_fidelity/core/mf_formulation.py +635 -0
- gemseo_multi_fidelity/core/mf_logger.py +216 -0
- gemseo_multi_fidelity/core/mf_opt_problem.py +480 -0
- gemseo_multi_fidelity/core/mf_scenario.py +205 -0
- gemseo_multi_fidelity/core/noise_criterion.py +94 -0
- gemseo_multi_fidelity/core/projpolytope.out +0 -0
- gemseo_multi_fidelity/core/scenario_adapter.py +568 -0
- gemseo_multi_fidelity/core/stop_criteria.py +201 -0
- gemseo_multi_fidelity/core/strict_chain.py +75 -0
- gemseo_multi_fidelity/core/utils_model_quality.py +74 -0
- gemseo_multi_fidelity/corrections/__init__.py +16 -0
- gemseo_multi_fidelity/corrections/add_corr_function.py +80 -0
- gemseo_multi_fidelity/corrections/correction_factory.py +65 -0
- gemseo_multi_fidelity/corrections/mul_corr_function.py +86 -0
- gemseo_multi_fidelity/drivers/__init__.py +16 -0
- gemseo_multi_fidelity/drivers/mf_algo_factory.py +38 -0
- gemseo_multi_fidelity/drivers/mf_driver_lib.py +462 -0
- gemseo_multi_fidelity/drivers/refinement.py +234 -0
- gemseo_multi_fidelity/drivers/settings/__init__.py +16 -0
- gemseo_multi_fidelity/drivers/settings/base_mf_driver_settings.py +59 -0
- gemseo_multi_fidelity/drivers/settings/mf_refine_settings.py +50 -0
- gemseo_multi_fidelity/formulations/__init__.py +16 -0
- gemseo_multi_fidelity/formulations/refinement.py +144 -0
- gemseo_multi_fidelity/mapping/__init__.py +16 -0
- gemseo_multi_fidelity/mapping/identity_mapper.py +74 -0
- gemseo_multi_fidelity/mapping/interp_mapper.py +422 -0
- gemseo_multi_fidelity/mapping/mapper_factory.py +70 -0
- gemseo_multi_fidelity/mapping/mapping_errors.py +46 -0
- gemseo_multi_fidelity/mapping/subset_mapper.py +122 -0
- gemseo_multi_fidelity/mf_rosenbrock/__init__.py +16 -0
- gemseo_multi_fidelity/mf_rosenbrock/delayed_disc.py +136 -0
- gemseo_multi_fidelity/mf_rosenbrock/refact_rosen_testcase.py +46 -0
- gemseo_multi_fidelity/mf_rosenbrock/rosen_mf_case.py +284 -0
- gemseo_multi_fidelity/mf_rosenbrock/rosen_mf_funcs.py +350 -0
- gemseo_multi_fidelity/models/__init__.py +16 -0
- gemseo_multi_fidelity/models/fake_updater.py +112 -0
- gemseo_multi_fidelity/models/model_updater.py +91 -0
- gemseo_multi_fidelity/models/rbf/__init__.py +16 -0
- gemseo_multi_fidelity/models/rbf/kernel_factory.py +66 -0
- gemseo_multi_fidelity/models/rbf/kernels/__init__.py +16 -0
- gemseo_multi_fidelity/models/rbf/kernels/gaussian.py +93 -0
- gemseo_multi_fidelity/models/rbf/kernels/matern_3_2.py +101 -0
- gemseo_multi_fidelity/models/rbf/kernels/matern_5_2.py +101 -0
- gemseo_multi_fidelity/models/rbf/kernels/rbf_kernel.py +172 -0
- gemseo_multi_fidelity/models/rbf/rbf_model.py +422 -0
- gemseo_multi_fidelity/models/sparse_rbf_updater.py +96 -0
- gemseo_multi_fidelity/models/taylor/__init__.py +16 -0
- gemseo_multi_fidelity/models/taylor/taylor.py +212 -0
- gemseo_multi_fidelity/models/taylor_updater.py +66 -0
- gemseo_multi_fidelity/models/updater_factory.py +62 -0
- gemseo_multi_fidelity/settings/__init__.py +16 -0
- gemseo_multi_fidelity/settings/drivers.py +22 -0
- gemseo_multi_fidelity/settings/formulations.py +16 -0
- gemseo_multi_fidelity-0.0.1.dist-info/METADATA +99 -0
- gemseo_multi_fidelity-0.0.1.dist-info/RECORD +76 -0
- gemseo_multi_fidelity-0.0.1.dist-info/WHEEL +5 -0
- gemseo_multi_fidelity-0.0.1.dist-info/entry_points.txt +2 -0
- gemseo_multi_fidelity-0.0.1.dist-info/licenses/LICENSE.txt +165 -0
- gemseo_multi_fidelity-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# Copyright 2021 IRT Saint Exupéry, https://www.irt-saintexupery.com
|
|
2
|
+
#
|
|
3
|
+
# This program is free software; you can redistribute it and/or
|
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
|
5
|
+
# License version 3 as published by the Free Software Foundation.
|
|
6
|
+
#
|
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
10
|
+
# Lesser General Public License for more details.
|
|
11
|
+
#
|
|
12
|
+
# You should have received a copy of the GNU Lesser General Public License
|
|
13
|
+
# along with this program; if not, write to the Free Software Foundation,
|
|
14
|
+
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
15
|
+
|
|
16
|
+
# Copyright (c) 2019 AIRBUS OPERATIONS
|
|
17
|
+
|
|
18
|
+
#
|
|
19
|
+
# Contributors:
|
|
20
|
+
# INITIAL AUTHORS - API and implementation and/or documentation
|
|
21
|
+
# :author: Romain Olivanti
|
|
22
|
+
# OTHER AUTHORS - MACROSCOPIC CHANGES
|
|
23
|
+
"""Base class for design space mapping."""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
from collections.abc import Mapping
|
|
28
|
+
from typing import TYPE_CHECKING
|
|
29
|
+
from typing import Any
|
|
30
|
+
|
|
31
|
+
from gemseo.algos.design_space import DesignSpace
|
|
32
|
+
from numpy import abs as np_abs
|
|
33
|
+
from numpy import arange
|
|
34
|
+
from numpy import ndarray
|
|
35
|
+
from numpy import zeros
|
|
36
|
+
|
|
37
|
+
if TYPE_CHECKING:
|
|
38
|
+
from collections.abc import Iterable
|
|
39
|
+
|
|
40
|
+
from numpy.typing import NDArray
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
import matplotlib.gridspec as gridspec
|
|
44
|
+
import pylab
|
|
45
|
+
# from matplotlib.cm import seismic
|
|
46
|
+
|
|
47
|
+
PLOT_AVAILABLE = True
|
|
48
|
+
except ImportError:
|
|
49
|
+
PLOT_AVAILABLE = False
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class DesignSpaceMapper:
|
|
53
|
+
"""Abstract class for design space mapping."""
|
|
54
|
+
|
|
55
|
+
def __init__(
|
|
56
|
+
self, design_space_in: DesignSpace, design_space_out: DesignSpace
|
|
57
|
+
) -> None:
|
|
58
|
+
"""
|
|
59
|
+
Args:
|
|
60
|
+
design_space_in: The input ``DesignSpace`` object.
|
|
61
|
+
design_space_out: The output ``DesignSpace`` object.
|
|
62
|
+
""" # noqa: D205 D212 D415
|
|
63
|
+
self._check_design_space(design_space_in, design_space_out)
|
|
64
|
+
self.design_space_in = design_space_in
|
|
65
|
+
self.design_space_out = design_space_out
|
|
66
|
+
|
|
67
|
+
@staticmethod
|
|
68
|
+
def _check_design_space(ds_in, ds_out):
|
|
69
|
+
if not isinstance(ds_in, DesignSpace):
|
|
70
|
+
msg = "design_space_in must be an instance of DesignSpace"
|
|
71
|
+
raise TypeError(msg)
|
|
72
|
+
if not isinstance(ds_out, DesignSpace):
|
|
73
|
+
msg = "design_space_out must be an instance of DesignSpace"
|
|
74
|
+
raise TypeError(msg)
|
|
75
|
+
|
|
76
|
+
def _map_vars_direct(self, dvs_dict_in: DesignSpace):
|
|
77
|
+
"""Real direct mapping routine. To be overloaded by subclasses."""
|
|
78
|
+
raise NotImplementedError
|
|
79
|
+
|
|
80
|
+
def _map_vars_reverse(self, dvs_dict_out: DesignSpace):
|
|
81
|
+
"""Real reverse mapping routine. To be overloaded by subclasses."""
|
|
82
|
+
raise NotImplementedError
|
|
83
|
+
|
|
84
|
+
def _map_vars(
|
|
85
|
+
self,
|
|
86
|
+
dvs: Mapping[str, Any] | NDArray,
|
|
87
|
+
to_array: bool = True,
|
|
88
|
+
direct: bool = True,
|
|
89
|
+
) -> Mapping[str, Any] | NDArray:
|
|
90
|
+
"""Take care of formating inputs/outputs using the DesignSpace API.
|
|
91
|
+
|
|
92
|
+
The real mapping is performed in _map_vars_direct and _map_vars_reverse.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
dvs: The variables to map.
|
|
96
|
+
to_array: The flag to specify if a dict or an array should be returned as
|
|
97
|
+
output.
|
|
98
|
+
direct: The flag to indicate if the mapping must be performed in the direct
|
|
99
|
+
or reverse order.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
The mapping.
|
|
103
|
+
"""
|
|
104
|
+
# Reverse if needed
|
|
105
|
+
if direct:
|
|
106
|
+
ds_in = self.design_space_in
|
|
107
|
+
ds_out = self.design_space_out
|
|
108
|
+
map_method = self._map_vars_direct
|
|
109
|
+
else:
|
|
110
|
+
ds_in = self.design_space_out
|
|
111
|
+
ds_out = self.design_space_in
|
|
112
|
+
map_method = self._map_vars_reverse
|
|
113
|
+
|
|
114
|
+
# Make sure the input is a dict
|
|
115
|
+
if isinstance(dvs, Mapping):
|
|
116
|
+
dvs_in_dict = dvs
|
|
117
|
+
elif isinstance(dvs, ndarray):
|
|
118
|
+
dvs_in_dict = ds_in.convert_array_to_dict(dvs)
|
|
119
|
+
else:
|
|
120
|
+
raise TypeError("Unsupported DV type " + str(type(dvs)))
|
|
121
|
+
dvs_out = map_method(dvs_in_dict)
|
|
122
|
+
if to_array:
|
|
123
|
+
dvs_out = ds_out.convert_dict_to_array(dvs_out)
|
|
124
|
+
return dvs_out
|
|
125
|
+
|
|
126
|
+
def map_vars_direct(
|
|
127
|
+
self, dvs_in: Mapping[str, Any] | NDArray, to_array: bool = False
|
|
128
|
+
) -> Mapping[str, Any] | NDArray:
|
|
129
|
+
"""Map the provided design variables from in to out.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
dvs_in: The design variables from the input design space.
|
|
133
|
+
to_array: The flag to specify if a dict or an array should be returned as
|
|
134
|
+
output.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
The design variables in the output design space either dict or array
|
|
138
|
+
depending on the ``to_array`` flag.
|
|
139
|
+
"""
|
|
140
|
+
return self._map_vars(dvs_in, to_array=to_array, direct=True)
|
|
141
|
+
|
|
142
|
+
def map_vars_reverse(
|
|
143
|
+
self, dvs_out: Mapping[str, Any] | NDArray, to_array: bool = False
|
|
144
|
+
) -> Mapping[str, Any] | NDArray:
|
|
145
|
+
"""Map the provided design variables from out to in.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
dvs_out: The design variables from the output design space.
|
|
149
|
+
to_array: The flag to specify if a dict or an array should be returned as
|
|
150
|
+
output.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
The design variables in the input design space either dict or array
|
|
154
|
+
depending on the ``to_array`` flag.
|
|
155
|
+
"""
|
|
156
|
+
return self._map_vars(dvs_out, to_array=to_array, direct=False)
|
|
157
|
+
|
|
158
|
+
def has_reverse(self) -> bool:
|
|
159
|
+
"""Check if the mapper can perform the reverse mapping.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
``True`` if reverse mapping can be performed.
|
|
163
|
+
"""
|
|
164
|
+
is_reverse_implemented = True
|
|
165
|
+
try:
|
|
166
|
+
self.map_vars_reverse(self.design_space_out.get_current_value(as_dict=True))
|
|
167
|
+
except NotImplementedError:
|
|
168
|
+
is_reverse_implemented = False
|
|
169
|
+
return is_reverse_implemented
|
|
170
|
+
|
|
171
|
+
def _map_ds(self, direct: bool = True):
|
|
172
|
+
"""Map the design spaces directly.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
direct: ``True`` for direct mode, ``False`` for the reverse mode.
|
|
176
|
+
"""
|
|
177
|
+
if direct:
|
|
178
|
+
ds_in = self.design_space_in
|
|
179
|
+
ds_out = self.design_space_out
|
|
180
|
+
map_method = self.map_vars_direct
|
|
181
|
+
else:
|
|
182
|
+
ds_in = self.design_space_out
|
|
183
|
+
ds_out = self.design_space_in
|
|
184
|
+
map_method = self.map_vars_reverse
|
|
185
|
+
|
|
186
|
+
x_in = ds_in.get_current_value(as_dict=True)
|
|
187
|
+
x_out = map_method(x_in, to_array=True)
|
|
188
|
+
# Make sure the new current x fits in the design space
|
|
189
|
+
x_out = ds_out.project_into_bounds(x_out, normalized=False)
|
|
190
|
+
ds_out.set_current_value(x_out)
|
|
191
|
+
|
|
192
|
+
def map_ds_direct(self) -> None:
|
|
193
|
+
"""Map the design spaces from in to out."""
|
|
194
|
+
self._map_ds(direct=True)
|
|
195
|
+
|
|
196
|
+
def map_ds_reverse(self) -> None:
|
|
197
|
+
"""Map the design spaces from out to in."""
|
|
198
|
+
self._map_ds(direct=False)
|
|
199
|
+
|
|
200
|
+
def get_jac(self) -> dict:
|
|
201
|
+
"""Get the jacobian of the mapper (direct).
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
The jacobian.
|
|
205
|
+
"""
|
|
206
|
+
raise NotImplementedError
|
|
207
|
+
|
|
208
|
+
def get_jac_array(self) -> tuple[NDArray, Iterable[str], Iterable[str]]:
|
|
209
|
+
"""Return an array of the mapper jacobian with the associated variables names.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
The tuple (jacobian, vars_in, vars_out).
|
|
213
|
+
"""
|
|
214
|
+
ds_out = self.design_space_out
|
|
215
|
+
ds_in = self.design_space_in
|
|
216
|
+
|
|
217
|
+
# Prepare the labels depending on the sizes
|
|
218
|
+
vars_out = []
|
|
219
|
+
vars_in = []
|
|
220
|
+
map_vars_out_to_indexes = {}
|
|
221
|
+
map_vars_in_to_indexes = {}
|
|
222
|
+
|
|
223
|
+
for vars_list, ds, map_inds in zip(
|
|
224
|
+
[vars_out, vars_in],
|
|
225
|
+
[ds_out, ds_in],
|
|
226
|
+
[map_vars_out_to_indexes, map_vars_in_to_indexes],
|
|
227
|
+
strict=False,
|
|
228
|
+
):
|
|
229
|
+
i_curr = 0
|
|
230
|
+
for var in ds.variable_names:
|
|
231
|
+
var_size = int(ds.get_size(var))
|
|
232
|
+
if var_size == 1:
|
|
233
|
+
vars_list += [var]
|
|
234
|
+
else:
|
|
235
|
+
vars_list += [f"{var}_{i:d}" for i in range(int(var_size))]
|
|
236
|
+
map_inds[var] = [i_curr, i_curr + var_size]
|
|
237
|
+
i_curr += var_size
|
|
238
|
+
|
|
239
|
+
# Build the jacobian array
|
|
240
|
+
jac_array = zeros((len(vars_out), len(vars_in)))
|
|
241
|
+
jac_dict = self.get_jac()
|
|
242
|
+
|
|
243
|
+
for var_out, jac_link in iter(list(jac_dict.items())):
|
|
244
|
+
i_start, i_end = map_vars_out_to_indexes[var_out]
|
|
245
|
+
for var_in, sub_array in iter(list(jac_link.items())):
|
|
246
|
+
j_start, j_end = map_vars_in_to_indexes[var_in]
|
|
247
|
+
jac_array[i_start:i_end, j_start:j_end] = sub_array
|
|
248
|
+
|
|
249
|
+
return jac_array, vars_in, vars_out
|
|
250
|
+
|
|
251
|
+
def plot_jac(self) -> None:
|
|
252
|
+
"""Plot the mapping jacobian using pylab.
|
|
253
|
+
|
|
254
|
+
If pylab is not available, no output is produced.
|
|
255
|
+
"""
|
|
256
|
+
if not PLOT_AVAILABLE:
|
|
257
|
+
return
|
|
258
|
+
|
|
259
|
+
jac_array, vars_in, vars_out = self.get_jac_array()
|
|
260
|
+
|
|
261
|
+
# Build the figure
|
|
262
|
+
max_value = np_abs(jac_array).max()
|
|
263
|
+
|
|
264
|
+
fig = pylab.plt.figure(figsize=(8, 8), dpi=80, facecolor="w", edgecolor="k")
|
|
265
|
+
grid = gridspec.GridSpec(
|
|
266
|
+
2, 2, width_ratios=[10, 1], height_ratios=[10, 1], wspace=0.04, hspace=0.2
|
|
267
|
+
)
|
|
268
|
+
ax1 = fig.add_subplot(grid[0, 0])
|
|
269
|
+
ax1.set_xlabel("variables in", labelpad=10, rotation="horizontal", size=12)
|
|
270
|
+
ax1.set_ylabel("variables out", labelpad=10, rotation="vertical", size=12)
|
|
271
|
+
# Set the title
|
|
272
|
+
ax1.set_title("Mapper jacobian")
|
|
273
|
+
|
|
274
|
+
# Plot the data
|
|
275
|
+
im = ax1.imshow(
|
|
276
|
+
jac_array / max_value,
|
|
277
|
+
cmap="seismic",
|
|
278
|
+
vmin=-1,
|
|
279
|
+
vmax=1.0,
|
|
280
|
+
interpolation="nearest",
|
|
281
|
+
aspect="auto",
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
ax1.set_yticklabels(vars_out)
|
|
285
|
+
ax1.set_yticks(arange(jac_array.shape[0]))
|
|
286
|
+
ax1.set_xticklabels(vars_in)
|
|
287
|
+
ax1.set_xticks(arange(jac_array.shape[1]))
|
|
288
|
+
|
|
289
|
+
# Custom colorbar
|
|
290
|
+
ax2 = fig.add_subplot(grid[0, 1])
|
|
291
|
+
pylab.plt.colorbar(im, cax=ax2)
|
|
292
|
+
|
|
293
|
+
# Set window size
|
|
294
|
+
mng = pylab.plt.get_current_fig_manager()
|
|
295
|
+
mng.resize(700, 1000)
|
|
296
|
+
pylab.plt.show()
|
|
297
|
+
|
|
298
|
+
def __str__(self) -> str:
|
|
299
|
+
"""Textual representation of the DesignSpaceMapper.
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
The textual representation.
|
|
303
|
+
"""
|
|
304
|
+
ds_in = self.design_space_in
|
|
305
|
+
ds_out = self.design_space_out
|
|
306
|
+
class_name = self.__class__.__name__
|
|
307
|
+
return f"{class_name}\n\n{ds_in}\n\n-- (direct) -->\n\n{ds_out}"
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Copyright 2021 IRT Saint Exupéry, https://www.irt-saintexupery.com
|
|
2
|
+
#
|
|
3
|
+
# This program is free software; you can redistribute it and/or
|
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
|
5
|
+
# License version 3 as published by the Free Software Foundation.
|
|
6
|
+
#
|
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
10
|
+
# Lesser General Public License for more details.
|
|
11
|
+
#
|
|
12
|
+
# You should have received a copy of the GNU Lesser General Public License
|
|
13
|
+
# along with this program; if not, write to the Free Software Foundation,
|
|
14
|
+
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
15
|
+
|
|
16
|
+
# Copyright (c) 2018 AIRBUS OPERATIONS
|
|
17
|
+
|
|
18
|
+
#
|
|
19
|
+
# Contributors:
|
|
20
|
+
# INITIAL AUTHORS - API and implementation and/or documentation
|
|
21
|
+
# :author: Romain Olivanti
|
|
22
|
+
# OTHER AUTHORS - MACROSCOPIC CHANGES
|
|
23
|
+
"""Errors."""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ConsistencyError(Exception):
|
|
29
|
+
"""Consistency error."""
|
|
30
|
+
|
|
31
|
+
def __init__(self, msg: str) -> None:
|
|
32
|
+
"""Constructor.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
msg: The explanation for the inconsistency.
|
|
36
|
+
"""
|
|
37
|
+
super().__init__()
|
|
38
|
+
self.msg = msg
|
|
39
|
+
|
|
40
|
+
def __str__(self) -> str:
|
|
41
|
+
"""Custom string representation."""
|
|
42
|
+
return repr(self.msg)
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Copyright 2021 IRT Saint Exupéry, https://www.irt-saintexupery.com
|
|
2
|
+
#
|
|
3
|
+
# This program is free software; you can redistribute it and/or
|
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
|
5
|
+
# License version 3 as published by the Free Software Foundation.
|
|
6
|
+
#
|
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
10
|
+
# Lesser General Public License for more details.
|
|
11
|
+
#
|
|
12
|
+
# You should have received a copy of the GNU Lesser General Public License
|
|
13
|
+
# along with this program; if not, write to the Free Software Foundation,
|
|
14
|
+
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
15
|
+
|
|
16
|
+
# Copyright (c) 2019 AIRBUS OPERATIONS
|
|
17
|
+
|
|
18
|
+
#
|
|
19
|
+
# Contributors:
|
|
20
|
+
# INITIAL AUTHORS - API and implementation and/or documentation
|
|
21
|
+
# :author: Romain Olivanti
|
|
22
|
+
# OTHER AUTHORS - MACROSCOPIC CHANGES
|
|
23
|
+
"""Evaluation Mapper."""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
from typing import TYPE_CHECKING
|
|
28
|
+
|
|
29
|
+
from gemseo.algos.database import Database
|
|
30
|
+
|
|
31
|
+
from gemseo_multi_fidelity.core.ds_mapper import DesignSpaceMapper
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from numpy.typing import NDArray
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class EvaluationMapper:
|
|
38
|
+
"""Object to perform the mapping of an evaluation between two levels."""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
ds_mapper: DesignSpaceMapper,
|
|
43
|
+
output_mapping: dict,
|
|
44
|
+
jac_tag: str = Database.GRAD_TAG,
|
|
45
|
+
) -> None:
|
|
46
|
+
"""Auto-detects jacobian inputs using the ``jac_tag`` prefix.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
ds_mapper: The designer space mapper between the two levels.
|
|
50
|
+
output_mapping: The mapping (direct).
|
|
51
|
+
jac_tag: The prefix to identify jacobian data.
|
|
52
|
+
"""
|
|
53
|
+
if not isinstance(ds_mapper, DesignSpaceMapper):
|
|
54
|
+
msg = "ds_mapper must be an instance of DesignSpaceMapper"
|
|
55
|
+
raise TypeError(msg)
|
|
56
|
+
|
|
57
|
+
if not isinstance(output_mapping, dict):
|
|
58
|
+
msg = "output_mapping must be a dict"
|
|
59
|
+
raise TypeError(msg)
|
|
60
|
+
|
|
61
|
+
if len(output_mapping) == 0:
|
|
62
|
+
msg = "output_mapping must not be empty"
|
|
63
|
+
raise ValueError(msg)
|
|
64
|
+
|
|
65
|
+
self.design_space_mapper = ds_mapper
|
|
66
|
+
self.output_mapping = output_mapping
|
|
67
|
+
self.jac_tag = jac_tag
|
|
68
|
+
self.reverse_output_mapping = self.build_reverse_output_mapping(output_mapping)
|
|
69
|
+
|
|
70
|
+
@staticmethod
|
|
71
|
+
def build_reverse_output_mapping(output_mapping: dict) -> dict:
|
|
72
|
+
"""Build the reverse mapping.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
output_mapping: The mapping of the functions in the direct order.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
The reverse mapping.
|
|
79
|
+
"""
|
|
80
|
+
reverse_mapping = {}
|
|
81
|
+
|
|
82
|
+
for var_in, var_out in iter(list(output_mapping.items())):
|
|
83
|
+
# Must be strings
|
|
84
|
+
for v in [var_in, var_out]:
|
|
85
|
+
if not isinstance(v, str):
|
|
86
|
+
msg = f"{v} not a str"
|
|
87
|
+
raise TypeError(msg)
|
|
88
|
+
reverse_mapping[var_out] = var_in
|
|
89
|
+
return reverse_mapping
|
|
90
|
+
|
|
91
|
+
def _map_eval(
|
|
92
|
+
self,
|
|
93
|
+
dict_eval: dict,
|
|
94
|
+
x_eval: dict | NDArray = None,
|
|
95
|
+
to_arrays: bool = False,
|
|
96
|
+
direct: bool = True,
|
|
97
|
+
) -> tuple[dict, NDArray]:
|
|
98
|
+
"""Actual mapping routine.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
dict_eval: The data to map.
|
|
102
|
+
x_eval: The variables linked to the data.
|
|
103
|
+
to_arrays: The flag to convert all dicts to arrays expressed in the output
|
|
104
|
+
space.
|
|
105
|
+
direct: ``True`` for the direct order.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
The tuple (data_mapped, x_mapped).
|
|
109
|
+
"""
|
|
110
|
+
if direct:
|
|
111
|
+
map_vars = self.design_space_mapper.map_vars_direct
|
|
112
|
+
# TODO update with dedicated method
|
|
113
|
+
map_jac = self.design_space_mapper.map_vars_direct
|
|
114
|
+
output_mapping = self.output_mapping
|
|
115
|
+
else:
|
|
116
|
+
map_vars = self.design_space_mapper.map_vars_reverse
|
|
117
|
+
# TODO update with dedicated method
|
|
118
|
+
map_jac = self.design_space_mapper.map_vars_reverse
|
|
119
|
+
output_mapping = self.reverse_output_mapping
|
|
120
|
+
|
|
121
|
+
x_mapped = map_vars(x_eval, to_array=to_arrays) if x_eval is not None else None
|
|
122
|
+
|
|
123
|
+
dict_mapped = {}
|
|
124
|
+
|
|
125
|
+
for data_name, value in dict_eval.items():
|
|
126
|
+
# process all inputs, all known funcs will be mapped
|
|
127
|
+
if data_name.startswith(self.jac_tag):
|
|
128
|
+
is_jac = True
|
|
129
|
+
data_name = data_name.split(self.jac_tag)[-1]
|
|
130
|
+
else:
|
|
131
|
+
is_jac = False
|
|
132
|
+
try:
|
|
133
|
+
name_out = output_mapping[data_name]
|
|
134
|
+
if is_jac:
|
|
135
|
+
mapped_value = map_jac(value, to_array=to_arrays)
|
|
136
|
+
name_out = self.jac_tag + name_out
|
|
137
|
+
else:
|
|
138
|
+
mapped_value = value
|
|
139
|
+
dict_mapped[name_out] = mapped_value
|
|
140
|
+
except KeyError:
|
|
141
|
+
# Not an item that must be mapped
|
|
142
|
+
pass
|
|
143
|
+
|
|
144
|
+
return dict_mapped, x_mapped
|
|
145
|
+
|
|
146
|
+
def map_eval_direct(
|
|
147
|
+
self,
|
|
148
|
+
dict_eval: dict,
|
|
149
|
+
x_eval: dict | NDArray = None,
|
|
150
|
+
to_arrays: bool = False,
|
|
151
|
+
) -> tuple[dict, NDArray]:
|
|
152
|
+
"""Map the provided data (direct logic).
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
dict_eval: The data to map.
|
|
156
|
+
x_eval: The variables linked to the data, if ``None``, x_mapped will be
|
|
157
|
+
``None``.
|
|
158
|
+
to_arrays: The flag to convert all dicts to arrays expressed in the output
|
|
159
|
+
space.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
The tuple (data_mapped, x_mapped).
|
|
163
|
+
"""
|
|
164
|
+
return self._map_eval(
|
|
165
|
+
dict_eval, x_eval=x_eval, to_arrays=to_arrays, direct=True
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
def map_eval_reverse(
|
|
169
|
+
self,
|
|
170
|
+
dict_eval: dict,
|
|
171
|
+
x_eval: dict | NDArray = None,
|
|
172
|
+
to_arrays: bool = False,
|
|
173
|
+
) -> tuple[dict, NDArray]:
|
|
174
|
+
"""Map the provided data (reverse logic).
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
dict_eval: The data to map.
|
|
178
|
+
x_eval: The variables linked to the data, if ``None``, x_mapped will be
|
|
179
|
+
``None``.
|
|
180
|
+
to_arrays: The flag to convert all dicts to arrays expressed in the input
|
|
181
|
+
space.
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
The tuple (data_mapped, x_mapped).
|
|
185
|
+
"""
|
|
186
|
+
return self._map_eval(
|
|
187
|
+
dict_eval, x_eval=x_eval, to_arrays=to_arrays, direct=False
|
|
188
|
+
)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Copyright 2021 IRT Saint Exupéry, https://www.irt-saintexupery.com
|
|
2
|
+
#
|
|
3
|
+
# This program is free software; you can redistribute it and/or
|
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
|
5
|
+
# License version 3 as published by the Free Software Foundation.
|
|
6
|
+
#
|
|
7
|
+
# This program is distributed in the hope that it will be useful,
|
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
10
|
+
# Lesser General Public License for more details.
|
|
11
|
+
#
|
|
12
|
+
# You should have received a copy of the GNU Lesser General Public License
|
|
13
|
+
# along with this program; if not, write to the Free Software Foundation,
|
|
14
|
+
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
15
|
+
|
|
16
|
+
# Copyright (c) 2019 AIRBUS OPERATIONS
|
|
17
|
+
|
|
18
|
+
#
|
|
19
|
+
# Contributors:
|
|
20
|
+
# INITIAL AUTHORS - API and implementation and/or documentation
|
|
21
|
+
# :author: Romain Olivanti
|
|
22
|
+
# OTHER AUTHORS - MACROSCOPIC CHANGES
|
|
23
|
+
"""Convenience adapter.
|
|
24
|
+
|
|
25
|
+
To map the output of a sub-optimization in the same design space.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
from gemseo_multi_fidelity.core.mapper_adapter import MFMapperAdapter
|
|
31
|
+
from gemseo_multi_fidelity.core.scenario_adapter import MFScenarioAdapter
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class MFIdMapperAdapter(MFMapperAdapter):
|
|
35
|
+
"""Multi-fidelity identity mapper adapter."""
|
|
36
|
+
|
|
37
|
+
def __init__(self, name: str, direct: bool = True) -> None:
|
|
38
|
+
"""Constructor.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
name: The name of the adapter.
|
|
42
|
+
direct: Whether it is direct.
|
|
43
|
+
"""
|
|
44
|
+
# Do not call the super of MFIdMapperAdapter as the evaluation mapper
|
|
45
|
+
# is checked
|
|
46
|
+
super(MFMapperAdapter, self).__init__(name=name)
|
|
47
|
+
|
|
48
|
+
if not isinstance(direct, bool):
|
|
49
|
+
msg = "direct must be a boolean"
|
|
50
|
+
raise TypeError(msg)
|
|
51
|
+
|
|
52
|
+
self._is_direct = direct
|
|
53
|
+
super()._init_grammars(self.__class__.__base__.__name__)
|
|
54
|
+
|
|
55
|
+
def _map_x_best(self) -> None:
|
|
56
|
+
"""Map the output best point as the next starting point."""
|
|
57
|
+
x_in = self.io.data[MFScenarioAdapter.X_BEST]
|
|
58
|
+
self.io.data[MFScenarioAdapter.X_START] = x_in
|
|
59
|
+
|
|
60
|
+
def _map_reference_data(self) -> None:
|
|
61
|
+
"""Map the reference data, filters out reference data in direct mode."""
|