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,422 @@
|
|
|
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
|
+
"""Interpolation mapper."""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import logging
|
|
28
|
+
from copy import deepcopy
|
|
29
|
+
from typing import TYPE_CHECKING
|
|
30
|
+
from typing import ClassVar
|
|
31
|
+
|
|
32
|
+
from numpy import array
|
|
33
|
+
from numpy import atleast_2d
|
|
34
|
+
from numpy import eye
|
|
35
|
+
from numpy import inf
|
|
36
|
+
from numpy import vstack
|
|
37
|
+
from numpy import zeros
|
|
38
|
+
from numpy.linalg import norm
|
|
39
|
+
|
|
40
|
+
from gemseo_multi_fidelity.core.ds_mapper import DesignSpaceMapper
|
|
41
|
+
from gemseo_multi_fidelity.mapping.mapping_errors import MappingError
|
|
42
|
+
|
|
43
|
+
if TYPE_CHECKING:
|
|
44
|
+
from gemseo.algos.design_space import DesignSpace
|
|
45
|
+
from numpy.typing import NDArray
|
|
46
|
+
|
|
47
|
+
LOGGER = logging.getLogger(__name__)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class InterpolationMapper(DesignSpaceMapper):
|
|
51
|
+
"""Interpolation mapper."""
|
|
52
|
+
|
|
53
|
+
LINEAR = "linear"
|
|
54
|
+
CUBIC = "cubic"
|
|
55
|
+
AUTO_INTERP_TYPES: ClassVar[list] = [LINEAR, CUBIC]
|
|
56
|
+
|
|
57
|
+
def __init__(
|
|
58
|
+
self,
|
|
59
|
+
design_space_in: DesignSpace,
|
|
60
|
+
design_space_out: DesignSpace,
|
|
61
|
+
interp_logic: dict | None = None,
|
|
62
|
+
auto_interp: bool = False,
|
|
63
|
+
split_pattern: str = "_",
|
|
64
|
+
auto_interp_type: str = "linear",
|
|
65
|
+
) -> None:
|
|
66
|
+
"""Constructor.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
design_space_in: The input design space.
|
|
70
|
+
design_space_out: The output design space.
|
|
71
|
+
interp_logic: The user-defined interpolation logic between input and output
|
|
72
|
+
variables.
|
|
73
|
+
auto_interp: ``True`` to activate the auto interpolation feature. Not used
|
|
74
|
+
if interp_logic is provided.
|
|
75
|
+
split_pattern: The pattern used to identify variables can that be linked to
|
|
76
|
+
interpolate i.e. 'name_0', 'name_1', ...
|
|
77
|
+
auto_interp_type: The type of interpolation law to consider.
|
|
78
|
+
"""
|
|
79
|
+
super().__init__(design_space_in, design_space_out)
|
|
80
|
+
self._prolg_oper = None
|
|
81
|
+
self._restr_oper = None
|
|
82
|
+
self._norm_prolg = None
|
|
83
|
+
self._restr_factor = None
|
|
84
|
+
|
|
85
|
+
if interp_logic is not None:
|
|
86
|
+
self._build_interp_from_logic(interp_logic)
|
|
87
|
+
else:
|
|
88
|
+
if not auto_interp:
|
|
89
|
+
msg = (
|
|
90
|
+
"Automatic interpolation not activated but "
|
|
91
|
+
"no interpolation logic has been provided"
|
|
92
|
+
)
|
|
93
|
+
raise ValueError(msg)
|
|
94
|
+
self._build_interp_auto(split_pattern, auto_interp_type)
|
|
95
|
+
|
|
96
|
+
self._check_interp()
|
|
97
|
+
self._compute_factors()
|
|
98
|
+
|
|
99
|
+
def _build_interp_from_logic(self, interp_logic: dict) -> None:
|
|
100
|
+
"""Build operators from the interp logic that contains all the logic.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
interp_logic: The interp logic.
|
|
104
|
+
"""
|
|
105
|
+
prolg_oper = {}
|
|
106
|
+
restr_oper = {}
|
|
107
|
+
|
|
108
|
+
for var_out, prolg_logic in iter(list(interp_logic.items())):
|
|
109
|
+
prolg_oper[var_out] = {}
|
|
110
|
+
for var_in, oper in iter(list(prolg_logic.items())):
|
|
111
|
+
try:
|
|
112
|
+
len(oper)
|
|
113
|
+
prolg_val = atleast_2d(oper).copy()
|
|
114
|
+
except TypeError:
|
|
115
|
+
# Light storage, make sure it is a float
|
|
116
|
+
prolg_val = float(oper)
|
|
117
|
+
prolg_oper[var_out][var_in] = prolg_val
|
|
118
|
+
if var_in not in restr_oper:
|
|
119
|
+
restr_oper[var_in] = {}
|
|
120
|
+
# Do not save the prolg transpose on purpose, use only a reference
|
|
121
|
+
restr_oper[var_in][var_out] = prolg_val
|
|
122
|
+
self._prolg_oper = prolg_oper
|
|
123
|
+
self._restr_oper = restr_oper
|
|
124
|
+
|
|
125
|
+
def _build_interp_on_var_size(
|
|
126
|
+
self,
|
|
127
|
+
var_name: str,
|
|
128
|
+
dim_in: int,
|
|
129
|
+
dim_out: int,
|
|
130
|
+
interp_type: str,
|
|
131
|
+
log_prefix: str,
|
|
132
|
+
) -> dict:
|
|
133
|
+
"""Build a prolongation operator based on input and output sizes.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
var_name: The name of the variable considered.
|
|
137
|
+
dim_in: The input dimension.
|
|
138
|
+
dim_out: The output dimension.
|
|
139
|
+
interp_type: The type of interpolation to use.
|
|
140
|
+
log_prefix: The prefix for logs.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
The prolongation operator.
|
|
144
|
+
"""
|
|
145
|
+
if dim_out == dim_in:
|
|
146
|
+
# Exact match
|
|
147
|
+
LOGGER.info("%s assuming identity law for %s", log_prefix, var_name)
|
|
148
|
+
oper_array = 1.0
|
|
149
|
+
else:
|
|
150
|
+
# Check if sizes allow deducing the operator and log it so that
|
|
151
|
+
# the user has a better understanding of what to expect
|
|
152
|
+
if dim_out == 2 * dim_in - 1:
|
|
153
|
+
# Outside values are exact
|
|
154
|
+
LOGGER.info(
|
|
155
|
+
"%s assuming %s interpolation law with exact outside values for %s",
|
|
156
|
+
log_prefix,
|
|
157
|
+
interp_type,
|
|
158
|
+
var_name,
|
|
159
|
+
)
|
|
160
|
+
elif dim_out == 2 * dim_in + 1:
|
|
161
|
+
# Outside values will be interpolated
|
|
162
|
+
LOGGER.info(
|
|
163
|
+
"%s assuming %s interpolation law "
|
|
164
|
+
"with interpolated outside values for %s",
|
|
165
|
+
log_prefix,
|
|
166
|
+
interp_type,
|
|
167
|
+
var_name,
|
|
168
|
+
)
|
|
169
|
+
else:
|
|
170
|
+
msg = (
|
|
171
|
+
"{None} not same size for the variable {None} "
|
|
172
|
+
"between in and out but cannot deduce "
|
|
173
|
+
"an interpolation pattern"
|
|
174
|
+
)
|
|
175
|
+
raise MappingError(msg)
|
|
176
|
+
# Actual array assembly
|
|
177
|
+
oper_array = self._build_prolg_oper(dim_in, dim_out, interp_type)
|
|
178
|
+
return {var_name: oper_array}
|
|
179
|
+
|
|
180
|
+
def _build_prolg_oper(self, dim_in: int, dim_out: int, interp_type: str) -> NDArray:
|
|
181
|
+
"""Build a linear interpolation operator.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
dim_in: The input dimension.
|
|
185
|
+
dim_out: The output dimension.
|
|
186
|
+
interp_type: The type of interpolation to use.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
The linear interpolation operator.
|
|
190
|
+
"""
|
|
191
|
+
# Checks
|
|
192
|
+
if interp_type not in self.AUTO_INTERP_TYPES:
|
|
193
|
+
err = "{} not in implemented auto interpolation laws: {}"
|
|
194
|
+
err = err.format(interp_type, self.AUTO_INTERP_TYPES)
|
|
195
|
+
raise ValueError(err)
|
|
196
|
+
|
|
197
|
+
if interp_type == self.CUBIC and dim_in % 3 != 0:
|
|
198
|
+
msg = "Cannot use cubic interpolation if dim_in % 3 != 0"
|
|
199
|
+
raise MappingError(msg)
|
|
200
|
+
|
|
201
|
+
assert dim_in < dim_out
|
|
202
|
+
|
|
203
|
+
is_outside_interpolated = dim_out == 2 * dim_in + 1
|
|
204
|
+
|
|
205
|
+
if is_outside_interpolated:
|
|
206
|
+
# Outside values will be added at the end
|
|
207
|
+
dim_out -= 2
|
|
208
|
+
|
|
209
|
+
i_in = 0
|
|
210
|
+
oper = zeros((dim_out, dim_in))
|
|
211
|
+
|
|
212
|
+
# TODO use a sparse representation?
|
|
213
|
+
for i in range(dim_out):
|
|
214
|
+
if i % 2 == 0:
|
|
215
|
+
oper[i, i_in] = 1.0
|
|
216
|
+
i_in += 1
|
|
217
|
+
else:
|
|
218
|
+
if interp_type == self.LINEAR:
|
|
219
|
+
oper[i, i_in - 1 : i_in + 1] = array([0.5, 0.5])
|
|
220
|
+
else:
|
|
221
|
+
if i % 3 == 0:
|
|
222
|
+
oper[i, i_in - 2 : i_in + 1] = array([-0.0625, 0.5625, 0.5625])
|
|
223
|
+
else:
|
|
224
|
+
oper[i, i_in - 1 : i_in + 2] = array([0.5625, 0.5625, -0.0625])
|
|
225
|
+
|
|
226
|
+
if is_outside_interpolated:
|
|
227
|
+
# stack first and last row
|
|
228
|
+
first_row = zeros((1, dim_in))
|
|
229
|
+
last_row = zeros((1, dim_in))
|
|
230
|
+
if interp_type == self.LINEAR:
|
|
231
|
+
first_row[0, 0] = 0.5
|
|
232
|
+
last_row[0, -1] = 0.5
|
|
233
|
+
else:
|
|
234
|
+
first_row[0, 0:2] = array([0.75, -0.125])
|
|
235
|
+
last_row[0, -2::] = array([-0.125, 0.75])
|
|
236
|
+
oper = vstack((first_row, oper, last_row))
|
|
237
|
+
return oper
|
|
238
|
+
|
|
239
|
+
def _build_inter_on_var_names(
|
|
240
|
+
self, var: str, var_dim: int, split_pattern: str, log_prefix: str
|
|
241
|
+
) -> dict:
|
|
242
|
+
"""Build interpolation on var names.
|
|
243
|
+
|
|
244
|
+
Tries to build the interpolation logic for var_out using other variables for the
|
|
245
|
+
input design space.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
var: The name of the output variable.
|
|
249
|
+
var_dim: The dimension of the output variable.
|
|
250
|
+
split_pattern: The pattern used to identify linked variables.
|
|
251
|
+
log_prefix: The prefix for logs.
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
The interpolation logic.
|
|
255
|
+
"""
|
|
256
|
+
ds_in = self.design_space_in
|
|
257
|
+
split_name = var.split(split_pattern)
|
|
258
|
+
var_gen_name = "".join(split_name[0:-1])
|
|
259
|
+
interp_dict = {}
|
|
260
|
+
|
|
261
|
+
try:
|
|
262
|
+
var_index = int(split_name[-1])
|
|
263
|
+
except ValueError as exc:
|
|
264
|
+
msg = f"{var} cannot be linked to variables in the input design space"
|
|
265
|
+
raise MappingError(msg) from exc
|
|
266
|
+
|
|
267
|
+
prev_var = f"{var_gen_name}{split_pattern}{var_index - 1:d}"
|
|
268
|
+
next_var = f"{var_gen_name}{split_pattern}{var_index + 1:d}"
|
|
269
|
+
|
|
270
|
+
# ds_in has prev var?
|
|
271
|
+
prev_dim = ds_in.get_size(prev_var)
|
|
272
|
+
if prev_dim is not None:
|
|
273
|
+
if prev_dim != var_dim:
|
|
274
|
+
err = "{} {} sizes do not match: {:d} != {:d}"
|
|
275
|
+
err = err.format(var, prev_var, var_dim, prev_dim)
|
|
276
|
+
raise MappingError(err)
|
|
277
|
+
interp_dict[prev_var] = 0.5
|
|
278
|
+
|
|
279
|
+
# ds_in has next var?
|
|
280
|
+
next_dim = ds_in.get_size(next_var)
|
|
281
|
+
if next_dim is not None:
|
|
282
|
+
if next_dim != var_dim:
|
|
283
|
+
err = "{} {} sizes do not match: {:d} != {:d}"
|
|
284
|
+
err = err.format(var, next_var, var_dim, next_dim)
|
|
285
|
+
raise MappingError(err)
|
|
286
|
+
interp_dict[next_var] = 0.5
|
|
287
|
+
return interp_dict
|
|
288
|
+
|
|
289
|
+
def _build_interp_auto(self, split_pattern: str, interp_type: str) -> dict:
|
|
290
|
+
"""Handle the auto interpolation detection.
|
|
291
|
+
|
|
292
|
+
Args:
|
|
293
|
+
split_pattern: The pattern used to identify linked variables.
|
|
294
|
+
interp_type: The type of interpolation to use.
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
The interpolation logic.
|
|
298
|
+
"""
|
|
299
|
+
ds_in = self.design_space_in
|
|
300
|
+
ds_out = self.design_space_out
|
|
301
|
+
interp_logic = {}
|
|
302
|
+
log_prefix = "Auto interpolation mapping:"
|
|
303
|
+
|
|
304
|
+
# Try to build the interp logic using auto rules
|
|
305
|
+
for var_out, dim_out in iter(list(ds_out.variable_sizes.items())):
|
|
306
|
+
# Checks if the var exists in the input design space
|
|
307
|
+
dim_in = ds_in.get_size(var_out)
|
|
308
|
+
if dim_in is not None:
|
|
309
|
+
# The variable exists in the input design space
|
|
310
|
+
interp_dict = self._build_interp_on_var_size(
|
|
311
|
+
var_out, dim_in, dim_out, interp_type, log_prefix
|
|
312
|
+
)
|
|
313
|
+
else:
|
|
314
|
+
if interp_type != self.LINEAR:
|
|
315
|
+
raise NotImplementedError
|
|
316
|
+
interp_dict = self._build_inter_on_var_names(
|
|
317
|
+
var_out, dim_out, split_pattern, log_prefix
|
|
318
|
+
)
|
|
319
|
+
interp_logic[var_out] = interp_dict
|
|
320
|
+
|
|
321
|
+
# Call _build_interp_from_logic using the logic built above
|
|
322
|
+
self._build_interp_from_logic(interp_logic)
|
|
323
|
+
|
|
324
|
+
def _check_interp(self) -> None:
|
|
325
|
+
"""Check that all variables are correctly connected."""
|
|
326
|
+
vars_in = self.design_space_in.variable_names
|
|
327
|
+
|
|
328
|
+
def _check_size_zero(item):
|
|
329
|
+
if len(item) == 0:
|
|
330
|
+
raise KeyError
|
|
331
|
+
|
|
332
|
+
for var_out in self.design_space_out.variable_names:
|
|
333
|
+
try:
|
|
334
|
+
dict_prolg = self._prolg_oper[var_out]
|
|
335
|
+
|
|
336
|
+
# Might have been declared empty by mistake
|
|
337
|
+
_check_size_zero(dict_prolg)
|
|
338
|
+
except KeyError as exc:
|
|
339
|
+
err = f"{var_out} not connected to any input variable"
|
|
340
|
+
raise MappingError(err) from exc
|
|
341
|
+
for var in iter(list(dict_prolg.keys())):
|
|
342
|
+
if var not in vars_in:
|
|
343
|
+
err = (
|
|
344
|
+
f"{var_out} connected to variable: {var} "
|
|
345
|
+
f"not belonging to input variables: {vars_in}"
|
|
346
|
+
)
|
|
347
|
+
raise MappingError(err)
|
|
348
|
+
|
|
349
|
+
def _compute_factors(self) -> None:
|
|
350
|
+
"""Compute factors."""
|
|
351
|
+
prolg_arr, _, _ = self.get_jac_array()
|
|
352
|
+
self._norm_prolg = norm(prolg_arr, ord=inf)
|
|
353
|
+
self._restr_factor = 1.0 / norm(prolg_arr.T, ord=inf)
|
|
354
|
+
|
|
355
|
+
def _map_vars_direct(self, dvs_dict_in: dict) -> dict:
|
|
356
|
+
"""TODO.
|
|
357
|
+
|
|
358
|
+
Args:
|
|
359
|
+
dvs_dict_in: ??? TODO.
|
|
360
|
+
|
|
361
|
+
Returns:
|
|
362
|
+
??? TODO
|
|
363
|
+
"""
|
|
364
|
+
dvs_dict_out = {}
|
|
365
|
+
# Loop on vars out
|
|
366
|
+
for var_out, prolg_link in iter(list(self._prolg_oper.items())):
|
|
367
|
+
val_out = zeros(self.design_space_out.get_size(var_out))
|
|
368
|
+
# Loop on vars in
|
|
369
|
+
for var_in, sub_oper in prolg_link.items():
|
|
370
|
+
dvs_in = dvs_dict_in[var_in]
|
|
371
|
+
if isinstance(sub_oper, float):
|
|
372
|
+
# Light storage
|
|
373
|
+
val_out += sub_oper * dvs_in
|
|
374
|
+
else:
|
|
375
|
+
# Array
|
|
376
|
+
val_out += sub_oper.dot(dvs_in)
|
|
377
|
+
dvs_dict_out[var_out] = val_out
|
|
378
|
+
return dvs_dict_out
|
|
379
|
+
|
|
380
|
+
def _map_vars_reverse(self, dvs_dict_out: dict) -> dict:
|
|
381
|
+
"""TODO.
|
|
382
|
+
|
|
383
|
+
Args:
|
|
384
|
+
dvs_dict_out: ??? TODO.
|
|
385
|
+
|
|
386
|
+
Returns:
|
|
387
|
+
??? TODO.
|
|
388
|
+
"""
|
|
389
|
+
dvs_dict_in = {}
|
|
390
|
+
# Loop on vars in
|
|
391
|
+
for var_in, restr_link in iter(list(self._restr_oper.items())):
|
|
392
|
+
val_in = zeros(self.design_space_in.get_size(var_in))
|
|
393
|
+
# Loop on vars out
|
|
394
|
+
for var_out, sub_oper in restr_link.items():
|
|
395
|
+
dvs_out = dvs_dict_out[var_out]
|
|
396
|
+
if isinstance(sub_oper, float):
|
|
397
|
+
val_in += sub_oper * dvs_out
|
|
398
|
+
else:
|
|
399
|
+
# sub_oper is a reference to the prolg operator for efficiency
|
|
400
|
+
# Therefore use sub_oper.T
|
|
401
|
+
val_in += dvs_out.dot(sub_oper)
|
|
402
|
+
# Apply the restriction factor
|
|
403
|
+
val_in *= self._restr_factor
|
|
404
|
+
dvs_dict_in[var_in] = val_in
|
|
405
|
+
return dvs_dict_in
|
|
406
|
+
|
|
407
|
+
def get_jac(self) -> dict:
|
|
408
|
+
"""Get the jacobian."""
|
|
409
|
+
jac_dict = {}
|
|
410
|
+
|
|
411
|
+
# Make sure everything is formated into arrays
|
|
412
|
+
for var_out, prolg_link in iter(list(self._prolg_oper.items())):
|
|
413
|
+
jac_dict[var_out] = {}
|
|
414
|
+
for var_in, oper in iter(list(prolg_link.items())):
|
|
415
|
+
if isinstance(oper, float):
|
|
416
|
+
prolg = eye(self.design_space_out.get_size(var_out))
|
|
417
|
+
else:
|
|
418
|
+
# Copy to be on the safe side
|
|
419
|
+
prolg = oper.copy()
|
|
420
|
+
jac_dict[var_out][var_in] = prolg
|
|
421
|
+
# Build arrays
|
|
422
|
+
return deepcopy(jac_dict)
|
|
@@ -0,0 +1,70 @@
|
|
|
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
|
+
"""A factory to create a design space mapper from its name."""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
from typing import TYPE_CHECKING
|
|
28
|
+
from typing import ClassVar
|
|
29
|
+
|
|
30
|
+
from gemseo_multi_fidelity.mapping.identity_mapper import IdentityMapper
|
|
31
|
+
from gemseo_multi_fidelity.mapping.interp_mapper import InterpolationMapper
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from gemseo.algos.design_space import DesignSpace
|
|
35
|
+
|
|
36
|
+
from gemseo_multi_fidelity.core.ds_mapper import DesignSpaceMapper
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class DSMapperFactory:
|
|
40
|
+
"""DesignSpaceMapper factory."""
|
|
41
|
+
|
|
42
|
+
ID = "identity"
|
|
43
|
+
INTERP = "interp"
|
|
44
|
+
|
|
45
|
+
AVAILABLE_MAPPERS: ClassVar[list] = [ID, INTERP]
|
|
46
|
+
"""List of available mappers."""
|
|
47
|
+
|
|
48
|
+
def create(
|
|
49
|
+
self, name: str, ds_in: DesignSpace, ds_out: DesignSpace, **kwargs
|
|
50
|
+
) -> DesignSpaceMapper:
|
|
51
|
+
"""Create a DesignSpace Mapper from its name.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
name: The name of the DesignSpace Mapper to use.
|
|
55
|
+
ds_in: The input design space.
|
|
56
|
+
ds_out: The output design space.
|
|
57
|
+
kwargs: The keyword arguments to be passed to the mapper class.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
The DesignSpace Mapper.
|
|
61
|
+
"""
|
|
62
|
+
if name == self.ID:
|
|
63
|
+
klass = IdentityMapper
|
|
64
|
+
elif name == self.INTERP:
|
|
65
|
+
klass = InterpolationMapper
|
|
66
|
+
else:
|
|
67
|
+
msg = f"{name} not in {self.AVAILABLE_MAPPERS}"
|
|
68
|
+
raise ValueError(msg)
|
|
69
|
+
|
|
70
|
+
return klass(ds_in, ds_out, **kwargs)
|
|
@@ -0,0 +1,46 @@
|
|
|
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
|
+
"""Mapping error."""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class MappingError(Exception):
|
|
29
|
+
"""Error to raise if a mapping is not consistent."""
|
|
30
|
+
|
|
31
|
+
def __init__(self, msg: str | None = None) -> None:
|
|
32
|
+
"""Constructor.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
msg: The explanation for the error.
|
|
36
|
+
"""
|
|
37
|
+
super().__init__()
|
|
38
|
+
self.msg = msg
|
|
39
|
+
|
|
40
|
+
def __str__(self) -> str:
|
|
41
|
+
"""Textual representation of the Error.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
The textual representation.
|
|
45
|
+
"""
|
|
46
|
+
return repr(self.msg)
|
|
@@ -0,0 +1,122 @@
|
|
|
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) 2020 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
|
+
"""Subset mapper."""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
from typing import TYPE_CHECKING
|
|
28
|
+
|
|
29
|
+
from numpy import array
|
|
30
|
+
|
|
31
|
+
from gemseo_multi_fidelity.core.ds_mapper import DesignSpaceMapper
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from gemseo.algos.design_space import DesignSpace
|
|
35
|
+
from numpy.typing import NDArray
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class SubsetMapper(DesignSpaceMapper):
|
|
39
|
+
"""Mapper of only a subset of variables."""
|
|
40
|
+
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
design_space_in: DesignSpace,
|
|
44
|
+
design_space_out: DesignSpace,
|
|
45
|
+
vars_mapping: dict | None = None,
|
|
46
|
+
) -> None:
|
|
47
|
+
"""Constructor.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
design_space_in: The input DesignSpace object.
|
|
51
|
+
design_space_out: The output DesignSpace object.
|
|
52
|
+
vars_mapping: The user-defined mapping between input and output variables.
|
|
53
|
+
"""
|
|
54
|
+
super().__init__(design_space_in, design_space_out)
|
|
55
|
+
self._vars_mapping = self._check_vars_mapping(vars_mapping)
|
|
56
|
+
|
|
57
|
+
def _check_vars_mapping(self, vars_mapping: dict) -> dict:
|
|
58
|
+
vars_in = self.design_space_in.variable_names
|
|
59
|
+
vars_out = self.design_space_out.variable_names
|
|
60
|
+
if vars_mapping is None:
|
|
61
|
+
# Find the variables that can be matched automatically
|
|
62
|
+
vars_mapping = {}
|
|
63
|
+
|
|
64
|
+
for var in vars_out:
|
|
65
|
+
if var in vars_in:
|
|
66
|
+
vars_mapping[var] = var
|
|
67
|
+
else:
|
|
68
|
+
if not isinstance(vars_mapping, dict):
|
|
69
|
+
msg = "vars_mapping must be a dict or None"
|
|
70
|
+
raise TypeError(msg)
|
|
71
|
+
linked_vars_in = []
|
|
72
|
+
for var_out, var_in in vars_mapping.items():
|
|
73
|
+
if var_out not in vars_out:
|
|
74
|
+
msg = f"{var_out} not in {vars_out}"
|
|
75
|
+
raise ValueError(msg)
|
|
76
|
+
if var_in not in vars_in:
|
|
77
|
+
msg = f"{var_in} not in {vars_in}"
|
|
78
|
+
raise ValueError(msg)
|
|
79
|
+
if var_in in linked_vars_in:
|
|
80
|
+
msg = f"{var_in} linked to more than one var out"
|
|
81
|
+
raise ValueError(msg)
|
|
82
|
+
linked_vars_in.append(var_in)
|
|
83
|
+
return vars_mapping
|
|
84
|
+
|
|
85
|
+
def _map_vars_direct(self, dvs_dict_in: DesignSpace) -> DesignSpace:
|
|
86
|
+
"""Map vars direct.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
dvs_dict_in: The input design space.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
The output design space.
|
|
93
|
+
"""
|
|
94
|
+
sub_dict_out = {
|
|
95
|
+
var_out: dvs_dict_in[var_in]
|
|
96
|
+
for var_out, var_in in self._vars_mapping.items()
|
|
97
|
+
}
|
|
98
|
+
dvs_dict_out = self.design_space_out.get_current_value(as_dict=True)
|
|
99
|
+
dvs_dict_out.update(sub_dict_out)
|
|
100
|
+
return dvs_dict_out
|
|
101
|
+
|
|
102
|
+
def _map_vars_reverse(self, dvs_dict_out: DesignSpace) -> DesignSpace:
|
|
103
|
+
"""Map vars reverse.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
dvs_dict_out: The output design space.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
The input design space.
|
|
110
|
+
"""
|
|
111
|
+
return {
|
|
112
|
+
var_in: dvs_dict_out[var_out]
|
|
113
|
+
for var_out, var_in in self._vars_mapping.items()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
def get_jac(self) -> NDArray:
|
|
117
|
+
"""Get the jacobian.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
The jacobian.
|
|
121
|
+
"""
|
|
122
|
+
return {key: {val: array([[1.0]])} for key, val in self._vars_mapping.items()}
|
|
@@ -0,0 +1,16 @@
|
|
|
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
|
+
"""Rosenbrock test case for multi-fidelity."""
|