pyfemtet 0.7.1__py3-none-any.whl → 0.8.0__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 +112 -90
- pyfemtet/_message/locales/messages.pot +105 -89
- pyfemtet/_message/messages.py +6 -2
- pyfemtet/_util/excel_parse_util.py +138 -0
- pyfemtet/_util/sample.xlsx +0 -0
- pyfemtet/brep/__init__.py +0 -3
- pyfemtet/brep/_impl.py +7 -3
- pyfemtet/opt/_femopt.py +42 -14
- pyfemtet/opt/_femopt_core.py +93 -34
- pyfemtet/opt/advanced_samples/excel_ui/(ref) original_project.femprj +0 -0
- pyfemtet/opt/advanced_samples/excel_ui/femtet-macro.xlsm +0 -0
- pyfemtet/opt/advanced_samples/excel_ui/pyfemtet-core.py +291 -0
- pyfemtet/opt/advanced_samples/excel_ui/test-pyfemtet-core.cmd +22 -0
- pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_create_training_data.py +60 -0
- pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_create_training_data_jp.py +57 -0
- pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_optimize_with_surrogate.py +100 -0
- pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_optimize_with_surrogate_jp.py +90 -0
- pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_parametric.femprj +0 -0
- pyfemtet/opt/interface/__init__.py +2 -0
- pyfemtet/opt/interface/_base.py +3 -0
- pyfemtet/opt/interface/_excel_interface.py +296 -124
- pyfemtet/opt/interface/_femtet.py +19 -9
- pyfemtet/opt/interface/_surrogate/__init__.py +5 -0
- pyfemtet/opt/interface/_surrogate/_base.py +85 -0
- pyfemtet/opt/interface/_surrogate/_chaospy.py +71 -0
- pyfemtet/opt/interface/_surrogate/_singletaskgp.py +70 -0
- pyfemtet/opt/optimizer/_base.py +28 -18
- pyfemtet/opt/optimizer/_optuna/_optuna.py +20 -8
- pyfemtet/opt/optimizer/_optuna/_pof_botorch.py +60 -18
- pyfemtet/opt/prediction/_base.py +8 -0
- pyfemtet/opt/prediction/single_task_gp.py +85 -62
- pyfemtet/opt/visualization/_complex_components/main_figure_creator.py +5 -5
- pyfemtet/opt/visualization/_complex_components/main_graph.py +7 -1
- pyfemtet/opt/visualization/_complex_components/pm_graph.py +1 -1
- pyfemtet/opt/visualization/_process_monitor/application.py +2 -2
- pyfemtet/opt/visualization/_process_monitor/pages.py +1 -1
- pyfemtet/opt/visualization/result_viewer/pages.py +1 -1
- {pyfemtet-0.7.1.dist-info → pyfemtet-0.8.0.dist-info}/METADATA +2 -2
- {pyfemtet-0.7.1.dist-info → pyfemtet-0.8.0.dist-info}/RECORD +44 -28
- {pyfemtet-0.7.1.dist-info → pyfemtet-0.8.0.dist-info}/WHEEL +1 -1
- {pyfemtet-0.7.1.dist-info → pyfemtet-0.8.0.dist-info}/LICENSE +0 -0
- {pyfemtet-0.7.1.dist-info → pyfemtet-0.8.0.dist-info}/entry_points.txt +0 -0
pyfemtet/opt/_femopt.py
CHANGED
|
@@ -165,6 +165,7 @@ class FEMOpt:
|
|
|
165
165
|
step: float = None,
|
|
166
166
|
properties: dict[str, str or float] = None,
|
|
167
167
|
pass_to_fem: bool = True,
|
|
168
|
+
fix: bool = False,
|
|
168
169
|
):
|
|
169
170
|
# noinspection PyUnresolvedReferences
|
|
170
171
|
"""Adds a parameter to the optimization problem.
|
|
@@ -177,6 +178,15 @@ class FEMOpt:
|
|
|
177
178
|
step (float, optional): The step of parameter. If specified, parameter is used as discrete. Defaults to None.
|
|
178
179
|
properties (dict[str, str or float], optional): Additional information about the parameter. Defaults to None.
|
|
179
180
|
pass_to_fem (bool, optional): If this variable is used directly in FEM model update or not. If False, this parameter can be just used as inpt of expressions. Defaults to True.
|
|
181
|
+
fix (bool, optiona):
|
|
182
|
+
パラメータを initial_value で固定します。
|
|
183
|
+
開発時にパラメータを振るか振らないかを
|
|
184
|
+
簡単に変更するための便利引数です。
|
|
185
|
+
True のとき、lower_bound, upper_bound, step, properties の
|
|
186
|
+
値は、有効かどうかのチェックには使われますが、最適化では
|
|
187
|
+
使われなくなります。
|
|
188
|
+
デフォルトは False です。
|
|
189
|
+
|
|
180
190
|
|
|
181
191
|
Raises:
|
|
182
192
|
ValueError: If initial_value is not specified and the value for the given name is also not specified in FEM.
|
|
@@ -209,16 +219,27 @@ class FEMOpt:
|
|
|
209
219
|
if initial_value is None:
|
|
210
220
|
raise ValueError('initial_value を指定してください.')
|
|
211
221
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
+
if not fix:
|
|
223
|
+
prm = Parameter(
|
|
224
|
+
name=name,
|
|
225
|
+
value=float(initial_value),
|
|
226
|
+
lower_bound=float(lower_bound) if lower_bound is not None else None,
|
|
227
|
+
upper_bound=float(upper_bound) if upper_bound is not None else None,
|
|
228
|
+
step=float(step) if step is not None else None,
|
|
229
|
+
pass_to_fem=pass_to_fem,
|
|
230
|
+
properties=properties,
|
|
231
|
+
)
|
|
232
|
+
self.opt.variables.add_parameter(prm)
|
|
233
|
+
|
|
234
|
+
else:
|
|
235
|
+
warnings.filterwarnings('ignore', category=UserWarning, message="The function 'add_expression' is experimental")
|
|
236
|
+
self.add_expression(
|
|
237
|
+
name=name,
|
|
238
|
+
fun=lambda: initial_value,
|
|
239
|
+
pass_to_fem=pass_to_fem,
|
|
240
|
+
properties=properties,
|
|
241
|
+
)
|
|
242
|
+
|
|
222
243
|
|
|
223
244
|
@experimental_feature
|
|
224
245
|
def add_expression(
|
|
@@ -299,7 +320,7 @@ class FEMOpt:
|
|
|
299
320
|
|
|
300
321
|
def add_objective(
|
|
301
322
|
self,
|
|
302
|
-
fun,
|
|
323
|
+
fun: callable or None = None,
|
|
303
324
|
name: str or None = None,
|
|
304
325
|
direction: str or float = 'minimize',
|
|
305
326
|
args: tuple or None = None,
|
|
@@ -309,7 +330,7 @@ class FEMOpt:
|
|
|
309
330
|
"""Adds an objective to the optimization problem.
|
|
310
331
|
|
|
311
332
|
Args:
|
|
312
|
-
fun (callable): The objective function.
|
|
333
|
+
fun (callable or None, optional): The objective function. This argument is optional but
|
|
313
334
|
name (str or None, optional): The name of the objective. Defaults to None.
|
|
314
335
|
direction (str or float, optional): The optimization direction. Varid values are 'maximize', 'minimize' or a float value. Defaults to 'minimize'.
|
|
315
336
|
args (tuple or None, optional): Additional arguments for the objective function. Defaults to None.
|
|
@@ -344,6 +365,10 @@ class FEMOpt:
|
|
|
344
365
|
"""
|
|
345
366
|
|
|
346
367
|
# 引数の処理
|
|
368
|
+
if fun is None:
|
|
369
|
+
from pyfemtet.opt.interface._surrogate._base import SurrogateModelInterfaceBase
|
|
370
|
+
if not isinstance(self.fem, SurrogateModelInterfaceBase):
|
|
371
|
+
raise ValueError('`fun` argument is not specified.')
|
|
347
372
|
if args is None:
|
|
348
373
|
args = tuple()
|
|
349
374
|
elif not isinstance(args, tuple):
|
|
@@ -403,7 +428,6 @@ class FEMOpt:
|
|
|
403
428
|
kwargs=kwargs,
|
|
404
429
|
)
|
|
405
430
|
|
|
406
|
-
|
|
407
431
|
def add_constraint(
|
|
408
432
|
self,
|
|
409
433
|
fun,
|
|
@@ -612,6 +636,7 @@ class FEMOpt:
|
|
|
612
636
|
if self.fem._load_problem_from_me:
|
|
613
637
|
self.fem.load_parameter(self.opt)
|
|
614
638
|
self.fem.load_objective(self.opt)
|
|
639
|
+
self.fem.load_constraint(self.opt)
|
|
615
640
|
|
|
616
641
|
# resolve expression dependencies
|
|
617
642
|
self.opt.variables.resolve()
|
|
@@ -892,7 +917,10 @@ class FEMOpt:
|
|
|
892
917
|
print('='*len(Msg.CONFIRM_BEFORE_EXIT))
|
|
893
918
|
input()
|
|
894
919
|
|
|
895
|
-
|
|
920
|
+
df = self.history.get_df() # with 文を抜けると actor は消えるが .copy() はこの段階では不要
|
|
921
|
+
|
|
922
|
+
return df
|
|
923
|
+
|
|
896
924
|
|
|
897
925
|
@staticmethod
|
|
898
926
|
def terminate_all():
|
pyfemtet/opt/_femopt_core.py
CHANGED
|
@@ -262,6 +262,8 @@ def is_feasible(value, lb, ub):
|
|
|
262
262
|
Returns:
|
|
263
263
|
bool: True if the value satisfies the bounds; False otherwise.
|
|
264
264
|
"""
|
|
265
|
+
if np.isnan(value):
|
|
266
|
+
return False
|
|
265
267
|
if lb is None and ub is not None:
|
|
266
268
|
return value <= ub
|
|
267
269
|
elif lb is not None and ub is None:
|
|
@@ -290,10 +292,11 @@ class Function:
|
|
|
290
292
|
# COM 定数を一度 _Scapegoat 型のオブジェクトにする
|
|
291
293
|
# ParametricIF で使う dll 関数は _FuncPtr 型であって __globals__ を持たないが、
|
|
292
294
|
# これは絶対に constants を持たないので単に無視すればよい。
|
|
293
|
-
if not
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
fun.__globals__[varname]
|
|
295
|
+
if fun is not None:
|
|
296
|
+
if not isinstance(fun, ctypes._CFuncPtr):
|
|
297
|
+
for varname in fun.__globals__:
|
|
298
|
+
if isinstance(fun.__globals__[varname], Constants):
|
|
299
|
+
fun.__globals__[varname] = _Scapegoat()
|
|
297
300
|
|
|
298
301
|
self.fun = fun
|
|
299
302
|
self.name = name
|
|
@@ -309,6 +312,9 @@ class Function:
|
|
|
309
312
|
Returns:
|
|
310
313
|
float
|
|
311
314
|
"""
|
|
315
|
+
if self.fun is None:
|
|
316
|
+
RuntimeError(f'`fun` of {self.name} is not specified.')
|
|
317
|
+
|
|
312
318
|
args = self.args
|
|
313
319
|
# Femtet 特有の処理
|
|
314
320
|
if isinstance(fem, FemtetInterface):
|
|
@@ -318,11 +324,12 @@ class Function:
|
|
|
318
324
|
def _restore_constants(self):
|
|
319
325
|
"""Helper function for parallelize Femtet."""
|
|
320
326
|
fun = self.fun
|
|
321
|
-
if not
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
if
|
|
325
|
-
fun.__globals__[varname]
|
|
327
|
+
if fun is not None:
|
|
328
|
+
if not isinstance(fun, ctypes._CFuncPtr):
|
|
329
|
+
for varname in fun.__globals__:
|
|
330
|
+
if isinstance(fun.__globals__[varname], _Scapegoat):
|
|
331
|
+
if not fun.__globals__[varname]._ignore_when_restore_constants:
|
|
332
|
+
fun.__globals__[varname] = constants
|
|
326
333
|
|
|
327
334
|
|
|
328
335
|
class Objective(Function):
|
|
@@ -621,16 +628,30 @@ class History:
|
|
|
621
628
|
|
|
622
629
|
self.set_df(df)
|
|
623
630
|
|
|
624
|
-
def
|
|
631
|
+
def filter_valid(self, df_, keep_trial_num=False):
|
|
632
|
+
buff = df_[self.obj_names].notna()
|
|
633
|
+
idx = buff.prod(axis=1).astype(bool)
|
|
634
|
+
filtered_df = df_[idx]
|
|
635
|
+
if not keep_trial_num:
|
|
636
|
+
filtered_df.loc[:, 'trial'] = np.arange(len(filtered_df)) + 1
|
|
637
|
+
return filtered_df
|
|
638
|
+
|
|
639
|
+
def get_df(self, valid_only=False) -> pd.DataFrame:
|
|
625
640
|
if self.__scheduler_address is None:
|
|
626
|
-
|
|
641
|
+
if valid_only:
|
|
642
|
+
return self.filter_valid(self._df)
|
|
643
|
+
else:
|
|
644
|
+
return self._df
|
|
627
645
|
else:
|
|
628
646
|
# scheduler がまだ存命か確認する
|
|
629
647
|
try:
|
|
630
648
|
with Lock('access-df'):
|
|
631
649
|
client_: 'Client' = get_client(self.__scheduler_address)
|
|
632
650
|
if 'df' in client_.list_datasets():
|
|
633
|
-
|
|
651
|
+
if valid_only:
|
|
652
|
+
return self.filter_valid(client_.get_dataset('df'))
|
|
653
|
+
else:
|
|
654
|
+
return client_.get_dataset('df')
|
|
634
655
|
else:
|
|
635
656
|
logger.debug('Access df of History before it is initialized.')
|
|
636
657
|
return pd.DataFrame()
|
|
@@ -806,17 +827,19 @@ class History:
|
|
|
806
827
|
df['non_domi'] = False
|
|
807
828
|
|
|
808
829
|
# feasible のものに non_domi の評価結果を代入する
|
|
809
|
-
|
|
830
|
+
if len(non_domi) > 0:
|
|
831
|
+
df.loc[idx, 'non_domi'] = non_domi
|
|
810
832
|
|
|
811
833
|
def _calc_hypervolume(self, objectives, df):
|
|
812
834
|
|
|
835
|
+
# 単目的最適化ならば 0 埋めして終了
|
|
813
836
|
if len(objectives) < 2:
|
|
814
837
|
df.loc[len(df) - 1, 'hypervolume'] = 0.
|
|
815
838
|
return
|
|
816
839
|
|
|
817
840
|
# 最小化問題に変換された objective values を取得
|
|
818
841
|
raw_objective_values = df[self.obj_names].values
|
|
819
|
-
objective_values = np.
|
|
842
|
+
objective_values = np.full_like(raw_objective_values, np.nan)
|
|
820
843
|
for n_trial in range(len(raw_objective_values)):
|
|
821
844
|
for obj_idx, (_, objective) in enumerate(objectives.items()):
|
|
822
845
|
objective_values[n_trial, obj_idx] = objective.convert(raw_objective_values[n_trial, obj_idx])
|
|
@@ -830,13 +853,20 @@ class History:
|
|
|
830
853
|
pareto_set_ = np.empty((0, len(self.obj_names)))
|
|
831
854
|
for i in range(len(objective_values_)):
|
|
832
855
|
target = objective_values_[i]
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
856
|
+
|
|
857
|
+
if any(np.isnan(target)):
|
|
858
|
+
# infeasible な場合 pareto_set の計算に含めない
|
|
859
|
+
dominated = True
|
|
860
|
+
|
|
861
|
+
else:
|
|
862
|
+
dominated = False
|
|
863
|
+
# TODO: Array の計算に直して高速化する
|
|
864
|
+
for j in range(len(objective_values_)):
|
|
865
|
+
compare = objective_values_[j]
|
|
866
|
+
if all(target > compare):
|
|
867
|
+
dominated = True
|
|
868
|
+
break
|
|
869
|
+
|
|
840
870
|
if not dominated:
|
|
841
871
|
pareto_set_ = np.concatenate([pareto_set_, [target]], axis=0)
|
|
842
872
|
|
|
@@ -848,34 +878,63 @@ class History:
|
|
|
848
878
|
else:
|
|
849
879
|
return pareto_set_
|
|
850
880
|
|
|
881
|
+
def get_valid_worst_converted_objective_values(objective_values_: np.ndarray) -> np.ndarray:
|
|
882
|
+
# objective_values.max(axis=0)
|
|
883
|
+
ret = []
|
|
884
|
+
for row in objective_values_:
|
|
885
|
+
if not any(np.isnan(row)):
|
|
886
|
+
ret.append(row)
|
|
887
|
+
return np.array(ret).max(axis=0)
|
|
888
|
+
|
|
851
889
|
if self._hv_reference == 'dynamic-pareto':
|
|
852
890
|
pareto_set, pareto_set_list = get_pareto(objective_values, with_partial=True)
|
|
853
891
|
for i, partial_pareto_set in enumerate(pareto_set_list):
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
892
|
+
# 並列計算時など Valid な解がまだ一つもない場合は pareto_set が長さ 0 になる
|
|
893
|
+
# その場合 max() を取るとエラーになる
|
|
894
|
+
if len(pareto_set) == 0:
|
|
895
|
+
df.loc[i, 'hypervolume'] = 0
|
|
896
|
+
else:
|
|
897
|
+
ref_point = pareto_set.max(axis=0) + 1e-8
|
|
898
|
+
hv = compute_hypervolume(partial_pareto_set, ref_point)
|
|
899
|
+
df.loc[i, 'hypervolume'] = hv
|
|
857
900
|
return
|
|
858
901
|
|
|
859
902
|
elif self._hv_reference == 'dynamic-nadir':
|
|
860
903
|
_, pareto_set_list = get_pareto(objective_values, with_partial=True)
|
|
861
904
|
for i, partial_pareto_set in enumerate(pareto_set_list):
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
905
|
+
# filter valid objective values only
|
|
906
|
+
values = get_valid_worst_converted_objective_values(objective_values)
|
|
907
|
+
|
|
908
|
+
# 並列計算時など Valid な解がまだ一つもない場合は長さ 0 になる
|
|
909
|
+
# その場合 max() を取るとエラーになる
|
|
910
|
+
if len(values) == 0:
|
|
911
|
+
df.loc[i, 'hypervolume'] = 0
|
|
912
|
+
|
|
913
|
+
else:
|
|
914
|
+
ref_point = values.max(axis=0) + 1e-8
|
|
915
|
+
hv = compute_hypervolume(partial_pareto_set, ref_point)
|
|
916
|
+
df.loc[i, 'hypervolume'] = hv
|
|
865
917
|
return
|
|
866
918
|
|
|
867
919
|
elif self._hv_reference == 'nadir':
|
|
868
920
|
pareto_set = get_pareto(objective_values)
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
921
|
+
values = get_valid_worst_converted_objective_values(objective_values)
|
|
922
|
+
if len(values) == 0:
|
|
923
|
+
df.loc[len(df) - 1, 'hypervolume'] = 0
|
|
924
|
+
else:
|
|
925
|
+
ref_point = values.max(axis=0) + 1e-8
|
|
926
|
+
hv = compute_hypervolume(pareto_set, ref_point)
|
|
927
|
+
df.loc[len(df) - 1, 'hypervolume'] = hv
|
|
872
928
|
return
|
|
873
929
|
|
|
874
930
|
elif self._hv_reference == 'pareto':
|
|
875
931
|
pareto_set = get_pareto(objective_values)
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
932
|
+
if len(pareto_set) == 0:
|
|
933
|
+
df.loc[len(df) - 1, 'hypervolume'] = 0
|
|
934
|
+
else:
|
|
935
|
+
ref_point = pareto_set.max(axis=0) + 1e-8
|
|
936
|
+
hv = compute_hypervolume(pareto_set, ref_point)
|
|
937
|
+
df.loc[len(df) - 1, 'hypervolume'] = hv
|
|
879
938
|
return
|
|
880
939
|
|
|
881
940
|
elif (
|
|
@@ -935,7 +994,7 @@ class History:
|
|
|
935
994
|
study = optuna.create_study(**kwargs)
|
|
936
995
|
|
|
937
996
|
# add trial to study
|
|
938
|
-
df: pd.DataFrame = self.get_df()
|
|
997
|
+
df: pd.DataFrame = self.get_df(valid_only=True)
|
|
939
998
|
for i, row in df.iterrows():
|
|
940
999
|
FD = optuna.distributions.FloatDistribution
|
|
941
1000
|
kwargs = dict(
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"""同梱する femtet-macro.xlsm から pyfemtet を呼び出す際の pyfemtet スクリプト。"""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
from fire import Fire
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_sampler_class(sampling_method):
|
|
10
|
+
if sampling_method is None:
|
|
11
|
+
# default
|
|
12
|
+
from pyfemtet.opt.optimizer import PoFBoTorchSampler
|
|
13
|
+
return PoFBoTorchSampler
|
|
14
|
+
elif sampling_method == 'QMC':
|
|
15
|
+
from optuna.samplers import QMCSampler
|
|
16
|
+
return QMCSampler
|
|
17
|
+
elif sampling_method == 'PoFBoTorch':
|
|
18
|
+
from pyfemtet.opt.optimizer import PoFBoTorchSampler
|
|
19
|
+
return PoFBoTorchSampler
|
|
20
|
+
elif sampling_method == 'Random':
|
|
21
|
+
from optuna.samplers import RandomSampler
|
|
22
|
+
return RandomSampler
|
|
23
|
+
elif sampling_method == 'NSGA2':
|
|
24
|
+
from optuna.samplers import NSGAIISampler
|
|
25
|
+
return NSGAIISampler
|
|
26
|
+
elif sampling_method == 'TPE':
|
|
27
|
+
from optuna.samplers import TPESampler
|
|
28
|
+
return TPESampler
|
|
29
|
+
elif sampling_method == 'BoTorch':
|
|
30
|
+
from optuna_integration import BoTorchSampler
|
|
31
|
+
return BoTorchSampler
|
|
32
|
+
else:
|
|
33
|
+
raise NotImplementedError(f'The method {sampling_method} is not implemented.')
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def core(
|
|
37
|
+
xlsm_path: str,
|
|
38
|
+
csv_path: str or None,
|
|
39
|
+
femprj_path: str or None, # xlsm と同じフォルダに配置する前提。
|
|
40
|
+
model_name: str or None,
|
|
41
|
+
input_sheet_name: str,
|
|
42
|
+
|
|
43
|
+
output_sheet_name: str or None,
|
|
44
|
+
constraint_sheet_name: str or None,
|
|
45
|
+
procedure_name: str or None, # 引数に拡張子を除く femprj ファイル名を取るように実装すること
|
|
46
|
+
setup_procedure_name: str or None,
|
|
47
|
+
teardown_procedure_name: str or None,
|
|
48
|
+
|
|
49
|
+
sampler_class: type('BaseSampler') or None,
|
|
50
|
+
sampler_kwargs: dict or None,
|
|
51
|
+
|
|
52
|
+
n_parallel: int,
|
|
53
|
+
n_trials: int or None,
|
|
54
|
+
timeout: float or None,
|
|
55
|
+
seed: int or None,
|
|
56
|
+
):
|
|
57
|
+
from pathlib import Path
|
|
58
|
+
from pyfemtet.opt import FEMOpt, OptunaOptimizer
|
|
59
|
+
from pyfemtet.opt.interface._excel_interface import ExcelInterface
|
|
60
|
+
|
|
61
|
+
procedure_args = []
|
|
62
|
+
related_file_paths = []
|
|
63
|
+
if femprj_path is not None:
|
|
64
|
+
prj_name = os.path.basename(femprj_path).removesuffix('.femprj')
|
|
65
|
+
procedure_args.append(prj_name)
|
|
66
|
+
related_file_paths = [Path(femprj_path)]
|
|
67
|
+
if model_name is not None:
|
|
68
|
+
procedure_args.append(model_name)
|
|
69
|
+
if femprj_path is None and model_name is not None:
|
|
70
|
+
raise NotImplementedError
|
|
71
|
+
|
|
72
|
+
fem = ExcelInterface(
|
|
73
|
+
input_xlsm_path=xlsm_path,
|
|
74
|
+
input_sheet_name=input_sheet_name,
|
|
75
|
+
output_xlsm_path=None,
|
|
76
|
+
output_sheet_name=output_sheet_name,
|
|
77
|
+
constraint_xlsm_path=None,
|
|
78
|
+
constraint_sheet_name=constraint_sheet_name,
|
|
79
|
+
procedure_name=procedure_name,
|
|
80
|
+
procedure_args=procedure_args,
|
|
81
|
+
connect_method='new',
|
|
82
|
+
setup_procedure_name=setup_procedure_name,
|
|
83
|
+
teardown_procedure_name=teardown_procedure_name,
|
|
84
|
+
related_file_paths=related_file_paths,
|
|
85
|
+
visible=False,
|
|
86
|
+
interactive=True,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
opt = OptunaOptimizer(
|
|
90
|
+
sampler_class=sampler_class,
|
|
91
|
+
sampler_kwargs=sampler_kwargs,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
femopt = FEMOpt(
|
|
95
|
+
fem=fem,
|
|
96
|
+
opt=opt,
|
|
97
|
+
history_path=csv_path,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if seed is not None:
|
|
101
|
+
femopt.set_random_seed(42)
|
|
102
|
+
|
|
103
|
+
femopt.optimize(
|
|
104
|
+
n_trials=n_trials,
|
|
105
|
+
n_parallel=n_parallel,
|
|
106
|
+
timeout=timeout,
|
|
107
|
+
confirm_before_exit=True,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def main(
|
|
112
|
+
# これらは Fire キーワード引数指定できるように None を与えているが必須
|
|
113
|
+
xlsm_path: str = None,
|
|
114
|
+
input_sheet_name: str = None,
|
|
115
|
+
n_parallel: int = 1,
|
|
116
|
+
output_sheet_name: str or None = None,
|
|
117
|
+
|
|
118
|
+
femprj_path: str or None = None, # 指定する場合は xlsm と同じフォルダに配置する前提にすること
|
|
119
|
+
model_name: str = None,
|
|
120
|
+
csv_path: str or None = None,
|
|
121
|
+
constraint_sheet_name: str or None = None,
|
|
122
|
+
procedure_name: str or None = None, # 引数に拡張子を除く femprj ファイル名を取るように実装すること
|
|
123
|
+
setup_procedure_name: str or None = None,
|
|
124
|
+
teardown_procedure_name: str or None = None,
|
|
125
|
+
|
|
126
|
+
algorithm: str or None = None,
|
|
127
|
+
|
|
128
|
+
n_trials: int or None = None,
|
|
129
|
+
timeout: float or None = None,
|
|
130
|
+
seed: int or None = None,
|
|
131
|
+
|
|
132
|
+
**algorithm_settings: dict,
|
|
133
|
+
|
|
134
|
+
):
|
|
135
|
+
import sys
|
|
136
|
+
import inspect
|
|
137
|
+
from pyfemtet.logger import get_module_logger
|
|
138
|
+
|
|
139
|
+
# ----- Fire memo -----
|
|
140
|
+
# print(csv_path) # 与えなければ None
|
|
141
|
+
# print(algorithm_settings) # 与えなければ {}, 与えれば {'n_startup_trials': 10} など
|
|
142
|
+
# print(n_parallel, type(n_parallel)) # int か float に自動変換される
|
|
143
|
+
# print(timeout, type(timeout)) # int か float に自動変換される
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# ----- check 必須 args -----
|
|
147
|
+
logger = get_module_logger('opt.script', __name__)
|
|
148
|
+
|
|
149
|
+
os.chdir(os.path.dirname(__file__))
|
|
150
|
+
|
|
151
|
+
if xlsm_path is None:
|
|
152
|
+
logger.error(f'xlsm_path を指定してください。')
|
|
153
|
+
input('終了するには Enter を押してください。')
|
|
154
|
+
sys.exit(1)
|
|
155
|
+
|
|
156
|
+
if input_sheet_name is None:
|
|
157
|
+
logger.error(f'input_sheet_name を指定してください。')
|
|
158
|
+
input('終了するには Enter を押してください。')
|
|
159
|
+
sys.exit(1)
|
|
160
|
+
|
|
161
|
+
if output_sheet_name is None:
|
|
162
|
+
logger.error(f'output_sheet_name を指定してください。')
|
|
163
|
+
input('終了するには Enter を押してください。')
|
|
164
|
+
sys.exit(1)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# ----- check args -----
|
|
168
|
+
logger.info(f'{os.path.basename(__file__)} は {os.path.basename(xlsm_path)} に呼び出されました.')
|
|
169
|
+
|
|
170
|
+
# xlsm_path
|
|
171
|
+
xlsm_path = os.path.abspath(xlsm_path)
|
|
172
|
+
if not os.path.exists(xlsm_path):
|
|
173
|
+
logger.error(f'{xlsm_path} が見つかりませんでした。')
|
|
174
|
+
input('終了するには Enter を押してください。')
|
|
175
|
+
sys.exit(1)
|
|
176
|
+
|
|
177
|
+
# femprj_path
|
|
178
|
+
if femprj_path is not None:
|
|
179
|
+
femprj_path = os.path.abspath(femprj_path)
|
|
180
|
+
if not os.path.exists(femprj_path):
|
|
181
|
+
logger.error(f'{femprj_path} が見つかりませんでした。')
|
|
182
|
+
input('終了するには Enter を押してください。')
|
|
183
|
+
sys.exit(1)
|
|
184
|
+
|
|
185
|
+
# model_name
|
|
186
|
+
if model_name is not None and femprj_path is None:
|
|
187
|
+
logger.error(f'model_name ({model_name}) を指定する場合は femprj_path も指定してください。')
|
|
188
|
+
input('終了するには Enter を押してください。')
|
|
189
|
+
sys.exit(1)
|
|
190
|
+
|
|
191
|
+
# n_parallel
|
|
192
|
+
try:
|
|
193
|
+
n_parallel = int(n_parallel)
|
|
194
|
+
except ValueError:
|
|
195
|
+
logger.error(f'n_parallel ({n_parallel}) は自然数にできません。')
|
|
196
|
+
input('終了するには Enter を押してください。')
|
|
197
|
+
sys.exit(1)
|
|
198
|
+
|
|
199
|
+
# csv_path
|
|
200
|
+
csv_path = os.path.abspath(csv_path) if csv_path is not None else csv_path
|
|
201
|
+
|
|
202
|
+
# n_trials
|
|
203
|
+
if n_trials is not None:
|
|
204
|
+
try:
|
|
205
|
+
n_trials = int(n_trials)
|
|
206
|
+
except ValueError:
|
|
207
|
+
logger.error(f'n_trials ({n_trials}) は自然数にできません。')
|
|
208
|
+
input('終了するには Enter を押してください。')
|
|
209
|
+
sys.exit(1)
|
|
210
|
+
|
|
211
|
+
# timeout
|
|
212
|
+
if timeout is not None:
|
|
213
|
+
try:
|
|
214
|
+
timeout = float(timeout)
|
|
215
|
+
except ValueError:
|
|
216
|
+
logger.error(f'timeout ({timeout}) は数値にできません。')
|
|
217
|
+
input('終了するには Enter を押してください。')
|
|
218
|
+
sys.exit(1)
|
|
219
|
+
|
|
220
|
+
# seed
|
|
221
|
+
if seed is not None:
|
|
222
|
+
try:
|
|
223
|
+
seed = int(seed)
|
|
224
|
+
except ValueError:
|
|
225
|
+
logger.error(f'seed ({seed}) は自然数にできません。')
|
|
226
|
+
input('終了するには Enter を押してください。')
|
|
227
|
+
sys.exit(1)
|
|
228
|
+
|
|
229
|
+
# sampler
|
|
230
|
+
try:
|
|
231
|
+
sampler_class = get_sampler_class(algorithm)
|
|
232
|
+
except NotImplementedError:
|
|
233
|
+
logger.error(f'algorithm ({algorithm}) は非対応です。')
|
|
234
|
+
input('終了するには Enter を押してください。')
|
|
235
|
+
sys.exit(1)
|
|
236
|
+
|
|
237
|
+
# sampler_kwargs
|
|
238
|
+
sampler_kwargs = algorithm_settings
|
|
239
|
+
# noinspection PyUnboundLocalVariable
|
|
240
|
+
available_sampler_kwarg_keys = inspect.signature(sampler_class).parameters.keys()
|
|
241
|
+
for given_key in sampler_kwargs.keys():
|
|
242
|
+
if given_key not in available_sampler_kwarg_keys:
|
|
243
|
+
print()
|
|
244
|
+
print(sampler_class.__doc__)
|
|
245
|
+
print()
|
|
246
|
+
logger.error(f'algorithm_setting の項目 ({given_key}) は {sampler_class.__name__} に設定できません。詳しくは上記のドキュメントをご覧ください。')
|
|
247
|
+
input('終了するには Enter を押してください。')
|
|
248
|
+
sys.exit(1)
|
|
249
|
+
|
|
250
|
+
logger.info('引数の整合性チェックが終了しました。最適化を実行します。しばらくお待ちください...')
|
|
251
|
+
|
|
252
|
+
core(
|
|
253
|
+
xlsm_path,
|
|
254
|
+
csv_path,
|
|
255
|
+
femprj_path, # xlsm と同じフォルダに配置する前提。
|
|
256
|
+
model_name,
|
|
257
|
+
input_sheet_name,
|
|
258
|
+
|
|
259
|
+
output_sheet_name,
|
|
260
|
+
constraint_sheet_name,
|
|
261
|
+
procedure_name, # 引数に拡張子を除く femprj ファイル名を取るように実装すること
|
|
262
|
+
setup_procedure_name,
|
|
263
|
+
teardown_procedure_name,
|
|
264
|
+
|
|
265
|
+
sampler_class,
|
|
266
|
+
sampler_kwargs,
|
|
267
|
+
|
|
268
|
+
n_parallel,
|
|
269
|
+
n_trials,
|
|
270
|
+
timeout,
|
|
271
|
+
seed,
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
if __name__ == '__main__':
|
|
276
|
+
Fire(main)
|
|
277
|
+
|
|
278
|
+
# ===== Debug Code =====
|
|
279
|
+
# import os
|
|
280
|
+
# os.chdir(os.path.dirname(__file__))
|
|
281
|
+
# main(
|
|
282
|
+
# xlsm_path='インターフェース.xlsm',
|
|
283
|
+
# input_sheet_name='設計変数',
|
|
284
|
+
# n_parallel=1,
|
|
285
|
+
# output_sheet_name='目的関数',
|
|
286
|
+
# constraint_sheet_name='拘束関数',
|
|
287
|
+
# procedure_name='FemtetMacro.FemtetMain',
|
|
288
|
+
# setup_procedure_name='setup',
|
|
289
|
+
# teardown_procedure_name='teardown',
|
|
290
|
+
# n_trials=3,
|
|
291
|
+
# )
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
cd %~dp0
|
|
2
|
+
rem poetry run python pyfemtet-core.py --help
|
|
3
|
+
rem poetry run python pyfemtet-core.py "xlsm" "femprj" "input_sheet" 3
|
|
4
|
+
rem poetry run python pyfemtet-core.py "xlsm" "femprj" "input_sheet" 3 --n_startup_trials=10 --timeout=3.14
|
|
5
|
+
rem poetry run python pyfemtet-core.py "xlsm" "femprj" "input_sheet" 3 --n_startup_trials=10 --timeout=5
|
|
6
|
+
rem poetry run python pyfemtet-core.py "xlsm" "femprj" "input_sheet" 3.14 --n_startup_trials=10 --timeout=5
|
|
7
|
+
poetry run python pyfemtet-core.py ^
|
|
8
|
+
�C���^�[�t�F�[�X.xlsm ^
|
|
9
|
+
--input_sheet_name="�v�ϐ�" ^
|
|
10
|
+
--output_sheet_name="�ړI��" ^
|
|
11
|
+
--constraint_sheet_name="�S����" ^
|
|
12
|
+
|
|
13
|
+
--n_parallel=1 ^
|
|
14
|
+
--csv_path="test.csv" ^
|
|
15
|
+
--procedure_name=FemtetMacro.FemtetMain ^
|
|
16
|
+
--setup_procedure_name=PrePostProcessing.setup ^
|
|
17
|
+
--teardown_procedure_name=PrePostProcessing.teardown ^
|
|
18
|
+
|
|
19
|
+
--algorithm=Random ^
|
|
20
|
+
--n_startup_trials=10 ^
|
|
21
|
+
|
|
22
|
+
pause
|