pyfemtet 0.4.25__py3-none-any.whl → 0.5.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.
Potentially problematic release.
This version of pyfemtet might be problematic. Click here for more details.
- pyfemtet/__init__.py +1 -1
- pyfemtet/message/locales/ja/LC_MESSAGES/messages.mo +0 -0
- pyfemtet/message/locales/ja/LC_MESSAGES/messages.po +114 -62
- pyfemtet/message/locales/messages.pot +114 -62
- pyfemtet/message/messages.py +6 -2
- pyfemtet/opt/__init__.py +2 -2
- pyfemtet/opt/_femopt.py +27 -46
- pyfemtet/opt/_femopt_core.py +50 -33
- pyfemtet/opt/_test_utils/__init__.py +0 -0
- pyfemtet/opt/_test_utils/control_femtet.py +45 -0
- pyfemtet/opt/_test_utils/hyper_sphere.py +24 -0
- pyfemtet/opt/_test_utils/record_history.py +72 -0
- pyfemtet/opt/interface/_femtet.py +39 -1
- pyfemtet/opt/optimizer/__init__.py +12 -0
- pyfemtet/opt/{opt → optimizer}/_base.py +30 -9
- pyfemtet/opt/{opt → optimizer}/_optuna.py +14 -33
- pyfemtet/opt/optimizer/_optuna_botorchsampler_parameter_constraint_helper.py +325 -0
- pyfemtet/opt/{opt → optimizer}/_scipy.py +10 -6
- pyfemtet/opt/{opt → optimizer}/_scipy_scalar.py +24 -12
- pyfemtet/opt/samples/femprj_sample/constrained_pipe.femprj +0 -0
- pyfemtet/opt/samples/femprj_sample/constrained_pipe.py +97 -0
- pyfemtet/opt/samples/femprj_sample/constrained_pipe_test_result.reccsv +13 -0
- pyfemtet/opt/{femprj_sample → samples/femprj_sample}/gal_ex58_parametric_test_result.reccsv +1 -1
- pyfemtet/opt/{femprj_sample → samples/femprj_sample}/her_ex40_parametric.py +10 -8
- pyfemtet/opt/samples/femprj_sample/her_ex40_parametric_test_result.reccsv +18 -0
- pyfemtet/opt/samples/femprj_sample_jp/constrained_pipe_jp.py +93 -0
- pyfemtet/opt/{femprj_sample_jp → samples/femprj_sample_jp}/her_ex40_parametric_jp.py +10 -8
- pyfemtet/opt/visualization/complex_components/main_figure_creator.py +69 -0
- pyfemtet/opt/visualization/complex_components/main_graph.py +299 -14
- pyfemtet/opt/visualization/process_monitor/pages.py +1 -1
- pyfemtet/opt/visualization/result_viewer/application.py +1 -1
- pyfemtet/opt/visualization/result_viewer/pages.py +9 -9
- {pyfemtet-0.4.25.dist-info → pyfemtet-0.5.1.dist-info}/METADATA +1 -1
- pyfemtet-0.5.1.dist-info/RECORD +115 -0
- pyfemtet/_test_util.py +0 -135
- pyfemtet/opt/femprj_sample/ParametricIF - True.femprj +0 -0
- pyfemtet/opt/femprj_sample/her_ex40_parametric_test_result.reccsv +0 -18
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14.pdt +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial1.jpg +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial1.pdt +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial10.jpg +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial10.pdt +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial11.jpg +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial11.pdt +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial12.jpg +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial12.pdt +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial13.jpg +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial13.pdt +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial14.jpg +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial14.pdt +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial15.jpg +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial15.pdt +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial2.jpg +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial2.pdt +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial3.jpg +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial3.pdt +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial4.jpg +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial4.pdt +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial5.jpg +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial5.pdt +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial6.jpg +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial6.pdt +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial7.jpg +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial7.pdt +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial8.jpg +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial8.pdt +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial9.jpg +0 -0
- pyfemtet/opt/femprj_sample/wat_ex14_parametric.Results/Ex14_trial9.pdt +0 -0
- pyfemtet/opt/opt/__init__.py +0 -12
- pyfemtet/opt/opt/_optuna_botorch_helper.py +0 -209
- pyfemtet-0.4.25.dist-info/RECORD +0 -140
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/.gitignore +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/ParametricIF.femprj +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/ParametricIF.py +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/ParametricIF_test_result.reccsv +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/cad_ex01_NX.femprj +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/cad_ex01_NX.prt +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/cad_ex01_NX.py +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/cad_ex01_NX_test_result.reccsv +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/cad_ex01_SW.SLDPRT +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/cad_ex01_SW.femprj +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/cad_ex01_SW.py +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/cad_ex01_SW_test_result.reccsv +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/gal_ex58_parametric.femprj +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/gal_ex58_parametric.py +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/gau_ex08_parametric.femprj +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/gau_ex08_parametric.py +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/gau_ex08_parametric_test_result.reccsv +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/her_ex40_parametric.femprj +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/paswat_ex1_parametric.femprj +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/paswat_ex1_parametric.py +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/paswat_ex1_parametric_parallel.py +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/paswat_ex1_parametric_test_result.reccsv +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/wat_ex14_parametric.femprj +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/wat_ex14_parametric.py +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/wat_ex14_parametric_parallel.py +0 -0
- /pyfemtet/opt/{femprj_sample → samples/femprj_sample}/wat_ex14_parametric_test_result.reccsv +0 -0
- /pyfemtet/opt/{femprj_sample_jp → samples/femprj_sample_jp}/ParametricIF_jp.femprj +0 -0
- /pyfemtet/opt/{femprj_sample_jp → samples/femprj_sample_jp}/ParametricIF_jp.py +0 -0
- /pyfemtet/opt/{femprj_sample_jp → samples/femprj_sample_jp}/cad_ex01_NX_jp.femprj +0 -0
- /pyfemtet/opt/{femprj_sample_jp → samples/femprj_sample_jp}/cad_ex01_NX_jp.py +0 -0
- /pyfemtet/opt/{femprj_sample_jp → samples/femprj_sample_jp}/cad_ex01_SW_jp.femprj +0 -0
- /pyfemtet/opt/{femprj_sample_jp → samples/femprj_sample_jp}/cad_ex01_SW_jp.py +0 -0
- /pyfemtet/opt/{femprj_sample_jp → samples/femprj_sample_jp}/gal_ex58_parametric_jp.femprj +0 -0
- /pyfemtet/opt/{femprj_sample_jp → samples/femprj_sample_jp}/gal_ex58_parametric_jp.py +0 -0
- /pyfemtet/opt/{femprj_sample_jp → samples/femprj_sample_jp}/gau_ex08_parametric_jp.femprj +0 -0
- /pyfemtet/opt/{femprj_sample_jp → samples/femprj_sample_jp}/gau_ex08_parametric_jp.py +0 -0
- /pyfemtet/opt/{femprj_sample_jp → samples/femprj_sample_jp}/her_ex40_parametric_jp.femprj +0 -0
- /pyfemtet/opt/{femprj_sample_jp → samples/femprj_sample_jp}/paswat_ex1_parametric_jp.femprj +0 -0
- /pyfemtet/opt/{femprj_sample_jp → samples/femprj_sample_jp}/paswat_ex1_parametric_jp.py +0 -0
- /pyfemtet/opt/{femprj_sample_jp → samples/femprj_sample_jp}/paswat_ex1_parametric_parallel_jp.py +0 -0
- /pyfemtet/opt/{femprj_sample_jp → samples/femprj_sample_jp}/wat_ex14_parametric_jp.femprj +0 -0
- /pyfemtet/opt/{femprj_sample_jp → samples/femprj_sample_jp}/wat_ex14_parametric_jp.py +0 -0
- /pyfemtet/opt/{femprj_sample_jp → samples/femprj_sample_jp}/wat_ex14_parametric_parallel_jp.py +0 -0
- {pyfemtet-0.4.25.dist-info → pyfemtet-0.5.1.dist-info}/LICENSE +0 -0
- {pyfemtet-0.4.25.dist-info → pyfemtet-0.5.1.dist-info}/WHEEL +0 -0
- {pyfemtet-0.4.25.dist-info → pyfemtet-0.5.1.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
from typing import Callable
|
|
2
|
+
from functools import partial
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
import torch
|
|
7
|
+
from torch import Tensor
|
|
8
|
+
|
|
9
|
+
from optuna.study import Study
|
|
10
|
+
from optuna.trial import Trial
|
|
11
|
+
from optuna._transform import _SearchSpaceTransform
|
|
12
|
+
|
|
13
|
+
from botorch.acquisition import AcquisitionFunction
|
|
14
|
+
from botorch.optim.initializers import gen_batch_initial_conditions
|
|
15
|
+
|
|
16
|
+
from pyfemtet.opt._femopt_core import Constraint
|
|
17
|
+
from pyfemtet.opt.optimizer import OptunaOptimizer, logger
|
|
18
|
+
from pyfemtet.message import Msg
|
|
19
|
+
|
|
20
|
+
from time import time
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
__all__ = ['do_patch']
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
BotorchConstraint = Callable[[Tensor], Tensor]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def do_patch(
|
|
30
|
+
study: Study,
|
|
31
|
+
constraints: dict[str, Constraint],
|
|
32
|
+
opt: OptunaOptimizer,
|
|
33
|
+
):
|
|
34
|
+
"""BoTorchSampler の optimize_acqf をパッチし、パラメータ拘束が実施できるようにします。
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
study (Study): Optuna study. Use to calculate bounds.
|
|
38
|
+
constraints (dict[str, Constraint]): Constraints.
|
|
39
|
+
opt (OptunaOptimizer): OptunaOptimizer.
|
|
40
|
+
"""
|
|
41
|
+
import optuna_integration
|
|
42
|
+
target_fun = optuna_integration.botorch.optimize_acqf
|
|
43
|
+
new_fun: callable = OptimizeReplacedACQF(target_fun)
|
|
44
|
+
new_fun.set_constraints(list(constraints.values()))
|
|
45
|
+
new_fun.set_study(study)
|
|
46
|
+
new_fun.set_opt(opt)
|
|
47
|
+
optuna_integration.botorch.optimize_acqf = new_fun
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class GeneralFunctionWithForwardDifference(torch.autograd.Function):
|
|
51
|
+
"""自作関数を pytorch で自動微分するためのクラスです。
|
|
52
|
+
|
|
53
|
+
ユーザー定義関数を botorch 形式に変換する過程で微分の計算ができなくなるのでこれが必要です。
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def forward(ctx, f, xs):
|
|
58
|
+
ys = f(xs)
|
|
59
|
+
ctx.save_for_backward(xs, ys)
|
|
60
|
+
ctx.f = f
|
|
61
|
+
return ys
|
|
62
|
+
|
|
63
|
+
@staticmethod
|
|
64
|
+
def backward(ctx, grad_output):
|
|
65
|
+
xs, ys = ctx.saved_tensors
|
|
66
|
+
f = ctx.f
|
|
67
|
+
dx = 0.001 # 入力は normalized なので決め打ちでよい
|
|
68
|
+
diff = []
|
|
69
|
+
xs = xs.detach() # xs に余計な計算履歴を残さないために、detachする。
|
|
70
|
+
for i in range(len(xs)):
|
|
71
|
+
xs[i] += dx
|
|
72
|
+
diff.append(torch.sum(grad_output * (f(xs) - ys)))
|
|
73
|
+
xs[i] -= dx
|
|
74
|
+
diff = torch.tensor(diff) / dx
|
|
75
|
+
return None, diff
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ConvertedConstraint:
|
|
79
|
+
"""ユーザーが定義した Constraint を botorch で処理できる形式に変換します。
|
|
80
|
+
|
|
81
|
+
`callable()` は形状 `d` の 1 次元テンソルを受け取り、スカラーを返します。
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
def __init__(self, constraint: Constraint, study: Study, bound: str, opt: OptunaOptimizer):
|
|
85
|
+
self._constraint: Constraint = constraint
|
|
86
|
+
self._study = study
|
|
87
|
+
self._bound = bound
|
|
88
|
+
self._opt = opt
|
|
89
|
+
|
|
90
|
+
def __call__(self, x: Tensor) -> Tensor: # BotorchConstraint
|
|
91
|
+
"""optimize_acqf() に渡される非線形拘束関数の処理です。
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
x (Tensor): Normalized parameters. Its length is d (== len(prm)).
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
float Tensor. >= 0 is feasible.
|
|
98
|
+
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
norm_x = x.detach().numpy()
|
|
102
|
+
c = evaluate_pyfemtet_cns(self._study, self._constraint, norm_x, self._opt)
|
|
103
|
+
if self._bound == 'lb':
|
|
104
|
+
return Tensor([c - self._constraint.lb])
|
|
105
|
+
elif self._bound == 'ub':
|
|
106
|
+
return Tensor([self._constraint.ub - c])
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def is_feasible(study: Study, constraints: list[Constraint], norm_x: np.ndarray, opt: OptunaOptimizer) -> bool:
|
|
110
|
+
feasible = True
|
|
111
|
+
cns: Constraint
|
|
112
|
+
for cns in constraints:
|
|
113
|
+
c = evaluate_pyfemtet_cns(study, cns, norm_x, opt)
|
|
114
|
+
if cns.lb is not None:
|
|
115
|
+
if cns.lb > c:
|
|
116
|
+
feasible = False
|
|
117
|
+
break
|
|
118
|
+
if cns.ub is not None:
|
|
119
|
+
if cns.ub < c:
|
|
120
|
+
feasible = False
|
|
121
|
+
break
|
|
122
|
+
return feasible
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def evaluate_pyfemtet_cns(study: Study, cns: Constraint, norm_x: np.ndarray, opt: OptunaOptimizer) -> float:
|
|
126
|
+
"""Evaluate given constraint function by given NORMALIZED x.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
study (Study): Optuna study. Use to detect search space from last trial's Distribution objects.
|
|
130
|
+
cns (Constraint): PyFemtet's format constraint.
|
|
131
|
+
norm_x (np.ndarray): NORMALIZED values of all parameters.
|
|
132
|
+
opt (OptunaOptimizer): PyFemtet's optimizer. Used for update values of `opt` and `fem` who may be used in `cns`.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
bool: feasible or not.
|
|
136
|
+
"""
|
|
137
|
+
# ===== unnormalize x =====
|
|
138
|
+
search_space = study.sampler.infer_relative_search_space(study, None)
|
|
139
|
+
trans = _SearchSpaceTransform(search_space, transform_0_1=True, transform_log=False, transform_step=False)
|
|
140
|
+
params = trans.untransform(norm_x)
|
|
141
|
+
|
|
142
|
+
# ===== update OptunaOptimizer and FEMInterface who is referenced by cns =====
|
|
143
|
+
|
|
144
|
+
# opt
|
|
145
|
+
opt.set_parameter(params)
|
|
146
|
+
|
|
147
|
+
# fem
|
|
148
|
+
if cns.using_fem:
|
|
149
|
+
df_to_fem = opt.variables.get_variables(format='df', filter_pass_to_fem=True)
|
|
150
|
+
opt.fem.update_parameter(df_to_fem)
|
|
151
|
+
|
|
152
|
+
# ===== calc cns =====
|
|
153
|
+
return cns.calc(opt.fem)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class NonlinearInequalityConstraints:
|
|
157
|
+
"""botorch の optimize_acqf に parameter constraints を設定するための引数を作成します。"""
|
|
158
|
+
|
|
159
|
+
def __init__(self, study: Study, constraints: list[Constraint], opt: OptunaOptimizer):
|
|
160
|
+
self._study = study
|
|
161
|
+
self._constraints = constraints
|
|
162
|
+
self._opt = opt
|
|
163
|
+
|
|
164
|
+
self._nonlinear_inequality_constraints = []
|
|
165
|
+
cns: Constraint
|
|
166
|
+
for cns in self._constraints:
|
|
167
|
+
if cns.lb is not None:
|
|
168
|
+
cns_botorch = ConvertedConstraint(cns, self._study, 'lb', self._opt)
|
|
169
|
+
item = (lambda x: GeneralFunctionWithForwardDifference.apply(cns_botorch, x), True)
|
|
170
|
+
self._nonlinear_inequality_constraints.append(item)
|
|
171
|
+
if cns.ub is not None:
|
|
172
|
+
cns_botorch = ConvertedConstraint(cns, self._study, 'ub', self._opt)
|
|
173
|
+
item = (lambda x: GeneralFunctionWithForwardDifference.apply(cns_botorch, x), True)
|
|
174
|
+
self._nonlinear_inequality_constraints.append(item)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _filter_feasible_conditions(self, ic_batch):
|
|
178
|
+
# List to store feasible initial conditions
|
|
179
|
+
feasible_ic_list = []
|
|
180
|
+
|
|
181
|
+
for each_num_restarts in ic_batch:
|
|
182
|
+
feasible_q_list = []
|
|
183
|
+
for each_q in each_num_restarts:
|
|
184
|
+
norm_x: np.ndarray = each_q.numpy() # normalized parameters
|
|
185
|
+
|
|
186
|
+
if is_feasible(self._study, self._constraints, norm_x, self._opt):
|
|
187
|
+
feasible_q_list.append(each_q) # Keep only feasible rows
|
|
188
|
+
|
|
189
|
+
if feasible_q_list: # Only add if there are feasible rows
|
|
190
|
+
feasible_ic_list.append(torch.stack(feasible_q_list))
|
|
191
|
+
|
|
192
|
+
# Stack feasible conditions back into tensor format
|
|
193
|
+
if feasible_ic_list:
|
|
194
|
+
return torch.stack(feasible_ic_list)
|
|
195
|
+
else:
|
|
196
|
+
return None # Return None if none are feasible
|
|
197
|
+
|
|
198
|
+
@staticmethod
|
|
199
|
+
def _generate_random_initial_conditions(shape):
|
|
200
|
+
# Generates random initial conditions with the same shape as ic_batch
|
|
201
|
+
return torch.rand(shape)
|
|
202
|
+
|
|
203
|
+
def _generate_feasible_initial_conditions(self, *args, **kwargs):
|
|
204
|
+
# A `num_restarts x q x d` tensor of initial conditions.
|
|
205
|
+
ic_batch = gen_batch_initial_conditions(*args, **kwargs)
|
|
206
|
+
feasible_ic_batch = self._filter_feasible_conditions(ic_batch)
|
|
207
|
+
|
|
208
|
+
while feasible_ic_batch is None:
|
|
209
|
+
# Generate new random ic_batch with the same shape
|
|
210
|
+
print('警告: gen_batch_initial_conditions() は feasible な初期値を提案しませんでした。'
|
|
211
|
+
'パラメータ提案を探索するための初期値をランダムに選定します。')
|
|
212
|
+
random_ic_batch = self._generate_random_initial_conditions(ic_batch.shape)
|
|
213
|
+
feasible_ic_batch = self._filter_feasible_conditions(random_ic_batch)
|
|
214
|
+
|
|
215
|
+
return feasible_ic_batch
|
|
216
|
+
|
|
217
|
+
def create_kwargs(self) -> dict:
|
|
218
|
+
"""
|
|
219
|
+
nonlinear_inequality_constraints:
|
|
220
|
+
非線形不等式制約を表すタプルのリスト。
|
|
221
|
+
タプルの最初の要素は、`callable(x) >= 0` という形式の制約を表す呼び出し可能オブジェクトです。
|
|
222
|
+
2 番目の要素はブール値で、点内制約の場合は `True`
|
|
223
|
+
制約は後で scipy ソルバーに渡されます。
|
|
224
|
+
この場合、`batch_initial_conditions` を渡す必要があります。
|
|
225
|
+
非線形不等式制約を使用するには、`batch_limit` を 1 に設定する必要もあります。
|
|
226
|
+
これは、`options` で指定されていない場合は自動的に行われます。
|
|
227
|
+
"""
|
|
228
|
+
return dict(
|
|
229
|
+
q=1,
|
|
230
|
+
options=dict(
|
|
231
|
+
batch_limit=1,
|
|
232
|
+
),
|
|
233
|
+
nonlinear_inequality_constraints=self._nonlinear_inequality_constraints,
|
|
234
|
+
ic_generator=self._generate_feasible_initial_conditions,
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class AcquisitionFunctionWithPenalty(AcquisitionFunction):
|
|
239
|
+
"""獲得関数に infeasible 項を追加します。"""
|
|
240
|
+
|
|
241
|
+
# noinspection PyAttributeOutsideInit
|
|
242
|
+
def set_acqf(self, acqf):
|
|
243
|
+
self._acqf = acqf
|
|
244
|
+
|
|
245
|
+
# noinspection PyAttributeOutsideInit
|
|
246
|
+
def set_constraints(self, constraints: list[Constraint]):
|
|
247
|
+
self._constraints: list[Constraint] = constraints
|
|
248
|
+
|
|
249
|
+
# noinspection PyAttributeOutsideInit
|
|
250
|
+
def set_study(self, study: Study):
|
|
251
|
+
self._study: Study = study
|
|
252
|
+
|
|
253
|
+
# noinspection PyAttributeOutsideInit
|
|
254
|
+
def set_opt(self, opt: OptunaOptimizer):
|
|
255
|
+
self._opt = opt
|
|
256
|
+
|
|
257
|
+
def forward(self, X: "Tensor") -> "Tensor":
|
|
258
|
+
"""
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
X (Tensor): batch_size x 1 x n_params tensor.
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Tensor: batch_size tensor.
|
|
265
|
+
|
|
266
|
+
"""
|
|
267
|
+
base = self._acqf.forward(X)
|
|
268
|
+
|
|
269
|
+
norm_x: np.ndarray
|
|
270
|
+
for i, _norm_x in enumerate(X.detach().numpy()):
|
|
271
|
+
|
|
272
|
+
cns: Constraint
|
|
273
|
+
for cns in self._constraints:
|
|
274
|
+
feasible = is_feasible(self._study, [cns], _norm_x[0], self._opt)
|
|
275
|
+
if not feasible:
|
|
276
|
+
base[i] = base[i] * 0. # ペナルティ
|
|
277
|
+
break
|
|
278
|
+
|
|
279
|
+
return base
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
class OptimizeReplacedACQF(partial):
|
|
283
|
+
"""optimize_acqf をこの partial 関数に置き換えます。"""
|
|
284
|
+
|
|
285
|
+
# noinspection PyAttributeOutsideInit
|
|
286
|
+
def set_constraints(self, constraints: list[Constraint]):
|
|
287
|
+
self._constraints: list[Constraint] = constraints
|
|
288
|
+
|
|
289
|
+
# noinspection PyAttributeOutsideInit
|
|
290
|
+
def set_study(self, study: Study):
|
|
291
|
+
self._study: Study = study
|
|
292
|
+
|
|
293
|
+
# noinspection PyAttributeOutsideInit
|
|
294
|
+
def set_opt(self, opt: OptunaOptimizer):
|
|
295
|
+
self._opt = opt
|
|
296
|
+
|
|
297
|
+
def __call__(self, *args, **kwargs):
|
|
298
|
+
"""置き換え先の関数の処理内容です。
|
|
299
|
+
|
|
300
|
+
kwargs を横入りして追記することで拘束を実現します。
|
|
301
|
+
"""
|
|
302
|
+
|
|
303
|
+
logger.info(Msg.START_CANDIDATE_WITH_PARAMETER_CONSTRAINT)
|
|
304
|
+
|
|
305
|
+
# FEM の更新が必要な場合、時間がかかることが多いので警告を出す
|
|
306
|
+
if any([cns.using_fem for cns in self._constraints]):
|
|
307
|
+
logger.warning(Msg.WARN_UPDATE_FEM_PARAMETER_TOOK_A_LONG_TIME)
|
|
308
|
+
|
|
309
|
+
# 獲得関数に infeasible な場合のペナルティ項を追加します。
|
|
310
|
+
acqf = kwargs['acq_function']
|
|
311
|
+
new_acqf = AcquisitionFunctionWithPenalty(...)
|
|
312
|
+
new_acqf.set_acqf(acqf)
|
|
313
|
+
new_acqf.set_constraints(self._constraints)
|
|
314
|
+
new_acqf.set_study(self._study)
|
|
315
|
+
new_acqf.set_opt(self._opt)
|
|
316
|
+
kwargs['acq_function'] = new_acqf
|
|
317
|
+
|
|
318
|
+
# optimize_acqf の探索に parameter constraints を追加します。
|
|
319
|
+
nlic = NonlinearInequalityConstraints(self._study, self._constraints, self._opt)
|
|
320
|
+
kwargs.update(nlic.create_kwargs())
|
|
321
|
+
|
|
322
|
+
# replace other arguments
|
|
323
|
+
...
|
|
324
|
+
|
|
325
|
+
return super().__call__(*args, **kwargs)
|
|
@@ -13,7 +13,7 @@ from scipy.optimize import minimize, OptimizeResult
|
|
|
13
13
|
|
|
14
14
|
# pyfemtet relative
|
|
15
15
|
from pyfemtet.opt._femopt_core import OptimizationStatus, generate_lhs
|
|
16
|
-
from pyfemtet.opt.
|
|
16
|
+
from pyfemtet.opt.optimizer import AbstractOptimizer, logger, OptimizationMethodChecker
|
|
17
17
|
from pyfemtet.core import MeshError, ModelError, SolveError
|
|
18
18
|
from pyfemtet.message import Msg
|
|
19
19
|
|
|
@@ -51,6 +51,9 @@ class StopIterationCallback:
|
|
|
51
51
|
|
|
52
52
|
class ScipyMethodChecker(OptimizationMethodChecker):
|
|
53
53
|
def check_incomplete_bounds(self, raise_error=True): return True
|
|
54
|
+
def check_seed(self, raise_error=True):
|
|
55
|
+
logger.warning(Msg.WARN_SCIPY_DOESNT_NEED_SEED)
|
|
56
|
+
return True
|
|
54
57
|
|
|
55
58
|
|
|
56
59
|
class ScipyOptimizer(AbstractOptimizer):
|
|
@@ -76,8 +79,9 @@ class ScipyOptimizer(AbstractOptimizer):
|
|
|
76
79
|
|
|
77
80
|
def _objective(self, x: np.ndarray): # x: candidate parameter
|
|
78
81
|
# update parameter
|
|
79
|
-
self.
|
|
80
|
-
|
|
82
|
+
df = self.get_parameter('df')
|
|
83
|
+
df['value'] = x
|
|
84
|
+
self.fem.update_parameter(df)
|
|
81
85
|
|
|
82
86
|
# strict constraints
|
|
83
87
|
...
|
|
@@ -108,13 +112,13 @@ class ScipyOptimizer(AbstractOptimizer):
|
|
|
108
112
|
def run(self):
|
|
109
113
|
|
|
110
114
|
# create init
|
|
111
|
-
x0 = self.
|
|
115
|
+
x0 = self.get_parameter('values')
|
|
112
116
|
|
|
113
117
|
# create bounds
|
|
114
118
|
if 'bounds' not in self.minimize_kwargs.keys():
|
|
115
119
|
bounds = []
|
|
116
|
-
for i, row in self.
|
|
117
|
-
lb, ub = row['
|
|
120
|
+
for i, row in self.get_parameter('df').iterrows():
|
|
121
|
+
lb, ub = row['lower_bound'], row['upper_bound']
|
|
118
122
|
if lb is None: lb = -np.inf
|
|
119
123
|
if ub is None: ub = np.inf
|
|
120
124
|
bounds.append([lb, ub])
|
|
@@ -13,12 +13,16 @@ from scipy.optimize import minimize_scalar, OptimizeResult
|
|
|
13
13
|
|
|
14
14
|
# pyfemtet relative
|
|
15
15
|
from pyfemtet.opt._femopt_core import OptimizationStatus, generate_lhs
|
|
16
|
-
from pyfemtet.opt.
|
|
16
|
+
from pyfemtet.opt.optimizer import AbstractOptimizer, logger, OptimizationMethodChecker
|
|
17
17
|
from pyfemtet.core import MeshError, ModelError, SolveError
|
|
18
|
+
from pyfemtet.message import Msg
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
class ScipyScalarMethodChecker(OptimizationMethodChecker):
|
|
21
22
|
def check_incomplete_bounds(self, raise_error=True): return True
|
|
23
|
+
def check_seed(self, raise_error=True):
|
|
24
|
+
logger.warning(Msg.WARN_SCIPY_DOESNT_NEED_SEED)
|
|
25
|
+
return True
|
|
22
26
|
|
|
23
27
|
|
|
24
28
|
class ScipyScalarOptimizer(AbstractOptimizer):
|
|
@@ -41,8 +45,10 @@ class ScipyScalarOptimizer(AbstractOptimizer):
|
|
|
41
45
|
|
|
42
46
|
def _objective(self, x: float): # x: candidate parameter
|
|
43
47
|
# update parameter
|
|
44
|
-
|
|
45
|
-
self.
|
|
48
|
+
|
|
49
|
+
df = self.get_parameter('df')
|
|
50
|
+
df['value'] = x
|
|
51
|
+
self.fem.update_parameter(df)
|
|
46
52
|
|
|
47
53
|
# strict constraints
|
|
48
54
|
...
|
|
@@ -72,19 +78,25 @@ class ScipyScalarOptimizer(AbstractOptimizer):
|
|
|
72
78
|
def run(self):
|
|
73
79
|
|
|
74
80
|
# create init
|
|
75
|
-
|
|
81
|
+
params = self.get_parameter()
|
|
82
|
+
assert len(params) == 1, print(f'{params} parameter(s) are passed.')
|
|
76
83
|
|
|
77
84
|
# create bounds
|
|
78
85
|
if 'bounds' not in self.minimize_kwargs.keys():
|
|
79
86
|
bounds = []
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
87
|
+
|
|
88
|
+
row = self.get_parameter('df')
|
|
89
|
+
lb, ub = row['lower_bound'].iloc[0], row['upper_bound'].iloc[0]
|
|
90
|
+
|
|
91
|
+
if lb is None and ub is None:
|
|
92
|
+
pass
|
|
93
|
+
elif lb is None or ub is None:
|
|
94
|
+
raise ValueError('Both lower and upper bounds must be set.')
|
|
95
|
+
else:
|
|
96
|
+
bounds = [lb, ub]
|
|
97
|
+
self.minimize_kwargs.update(
|
|
98
|
+
{'bounds': bounds}
|
|
99
|
+
)
|
|
88
100
|
|
|
89
101
|
# run optimize
|
|
90
102
|
try:
|
|
Binary file
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""A sample to implement constrained optimization.
|
|
2
|
+
|
|
3
|
+
This section describes the types of constraints and
|
|
4
|
+
the steps to run optimization on models that require them.
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from optuna_integration import BoTorchSampler
|
|
9
|
+
from pyfemtet.opt import FEMOpt, OptunaOptimizer
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def mises_stress(Femtet):
|
|
13
|
+
"""Calculate the von Mises stress as the objective function.
|
|
14
|
+
|
|
15
|
+
This function is called automatically by the FEMOpt
|
|
16
|
+
object while the optimization is running.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
Femtet: When defining an objective or constraint
|
|
20
|
+
function using PyFemtet, the first argument
|
|
21
|
+
must take a Femtet instance.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
float: A single float representing the expression value you want to constrain.
|
|
25
|
+
"""
|
|
26
|
+
return Femtet.Gogh.Galileo.GetMaxStress_py()[2]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def radius_diff(Femtet, opt):
|
|
30
|
+
"""Calculate the difference between the outer and inner radii of the pipe.
|
|
31
|
+
|
|
32
|
+
This constraint is called to ensure that the
|
|
33
|
+
inner radius of the pipe does not exceed the
|
|
34
|
+
outer radius while the optimization is running.
|
|
35
|
+
|
|
36
|
+
Note:
|
|
37
|
+
If you are using BoTorchSampler of OptunaOptimizer
|
|
38
|
+
and use strict constraints, be aware that accessing
|
|
39
|
+
the Femtet can be very slow, as it requires repeated
|
|
40
|
+
calculations to propose parameters.
|
|
41
|
+
We recommend that you do not access the Femtet,
|
|
42
|
+
but rather get the parameters and perform the
|
|
43
|
+
calculations via the Optimizer object, as in this
|
|
44
|
+
function example.
|
|
45
|
+
|
|
46
|
+
NOT recommended::
|
|
47
|
+
|
|
48
|
+
p = Femtet.GetVariableValue('p')
|
|
49
|
+
|
|
50
|
+
instead, use optimizer::
|
|
51
|
+
|
|
52
|
+
params = opt.get_parameter()
|
|
53
|
+
p = params['p']
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
Femtet: When defining an objective or constraint
|
|
57
|
+
function using PyFemtet, the first argument
|
|
58
|
+
must take a Femtet instance.
|
|
59
|
+
opt: This object allows you to obtain the outer
|
|
60
|
+
radius and inner radius values without going
|
|
61
|
+
through Femtet.
|
|
62
|
+
"""
|
|
63
|
+
params = opt.get_parameter()
|
|
64
|
+
internal_r = params['internal_r']
|
|
65
|
+
external_r = params['external_r']
|
|
66
|
+
return external_r - internal_r
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if __name__ == '__main__':
|
|
70
|
+
# Setup optimization method
|
|
71
|
+
opt = OptunaOptimizer(
|
|
72
|
+
sampler_class=BoTorchSampler,
|
|
73
|
+
sampler_kwargs=dict(
|
|
74
|
+
n_startup_trials=3, # The first three samples are randomly sampled.
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
femopt = FEMOpt(opt=opt)
|
|
78
|
+
|
|
79
|
+
# Add parameters
|
|
80
|
+
femopt.add_parameter("external_r", 10, lower_bound=0.1, upper_bound=10)
|
|
81
|
+
femopt.add_parameter("internal_r", 5, lower_bound=0.1, upper_bound=10)
|
|
82
|
+
|
|
83
|
+
# Add the strict constraint not to exceed the
|
|
84
|
+
# outer radius while the optimization is running.
|
|
85
|
+
femopt.add_constraint(
|
|
86
|
+
radius_diff, # Constraint function (returns external radius - internal radius).
|
|
87
|
+
name='wall thickness', # You can name the function anything you want.
|
|
88
|
+
lower_bound=1, # Lower bound of constraint function (set minimum wall thickness is 1).
|
|
89
|
+
args=(femopt.opt,) # Additional arguments passed to the function.
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Add the objective
|
|
93
|
+
femopt.add_objective(mises_stress, name='Mises Stress')
|
|
94
|
+
|
|
95
|
+
# Run optimization.
|
|
96
|
+
femopt.set_random_seed(42)
|
|
97
|
+
femopt.optimize(n_trials=10)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"",prm,prm_lb,prm_ub,prm,prm_lb,prm_ub,obj,obj_direction,,cns,cns_lb,cns_ub,,,,
|
|
2
|
+
,,,,,,,,,,,,,,,,
|
|
3
|
+
trial,external_r,external_r_lower_bound,external_r_upper_bound,internal_r,internal_r_lower_bound,internal_r_upper_bound,Mises Stress,Mises Stress_direction,non_domi,wall thickness,wall thickness_lower_bound,wall thickness_upper_bound,feasible,hypervolume,message,time
|
|
4
|
+
1,10.0,0.1,10.0,5.0,0.1,10.0,2369132.9299968677,minimize,False,5.0,1,,True,-1.0,initial,2024-09-17 14:52:32.436730
|
|
5
|
+
2,7.34674002393291,0.1,10.0,6.026718993550662,0.1,10.0,5797829.750527166,minimize,False,1.3200210303822484,1,,True,-1.0,,2024-09-17 14:52:42.927138
|
|
6
|
+
3,8.341182143924176,0.1,10.0,2.202157195714934,0.1,10.0,1977631.590419345,minimize,False,6.139024948209242,1,,True,-1.0,,2024-09-17 14:52:54.382893
|
|
7
|
+
4,9.999999999999995,0.1,10.0,0.6764719819158711,0.1,10.0,1514026.80705058,minimize,False,9.323528018084124,1,,True,-1.0,,2024-09-17 14:53:32.128331
|
|
8
|
+
5,7.61174007452904,0.1,10.0,0.10000000000001566,0.1,10.0,398224.5447358758,minimize,True,7.511740074529024,1,,True,-1.0,,2024-09-17 14:54:08.541865
|
|
9
|
+
6,5.026961232239628,0.1,10.0,0.10000000000001101,0.1,10.0,485322.32959281537,minimize,False,4.926961232239617,1,,True,-1.0,,2024-09-17 14:54:39.695340
|
|
10
|
+
7,1.1000000916874046,0.1,10.0,0.1,0.1,10.0,613872.6231008903,minimize,False,1.0000000916874046,1,,True,-1.0,,2024-09-17 14:55:07.711879
|
|
11
|
+
8,3.122874622702614,0.1,10.0,0.1,0.1,10.0,497544.0108231131,minimize,False,3.0228746227026138,1,,True,-1.0,,2024-09-17 14:55:41.036763
|
|
12
|
+
9,9.999999999999998,0.1,10.0,3.7062659141309533,0.1,10.0,2092780.7380693094,minimize,False,6.293734085869045,1,,True,-1.0,,2024-09-17 14:56:28.269065
|
|
13
|
+
10,6.601571760977393,0.1,10.0,0.10000000004171199,0.1,10.0,456263.04356135987,minimize,False,6.501571760935681,1,,True,-1.0,,2024-09-17 14:57:04.427906
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"
|
|
1
|
+
"",prm,prm_lb,prm_ub,obj,obj_direction,,,,,
|
|
2
2
|
,,,,,,,,,,
|
|
3
3
|
trial,rot,rot_lower_bound,rot_upper_bound,final angle (degree),final angle (degree)_direction,non_domi,feasible,hypervolume,message,time
|
|
4
4
|
1,90.0,80.0,100.0,81.62540666214329,90,False,True,-1.0,initial,2024-07-24 10:57:24.248429
|
|
@@ -80,7 +80,7 @@ class SParameterCalculator:
|
|
|
80
80
|
return self.resonance_frequency # unit: Hz
|
|
81
81
|
|
|
82
82
|
|
|
83
|
-
def antenna_is_smaller_than_substrate(Femtet):
|
|
83
|
+
def antenna_is_smaller_than_substrate(Femtet, opt):
|
|
84
84
|
"""Calculate the relationship between antenna size and board size.
|
|
85
85
|
|
|
86
86
|
This function is used to constrain the model
|
|
@@ -89,15 +89,17 @@ def antenna_is_smaller_than_substrate(Femtet):
|
|
|
89
89
|
Returns:
|
|
90
90
|
float: Difference between the substrate size and antenna size. Must be equal to or grater than 1 mm.
|
|
91
91
|
"""
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
params = opt.get_parameter()
|
|
93
|
+
r = params['antenna_radius']
|
|
94
|
+
w = params['substrate_w']
|
|
94
95
|
return w / 2 - r # unit: mm
|
|
95
96
|
|
|
96
97
|
|
|
97
|
-
def port_is_inside_antenna(Femtet):
|
|
98
|
+
def port_is_inside_antenna(Femtet, opt):
|
|
98
99
|
"""Calculate the relationship between the feed port location and antenna size."""
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
params = opt.get_parameter()
|
|
101
|
+
r = params['antenna_radius']
|
|
102
|
+
x = params['port_x']
|
|
101
103
|
return r - x # unit: mm. Must be equal to or grater than 1 mm.
|
|
102
104
|
|
|
103
105
|
|
|
@@ -125,8 +127,8 @@ if __name__ == '__main__':
|
|
|
125
127
|
femopt.add_parameter('port_x', 5, 1, 20)
|
|
126
128
|
|
|
127
129
|
# Add the constraint function to the optimization problem.
|
|
128
|
-
femopt.add_constraint(antenna_is_smaller_than_substrate, 'antenna and substrate clearance', lower_bound=1)
|
|
129
|
-
femopt.add_constraint(port_is_inside_antenna, 'antenna and port clearance', lower_bound=1)
|
|
130
|
+
femopt.add_constraint(antenna_is_smaller_than_substrate, 'antenna and substrate clearance', lower_bound=1, args=(opt,))
|
|
131
|
+
femopt.add_constraint(port_is_inside_antenna, 'antenna and port clearance', lower_bound=1, args=(opt,))
|
|
130
132
|
|
|
131
133
|
# Add the objective function to the optimization problem.
|
|
132
134
|
# The target frequency is 3.0 GHz.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"{""femprj_path"": ""E:\\pyfemtet\\pyfemtet\\pyfemtet\\opt\\femprj_sample\\her_ex40_parametric.femprj"", ""model_name"": ""Harm""}",prm,prm_lb,prm_ub,prm,prm_lb,prm_ub,prm,prm_lb,prm_ub,obj,obj_direction,,cns,cns_lb,cns_ub,cns,cns_lb,cns_ub,,,,
|
|
2
|
+
,,,,,,,,,,,,,,,,,,,,,,
|
|
3
|
+
trial,antenna_radius,antenna_radius_lower_bound,antenna_radius_upper_bound,substrate_w,substrate_w_lower_bound,substrate_w_upper_bound,port_x,port_x_lower_bound,port_x_upper_bound,first resonant frequency(Hz),first resonant frequency(Hz)_direction,non_domi,antenna and substrate clearance,antenna and substrate clearance_lower_bound,antenna and substrate clearance_upper_bound,antenna and port clearance,antenna and port clearance_lower_bound,antenna and port clearance_upper_bound,feasible,hypervolume,message,time
|
|
4
|
+
1,10.0,5.0,20.0,50.0,40.0,60.0,5.0,1.0,20.0,4289000000.0,3000000000.0,False,15.0,1,,5.0,1,,True,-1.0,initial,2024-09-11 15:22:45.703728
|
|
5
|
+
2,13.979877262955549,5.0,20.0,43.12037280884873,40.0,60.0,3.96389588638785,1.0,20.0,3050000000.0,3000000000.0,False,7.580309141468815,1,,10.015981376567698,1,,True,-1.0,,2024-09-11 15:23:00.502240
|
|
6
|
+
3,17.48663961200633,5.0,20.0,44.246782213565524,40.0,60.0,4.4546743769349115,1.0,20.0,2460000000.0,3000000000.0,False,4.6367514947764334,1,,13.031965235071418,1,,True,-1.0,,2024-09-11 15:23:19.497216
|
|
7
|
+
4,11.841049763255539,5.0,20.0,55.70351922786027,40.0,60.0,4.793801861008835,1.0,20.0,3581000000.0,3000000000.0,False,16.010709850674594,1,,7.047247902246704,1,,True,-1.0,,2024-09-11 15:23:38.392090
|
|
8
|
+
5,12.713516576204174,5.0,20.0,51.84829137724085,40.0,60.0,1.8825578416799567,1.0,20.0,2932000000.0,3000000000.0,False,13.21062911241625,1,,10.830958734524218,1,,True,-1.0,,2024-09-11 15:23:50.146083
|
|
9
|
+
6,14.113172778521575,5.0,20.0,43.41048247374583,40.0,60.0,2.235980266720311,1.0,20.0,2991000000.0,3000000000.0,True,7.59206845835134,1,,11.877192511801264,1,,True,-1.0,,2024-09-11 15:24:04.616007
|
|
10
|
+
7,19.233283058799998,5.0,20.0,59.31264066149119,40.0,60.0,16.35954961421276,1.0,20.0,2283000000.0,3000000000.0,False,10.423037271945596,1,,2.873733444587238,1,,True,-1.0,,2024-09-11 15:24:33.762831
|
|
11
|
+
8,11.60228740609402,5.0,20.0,42.44076469689558,40.0,60.0,10.408361292114133,1.0,20.0,3758000000.0,3000000000.0,False,9.61809494235377,1,,1.1939261139798862,1,,True,-1.0,,2024-09-11 15:24:53.700769
|
|
12
|
+
9,14.93783426530973,5.0,20.0,46.23422152178822,40.0,60.0,10.881292402378406,1.0,20.0,2932000000.0,3000000000.0,False,8.17927649558438,1,,4.056541862931324,1,,True,-1.0,,2024-09-11 15:25:17.611571
|
|
13
|
+
10,13.968499682166277,5.0,20.0,58.437484700462335,40.0,60.0,2.6813575389864703,1.0,20.0,2991000000.0,3000000000.0,True,15.25024266806489,1,,11.287142143179807,1,,True,-1.0,,2024-09-11 15:25:33.276468
|
|
14
|
+
11,14.05789863217095,5.0,20.0,51.01059902831617,40.0,60.0,1.737571530783426,1.0,20.0,2991000000.0,3000000000.0,True,11.447400881987136,1,,12.320327101387523,1,,True,-1.0,,2024-09-11 15:26:05.718398
|
|
15
|
+
12,14.201356537883626,5.0,20.0,52.843533197525375,40.0,60.0,7.413439727286647,1.0,20.0,3050000000.0,3000000000.0,False,12.220410060879061,1,,6.787916810596979,1,,True,-1.0,,2024-09-11 15:26:37.472938
|
|
16
|
+
13,13.425463212064672,5.0,20.0,52.02680338112066,40.0,60.0,1.0,1.0,20.0,3109000000.0,3000000000.0,False,12.587938478495657,1,,12.425463212064672,1,,True,-1.0,,2024-09-11 15:27:05.737378
|
|
17
|
+
14,14.859313853083268,5.0,20.0,48.76719051860414,40.0,60.0,4.518838210753849,1.0,20.0,2873000000.0,3000000000.0,False,9.524281406218801,1,,10.34047564232942,1,,True,-1.0,,2024-09-11 15:27:37.696689
|
|
18
|
+
15,13.72477890084936,5.0,20.0,49.21346368364997,40.0,60.0,4.707562522313161,1.0,20.0,3050000000.0,3000000000.0,False,10.881952940975625,1,,9.0172163785362,1,,True,-1.0,,2024-09-11 15:28:09.261355
|