pyfemtet 0.9.5__py3-none-any.whl → 1.0.0b0__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 +6 -1
- pyfemtet/_i18n/1. make_pot_and_update_po.bat +8 -0
- pyfemtet/_i18n/2. build_mo.bat +5 -0
- pyfemtet/_i18n/__init__.py +4 -0
- pyfemtet/_i18n/babel.cfg +2 -0
- pyfemtet/_i18n/i18n.py +37 -0
- pyfemtet/_i18n/locales/ja/LC_MESSAGES/messages.mo +0 -0
- pyfemtet/_i18n/locales/ja/LC_MESSAGES/messages.po +1020 -0
- pyfemtet/_i18n/locales/messages.pot +987 -0
- pyfemtet/{_message → _i18n}/messages.py +128 -41
- pyfemtet/_util/closing.py +19 -0
- pyfemtet/_util/dask_util.py +89 -7
- pyfemtet/_util/df_util.py +29 -0
- pyfemtet/_util/excel_macro_util.py +8 -3
- pyfemtet/_util/excel_parse_util.py +43 -23
- pyfemtet/_util/femtet_access_inspection.py +120 -0
- pyfemtet/{_femtet_config_util/autosave.py → _util/femtet_autosave.py} +7 -0
- pyfemtet/_util/femtet_exit.py +105 -0
- pyfemtet/_util/femtet_version.py +20 -0
- pyfemtet/_util/helper.py +94 -0
- pyfemtet/_util/process_util.py +107 -0
- pyfemtet/_util/str_enum.py +44 -0
- pyfemtet/core.py +15 -47
- pyfemtet/dispatch_extensions/__init__.py +8 -11
- pyfemtet/dispatch_extensions/_impl.py +42 -198
- pyfemtet/logger/__init__.py +8 -1
- pyfemtet/logger/_impl.py +5 -6
- pyfemtet/opt/__init__.py +3 -17
- pyfemtet/opt/exceptions.py +45 -0
- pyfemtet/opt/femopt.py +608 -0
- pyfemtet/opt/history/__init__.py +11 -0
- pyfemtet/opt/history/_history.py +1404 -0
- pyfemtet/opt/history/_hypervolume.py +169 -0
- pyfemtet/opt/history/_optimality.py +79 -0
- pyfemtet/opt/interface/__init__.py +17 -24
- pyfemtet/opt/interface/_base_interface.py +222 -0
- pyfemtet/opt/interface/_excel_interface/__init__.py +3 -0
- pyfemtet/opt/interface/_excel_interface/debug-excel-interface.xlsm +0 -0
- pyfemtet/opt/interface/_excel_interface/excel_interface.py +999 -0
- pyfemtet/opt/interface/_femtet_interface/__init__.py +3 -0
- pyfemtet/opt/interface/{_femtet_parametric.py → _femtet_interface/_femtet_parametric.py} +20 -12
- pyfemtet/opt/interface/{_femtet.py → _femtet_interface/femtet_interface.py} +505 -349
- pyfemtet/opt/interface/_femtet_with_nx_interface/__init__.py +5 -0
- pyfemtet/opt/interface/_femtet_with_nx_interface/femtet_with_nx_interface.py +230 -0
- pyfemtet/opt/interface/_femtet_with_nx_interface/model1.prt +0 -0
- pyfemtet/opt/interface/_femtet_with_nx_interface/model1.x_t +98 -0
- pyfemtet/opt/interface/{_femtet_with_nx → _femtet_with_nx_interface}/update_model.py +1 -3
- pyfemtet/opt/interface/_femtet_with_solidworks/__init__.py +5 -0
- pyfemtet/opt/interface/_femtet_with_solidworks/femtet_with_solidworks_interface.py +122 -0
- pyfemtet/opt/interface/_solidworks_interface/__init__.py +5 -0
- pyfemtet/opt/interface/_solidworks_interface/solidworks_interface.py +206 -0
- pyfemtet/opt/interface/_surrogate_model_interface/__init__.py +8 -0
- pyfemtet/opt/interface/_surrogate_model_interface/base_surrogate_interface.py +150 -0
- pyfemtet/opt/interface/_surrogate_model_interface/botorch_interface.py +298 -0
- pyfemtet/opt/interface/_surrogate_model_interface/debug-pof-botorch.reccsv +18 -0
- pyfemtet/opt/interface/_with_excel_settings/__init__.py +61 -0
- pyfemtet/opt/interface/_with_excel_settings/with_excel_settings.py +134 -0
- pyfemtet/opt/meta_script/YAML_Generator.xlsm +0 -0
- pyfemtet/opt/meta_script/__main__.py +58 -36
- pyfemtet/opt/optimizer/__init__.py +7 -9
- pyfemtet/opt/optimizer/_base_optimizer.py +885 -0
- pyfemtet/opt/optimizer/optuna_optimizer/__init__.py +9 -0
- pyfemtet/opt/optimizer/optuna_optimizer/_optuna_attribute.py +73 -0
- pyfemtet/opt/optimizer/optuna_optimizer/_optuna_optimizer.py +678 -0
- pyfemtet/opt/optimizer/optuna_optimizer/_pof_botorch/__init__.py +7 -0
- pyfemtet/opt/optimizer/optuna_optimizer/_pof_botorch/debug-pof-botorch.reccsv +18 -0
- pyfemtet/opt/optimizer/optuna_optimizer/_pof_botorch/enable_nonlinear_constraint.py +244 -0
- pyfemtet/opt/optimizer/optuna_optimizer/_pof_botorch/pof_botorch_sampler.py +1249 -0
- pyfemtet/opt/optimizer/optuna_optimizer/wat_ex14_parametric_jp.femprj +0 -0
- pyfemtet/opt/optimizer/scipy_optimizer/__init__.py +1 -0
- pyfemtet/opt/optimizer/scipy_optimizer/_scipy_optimizer.py +364 -0
- pyfemtet/opt/prediction/__init__.py +7 -0
- pyfemtet/opt/prediction/_botorch_utils.py +133 -0
- pyfemtet/opt/prediction/_gpytorch_modules_extension.py +142 -0
- pyfemtet/opt/prediction/_helper.py +155 -0
- pyfemtet/opt/prediction/_model.py +118 -0
- pyfemtet/opt/problem/problem.py +304 -0
- pyfemtet/opt/problem/variable_manager/__init__.py +20 -0
- pyfemtet/opt/problem/variable_manager/_string_as_expression.py +115 -0
- pyfemtet/opt/problem/variable_manager/_variable_manager.py +295 -0
- pyfemtet/opt/visualization/history_viewer/__main__.py +5 -0
- pyfemtet/opt/visualization/{_base.py → history_viewer/_base_application.py} +18 -13
- pyfemtet/opt/visualization/history_viewer/_common_pages.py +150 -0
- pyfemtet/opt/visualization/{_complex_components → history_viewer/_complex_components}/alert_region.py +10 -5
- pyfemtet/opt/visualization/{_complex_components → history_viewer/_complex_components}/control_femtet.py +16 -13
- pyfemtet/opt/visualization/{_complex_components → history_viewer/_complex_components}/main_graph.py +117 -47
- pyfemtet/opt/visualization/{_complex_components → history_viewer/_complex_components}/pm_graph.py +159 -138
- pyfemtet/opt/visualization/history_viewer/_process_monitor/_application.py +173 -0
- pyfemtet/opt/visualization/history_viewer/_process_monitor/_pages.py +291 -0
- pyfemtet/opt/visualization/{_wrapped_components → history_viewer/_wrapped_components}/dbc.py +1 -1
- pyfemtet/opt/visualization/{_wrapped_components → history_viewer/_wrapped_components}/dcc.py +1 -1
- pyfemtet/opt/visualization/{_wrapped_components → history_viewer/_wrapped_components}/html.py +1 -1
- pyfemtet/opt/visualization/history_viewer/result_viewer/__main__.py +5 -0
- pyfemtet/opt/visualization/{result_viewer/application.py → history_viewer/result_viewer/_application.py} +6 -6
- pyfemtet/opt/visualization/{result_viewer/pages.py → history_viewer/result_viewer/_pages.py} +106 -82
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08.csv +18 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08.db +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8.jpg +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8.log +45 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8.pdt +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_1.jpg +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_1.pdt +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_10.jpg +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_10.pdt +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_11.jpg +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_11.pdt +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_12.jpg +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_12.pdt +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_13.jpg +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_13.pdt +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_14.jpg +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_14.pdt +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_15.jpg +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_15.pdt +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_16.jpg +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_16.pdt +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_17.jpg +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_17.pdt +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_18.jpg +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_18.pdt +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_19.jpg +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_19.pdt +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_2.jpg +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_2.pdt +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_20.jpg +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_20.pdt +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_3.jpg +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_3.pdt +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_4.bgr +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_4.bnd +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_4.btr +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_4.jpg +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_4.mtl +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_4.pdt +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_4.prm +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_5.jpg +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_5.pdt +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_6.jpg +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_6.pdt +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_7.jpg +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_7.pdt +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_8.jpg +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_8.pdt +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_9.jpg +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_9.pdt +0 -0
- pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.femprj +0 -0
- pyfemtet/opt/visualization/plotter/main_figure_creator.py +536 -0
- pyfemtet/opt/visualization/plotter/pm_graph_creator.py +359 -0
- pyfemtet/opt/worker_status.py +120 -0
- {pyfemtet-0.9.5.dist-info → pyfemtet-1.0.0b0.dist-info}/METADATA +23 -24
- pyfemtet-1.0.0b0.dist-info/RECORD +172 -0
- pyfemtet-1.0.0b0.dist-info/entry_points.txt +3 -0
- pyfemtet/_femtet_config_util/exit.py +0 -59
- pyfemtet/_message/1. make_pot.bat +0 -11
- pyfemtet/_message/2. make_mo.bat +0 -6
- pyfemtet/_message/__init__.py +0 -5
- pyfemtet/_message/babel.cfg +0 -2
- pyfemtet/_message/locales/ja/LC_MESSAGES/messages.mo +0 -0
- pyfemtet/_message/locales/ja/LC_MESSAGES/messages.po +0 -570
- pyfemtet/_message/locales/messages.pot +0 -551
- pyfemtet/_warning.py +0 -87
- pyfemtet/brep/_impl.py +0 -18
- pyfemtet/opt/_femopt.py +0 -1007
- pyfemtet/opt/_femopt_core.py +0 -1169
- pyfemtet/opt/_test_utils/control_femtet.py +0 -39
- pyfemtet/opt/_test_utils/hyper_sphere.py +0 -24
- pyfemtet/opt/_test_utils/record_history.py +0 -130
- 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 +0 -291
- pyfemtet/opt/advanced_samples/excel_ui/test-pyfemtet-core.cmd +0 -22
- pyfemtet/opt/advanced_samples/restart/gal_ex13_parametric.femprj +0 -0
- pyfemtet/opt/advanced_samples/restart/gal_ex13_parametric_restart.py +0 -99
- pyfemtet/opt/advanced_samples/restart/gal_ex13_parametric_restart_jp.py +0 -102
- pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_create_training_data.py +0 -60
- pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_create_training_data_jp.py +0 -57
- pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_optimize_with_surrogate.py +0 -100
- pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_optimize_with_surrogate_jp.py +0 -90
- pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_parametric.femprj +0 -0
- pyfemtet/opt/interface/_base.py +0 -101
- pyfemtet/opt/interface/_excel_interface.py +0 -984
- pyfemtet/opt/interface/_femtet_excel.py +0 -141
- pyfemtet/opt/interface/_femtet_with_nx/__init__.py +0 -3
- pyfemtet/opt/interface/_femtet_with_nx/_interface.py +0 -178
- pyfemtet/opt/interface/_femtet_with_sldworks.py +0 -298
- pyfemtet/opt/interface/_surrogate/__init__.py +0 -5
- pyfemtet/opt/interface/_surrogate/_base.py +0 -129
- pyfemtet/opt/interface/_surrogate/_chaospy.py +0 -71
- pyfemtet/opt/interface/_surrogate/_singletaskgp.py +0 -71
- pyfemtet/opt/interface/_surrogate_excel.py +0 -102
- pyfemtet/opt/optimizer/_base.py +0 -376
- pyfemtet/opt/optimizer/_optuna/_botorch_patch/enable_nonlinear_constraint.py +0 -220
- pyfemtet/opt/optimizer/_optuna/_optuna.py +0 -434
- pyfemtet/opt/optimizer/_optuna/_pof_botorch.py +0 -1914
- pyfemtet/opt/optimizer/_scipy.py +0 -159
- pyfemtet/opt/optimizer/_scipy_scalar.py +0 -127
- pyfemtet/opt/optimizer/parameter.py +0 -113
- pyfemtet/opt/prediction/_base.py +0 -61
- pyfemtet/opt/prediction/single_task_gp.py +0 -119
- pyfemtet/opt/samples/femprj_sample/ParametricIF.femprj +0 -0
- pyfemtet/opt/samples/femprj_sample/ParametricIF.py +0 -29
- pyfemtet/opt/samples/femprj_sample/ParametricIF_test_result.reccsv +0 -13
- pyfemtet/opt/samples/femprj_sample/cad_ex01_NX.femprj +0 -0
- pyfemtet/opt/samples/femprj_sample/cad_ex01_NX.prt +0 -0
- pyfemtet/opt/samples/femprj_sample/cad_ex01_NX.py +0 -135
- pyfemtet/opt/samples/femprj_sample/cad_ex01_NX_test_result.reccsv +0 -23
- pyfemtet/opt/samples/femprj_sample/cad_ex01_SW.SLDPRT +0 -0
- pyfemtet/opt/samples/femprj_sample/cad_ex01_SW.femprj +0 -0
- pyfemtet/opt/samples/femprj_sample/cad_ex01_SW.py +0 -131
- pyfemtet/opt/samples/femprj_sample/cad_ex01_SW_test_result.reccsv +0 -23
- pyfemtet/opt/samples/femprj_sample/constrained_pipe.femprj +0 -0
- pyfemtet/opt/samples/femprj_sample/constrained_pipe.py +0 -96
- pyfemtet/opt/samples/femprj_sample/constrained_pipe_test_result.reccsv +0 -13
- pyfemtet/opt/samples/femprj_sample/gal_ex58_parametric.femprj +0 -0
- pyfemtet/opt/samples/femprj_sample/gal_ex58_parametric.py +0 -74
- pyfemtet/opt/samples/femprj_sample/gal_ex58_parametric_test_result.reccsv +0 -13
- pyfemtet/opt/samples/femprj_sample/gau_ex08_parametric.femprj +0 -0
- pyfemtet/opt/samples/femprj_sample/gau_ex08_parametric.py +0 -58
- pyfemtet/opt/samples/femprj_sample/gau_ex08_parametric_test_result.reccsv +0 -23
- pyfemtet/opt/samples/femprj_sample/gau_ex12_parametric.femprj +0 -0
- pyfemtet/opt/samples/femprj_sample/gau_ex12_parametric.py +0 -52
- pyfemtet/opt/samples/femprj_sample/her_ex40_parametric.femprj +0 -0
- pyfemtet/opt/samples/femprj_sample/her_ex40_parametric.py +0 -138
- pyfemtet/opt/samples/femprj_sample/her_ex40_parametric_test_result.reccsv +0 -18
- pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric.femprj +0 -0
- pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric.py +0 -60
- pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric_parallel.py +0 -61
- pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric_test_result.reccsv +0 -18
- pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric.femprj +0 -0
- pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric.py +0 -58
- pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric_parallel.py +0 -58
- pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric_test_result.reccsv +0 -18
- pyfemtet/opt/samples/femprj_sample_jp/ParametricIF_jp.femprj +0 -0
- pyfemtet/opt/samples/femprj_sample_jp/ParametricIF_jp.py +0 -29
- pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_NX_jp.femprj +0 -0
- pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_NX_jp.py +0 -129
- pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_SW_jp.femprj +0 -0
- pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_SW_jp.py +0 -125
- pyfemtet/opt/samples/femprj_sample_jp/constrained_pipe_jp.py +0 -93
- pyfemtet/opt/samples/femprj_sample_jp/gal_ex58_parametric_jp.femprj +0 -0
- pyfemtet/opt/samples/femprj_sample_jp/gal_ex58_parametric_jp.py +0 -70
- pyfemtet/opt/samples/femprj_sample_jp/gau_ex08_parametric_jp.femprj +0 -0
- pyfemtet/opt/samples/femprj_sample_jp/gau_ex08_parametric_jp.py +0 -57
- pyfemtet/opt/samples/femprj_sample_jp/gau_ex12_parametric_jp.py +0 -52
- pyfemtet/opt/samples/femprj_sample_jp/her_ex40_parametric_jp.femprj +0 -0
- pyfemtet/opt/samples/femprj_sample_jp/her_ex40_parametric_jp.py +0 -138
- pyfemtet/opt/samples/femprj_sample_jp/paswat_ex1_parametric_jp.femprj +0 -0
- pyfemtet/opt/samples/femprj_sample_jp/paswat_ex1_parametric_jp.py +0 -58
- pyfemtet/opt/samples/femprj_sample_jp/paswat_ex1_parametric_parallel_jp.py +0 -59
- pyfemtet/opt/samples/femprj_sample_jp/wat_ex14_parametric_jp.py +0 -56
- pyfemtet/opt/samples/femprj_sample_jp/wat_ex14_parametric_parallel_jp.py +0 -56
- pyfemtet/opt/visualization/_complex_components/main_figure_creator.py +0 -332
- pyfemtet/opt/visualization/_complex_components/pm_graph_creator.py +0 -201
- pyfemtet/opt/visualization/_process_monitor/application.py +0 -226
- pyfemtet/opt/visualization/_process_monitor/pages.py +0 -406
- pyfemtet/opt/visualization/_wrapped_components/__init__.py +0 -0
- pyfemtet/opt/visualization/result_viewer/__init__.py +0 -0
- pyfemtet-0.9.5.dist-info/RECORD +0 -158
- pyfemtet-0.9.5.dist-info/entry_points.txt +0 -3
- /pyfemtet/{_femtet_config_util → opt/problem}/__init__.py +0 -0
- /pyfemtet/{brep → opt/visualization/history_viewer}/__init__.py +0 -0
- /pyfemtet/opt/{_test_utils → visualization/history_viewer/_complex_components}/__init__.py +0 -0
- /pyfemtet/opt/{optimizer/_optuna → visualization/history_viewer/_process_monitor}/__init__.py +0 -0
- /pyfemtet/opt/{optimizer/_optuna/_botorch_patch → visualization/history_viewer/_wrapped_components}/__init__.py +0 -0
- /pyfemtet/opt/visualization/{_wrapped_components → history_viewer/_wrapped_components}/str_enum.py +0 -0
- /pyfemtet/opt/visualization/{result_viewer → history_viewer/result_viewer}/.gitignore +0 -0
- /pyfemtet/opt/visualization/{_complex_components → history_viewer/result_viewer}/__init__.py +0 -0
- /pyfemtet/opt/visualization/{_process_monitor → plotter}/__init__.py +0 -0
- /pyfemtet/opt/{samples/femprj_sample_jp/wat_ex14_parametric_jp.femprj → wat_ex14_parametric_jp.femprj} +0 -0
- {pyfemtet-0.9.5.dist-info → pyfemtet-1.0.0b0.dist-info}/LICENSE +0 -0
- {pyfemtet-0.9.5.dist-info → pyfemtet-1.0.0b0.dist-info}/LICENSE_THIRD_PARTY.txt +0 -0
- {pyfemtet-0.9.5.dist-info → pyfemtet-1.0.0b0.dist-info}/WHEEL +0 -0
pyfemtet/opt/_femopt_core.py
DELETED
|
@@ -1,1169 +0,0 @@
|
|
|
1
|
-
# typing
|
|
2
|
-
import json
|
|
3
|
-
from typing import List, TYPE_CHECKING
|
|
4
|
-
|
|
5
|
-
# built-in
|
|
6
|
-
import os
|
|
7
|
-
import datetime
|
|
8
|
-
import inspect
|
|
9
|
-
import ast
|
|
10
|
-
import csv
|
|
11
|
-
import ctypes
|
|
12
|
-
from packaging import version
|
|
13
|
-
import platform
|
|
14
|
-
|
|
15
|
-
# 3rd-party
|
|
16
|
-
import numpy as np
|
|
17
|
-
import pandas as pd
|
|
18
|
-
from scipy.stats.qmc import LatinHypercube
|
|
19
|
-
import optuna
|
|
20
|
-
if version.parse(optuna.version.__version__) < version.parse('4.0.0'):
|
|
21
|
-
from optuna._hypervolume import WFG
|
|
22
|
-
wfg = WFG()
|
|
23
|
-
compute_hypervolume = wfg.compute
|
|
24
|
-
else:
|
|
25
|
-
from optuna._hypervolume import wfg
|
|
26
|
-
compute_hypervolume = wfg.compute_hypervolume
|
|
27
|
-
from dask.distributed import Lock, get_client, Client
|
|
28
|
-
|
|
29
|
-
# win32com
|
|
30
|
-
if (platform.system() == 'Windows') or TYPE_CHECKING:
|
|
31
|
-
from win32com.client import constants, Constants
|
|
32
|
-
else:
|
|
33
|
-
class Constants:
|
|
34
|
-
pass
|
|
35
|
-
constants = None
|
|
36
|
-
|
|
37
|
-
# pyfemtet relative
|
|
38
|
-
from pyfemtet.opt.interface import FEMInterface, FemtetInterface
|
|
39
|
-
from pyfemtet._message import encoding, Msg
|
|
40
|
-
|
|
41
|
-
# logger
|
|
42
|
-
from pyfemtet.logger import get_module_logger
|
|
43
|
-
|
|
44
|
-
logger = get_module_logger('opt.core', __name__)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
__all__ = [
|
|
48
|
-
'generate_lhs',
|
|
49
|
-
'_check_bound',
|
|
50
|
-
'_is_access_gogh',
|
|
51
|
-
'is_feasible',
|
|
52
|
-
'Objective',
|
|
53
|
-
'Constraint',
|
|
54
|
-
'History',
|
|
55
|
-
'OptimizationStatus',
|
|
56
|
-
'logger',
|
|
57
|
-
]
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def generate_lhs(bounds: List[List[float]], seed: int or None = None) -> np.ndarray:
|
|
61
|
-
"""Latin Hypercube Sampling from given design parameter bounds.
|
|
62
|
-
|
|
63
|
-
If the number of parameters is d,
|
|
64
|
-
sampler returns (N, d) shape ndarray.
|
|
65
|
-
N equals p**2, p is the minimum prime number over d.
|
|
66
|
-
For example, when d=3, then p=5 and N=25.
|
|
67
|
-
|
|
68
|
-
Args:
|
|
69
|
-
bounds (list[list[float]]): List of [lower_bound, upper_bound] of parameters.
|
|
70
|
-
seed (int or None, optional): Random seed. Defaults to None.
|
|
71
|
-
|
|
72
|
-
Returns:
|
|
73
|
-
np.ndarray: (N, d) shape ndarray.
|
|
74
|
-
"""
|
|
75
|
-
|
|
76
|
-
d = len(bounds)
|
|
77
|
-
|
|
78
|
-
sampler = LatinHypercube(
|
|
79
|
-
d,
|
|
80
|
-
scramble=False,
|
|
81
|
-
strength=2,
|
|
82
|
-
# optimization='lloyd',
|
|
83
|
-
optimization='random-cd',
|
|
84
|
-
seed=seed,
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
LIMIT = 100
|
|
88
|
-
|
|
89
|
-
def is_prime(p):
|
|
90
|
-
for j in range(2, p):
|
|
91
|
-
if p % j == 0:
|
|
92
|
-
return False
|
|
93
|
-
return True
|
|
94
|
-
|
|
95
|
-
def get_prime(_minimum):
|
|
96
|
-
for p in range(_minimum, LIMIT):
|
|
97
|
-
if is_prime(p):
|
|
98
|
-
return p
|
|
99
|
-
|
|
100
|
-
n = get_prime(d + 1) ** 2
|
|
101
|
-
data = sampler.random(n) # [0,1)
|
|
102
|
-
|
|
103
|
-
for i, (data_range, datum) in enumerate(zip(bounds, data.T)):
|
|
104
|
-
minimum, maximum = data_range
|
|
105
|
-
band = maximum - minimum
|
|
106
|
-
converted_datum = datum * band + minimum
|
|
107
|
-
data[:, i] = converted_datum
|
|
108
|
-
|
|
109
|
-
return data # data.shape = (N, d)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
def symlog(x: float or np.ndarray):
|
|
113
|
-
"""Log function whose domain is extended to the negative region.
|
|
114
|
-
|
|
115
|
-
Symlog processing is performed internally as a measure to reduce
|
|
116
|
-
unintended trends caused by scale differences
|
|
117
|
-
between objective functions in multi-objective optimization.
|
|
118
|
-
|
|
119
|
-
Args:
|
|
120
|
-
x (float or np.ndarray)
|
|
121
|
-
|
|
122
|
-
Returns:
|
|
123
|
-
float
|
|
124
|
-
"""
|
|
125
|
-
|
|
126
|
-
if isinstance(x, np.ndarray):
|
|
127
|
-
ret = np.zeros(x.shape)
|
|
128
|
-
idx = np.where(x >= 0)
|
|
129
|
-
ret[idx] = np.log10(x[idx] + 1)
|
|
130
|
-
idx = np.where(x < 0)
|
|
131
|
-
ret[idx] = -np.log10(1 - x[idx])
|
|
132
|
-
else:
|
|
133
|
-
if x >= 0:
|
|
134
|
-
ret = np.log10(x + 1)
|
|
135
|
-
else:
|
|
136
|
-
ret = -np.log10(1 - x)
|
|
137
|
-
|
|
138
|
-
return ret
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def _check_bound(lb, ub, name=None):
|
|
142
|
-
message = Msg.ERR_CHECK_MINMAX
|
|
143
|
-
if (lb is not None) and (ub is not None):
|
|
144
|
-
if lb > ub:
|
|
145
|
-
raise ValueError(message)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
def _get_scope_indent(source: str) -> int:
|
|
149
|
-
SPACES = [' ', '\t']
|
|
150
|
-
indent = 0
|
|
151
|
-
while True:
|
|
152
|
-
if source[indent] not in SPACES:
|
|
153
|
-
break
|
|
154
|
-
else:
|
|
155
|
-
indent += 1
|
|
156
|
-
return indent
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
def _remove_indent(source: str, indent: int) -> str: # returns source
|
|
160
|
-
lines = source.splitlines()
|
|
161
|
-
edited_lines = [l[indent:] for l in lines]
|
|
162
|
-
edited_source = '\n'.join(edited_lines)
|
|
163
|
-
return edited_source
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
def _check_access_femtet_objects(fun, target: str = 'Femtet'):
|
|
167
|
-
|
|
168
|
-
# 関数fのソースコードを取得
|
|
169
|
-
source = inspect.getsource(fun)
|
|
170
|
-
|
|
171
|
-
# ソースコードを抽象構文木(AST)に変換
|
|
172
|
-
try:
|
|
173
|
-
# instanceメソッドなどの場合を想定してインデントを削除
|
|
174
|
-
source = _remove_indent(source, _get_scope_indent(source))
|
|
175
|
-
tree = ast.parse(source)
|
|
176
|
-
|
|
177
|
-
except Exception:
|
|
178
|
-
return False # パースに失敗するからと言ってエラーにするまででもない
|
|
179
|
-
|
|
180
|
-
# if function or staticmethod, 1st argument is Femtet. Find the name.
|
|
181
|
-
varname_contains_femtet = '' # invalid variable name
|
|
182
|
-
for node in ast.walk(tree):
|
|
183
|
-
if isinstance(node, ast.FunctionDef):
|
|
184
|
-
all_arguments: ast.arguments = node.args
|
|
185
|
-
|
|
186
|
-
args: list[ast.arg] = all_arguments.args
|
|
187
|
-
# args.extend(all_arguments.posonlyargs) # 先にこっちを入れるべきかも
|
|
188
|
-
|
|
189
|
-
target_arg = args[0]
|
|
190
|
-
|
|
191
|
-
# if class method or instance method, 2nd argument is it.
|
|
192
|
-
# In this implementation, we cannot detect the FunctionDef is
|
|
193
|
-
# method or not because the part of source code is unindented and parsed.
|
|
194
|
-
if target_arg.arg == 'self' or target_arg.arg == 'cls':
|
|
195
|
-
if len(args) > 1:
|
|
196
|
-
target_arg = args[1]
|
|
197
|
-
else:
|
|
198
|
-
target_arg = None
|
|
199
|
-
|
|
200
|
-
if target_arg is not None:
|
|
201
|
-
varname_contains_femtet = target_arg.arg
|
|
202
|
-
|
|
203
|
-
# check Femtet access
|
|
204
|
-
if target == 'Femtet':
|
|
205
|
-
for node in ast.walk(tree):
|
|
206
|
-
|
|
207
|
-
# by accessing argument directory
|
|
208
|
-
if isinstance(node, ast.Name):
|
|
209
|
-
# found local variables
|
|
210
|
-
node: ast.Name
|
|
211
|
-
if node.id == varname_contains_femtet:
|
|
212
|
-
# found Femtet
|
|
213
|
-
return True
|
|
214
|
-
|
|
215
|
-
# by accessing inside method
|
|
216
|
-
elif isinstance(node, ast.Attribute):
|
|
217
|
-
# found attribute of something
|
|
218
|
-
node: ast.Attribute
|
|
219
|
-
if node.attr == 'Femtet':
|
|
220
|
-
# found **.Femtet.**
|
|
221
|
-
return True
|
|
222
|
-
|
|
223
|
-
# check Gogh access
|
|
224
|
-
elif target == 'Gogh':
|
|
225
|
-
for node in ast.walk(tree):
|
|
226
|
-
if isinstance(node, ast.Attribute):
|
|
227
|
-
if node.attr == 'Gogh':
|
|
228
|
-
# found **.Gogh.**
|
|
229
|
-
node: ast.Attribute
|
|
230
|
-
parent = node.value
|
|
231
|
-
|
|
232
|
-
# by accessing argument directory
|
|
233
|
-
if isinstance(parent, ast.Name):
|
|
234
|
-
# found *.Gogh.**
|
|
235
|
-
parent: ast.Name
|
|
236
|
-
if parent.id == varname_contains_femtet:
|
|
237
|
-
# found Femtet.Gogh.**
|
|
238
|
-
return True
|
|
239
|
-
|
|
240
|
-
# by accessing inside method
|
|
241
|
-
if isinstance(parent, ast.Attribute):
|
|
242
|
-
# found **.*.Gogh.**
|
|
243
|
-
parent: ast.Attribute
|
|
244
|
-
if parent.attr == 'Femtet':
|
|
245
|
-
# found **.Femtet.Gogh.**
|
|
246
|
-
return True
|
|
247
|
-
|
|
248
|
-
# ここまで来たならば target へのアクセスはおそらくない
|
|
249
|
-
return False
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
def _is_access_gogh(fun):
|
|
253
|
-
return _check_access_femtet_objects(fun, target='Gogh')
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
def _is_access_femtet(fun):
|
|
257
|
-
return _check_access_femtet_objects(fun, target='Femtet')
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
def is_feasible(value, lb, ub):
|
|
261
|
-
"""
|
|
262
|
-
Check if a value is within the specified lower bound and upper bound.
|
|
263
|
-
|
|
264
|
-
Args:
|
|
265
|
-
value (numeric): The value to check.
|
|
266
|
-
lb (optional, numeric): The lower bound. If not specified, there is no lower bound.
|
|
267
|
-
ub (optional, numeric): The upper bound. If not specified, there is no upper bound.
|
|
268
|
-
|
|
269
|
-
Returns:
|
|
270
|
-
bool: True if the value satisfies the bounds; False otherwise.
|
|
271
|
-
"""
|
|
272
|
-
if np.isnan(value):
|
|
273
|
-
return False
|
|
274
|
-
if lb is None and ub is not None:
|
|
275
|
-
return value <= ub
|
|
276
|
-
elif lb is not None and ub is None:
|
|
277
|
-
return lb <= value
|
|
278
|
-
elif lb is not None and ub is not None:
|
|
279
|
-
return lb <= value <= ub
|
|
280
|
-
else:
|
|
281
|
-
return True
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
class _Scapegoat:
|
|
285
|
-
"""Helper class for parallelize Femtet."""
|
|
286
|
-
# constants を含む関数を並列化するために
|
|
287
|
-
# メイン処理で一時的に constants への参照を
|
|
288
|
-
# このオブジェクトにして、後で restore する
|
|
289
|
-
def __init__(self, ignore=False):
|
|
290
|
-
self._ignore_when_restore_constants = ignore
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
class Function:
|
|
294
|
-
"""Base class for Objective and Constraint."""
|
|
295
|
-
|
|
296
|
-
def __init__(self, fun, name, args, kwargs):
|
|
297
|
-
|
|
298
|
-
# ===== for 1.0 warning =====
|
|
299
|
-
self._shown = False
|
|
300
|
-
|
|
301
|
-
# serializable でない COM 定数を parallelize するため
|
|
302
|
-
# COM 定数を一度 _Scapegoat 型のオブジェクトにする
|
|
303
|
-
# ParametricIF で使う dll 関数は _FuncPtr 型であって __globals__ を持たないが、
|
|
304
|
-
# これは絶対に constants を持たないので単に無視すればよい。
|
|
305
|
-
if fun is not None:
|
|
306
|
-
if not isinstance(fun, ctypes._CFuncPtr):
|
|
307
|
-
for varname in fun.__globals__:
|
|
308
|
-
if isinstance(fun.__globals__[varname], Constants):
|
|
309
|
-
fun.__globals__[varname] = _Scapegoat()
|
|
310
|
-
|
|
311
|
-
self.fun = fun
|
|
312
|
-
self.name: str = name
|
|
313
|
-
self.args = args
|
|
314
|
-
self.kwargs = kwargs
|
|
315
|
-
|
|
316
|
-
def calc(self, fem: FEMInterface):
|
|
317
|
-
"""Execute user-defined fun.
|
|
318
|
-
|
|
319
|
-
Args:
|
|
320
|
-
fem (FEMInterface)
|
|
321
|
-
|
|
322
|
-
Returns:
|
|
323
|
-
float
|
|
324
|
-
"""
|
|
325
|
-
if self.fun is None:
|
|
326
|
-
RuntimeError(f'`fun` of {self.name} is not specified.')
|
|
327
|
-
|
|
328
|
-
args = self.args
|
|
329
|
-
# Femtet 特有の処理
|
|
330
|
-
if isinstance(fem, FemtetInterface):
|
|
331
|
-
args = (fem.object_passed_to_functions, *args)
|
|
332
|
-
else:
|
|
333
|
-
# ===== for 1.0 warning =====
|
|
334
|
-
if not self._shown:
|
|
335
|
-
logger.warning('From version 1.0, an object associated with the Interface '
|
|
336
|
-
'will be automatically passed as the first argument. '
|
|
337
|
-
'For more details, please see https://pyfemtet.readthedocs.io/en/stable/pages/migration_to_v1.html. '
|
|
338
|
-
'(日本語版サイト; https://pyfemtet.readthedocs.io/ja/stable/pages/migration_to_v1.html)')
|
|
339
|
-
self._shown = True
|
|
340
|
-
|
|
341
|
-
return float(self.fun(*args, **self.kwargs))
|
|
342
|
-
|
|
343
|
-
def _restore_constants(self):
|
|
344
|
-
"""Helper function for parallelize Femtet."""
|
|
345
|
-
fun = self.fun
|
|
346
|
-
if fun is not None:
|
|
347
|
-
if not isinstance(fun, ctypes._CFuncPtr):
|
|
348
|
-
for varname in fun.__globals__:
|
|
349
|
-
if isinstance(fun.__globals__[varname], _Scapegoat):
|
|
350
|
-
if not fun.__globals__[varname]._ignore_when_restore_constants:
|
|
351
|
-
fun.__globals__[varname] = constants
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
class Objective(Function):
|
|
355
|
-
"""Class for registering user-defined objective function."""
|
|
356
|
-
|
|
357
|
-
default_name = 'obj'
|
|
358
|
-
|
|
359
|
-
def __init__(self, fun, name, direction, args, kwargs, with_symlog=False):
|
|
360
|
-
"""Initializes an Objective instance.
|
|
361
|
-
|
|
362
|
-
Args:
|
|
363
|
-
fun: The user-defined objective function.
|
|
364
|
-
name (str): The name of the objective function.
|
|
365
|
-
direction (str or float or int): The direction of optimization.
|
|
366
|
-
args: Additional arguments for the objective function.
|
|
367
|
-
kwargs: Additional keyword arguments for the objective function.
|
|
368
|
-
|
|
369
|
-
Raises:
|
|
370
|
-
ValueError: If the direction is not valid.
|
|
371
|
-
|
|
372
|
-
Note:
|
|
373
|
-
If FEMOpt.fem is a instance of FemtetInterface or its subclass,
|
|
374
|
-
the 1st argument of fun is set to fem automatically.
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
"""
|
|
378
|
-
self._check_direction(direction)
|
|
379
|
-
self.direction = direction
|
|
380
|
-
self.with_symlog = with_symlog
|
|
381
|
-
super().__init__(fun, name, args, kwargs)
|
|
382
|
-
|
|
383
|
-
def convert(self, value: float):
|
|
384
|
-
"""Converts an evaluation value to the value of objective function based on the specified direction.
|
|
385
|
-
|
|
386
|
-
When direction is `'minimize'`, ``value`` is calculated.
|
|
387
|
-
When direction is `'maximize'`, ``-value`` is calculated.
|
|
388
|
-
When direction is float, ``abs(value - direction)`` is calculated.
|
|
389
|
-
Finally, the calculated value is passed to the symlog function and returns it.
|
|
390
|
-
|
|
391
|
-
``value`` is the return value of the user-defined function.
|
|
392
|
-
|
|
393
|
-
Args:
|
|
394
|
-
value (float): The evaluation value to be converted.
|
|
395
|
-
|
|
396
|
-
Returns:
|
|
397
|
-
float: The converted objective value.
|
|
398
|
-
|
|
399
|
-
"""
|
|
400
|
-
|
|
401
|
-
# 評価関数(direction 任意)を目的関数(minimize, symlog)に変換する
|
|
402
|
-
ret = value
|
|
403
|
-
if isinstance(self.direction, float) or isinstance(self.direction, int):
|
|
404
|
-
ret = abs(value - self.direction)
|
|
405
|
-
elif self.direction == 'minimize':
|
|
406
|
-
ret = value
|
|
407
|
-
elif self.direction == 'maximize':
|
|
408
|
-
ret = -value
|
|
409
|
-
|
|
410
|
-
if self.with_symlog:
|
|
411
|
-
ret = symlog(ret)
|
|
412
|
-
|
|
413
|
-
return float(ret)
|
|
414
|
-
|
|
415
|
-
@staticmethod
|
|
416
|
-
def _check_direction(direction):
|
|
417
|
-
message = Msg.ERR_CHECK_DIRECTION
|
|
418
|
-
if isinstance(direction, float) or isinstance(direction, int):
|
|
419
|
-
pass
|
|
420
|
-
elif isinstance(direction, str):
|
|
421
|
-
if (direction != 'minimize') and (direction != 'maximize'):
|
|
422
|
-
raise ValueError(message)
|
|
423
|
-
else:
|
|
424
|
-
raise ValueError(message)
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
class Constraint(Function):
|
|
428
|
-
"""Class for registering user-defined constraint function."""
|
|
429
|
-
|
|
430
|
-
default_name = 'cns'
|
|
431
|
-
|
|
432
|
-
def __init__(self, fun, name, lb, ub, strict, args, kwargs, using_fem):
|
|
433
|
-
"""Initializes a Constraint instance.
|
|
434
|
-
|
|
435
|
-
Args:
|
|
436
|
-
fun: The user-defined constraint function.
|
|
437
|
-
name (str): The name of the constraint function.
|
|
438
|
-
lb: The lower bound of the constraint.
|
|
439
|
-
ub: The upper bound of the constraint.
|
|
440
|
-
strict (bool): Whether to enforce strict inequality for the bounds.
|
|
441
|
-
args: Additional arguments for the constraint function.
|
|
442
|
-
kwargs: Additional keyword arguments for the constraint function.
|
|
443
|
-
using_fem: Update fem or not before run calc().
|
|
444
|
-
|
|
445
|
-
Raises:
|
|
446
|
-
ValueError: If the lower bound is greater than or equal to the upper bound.
|
|
447
|
-
|
|
448
|
-
"""
|
|
449
|
-
|
|
450
|
-
_check_bound(lb, ub)
|
|
451
|
-
self.lb = lb
|
|
452
|
-
self.ub = ub
|
|
453
|
-
self.strict = strict
|
|
454
|
-
self.using_fem = using_fem
|
|
455
|
-
super().__init__(fun, name, args, kwargs)
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
class ObjectivesFunc:
|
|
459
|
-
"""複数の値を返す関数を objective として扱うためのクラス
|
|
460
|
-
|
|
461
|
-
複数の値を返す関数を受け取る。
|
|
462
|
-
|
|
463
|
-
最初に評価されたときに計算を実行し
|
|
464
|
-
そうでない場合は保持した値を返す
|
|
465
|
-
callable のリストを提供する。
|
|
466
|
-
|
|
467
|
-
"""
|
|
468
|
-
|
|
469
|
-
def __init__(self, fun, n_return):
|
|
470
|
-
self._evaluated = [False for _ in range(n_return)]
|
|
471
|
-
self._values = [None for _ in range(n_return)]
|
|
472
|
-
self._i = 0
|
|
473
|
-
self.fun = fun
|
|
474
|
-
self.n_return = n_return
|
|
475
|
-
|
|
476
|
-
def __iter__(self):
|
|
477
|
-
return self
|
|
478
|
-
|
|
479
|
-
def __next__(self):
|
|
480
|
-
|
|
481
|
-
# iter の長さ
|
|
482
|
-
if self._i == self.n_return:
|
|
483
|
-
self._i = 0
|
|
484
|
-
raise StopIteration
|
|
485
|
-
|
|
486
|
-
# iter として提供する callable オブジェクト
|
|
487
|
-
# self の情報にもアクセスする必要があり
|
|
488
|
-
# それぞれが iter された時点での i 番目という
|
|
489
|
-
# 情報も必要なのでこのスコープで定義する必要がある
|
|
490
|
-
class NthFunc:
|
|
491
|
-
def __init__(self_, i):
|
|
492
|
-
# 何番目の要素であるかを保持
|
|
493
|
-
# self._i を直接参照すると
|
|
494
|
-
# 実行時点での ObjectiveFunc の
|
|
495
|
-
# 値を参照してしまう
|
|
496
|
-
self_.i = i
|
|
497
|
-
|
|
498
|
-
def __call__(self_, *args, **kwargs):
|
|
499
|
-
# 何番目の要素であるか
|
|
500
|
-
i = self_.i
|
|
501
|
-
|
|
502
|
-
# 一度も評価されていなければ評価する
|
|
503
|
-
if not any(self._evaluated):
|
|
504
|
-
self._values = tuple(self.fun(*args, **kwargs))
|
|
505
|
-
assert len(self._values) == self.n_return, '予期しない戻り値の数'
|
|
506
|
-
|
|
507
|
-
# 評価したらフラグを立てる
|
|
508
|
-
self._evaluated[i] = True
|
|
509
|
-
|
|
510
|
-
# すべてのフラグが立ったらクリアする
|
|
511
|
-
if all(self._evaluated):
|
|
512
|
-
self._evaluated = [False for _ in range(self.n_return)]
|
|
513
|
-
|
|
514
|
-
# 値を返す
|
|
515
|
-
return self._values[i]
|
|
516
|
-
|
|
517
|
-
@property
|
|
518
|
-
def __globals__(self_):
|
|
519
|
-
# ScapeGoat 実装への対処
|
|
520
|
-
return self.fun.__globals__
|
|
521
|
-
|
|
522
|
-
# callable を作成
|
|
523
|
-
f = NthFunc(self._i)
|
|
524
|
-
|
|
525
|
-
# index を更新
|
|
526
|
-
self._i += 1
|
|
527
|
-
|
|
528
|
-
return f
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
class History:
|
|
532
|
-
"""Class for managing the history of optimization results.
|
|
533
|
-
|
|
534
|
-
Args:
|
|
535
|
-
history_path (str): The path to the csv file.
|
|
536
|
-
prm_names (List[str], optional): The names of parameters. Defaults to None.
|
|
537
|
-
obj_names (List[str], optional): The names of objectives. Defaults to None.
|
|
538
|
-
cns_names (List[str], optional): The names of constraints. Defaults to None.
|
|
539
|
-
client (dask.distributed.Client): Dask client.
|
|
540
|
-
hv_reference (str or list[float or np.ndarray, optional):
|
|
541
|
-
The method to calculate hypervolume or
|
|
542
|
-
the reference point itself.
|
|
543
|
-
Valid values are 'dynamic-pareto' or
|
|
544
|
-
'dynamic-nadir' or 'nadir' or 'pareto'
|
|
545
|
-
or fixed point (in objective function space).
|
|
546
|
-
|
|
547
|
-
"""
|
|
548
|
-
|
|
549
|
-
HEADER_ROW = 2
|
|
550
|
-
ENCODING = encoding
|
|
551
|
-
prm_names = []
|
|
552
|
-
obj_names = []
|
|
553
|
-
cns_names = []
|
|
554
|
-
is_restart = False
|
|
555
|
-
is_processing = False
|
|
556
|
-
_df = None # in case without client
|
|
557
|
-
|
|
558
|
-
def __init__(
|
|
559
|
-
self,
|
|
560
|
-
history_path,
|
|
561
|
-
prm_names=None,
|
|
562
|
-
obj_names=None,
|
|
563
|
-
cns_names=None,
|
|
564
|
-
client=None,
|
|
565
|
-
hv_reference=None,
|
|
566
|
-
):
|
|
567
|
-
# hypervolume 計算メソッド
|
|
568
|
-
self._hv_reference = 'dynamic-pareto' if hv_reference is None else hv_reference
|
|
569
|
-
|
|
570
|
-
# 引数の処理
|
|
571
|
-
self.path = history_path # .csv
|
|
572
|
-
self.prm_names = prm_names
|
|
573
|
-
self.obj_names = obj_names
|
|
574
|
-
self.cns_names = cns_names
|
|
575
|
-
self.extra_data = dict()
|
|
576
|
-
self.meta_columns = None
|
|
577
|
-
self.__scheduler_address = client.scheduler.address if client is not None else None
|
|
578
|
-
|
|
579
|
-
# 最適化実行中かどうか
|
|
580
|
-
self.is_processing = client is not None
|
|
581
|
-
|
|
582
|
-
# 最適化実行中の process monitor である場合
|
|
583
|
-
if self.is_processing:
|
|
584
|
-
|
|
585
|
-
# csv が存在すれば続きからモード
|
|
586
|
-
self.is_restart = os.path.isfile(self.path)
|
|
587
|
-
|
|
588
|
-
# 続きからなら df を読み込んで df にコピー
|
|
589
|
-
if self.is_restart:
|
|
590
|
-
self.load() # 中で meta_columns を読む
|
|
591
|
-
|
|
592
|
-
# そうでなければ df を初期化
|
|
593
|
-
else:
|
|
594
|
-
columns, meta_columns = self.create_df_columns()
|
|
595
|
-
df = pd.DataFrame()
|
|
596
|
-
for c in columns:
|
|
597
|
-
df[c] = None
|
|
598
|
-
self.meta_columns = meta_columns
|
|
599
|
-
self.set_df(df)
|
|
600
|
-
|
|
601
|
-
# 一時ファイルに書き込みを試み、UnicodeEncodeError が出ないかチェック
|
|
602
|
-
import tempfile
|
|
603
|
-
try:
|
|
604
|
-
with tempfile.TemporaryFile() as f:
|
|
605
|
-
self.save(_f=f)
|
|
606
|
-
except UnicodeEncodeError:
|
|
607
|
-
raise ValueError(Msg.ERR_CANNOT_ENCODING)
|
|
608
|
-
|
|
609
|
-
# visualization only の場合
|
|
610
|
-
else:
|
|
611
|
-
# csv が存在しなければおかしい
|
|
612
|
-
if not os.path.isfile(self.path):
|
|
613
|
-
raise FileNotFoundError(self.path)
|
|
614
|
-
|
|
615
|
-
# csv の読み込み
|
|
616
|
-
self.load()
|
|
617
|
-
|
|
618
|
-
def load(self):
|
|
619
|
-
"""Load existing result csv."""
|
|
620
|
-
|
|
621
|
-
# df を読み込む
|
|
622
|
-
df = pd.read_csv(self.path, encoding=self.ENCODING, header=self.HEADER_ROW)
|
|
623
|
-
|
|
624
|
-
# meta_columns を読み込む
|
|
625
|
-
with open(self.path, mode='r', encoding=self.ENCODING, newline='\n') as f:
|
|
626
|
-
reader = csv.reader(f, delimiter=',')
|
|
627
|
-
self.meta_columns = reader.__next__()
|
|
628
|
-
|
|
629
|
-
# 最適化問題を読み込む
|
|
630
|
-
columns = df.columns
|
|
631
|
-
prm_names = [column for i, column in enumerate(columns) if self.meta_columns[i] == 'prm']
|
|
632
|
-
obj_names = [column for i, column in enumerate(columns) if self.meta_columns[i] == 'obj']
|
|
633
|
-
cns_names = [column for i, column in enumerate(columns) if self.meta_columns[i] == 'cns']
|
|
634
|
-
|
|
635
|
-
# is_restart の場合、読み込んだ names と引数の names が一致するか確認しておく
|
|
636
|
-
if self.is_restart:
|
|
637
|
-
if prm_names != self.prm_names: raise ValueError(Msg.ERR_PROBLEM_MISMATCH)
|
|
638
|
-
if obj_names != self.obj_names: raise ValueError(Msg.ERR_PROBLEM_MISMATCH)
|
|
639
|
-
if cns_names != self.cns_names: raise ValueError(Msg.ERR_PROBLEM_MISMATCH)
|
|
640
|
-
|
|
641
|
-
# visualization only の場合、読み込んだ names をも load する
|
|
642
|
-
if not self.is_processing:
|
|
643
|
-
self.prm_names = prm_names
|
|
644
|
-
self.obj_names = obj_names
|
|
645
|
-
self.cns_names = cns_names
|
|
646
|
-
|
|
647
|
-
self.set_df(df)
|
|
648
|
-
|
|
649
|
-
def filter_valid(self, df_, keep_trial_num=True):
|
|
650
|
-
buff = df_[self.obj_names].notna()
|
|
651
|
-
idx = buff.prod(axis=1).astype(bool)
|
|
652
|
-
filtered_df = df_[idx]
|
|
653
|
-
if not keep_trial_num:
|
|
654
|
-
filtered_df.loc[:, 'trial'] = np.arange(len(filtered_df)) + 1
|
|
655
|
-
return filtered_df
|
|
656
|
-
|
|
657
|
-
def get_df(self, valid_only=False) -> pd.DataFrame:
|
|
658
|
-
if self.__scheduler_address is None:
|
|
659
|
-
if valid_only:
|
|
660
|
-
return self.filter_valid(self._df)
|
|
661
|
-
else:
|
|
662
|
-
return self._df
|
|
663
|
-
else:
|
|
664
|
-
# scheduler がまだ存命か確認する
|
|
665
|
-
try:
|
|
666
|
-
with Lock('access-df'):
|
|
667
|
-
client_: 'Client' = get_client(self.__scheduler_address)
|
|
668
|
-
if 'df' in client_.list_datasets():
|
|
669
|
-
if valid_only:
|
|
670
|
-
return self.filter_valid(client_.get_dataset('df'))
|
|
671
|
-
else:
|
|
672
|
-
return client_.get_dataset('df')
|
|
673
|
-
else:
|
|
674
|
-
logger.debug('Access df of History before it is initialized.')
|
|
675
|
-
return pd.DataFrame()
|
|
676
|
-
except OSError:
|
|
677
|
-
logger.error('Scheduler is already dead. Most frequent reason to show this message is that the pyfemtet monitor UI is not refreshed even if the main optimization process is terminated.')
|
|
678
|
-
return pd.DataFrame()
|
|
679
|
-
|
|
680
|
-
def set_df(self, df: pd.DataFrame):
|
|
681
|
-
if self.__scheduler_address is None:
|
|
682
|
-
self._df = df
|
|
683
|
-
else:
|
|
684
|
-
try:
|
|
685
|
-
with Lock('access-df'):
|
|
686
|
-
client_: 'Client' = get_client(self.__scheduler_address)
|
|
687
|
-
if 'df' in client_.list_datasets():
|
|
688
|
-
client_.unpublish_dataset('df') # 更新する場合は前もって削除が必要、本来は dask collection をここに入れる使い方をする。
|
|
689
|
-
client_.publish_dataset(**dict(
|
|
690
|
-
df=df
|
|
691
|
-
))
|
|
692
|
-
except OSError:
|
|
693
|
-
logger.error('Scheduler is already dead. Most frequent reasen to show this message is that the pyfemtet monitor UI is not refreshed even if the main optimization process is terminated.')
|
|
694
|
-
|
|
695
|
-
def create_df_columns(self):
|
|
696
|
-
"""Create columns of history."""
|
|
697
|
-
|
|
698
|
-
# df として保有するカラムを生成
|
|
699
|
-
columns = list()
|
|
700
|
-
|
|
701
|
-
# columns のメタデータを作成
|
|
702
|
-
meta_columns = list()
|
|
703
|
-
|
|
704
|
-
# trial
|
|
705
|
-
columns.append('trial') # index
|
|
706
|
-
meta_columns.append('') # extra_data. save 時に中身を記入する。
|
|
707
|
-
|
|
708
|
-
# parameter
|
|
709
|
-
for prm_name in self.prm_names:
|
|
710
|
-
columns.extend([prm_name, prm_name + '_lower_bound', prm_name + '_upper_bound'])
|
|
711
|
-
meta_columns.extend(['prm', 'prm_lb', 'prm_ub'])
|
|
712
|
-
|
|
713
|
-
# objective relative
|
|
714
|
-
for name in self.obj_names:
|
|
715
|
-
columns.append(name)
|
|
716
|
-
meta_columns.append('obj')
|
|
717
|
-
columns.append(name + '_direction')
|
|
718
|
-
meta_columns.append('obj_direction')
|
|
719
|
-
columns.append('non_domi')
|
|
720
|
-
meta_columns.append('')
|
|
721
|
-
|
|
722
|
-
# constraint relative
|
|
723
|
-
for name in self.cns_names:
|
|
724
|
-
columns.append(name)
|
|
725
|
-
meta_columns.append('cns')
|
|
726
|
-
columns.append(name + '_lower_bound')
|
|
727
|
-
meta_columns.append('cns_lb')
|
|
728
|
-
columns.append(name + '_upper_bound')
|
|
729
|
-
meta_columns.append('cns_ub')
|
|
730
|
-
columns.append('feasible')
|
|
731
|
-
meta_columns.append('')
|
|
732
|
-
|
|
733
|
-
# the others
|
|
734
|
-
columns.append('hypervolume')
|
|
735
|
-
meta_columns.append('')
|
|
736
|
-
columns.append('message')
|
|
737
|
-
meta_columns.append('')
|
|
738
|
-
columns.append('time')
|
|
739
|
-
meta_columns.append('')
|
|
740
|
-
|
|
741
|
-
return columns, meta_columns
|
|
742
|
-
|
|
743
|
-
def generate_hidden_infeasible_result(self):
|
|
744
|
-
y = np.full_like(np.zeros(len(self.obj_names)), np.nan)
|
|
745
|
-
c = np.full_like(np.zeros(len(self.cns_names)), np.nan)
|
|
746
|
-
return y, c
|
|
747
|
-
|
|
748
|
-
def is_hidden_infeasible_result(self, y):
|
|
749
|
-
return np.all(np.isnan(y))
|
|
750
|
-
|
|
751
|
-
def record(
|
|
752
|
-
self,
|
|
753
|
-
parameters,
|
|
754
|
-
objectives,
|
|
755
|
-
constraints,
|
|
756
|
-
obj_values,
|
|
757
|
-
cns_values,
|
|
758
|
-
message,
|
|
759
|
-
postprocess_func,
|
|
760
|
-
postprocess_args,
|
|
761
|
-
):
|
|
762
|
-
"""Records the optimization results in the history.
|
|
763
|
-
|
|
764
|
-
Record only. NOT save.
|
|
765
|
-
|
|
766
|
-
Args:
|
|
767
|
-
parameters (pd.DataFrame): The parameter values.
|
|
768
|
-
objectives (dict): The objective functions.
|
|
769
|
-
constraints (dict): The constraint functions.
|
|
770
|
-
obj_values (list): The objective values.
|
|
771
|
-
cns_values (list): The constraint values.
|
|
772
|
-
message (str): Additional information or messages related to the optimization results.
|
|
773
|
-
postprocess_func (Callable): fem method to call after solving. i.e. save result file. Must take trial(int) for 1st argument.
|
|
774
|
-
postprocess_args (dict): arguments for `postprocess_func`. i.e. create binary data of result file in the worker process.
|
|
775
|
-
"""
|
|
776
|
-
|
|
777
|
-
# create row
|
|
778
|
-
row = list()
|
|
779
|
-
|
|
780
|
-
# trial(dummy)
|
|
781
|
-
row.append(-1)
|
|
782
|
-
|
|
783
|
-
# parameters
|
|
784
|
-
for i, _row in parameters.iterrows():
|
|
785
|
-
row.append(_row['value'])
|
|
786
|
-
row.append(_row['lower_bound'])
|
|
787
|
-
row.append(_row['upper_bound'])
|
|
788
|
-
|
|
789
|
-
# objectives and their direction
|
|
790
|
-
for (_, obj), obj_value in zip(objectives.items(), obj_values): # objectives, direction
|
|
791
|
-
row.extend([obj_value, obj.direction])
|
|
792
|
-
|
|
793
|
-
# non_domi (dummy)
|
|
794
|
-
row.append(False)
|
|
795
|
-
|
|
796
|
-
# constraints and their lb, ub and calculate each feasibility
|
|
797
|
-
feasible_list = []
|
|
798
|
-
for (_, cns), cns_value in zip(constraints.items(), cns_values): # cns, lb, ub
|
|
799
|
-
row.extend([cns_value, cns.lb, cns.ub])
|
|
800
|
-
feasible_list.append(is_feasible(cns_value, cns.lb, cns.ub))
|
|
801
|
-
|
|
802
|
-
# feasibility
|
|
803
|
-
row.append(all(feasible_list) and not self.is_hidden_infeasible_result(obj_values))
|
|
804
|
-
|
|
805
|
-
# the others
|
|
806
|
-
row.append(-1.) # dummy hypervolume
|
|
807
|
-
row.append(message) # message
|
|
808
|
-
row.append(datetime.datetime.now()) # time
|
|
809
|
-
|
|
810
|
-
with Lock('calc-history'):
|
|
811
|
-
|
|
812
|
-
df = self.get_df()
|
|
813
|
-
|
|
814
|
-
# append
|
|
815
|
-
if len(df) == 0:
|
|
816
|
-
df = pd.DataFrame([row], columns=df.columns)
|
|
817
|
-
else:
|
|
818
|
-
df.loc[len(df)] = row
|
|
819
|
-
|
|
820
|
-
# calc
|
|
821
|
-
df['trial'] = np.arange(len(df)) + 1 # 1 始まり
|
|
822
|
-
self._calc_non_domi(objectives, df) # update df
|
|
823
|
-
self._calc_hypervolume(objectives, df) # update df
|
|
824
|
-
|
|
825
|
-
self.set_df(df)
|
|
826
|
-
|
|
827
|
-
# save file
|
|
828
|
-
if postprocess_args is not None:
|
|
829
|
-
df = self.get_df()
|
|
830
|
-
trial = df['trial'].values[-1]
|
|
831
|
-
client = get_client() # always returns valid client
|
|
832
|
-
client.run_on_scheduler(postprocess_func, *(trial, df), **postprocess_args)
|
|
833
|
-
|
|
834
|
-
def _calc_non_domi(self, objectives, df):
|
|
835
|
-
|
|
836
|
-
# feasible のもののみ取り出してくる
|
|
837
|
-
idx = df['feasible'].values
|
|
838
|
-
pdf = df[idx]
|
|
839
|
-
|
|
840
|
-
# 目的関数の履歴を取り出してくる
|
|
841
|
-
solution_set = pdf[self.obj_names]
|
|
842
|
-
|
|
843
|
-
# 最小化問題の座標空間に変換する
|
|
844
|
-
for obj_column, (_, objective) in zip(self.obj_names, objectives.items()):
|
|
845
|
-
solution_set.loc[:, obj_column] = solution_set[obj_column].map(objective.convert)
|
|
846
|
-
|
|
847
|
-
# 非劣解(改)の計算
|
|
848
|
-
# 「全ての項目において
|
|
849
|
-
# 重複する解を除いて
|
|
850
|
-
# より優れているか又は同等である
|
|
851
|
-
# 他の解が存在しない解」であるかどうかを判定
|
|
852
|
-
non_domi = []
|
|
853
|
-
row: np.ndarray
|
|
854
|
-
another: np.ndarray
|
|
855
|
-
for i, row in enumerate(solution_set.values):
|
|
856
|
-
for j, another in enumerate(solution_set.values):
|
|
857
|
-
if i == j:
|
|
858
|
-
# 他と比べたいので
|
|
859
|
-
continue
|
|
860
|
-
elif np.allclose(row, another):
|
|
861
|
-
# 重複してたら他の解との比較に
|
|
862
|
-
# 判定を委ねたいので
|
|
863
|
-
continue
|
|
864
|
-
elif all(another <= row):
|
|
865
|
-
# 全項目について
|
|
866
|
-
# another のほうが
|
|
867
|
-
# 優れているか又は同等である
|
|
868
|
-
non_domi.append(False)
|
|
869
|
-
break
|
|
870
|
-
else:
|
|
871
|
-
# 全項目について優れているか又は
|
|
872
|
-
# 同等であるような他の解が
|
|
873
|
-
# 存在しなかった
|
|
874
|
-
non_domi.append(True)
|
|
875
|
-
|
|
876
|
-
# feasible も infeasible も一旦劣解にする
|
|
877
|
-
df['non_domi'] = False
|
|
878
|
-
|
|
879
|
-
# feasible のものに non_domi の評価結果を代入する
|
|
880
|
-
if len(non_domi) > 0:
|
|
881
|
-
df.loc[idx, 'non_domi'] = non_domi
|
|
882
|
-
|
|
883
|
-
def _calc_hypervolume(self, objectives, df):
|
|
884
|
-
|
|
885
|
-
# 単目的最適化ならば 0 埋めして終了
|
|
886
|
-
if len(objectives) < 2:
|
|
887
|
-
df.loc[len(df) - 1, 'hypervolume'] = 0.
|
|
888
|
-
return
|
|
889
|
-
|
|
890
|
-
# 最小化問題に変換された objective values を取得
|
|
891
|
-
raw_objective_values = df[self.obj_names].values
|
|
892
|
-
objective_values = np.full_like(raw_objective_values, np.nan, dtype=float)
|
|
893
|
-
for n_trial in range(len(raw_objective_values)):
|
|
894
|
-
for obj_idx, (_, objective) in enumerate(objectives.items()):
|
|
895
|
-
objective_values[n_trial, obj_idx] = objective.convert(raw_objective_values[n_trial, obj_idx])
|
|
896
|
-
|
|
897
|
-
# pareto front を取得
|
|
898
|
-
def get_pareto(objective_values_, with_partial=False):
|
|
899
|
-
ret = None
|
|
900
|
-
if with_partial:
|
|
901
|
-
ret = []
|
|
902
|
-
|
|
903
|
-
pareto_set_ = np.empty((0, len(self.obj_names)))
|
|
904
|
-
for i in range(len(objective_values_)):
|
|
905
|
-
target = objective_values_[i]
|
|
906
|
-
|
|
907
|
-
if any(np.isnan(target)):
|
|
908
|
-
# infeasible な場合 pareto_set の計算に含めない
|
|
909
|
-
dominated = True
|
|
910
|
-
|
|
911
|
-
else:
|
|
912
|
-
dominated = False
|
|
913
|
-
# TODO: Array の計算に直して高速化する
|
|
914
|
-
for j in range(len(objective_values_)):
|
|
915
|
-
compare = objective_values_[j]
|
|
916
|
-
if all(target > compare):
|
|
917
|
-
dominated = True
|
|
918
|
-
break
|
|
919
|
-
|
|
920
|
-
if not dominated:
|
|
921
|
-
pareto_set_ = np.concatenate([pareto_set_, [target]], axis=0)
|
|
922
|
-
|
|
923
|
-
if ret is not None:
|
|
924
|
-
ret.append(np.array(pareto_set_))
|
|
925
|
-
|
|
926
|
-
if ret is not None:
|
|
927
|
-
return pareto_set_, ret
|
|
928
|
-
else:
|
|
929
|
-
return pareto_set_
|
|
930
|
-
|
|
931
|
-
def get_valid_worst_converted_objective_values(objective_values_: np.ndarray) -> np.ndarray:
|
|
932
|
-
# objective_values.max(axis=0)
|
|
933
|
-
ret = []
|
|
934
|
-
for row in objective_values_:
|
|
935
|
-
if not any(np.isnan(row)):
|
|
936
|
-
ret.append(row)
|
|
937
|
-
return np.array(ret).max(axis=0)
|
|
938
|
-
|
|
939
|
-
if (
|
|
940
|
-
isinstance(self._hv_reference, np.ndarray)
|
|
941
|
-
or isinstance(self._hv_reference, list)
|
|
942
|
-
):
|
|
943
|
-
_buff = np.array(self._hv_reference)
|
|
944
|
-
assert _buff.shape == (len(self.obj_names),)
|
|
945
|
-
|
|
946
|
-
ref_point = np.array(
|
|
947
|
-
[obj.convert(raw_value) for obj, raw_value in zip(objectives.values(), _buff)]
|
|
948
|
-
)
|
|
949
|
-
|
|
950
|
-
_buff = get_pareto(objective_values)
|
|
951
|
-
|
|
952
|
-
pareto_set = np.empty((0, len(objectives)))
|
|
953
|
-
for pareto_sol in _buff:
|
|
954
|
-
if all(pareto_sol < ref_point):
|
|
955
|
-
pareto_set = np.concatenate([pareto_set, [pareto_sol]], axis=0)
|
|
956
|
-
|
|
957
|
-
hv = compute_hypervolume(pareto_set, ref_point)
|
|
958
|
-
df.loc[len(df) - 1, 'hypervolume'] = hv
|
|
959
|
-
return
|
|
960
|
-
|
|
961
|
-
elif self._hv_reference == 'dynamic-pareto':
|
|
962
|
-
pareto_set, pareto_set_list = get_pareto(objective_values, with_partial=True)
|
|
963
|
-
for i, partial_pareto_set in enumerate(pareto_set_list):
|
|
964
|
-
# 並列計算時など Valid な解がまだ一つもない場合は pareto_set が長さ 0 になる
|
|
965
|
-
# その場合 max() を取るとエラーになる
|
|
966
|
-
if len(pareto_set) == 0:
|
|
967
|
-
df.loc[i, 'hypervolume'] = 0
|
|
968
|
-
else:
|
|
969
|
-
ref_point = pareto_set.max(axis=0) + 1e-8
|
|
970
|
-
hv = compute_hypervolume(partial_pareto_set, ref_point)
|
|
971
|
-
df.loc[i, 'hypervolume'] = hv
|
|
972
|
-
return
|
|
973
|
-
|
|
974
|
-
elif self._hv_reference == 'dynamic-nadir':
|
|
975
|
-
_, pareto_set_list = get_pareto(objective_values, with_partial=True)
|
|
976
|
-
for i, partial_pareto_set in enumerate(pareto_set_list):
|
|
977
|
-
# filter valid objective values only
|
|
978
|
-
values = get_valid_worst_converted_objective_values(objective_values)
|
|
979
|
-
|
|
980
|
-
# 並列計算時など Valid な解がまだ一つもない場合は長さ 0 になる
|
|
981
|
-
# その場合 max() を取るとエラーになる
|
|
982
|
-
if len(values) == 0:
|
|
983
|
-
df.loc[i, 'hypervolume'] = 0
|
|
984
|
-
|
|
985
|
-
else:
|
|
986
|
-
ref_point = values.max(axis=0) + 1e-8
|
|
987
|
-
hv = compute_hypervolume(partial_pareto_set, ref_point)
|
|
988
|
-
df.loc[i, 'hypervolume'] = hv
|
|
989
|
-
return
|
|
990
|
-
|
|
991
|
-
elif self._hv_reference == 'nadir':
|
|
992
|
-
pareto_set = get_pareto(objective_values)
|
|
993
|
-
values = get_valid_worst_converted_objective_values(objective_values)
|
|
994
|
-
if len(values) == 0:
|
|
995
|
-
df.loc[len(df) - 1, 'hypervolume'] = 0
|
|
996
|
-
else:
|
|
997
|
-
ref_point = values.max(axis=0) + 1e-8
|
|
998
|
-
hv = compute_hypervolume(pareto_set, ref_point)
|
|
999
|
-
df.loc[len(df) - 1, 'hypervolume'] = hv
|
|
1000
|
-
return
|
|
1001
|
-
|
|
1002
|
-
elif self._hv_reference == 'pareto':
|
|
1003
|
-
pareto_set = get_pareto(objective_values)
|
|
1004
|
-
if len(pareto_set) == 0:
|
|
1005
|
-
df.loc[len(df) - 1, 'hypervolume'] = 0
|
|
1006
|
-
else:
|
|
1007
|
-
ref_point = pareto_set.max(axis=0) + 1e-8
|
|
1008
|
-
hv = compute_hypervolume(pareto_set, ref_point)
|
|
1009
|
-
df.loc[len(df) - 1, 'hypervolume'] = hv
|
|
1010
|
-
return
|
|
1011
|
-
|
|
1012
|
-
else:
|
|
1013
|
-
raise NotImplementedError(f'Invalid Hypervolume reference point calculation method: {self._hv_reference}')
|
|
1014
|
-
|
|
1015
|
-
def save(self, _f=None):
|
|
1016
|
-
"""Save csv file."""
|
|
1017
|
-
|
|
1018
|
-
df = self.get_df()
|
|
1019
|
-
|
|
1020
|
-
# extra_data の更新
|
|
1021
|
-
self.meta_columns[0] = json.dumps(self.extra_data)
|
|
1022
|
-
|
|
1023
|
-
if _f is None:
|
|
1024
|
-
# save df with columns with prefix
|
|
1025
|
-
with open(self.path, 'w', encoding=self.ENCODING) as f:
|
|
1026
|
-
writer = csv.writer(f, delimiter=',', lineterminator="\n")
|
|
1027
|
-
writer.writerow(self.meta_columns)
|
|
1028
|
-
for i in range(self.HEADER_ROW-1):
|
|
1029
|
-
writer.writerow([''] * len(self.meta_columns))
|
|
1030
|
-
df.to_csv(f, index=None, encoding=self.ENCODING, lineterminator='\n')
|
|
1031
|
-
else: # test
|
|
1032
|
-
df.to_csv(_f, index=None, encoding=self.ENCODING, lineterminator='\n')
|
|
1033
|
-
|
|
1034
|
-
def create_optuna_study(self):
|
|
1035
|
-
# create study
|
|
1036
|
-
kwargs = dict(
|
|
1037
|
-
# storage='sqlite:///' + os.path.basename(self.path) + '_dummy.db',
|
|
1038
|
-
sampler=None, pruner=None, study_name='dummy',
|
|
1039
|
-
load_if_exists=True,
|
|
1040
|
-
)
|
|
1041
|
-
|
|
1042
|
-
if len(self.obj_names) == 1:
|
|
1043
|
-
kwargs.update(dict(direction='minimize'))
|
|
1044
|
-
else:
|
|
1045
|
-
kwargs.update(dict(directions=['minimize']*len(self.obj_names)))
|
|
1046
|
-
|
|
1047
|
-
study = optuna.create_study(**kwargs)
|
|
1048
|
-
|
|
1049
|
-
# add trial to study
|
|
1050
|
-
df: pd.DataFrame = self.get_df(valid_only=True)
|
|
1051
|
-
for i, row in df.iterrows():
|
|
1052
|
-
FD = optuna.distributions.FloatDistribution
|
|
1053
|
-
kwargs = dict(
|
|
1054
|
-
state=optuna.trial.TrialState.COMPLETE,
|
|
1055
|
-
params={k: v for k, v in zip(self.prm_names, row[self.prm_names])},
|
|
1056
|
-
distributions={k: FD(row[f'{k}_lower_bound'], row[f'{k}_upper_bound']) for k in self.prm_names},
|
|
1057
|
-
user_attrs=None, # TODO: add constraint information by row['feasible']
|
|
1058
|
-
)
|
|
1059
|
-
|
|
1060
|
-
# objective or objectives
|
|
1061
|
-
if len(self.obj_names) == 1:
|
|
1062
|
-
kwargs.update(dict(value=row[self.obj_names].values[0]))
|
|
1063
|
-
else:
|
|
1064
|
-
kwargs.update(dict(values=row[self.obj_names].values))
|
|
1065
|
-
trial = optuna.create_trial(**kwargs)
|
|
1066
|
-
study.add_trial(trial)
|
|
1067
|
-
|
|
1068
|
-
return study
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
class _OptimizationStatusActor:
|
|
1072
|
-
status_int = -1
|
|
1073
|
-
status = 'undefined'
|
|
1074
|
-
|
|
1075
|
-
def set(self, value, text):
|
|
1076
|
-
self.status_int = value
|
|
1077
|
-
self.status = text
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
class OptimizationStatus:
|
|
1081
|
-
"""Optimization status."""
|
|
1082
|
-
|
|
1083
|
-
UNDEFINED = -1
|
|
1084
|
-
INITIALIZING = 0
|
|
1085
|
-
SETTING_UP = 10
|
|
1086
|
-
LAUNCHING_FEM = 20
|
|
1087
|
-
WAIT_OTHER_WORKERS = 22
|
|
1088
|
-
# WAIT_1ST = 25
|
|
1089
|
-
RUNNING = 30
|
|
1090
|
-
INTERRUPTING = 40
|
|
1091
|
-
TERMINATED = 50
|
|
1092
|
-
TERMINATE_ALL = 60
|
|
1093
|
-
CRASHED = 70
|
|
1094
|
-
|
|
1095
|
-
def __init__(self, client, worker_address, name='entire'):
|
|
1096
|
-
self._future = client.submit(
|
|
1097
|
-
_OptimizationStatusActor,
|
|
1098
|
-
actor=True,
|
|
1099
|
-
workers=[worker_address],
|
|
1100
|
-
allow_other_workers=False,
|
|
1101
|
-
)
|
|
1102
|
-
self._actor = self._future.result()
|
|
1103
|
-
self.name = name
|
|
1104
|
-
self.set(self.INITIALIZING)
|
|
1105
|
-
|
|
1106
|
-
@classmethod
|
|
1107
|
-
def const_to_str(cls, status_const):
|
|
1108
|
-
"""Convert optimization status integer to message."""
|
|
1109
|
-
if status_const == cls.UNDEFINED: return 'Undefined'
|
|
1110
|
-
if status_const == cls.INITIALIZING: return 'Initializing'
|
|
1111
|
-
if status_const == cls.SETTING_UP: return 'Setting up'
|
|
1112
|
-
if status_const == cls.LAUNCHING_FEM: return 'Launching FEM processes'
|
|
1113
|
-
if status_const == cls.WAIT_OTHER_WORKERS: return 'Waiting for launching other processes'
|
|
1114
|
-
# if status_const == cls.WAIT_1ST: return 'Running and waiting for 1st FEM result.'
|
|
1115
|
-
if status_const == cls.RUNNING: return 'Running'
|
|
1116
|
-
if status_const == cls.INTERRUPTING: return 'Interrupting'
|
|
1117
|
-
if status_const == cls.TERMINATED: return 'Terminated'
|
|
1118
|
-
if status_const == cls.TERMINATE_ALL: return 'Terminate_all'
|
|
1119
|
-
if status_const == cls.CRASHED: return 'Crashed'
|
|
1120
|
-
|
|
1121
|
-
def set(self, status_const):
|
|
1122
|
-
"""Set optimization status."""
|
|
1123
|
-
self._actor.set(status_const, self.const_to_str(status_const)).result()
|
|
1124
|
-
msg = f'---{self.const_to_str(status_const)}---'
|
|
1125
|
-
if (status_const == self.INITIALIZING) and (self.name != 'entire'):
|
|
1126
|
-
msg += f' (for Worker {self.name})'
|
|
1127
|
-
if self.name == 'entire':
|
|
1128
|
-
msg = '(entire) ' + msg
|
|
1129
|
-
logger.info(msg)
|
|
1130
|
-
|
|
1131
|
-
def get(self) -> int:
|
|
1132
|
-
"""Get optimization status."""
|
|
1133
|
-
return self._actor.status_int
|
|
1134
|
-
|
|
1135
|
-
def get_text(self) -> str:
|
|
1136
|
-
"""Get optimization status message."""
|
|
1137
|
-
return self._actor.status
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
class _MonitorHostRecordActor:
|
|
1141
|
-
host = None
|
|
1142
|
-
port = None
|
|
1143
|
-
|
|
1144
|
-
def set(self, host, port):
|
|
1145
|
-
self.host = host
|
|
1146
|
-
self.port = port
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
class MonitorHostRecord:
|
|
1150
|
-
|
|
1151
|
-
def __init__(self, client, worker_name):
|
|
1152
|
-
self._future = client.submit(
|
|
1153
|
-
_MonitorHostRecordActor,
|
|
1154
|
-
actor=True,
|
|
1155
|
-
workers=(worker_name,),
|
|
1156
|
-
allow_other_workers=False,
|
|
1157
|
-
)
|
|
1158
|
-
self._actor = self._future.result()
|
|
1159
|
-
|
|
1160
|
-
def set(self, host, port):
|
|
1161
|
-
self._actor.set(host, port).result()
|
|
1162
|
-
|
|
1163
|
-
def get(self):
|
|
1164
|
-
host = self._actor.host
|
|
1165
|
-
port = self._actor.port
|
|
1166
|
-
if host is None and port is None:
|
|
1167
|
-
return dict()
|
|
1168
|
-
else:
|
|
1169
|
-
return dict(host=host, port=port)
|