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.

Files changed (272) hide show
  1. pyfemtet/__init__.py +6 -1
  2. pyfemtet/_i18n/1. make_pot_and_update_po.bat +8 -0
  3. pyfemtet/_i18n/2. build_mo.bat +5 -0
  4. pyfemtet/_i18n/__init__.py +4 -0
  5. pyfemtet/_i18n/babel.cfg +2 -0
  6. pyfemtet/_i18n/i18n.py +37 -0
  7. pyfemtet/_i18n/locales/ja/LC_MESSAGES/messages.mo +0 -0
  8. pyfemtet/_i18n/locales/ja/LC_MESSAGES/messages.po +1020 -0
  9. pyfemtet/_i18n/locales/messages.pot +987 -0
  10. pyfemtet/{_message → _i18n}/messages.py +128 -41
  11. pyfemtet/_util/closing.py +19 -0
  12. pyfemtet/_util/dask_util.py +89 -7
  13. pyfemtet/_util/df_util.py +29 -0
  14. pyfemtet/_util/excel_macro_util.py +8 -3
  15. pyfemtet/_util/excel_parse_util.py +43 -23
  16. pyfemtet/_util/femtet_access_inspection.py +120 -0
  17. pyfemtet/{_femtet_config_util/autosave.py → _util/femtet_autosave.py} +7 -0
  18. pyfemtet/_util/femtet_exit.py +105 -0
  19. pyfemtet/_util/femtet_version.py +20 -0
  20. pyfemtet/_util/helper.py +94 -0
  21. pyfemtet/_util/process_util.py +107 -0
  22. pyfemtet/_util/str_enum.py +44 -0
  23. pyfemtet/core.py +15 -47
  24. pyfemtet/dispatch_extensions/__init__.py +8 -11
  25. pyfemtet/dispatch_extensions/_impl.py +42 -198
  26. pyfemtet/logger/__init__.py +8 -1
  27. pyfemtet/logger/_impl.py +5 -6
  28. pyfemtet/opt/__init__.py +3 -17
  29. pyfemtet/opt/exceptions.py +45 -0
  30. pyfemtet/opt/femopt.py +608 -0
  31. pyfemtet/opt/history/__init__.py +11 -0
  32. pyfemtet/opt/history/_history.py +1404 -0
  33. pyfemtet/opt/history/_hypervolume.py +169 -0
  34. pyfemtet/opt/history/_optimality.py +79 -0
  35. pyfemtet/opt/interface/__init__.py +17 -24
  36. pyfemtet/opt/interface/_base_interface.py +222 -0
  37. pyfemtet/opt/interface/_excel_interface/__init__.py +3 -0
  38. pyfemtet/opt/interface/_excel_interface/debug-excel-interface.xlsm +0 -0
  39. pyfemtet/opt/interface/_excel_interface/excel_interface.py +999 -0
  40. pyfemtet/opt/interface/_femtet_interface/__init__.py +3 -0
  41. pyfemtet/opt/interface/{_femtet_parametric.py → _femtet_interface/_femtet_parametric.py} +20 -12
  42. pyfemtet/opt/interface/{_femtet.py → _femtet_interface/femtet_interface.py} +505 -349
  43. pyfemtet/opt/interface/_femtet_with_nx_interface/__init__.py +5 -0
  44. pyfemtet/opt/interface/_femtet_with_nx_interface/femtet_with_nx_interface.py +230 -0
  45. pyfemtet/opt/interface/_femtet_with_nx_interface/model1.prt +0 -0
  46. pyfemtet/opt/interface/_femtet_with_nx_interface/model1.x_t +98 -0
  47. pyfemtet/opt/interface/{_femtet_with_nx → _femtet_with_nx_interface}/update_model.py +1 -3
  48. pyfemtet/opt/interface/_femtet_with_solidworks/__init__.py +5 -0
  49. pyfemtet/opt/interface/_femtet_with_solidworks/femtet_with_solidworks_interface.py +122 -0
  50. pyfemtet/opt/interface/_solidworks_interface/__init__.py +5 -0
  51. pyfemtet/opt/interface/_solidworks_interface/solidworks_interface.py +206 -0
  52. pyfemtet/opt/interface/_surrogate_model_interface/__init__.py +8 -0
  53. pyfemtet/opt/interface/_surrogate_model_interface/base_surrogate_interface.py +150 -0
  54. pyfemtet/opt/interface/_surrogate_model_interface/botorch_interface.py +298 -0
  55. pyfemtet/opt/interface/_surrogate_model_interface/debug-pof-botorch.reccsv +18 -0
  56. pyfemtet/opt/interface/_with_excel_settings/__init__.py +61 -0
  57. pyfemtet/opt/interface/_with_excel_settings/with_excel_settings.py +134 -0
  58. pyfemtet/opt/meta_script/YAML_Generator.xlsm +0 -0
  59. pyfemtet/opt/meta_script/__main__.py +58 -36
  60. pyfemtet/opt/optimizer/__init__.py +7 -9
  61. pyfemtet/opt/optimizer/_base_optimizer.py +885 -0
  62. pyfemtet/opt/optimizer/optuna_optimizer/__init__.py +9 -0
  63. pyfemtet/opt/optimizer/optuna_optimizer/_optuna_attribute.py +73 -0
  64. pyfemtet/opt/optimizer/optuna_optimizer/_optuna_optimizer.py +678 -0
  65. pyfemtet/opt/optimizer/optuna_optimizer/_pof_botorch/__init__.py +7 -0
  66. pyfemtet/opt/optimizer/optuna_optimizer/_pof_botorch/debug-pof-botorch.reccsv +18 -0
  67. pyfemtet/opt/optimizer/optuna_optimizer/_pof_botorch/enable_nonlinear_constraint.py +244 -0
  68. pyfemtet/opt/optimizer/optuna_optimizer/_pof_botorch/pof_botorch_sampler.py +1249 -0
  69. pyfemtet/opt/optimizer/optuna_optimizer/wat_ex14_parametric_jp.femprj +0 -0
  70. pyfemtet/opt/optimizer/scipy_optimizer/__init__.py +1 -0
  71. pyfemtet/opt/optimizer/scipy_optimizer/_scipy_optimizer.py +364 -0
  72. pyfemtet/opt/prediction/__init__.py +7 -0
  73. pyfemtet/opt/prediction/_botorch_utils.py +133 -0
  74. pyfemtet/opt/prediction/_gpytorch_modules_extension.py +142 -0
  75. pyfemtet/opt/prediction/_helper.py +155 -0
  76. pyfemtet/opt/prediction/_model.py +118 -0
  77. pyfemtet/opt/problem/problem.py +304 -0
  78. pyfemtet/opt/problem/variable_manager/__init__.py +20 -0
  79. pyfemtet/opt/problem/variable_manager/_string_as_expression.py +115 -0
  80. pyfemtet/opt/problem/variable_manager/_variable_manager.py +295 -0
  81. pyfemtet/opt/visualization/history_viewer/__main__.py +5 -0
  82. pyfemtet/opt/visualization/{_base.py → history_viewer/_base_application.py} +18 -13
  83. pyfemtet/opt/visualization/history_viewer/_common_pages.py +150 -0
  84. pyfemtet/opt/visualization/{_complex_components → history_viewer/_complex_components}/alert_region.py +10 -5
  85. pyfemtet/opt/visualization/{_complex_components → history_viewer/_complex_components}/control_femtet.py +16 -13
  86. pyfemtet/opt/visualization/{_complex_components → history_viewer/_complex_components}/main_graph.py +117 -47
  87. pyfemtet/opt/visualization/{_complex_components → history_viewer/_complex_components}/pm_graph.py +159 -138
  88. pyfemtet/opt/visualization/history_viewer/_process_monitor/_application.py +173 -0
  89. pyfemtet/opt/visualization/history_viewer/_process_monitor/_pages.py +291 -0
  90. pyfemtet/opt/visualization/{_wrapped_components → history_viewer/_wrapped_components}/dbc.py +1 -1
  91. pyfemtet/opt/visualization/{_wrapped_components → history_viewer/_wrapped_components}/dcc.py +1 -1
  92. pyfemtet/opt/visualization/{_wrapped_components → history_viewer/_wrapped_components}/html.py +1 -1
  93. pyfemtet/opt/visualization/history_viewer/result_viewer/__main__.py +5 -0
  94. pyfemtet/opt/visualization/{result_viewer/application.py → history_viewer/result_viewer/_application.py} +6 -6
  95. pyfemtet/opt/visualization/{result_viewer/pages.py → history_viewer/result_viewer/_pages.py} +106 -82
  96. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08.csv +18 -0
  97. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08.db +0 -0
  98. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8.jpg +0 -0
  99. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8.log +45 -0
  100. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8.pdt +0 -0
  101. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_1.jpg +0 -0
  102. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_1.pdt +0 -0
  103. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_10.jpg +0 -0
  104. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_10.pdt +0 -0
  105. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_11.jpg +0 -0
  106. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_11.pdt +0 -0
  107. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_12.jpg +0 -0
  108. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_12.pdt +0 -0
  109. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_13.jpg +0 -0
  110. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_13.pdt +0 -0
  111. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_14.jpg +0 -0
  112. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_14.pdt +0 -0
  113. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_15.jpg +0 -0
  114. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_15.pdt +0 -0
  115. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_16.jpg +0 -0
  116. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_16.pdt +0 -0
  117. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_17.jpg +0 -0
  118. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_17.pdt +0 -0
  119. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_18.jpg +0 -0
  120. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_18.pdt +0 -0
  121. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_19.jpg +0 -0
  122. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_19.pdt +0 -0
  123. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_2.jpg +0 -0
  124. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_2.pdt +0 -0
  125. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_20.jpg +0 -0
  126. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_20.pdt +0 -0
  127. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_3.jpg +0 -0
  128. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_3.pdt +0 -0
  129. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_4.bgr +0 -0
  130. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_4.bnd +0 -0
  131. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_4.btr +0 -0
  132. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_4.jpg +0 -0
  133. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_4.mtl +0 -0
  134. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_4.pdt +0 -0
  135. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_4.prm +0 -0
  136. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_5.jpg +0 -0
  137. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_5.pdt +0 -0
  138. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_6.jpg +0 -0
  139. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_6.pdt +0 -0
  140. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_7.jpg +0 -0
  141. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_7.pdt +0 -0
  142. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_8.jpg +0 -0
  143. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_8.pdt +0 -0
  144. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_9.jpg +0 -0
  145. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.Results/ex8_trial_9.pdt +0 -0
  146. pyfemtet/opt/visualization/history_viewer/result_viewer/tutorial_files/tutorial_gau_ex08_parametric.femprj +0 -0
  147. pyfemtet/opt/visualization/plotter/main_figure_creator.py +536 -0
  148. pyfemtet/opt/visualization/plotter/pm_graph_creator.py +359 -0
  149. pyfemtet/opt/worker_status.py +120 -0
  150. {pyfemtet-0.9.5.dist-info → pyfemtet-1.0.0b0.dist-info}/METADATA +23 -24
  151. pyfemtet-1.0.0b0.dist-info/RECORD +172 -0
  152. pyfemtet-1.0.0b0.dist-info/entry_points.txt +3 -0
  153. pyfemtet/_femtet_config_util/exit.py +0 -59
  154. pyfemtet/_message/1. make_pot.bat +0 -11
  155. pyfemtet/_message/2. make_mo.bat +0 -6
  156. pyfemtet/_message/__init__.py +0 -5
  157. pyfemtet/_message/babel.cfg +0 -2
  158. pyfemtet/_message/locales/ja/LC_MESSAGES/messages.mo +0 -0
  159. pyfemtet/_message/locales/ja/LC_MESSAGES/messages.po +0 -570
  160. pyfemtet/_message/locales/messages.pot +0 -551
  161. pyfemtet/_warning.py +0 -87
  162. pyfemtet/brep/_impl.py +0 -18
  163. pyfemtet/opt/_femopt.py +0 -1007
  164. pyfemtet/opt/_femopt_core.py +0 -1169
  165. pyfemtet/opt/_test_utils/control_femtet.py +0 -39
  166. pyfemtet/opt/_test_utils/hyper_sphere.py +0 -24
  167. pyfemtet/opt/_test_utils/record_history.py +0 -130
  168. pyfemtet/opt/advanced_samples/excel_ui/(ref) original_project.femprj +0 -0
  169. pyfemtet/opt/advanced_samples/excel_ui/femtet-macro.xlsm +0 -0
  170. pyfemtet/opt/advanced_samples/excel_ui/pyfemtet-core.py +0 -291
  171. pyfemtet/opt/advanced_samples/excel_ui/test-pyfemtet-core.cmd +0 -22
  172. pyfemtet/opt/advanced_samples/restart/gal_ex13_parametric.femprj +0 -0
  173. pyfemtet/opt/advanced_samples/restart/gal_ex13_parametric_restart.py +0 -99
  174. pyfemtet/opt/advanced_samples/restart/gal_ex13_parametric_restart_jp.py +0 -102
  175. pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_create_training_data.py +0 -60
  176. pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_create_training_data_jp.py +0 -57
  177. pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_optimize_with_surrogate.py +0 -100
  178. pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_optimize_with_surrogate_jp.py +0 -90
  179. pyfemtet/opt/advanced_samples/surrogate_model/gal_ex13_parametric.femprj +0 -0
  180. pyfemtet/opt/interface/_base.py +0 -101
  181. pyfemtet/opt/interface/_excel_interface.py +0 -984
  182. pyfemtet/opt/interface/_femtet_excel.py +0 -141
  183. pyfemtet/opt/interface/_femtet_with_nx/__init__.py +0 -3
  184. pyfemtet/opt/interface/_femtet_with_nx/_interface.py +0 -178
  185. pyfemtet/opt/interface/_femtet_with_sldworks.py +0 -298
  186. pyfemtet/opt/interface/_surrogate/__init__.py +0 -5
  187. pyfemtet/opt/interface/_surrogate/_base.py +0 -129
  188. pyfemtet/opt/interface/_surrogate/_chaospy.py +0 -71
  189. pyfemtet/opt/interface/_surrogate/_singletaskgp.py +0 -71
  190. pyfemtet/opt/interface/_surrogate_excel.py +0 -102
  191. pyfemtet/opt/optimizer/_base.py +0 -376
  192. pyfemtet/opt/optimizer/_optuna/_botorch_patch/enable_nonlinear_constraint.py +0 -220
  193. pyfemtet/opt/optimizer/_optuna/_optuna.py +0 -434
  194. pyfemtet/opt/optimizer/_optuna/_pof_botorch.py +0 -1914
  195. pyfemtet/opt/optimizer/_scipy.py +0 -159
  196. pyfemtet/opt/optimizer/_scipy_scalar.py +0 -127
  197. pyfemtet/opt/optimizer/parameter.py +0 -113
  198. pyfemtet/opt/prediction/_base.py +0 -61
  199. pyfemtet/opt/prediction/single_task_gp.py +0 -119
  200. pyfemtet/opt/samples/femprj_sample/ParametricIF.femprj +0 -0
  201. pyfemtet/opt/samples/femprj_sample/ParametricIF.py +0 -29
  202. pyfemtet/opt/samples/femprj_sample/ParametricIF_test_result.reccsv +0 -13
  203. pyfemtet/opt/samples/femprj_sample/cad_ex01_NX.femprj +0 -0
  204. pyfemtet/opt/samples/femprj_sample/cad_ex01_NX.prt +0 -0
  205. pyfemtet/opt/samples/femprj_sample/cad_ex01_NX.py +0 -135
  206. pyfemtet/opt/samples/femprj_sample/cad_ex01_NX_test_result.reccsv +0 -23
  207. pyfemtet/opt/samples/femprj_sample/cad_ex01_SW.SLDPRT +0 -0
  208. pyfemtet/opt/samples/femprj_sample/cad_ex01_SW.femprj +0 -0
  209. pyfemtet/opt/samples/femprj_sample/cad_ex01_SW.py +0 -131
  210. pyfemtet/opt/samples/femprj_sample/cad_ex01_SW_test_result.reccsv +0 -23
  211. pyfemtet/opt/samples/femprj_sample/constrained_pipe.femprj +0 -0
  212. pyfemtet/opt/samples/femprj_sample/constrained_pipe.py +0 -96
  213. pyfemtet/opt/samples/femprj_sample/constrained_pipe_test_result.reccsv +0 -13
  214. pyfemtet/opt/samples/femprj_sample/gal_ex58_parametric.femprj +0 -0
  215. pyfemtet/opt/samples/femprj_sample/gal_ex58_parametric.py +0 -74
  216. pyfemtet/opt/samples/femprj_sample/gal_ex58_parametric_test_result.reccsv +0 -13
  217. pyfemtet/opt/samples/femprj_sample/gau_ex08_parametric.femprj +0 -0
  218. pyfemtet/opt/samples/femprj_sample/gau_ex08_parametric.py +0 -58
  219. pyfemtet/opt/samples/femprj_sample/gau_ex08_parametric_test_result.reccsv +0 -23
  220. pyfemtet/opt/samples/femprj_sample/gau_ex12_parametric.femprj +0 -0
  221. pyfemtet/opt/samples/femprj_sample/gau_ex12_parametric.py +0 -52
  222. pyfemtet/opt/samples/femprj_sample/her_ex40_parametric.femprj +0 -0
  223. pyfemtet/opt/samples/femprj_sample/her_ex40_parametric.py +0 -138
  224. pyfemtet/opt/samples/femprj_sample/her_ex40_parametric_test_result.reccsv +0 -18
  225. pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric.femprj +0 -0
  226. pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric.py +0 -60
  227. pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric_parallel.py +0 -61
  228. pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric_test_result.reccsv +0 -18
  229. pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric.femprj +0 -0
  230. pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric.py +0 -58
  231. pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric_parallel.py +0 -58
  232. pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric_test_result.reccsv +0 -18
  233. pyfemtet/opt/samples/femprj_sample_jp/ParametricIF_jp.femprj +0 -0
  234. pyfemtet/opt/samples/femprj_sample_jp/ParametricIF_jp.py +0 -29
  235. pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_NX_jp.femprj +0 -0
  236. pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_NX_jp.py +0 -129
  237. pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_SW_jp.femprj +0 -0
  238. pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_SW_jp.py +0 -125
  239. pyfemtet/opt/samples/femprj_sample_jp/constrained_pipe_jp.py +0 -93
  240. pyfemtet/opt/samples/femprj_sample_jp/gal_ex58_parametric_jp.femprj +0 -0
  241. pyfemtet/opt/samples/femprj_sample_jp/gal_ex58_parametric_jp.py +0 -70
  242. pyfemtet/opt/samples/femprj_sample_jp/gau_ex08_parametric_jp.femprj +0 -0
  243. pyfemtet/opt/samples/femprj_sample_jp/gau_ex08_parametric_jp.py +0 -57
  244. pyfemtet/opt/samples/femprj_sample_jp/gau_ex12_parametric_jp.py +0 -52
  245. pyfemtet/opt/samples/femprj_sample_jp/her_ex40_parametric_jp.femprj +0 -0
  246. pyfemtet/opt/samples/femprj_sample_jp/her_ex40_parametric_jp.py +0 -138
  247. pyfemtet/opt/samples/femprj_sample_jp/paswat_ex1_parametric_jp.femprj +0 -0
  248. pyfemtet/opt/samples/femprj_sample_jp/paswat_ex1_parametric_jp.py +0 -58
  249. pyfemtet/opt/samples/femprj_sample_jp/paswat_ex1_parametric_parallel_jp.py +0 -59
  250. pyfemtet/opt/samples/femprj_sample_jp/wat_ex14_parametric_jp.py +0 -56
  251. pyfemtet/opt/samples/femprj_sample_jp/wat_ex14_parametric_parallel_jp.py +0 -56
  252. pyfemtet/opt/visualization/_complex_components/main_figure_creator.py +0 -332
  253. pyfemtet/opt/visualization/_complex_components/pm_graph_creator.py +0 -201
  254. pyfemtet/opt/visualization/_process_monitor/application.py +0 -226
  255. pyfemtet/opt/visualization/_process_monitor/pages.py +0 -406
  256. pyfemtet/opt/visualization/_wrapped_components/__init__.py +0 -0
  257. pyfemtet/opt/visualization/result_viewer/__init__.py +0 -0
  258. pyfemtet-0.9.5.dist-info/RECORD +0 -158
  259. pyfemtet-0.9.5.dist-info/entry_points.txt +0 -3
  260. /pyfemtet/{_femtet_config_util → opt/problem}/__init__.py +0 -0
  261. /pyfemtet/{brep → opt/visualization/history_viewer}/__init__.py +0 -0
  262. /pyfemtet/opt/{_test_utils → visualization/history_viewer/_complex_components}/__init__.py +0 -0
  263. /pyfemtet/opt/{optimizer/_optuna → visualization/history_viewer/_process_monitor}/__init__.py +0 -0
  264. /pyfemtet/opt/{optimizer/_optuna/_botorch_patch → visualization/history_viewer/_wrapped_components}/__init__.py +0 -0
  265. /pyfemtet/opt/visualization/{_wrapped_components → history_viewer/_wrapped_components}/str_enum.py +0 -0
  266. /pyfemtet/opt/visualization/{result_viewer → history_viewer/result_viewer}/.gitignore +0 -0
  267. /pyfemtet/opt/visualization/{_complex_components → history_viewer/result_viewer}/__init__.py +0 -0
  268. /pyfemtet/opt/visualization/{_process_monitor → plotter}/__init__.py +0 -0
  269. /pyfemtet/opt/{samples/femprj_sample_jp/wat_ex14_parametric_jp.femprj → wat_ex14_parametric_jp.femprj} +0 -0
  270. {pyfemtet-0.9.5.dist-info → pyfemtet-1.0.0b0.dist-info}/LICENSE +0 -0
  271. {pyfemtet-0.9.5.dist-info → pyfemtet-1.0.0b0.dist-info}/LICENSE_THIRD_PARTY.txt +0 -0
  272. {pyfemtet-0.9.5.dist-info → pyfemtet-1.0.0b0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,1404 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, TypeAlias, Literal
4
+
5
+ import os
6
+ import csv
7
+ import ast
8
+ import math
9
+ import json
10
+ import datetime
11
+ import dataclasses
12
+ from time import sleep
13
+ from contextlib import nullcontext
14
+
15
+ import numpy as np
16
+ import pandas as pd
17
+
18
+ import pyfemtet
19
+
20
+ from pyfemtet._i18n import *
21
+ from pyfemtet._util.df_util import *
22
+ from pyfemtet._util.dask_util import *
23
+ from pyfemtet._util.str_enum import StrEnum
24
+ from pyfemtet.opt.exceptions import *
25
+ from pyfemtet.opt.problem.problem import *
26
+ from pyfemtet.opt.problem.variable_manager import *
27
+ from pyfemtet.logger import get_module_logger
28
+
29
+ from pyfemtet.opt.history._optimality import *
30
+ from pyfemtet.opt.history._hypervolume import *
31
+
32
+ if TYPE_CHECKING:
33
+ from pyfemtet.opt.interface import AbstractFEMInterface
34
+
35
+
36
+ __all__ = [
37
+ 'TrialState',
38
+ 'History',
39
+ 'ColumnOrderMode',
40
+ 'Record',
41
+ 'create_err_msg_from_exception',
42
+ 'CorrespondingColumnNameRuler',
43
+ 'MAIN_FILTER',
44
+ ]
45
+
46
+ MAIN_FILTER: dict = {'sub_fidelity_name': MAIN_FIDELITY_NAME}
47
+
48
+
49
+ logger = get_module_logger('opt.history', False)
50
+
51
+
52
+ def create_err_msg_from_exception(e: Exception):
53
+ """:meta private:"""
54
+ additional = ' '.join(map(str, e.args))
55
+ if additional == '':
56
+ return type(e).__name__
57
+ else:
58
+ return type(e).__name__ + f'({additional})'
59
+
60
+
61
+ class TrialState(StrEnum):
62
+
63
+ succeeded = 'Success'
64
+ skipped = 'Skip'
65
+ hard_constraint_violation = 'Hard constraint violation'
66
+ soft_constraint_violation = 'Soft constraint violation'
67
+
68
+ # Hidden Constraint
69
+ model_error = 'Model error'
70
+ mesh_error = 'Mesh error'
71
+ solve_error = 'Solve error'
72
+ post_error = 'Post-processing error'
73
+
74
+ unknown_error = 'Unknown error'
75
+ undefined = 'undefined'
76
+
77
+ @staticmethod
78
+ def get_corresponding_state_from_exception(e: Exception) -> TrialState:
79
+ """:meta private:"""
80
+ if isinstance(e, ModelError):
81
+ state = TrialState.model_error
82
+ elif isinstance(e, MeshError):
83
+ state = TrialState.mesh_error
84
+ elif isinstance(e, SolveError):
85
+ state = TrialState.solve_error
86
+ elif isinstance(e, PostProcessError):
87
+ state = TrialState.post_error
88
+ elif isinstance(e, HardConstraintViolation):
89
+ state = TrialState.hard_constraint_violation
90
+ elif isinstance(e, SkipSolve):
91
+ state = TrialState.skipped
92
+ else:
93
+ state = TrialState.unknown_error
94
+ return state
95
+
96
+ @staticmethod
97
+ def get_corresponding_exception_from_state(state: TrialState) -> Exception | None:
98
+ """:meta private:"""
99
+ if state == TrialState.model_error:
100
+ e = ModelError()
101
+ elif state == TrialState.mesh_error:
102
+ e = MeshError()
103
+ elif state == TrialState.solve_error:
104
+ e = SolveError()
105
+ elif state == TrialState.post_error:
106
+ e = PostProcessError()
107
+ elif state == TrialState.unknown_error:
108
+ e = Exception()
109
+ elif state == TrialState.hard_constraint_violation:
110
+ e = HardConstraintViolation
111
+ elif state == TrialState.skipped:
112
+ e = SkipSolve
113
+ else:
114
+ e = None
115
+ return e
116
+
117
+ @classmethod
118
+ def get_hidden_constraint_violation_states(cls):
119
+ """:meta private:"""
120
+ return [cls.get_corresponding_state_from_exception(exception_type())
121
+ for exception_type in _HiddenConstraintViolation.__pyfemtet_subclasses__]
122
+
123
+
124
+ class DataFrameWrapper:
125
+ """:meta private:"""
126
+
127
+ __df: pd.DataFrame
128
+ _lock_name = 'edit-df'
129
+ _dataset_name = 'df'
130
+
131
+ def __init__(self, df: pd.DataFrame):
132
+ self.set_df(df)
133
+
134
+ def __len__(self):
135
+ return len(self.get_df())
136
+
137
+ def __str__(self):
138
+ return self.get_df().__str__()
139
+
140
+ @property
141
+ def lock(self):
142
+ return Lock(self._lock_name)
143
+
144
+ @property
145
+ def lock_if_not_locked(self):
146
+ if self.lock.locked():
147
+ return nullcontext()
148
+ else:
149
+ return self.lock
150
+
151
+ def get_df(self, equality_filters: dict = None) -> pd.DataFrame:
152
+ """
153
+
154
+ Args:
155
+ equality_filters (dict, optional):
156
+ {column: value} formatted dict.
157
+ Each condition is considered as
158
+ an 'and' condition.
159
+
160
+ Defaults to no filter.
161
+
162
+ Returns (pd.DataFrame):
163
+
164
+ """
165
+
166
+ client = get_client()
167
+
168
+ # dask クラスターがある場合
169
+ if client is not None:
170
+
171
+ # あるけど with を抜けている場合
172
+ if client.scheduler is None:
173
+ df = self.__df
174
+
175
+ # 健在の場合
176
+ else:
177
+
178
+ df = None
179
+
180
+ with Lock('access_dataset_df'):
181
+ # datasets 内に存在する場合
182
+ if self._dataset_name in client.list_datasets():
183
+ df = client.get_dataset(self._dataset_name)
184
+
185
+ # set の前に get されることはあってはならない
186
+ else:
187
+ raise RuntimeError
188
+
189
+ assert df is not None
190
+
191
+ # dask クラスターがない場合
192
+ else:
193
+ df = self.__df
194
+
195
+ # filter に合致するものを取得
196
+ if equality_filters is not None:
197
+ df = get_partial_df(df, equality_filters)
198
+
199
+ return df
200
+
201
+ def set_df(self, df, equality_filters: dict = None):
202
+ """
203
+
204
+ Args:
205
+ df:
206
+ equality_filters (dict, optional):
207
+ {column: value} formatted dict.
208
+ Each condition is considered as
209
+ an 'and' condition.
210
+ Only the indexed rows will be updated.
211
+
212
+ Defaults to no filter.
213
+
214
+ Returns (pd.DataFrame):
215
+
216
+ """
217
+
218
+ # フィルタを適用
219
+ # partial_df を get_df した時点のものから
220
+ # 変わっていたらエラーになる
221
+ if equality_filters is not None:
222
+ assert self.lock.locked(), 'set_df() with equality_filters must be called with locking.'
223
+ partial_df = df
224
+ df = self.get_df()
225
+ apply_partial_df(df, partial_df, equality_filters)
226
+
227
+ # dask クラスター上のデータを更新
228
+ client = get_client()
229
+ if client is not None:
230
+ if client.scheduler is not None:
231
+ with Lock('access_dataset_df'):
232
+
233
+ # datasets 上に存在する場合は削除(上書きができない)
234
+ if self._dataset_name in client.list_datasets():
235
+
236
+ # remove
237
+ client.unpublish_dataset(self._dataset_name)
238
+
239
+ # update
240
+ client.publish_dataset(**{self._dataset_name: df})
241
+ sleep(0.1)
242
+
243
+ # local のデータを更新
244
+ self.__df = df
245
+
246
+ def start_dask(self):
247
+ # Register the df initialized before dask context.
248
+ self.set_df(self.__df)
249
+
250
+ def end_dask(self):
251
+ # Get back the df on dask to use the value outside
252
+ # dask context.
253
+ self.__df = self.get_df()
254
+
255
+
256
+ class CorrespondingColumnNameRuler:
257
+ """:meta private:"""
258
+
259
+ @staticmethod
260
+ def direction_name(obj_name):
261
+ return obj_name + '_direction'
262
+
263
+ @staticmethod
264
+ def prm_lower_bound_name(prm_name):
265
+ return prm_name + '_lower_bound'
266
+
267
+ @staticmethod
268
+ def prm_upper_bound_name(prm_name):
269
+ return prm_name + '_upper_bound'
270
+
271
+ @staticmethod
272
+ def prm_choices_name(prm_name):
273
+ return prm_name + '_choices'
274
+
275
+ @staticmethod
276
+ def prm_step_name(prm_name):
277
+ return prm_name + '_step'
278
+
279
+
280
+ class ColumnOrderMode(StrEnum):
281
+ """The order rule of the history csv columns."""
282
+ per_category = 'per_category' #: Sort per each object.
283
+ important_first = 'important_first' #: The values of parameters and objectives first.
284
+
285
+
286
+ ColumnOrderModeStr: TypeAlias = Literal['per_category', 'important_first']
287
+
288
+
289
+ class DuplicatedColumnNameError(Exception):
290
+ """:meta private:"""
291
+
292
+
293
+ class NoDuplicateDict(dict):
294
+ def update(self, m: dict, /, **kwargs):
295
+ for key_ in m.keys():
296
+ if key_ in self.keys():
297
+ raise DuplicatedColumnNameError(
298
+ _(
299
+ en_message='The name `{name}` is duplicated. '
300
+ 'Please use another name.',
301
+ jp_message='名前 「{name}」 が重複しています。'
302
+ '別の名前を使ってください。',
303
+ name=key_,
304
+ )
305
+ )
306
+
307
+ super().update(m, **kwargs)
308
+
309
+
310
+ class ColumnManager:
311
+ """:meta private:"""
312
+
313
+ parameters: TrialInput
314
+ y_names: list[str]
315
+ c_names: list[str]
316
+ column_dtypes: dict[str, type]
317
+ meta_columns: list[str]
318
+
319
+ @staticmethod
320
+ def columns_to_keep_even_if_nan():
321
+ return [
322
+ 'messages',
323
+ ]
324
+
325
+ def initialize(
326
+ self,
327
+ parameters: TrialInput,
328
+ y_names,
329
+ c_names,
330
+ additional_data: dict,
331
+ column_order_mode: str = ColumnOrderMode.per_category,
332
+ ):
333
+ self.parameters = parameters
334
+ self.y_names = y_names
335
+ self.c_names = c_names
336
+ self.set_full_sorted_column_information(
337
+ additional_data=additional_data,
338
+ column_order_mode=column_order_mode,
339
+ )
340
+
341
+ def set_full_sorted_column_information(
342
+ self,
343
+ extra_parameters: TrialInput = None,
344
+ extra_y_names: list[str] = None,
345
+ extra_c_names: list[str] = None,
346
+ additional_data: dict = None,
347
+ column_order_mode: str = ColumnOrderMode.per_category,
348
+ ):
349
+ extra_parameters = extra_parameters or TrialInput()
350
+ extra_y_names = extra_y_names or []
351
+ extra_c_names = extra_c_names or []
352
+
353
+ # column name になるので重複は許されない
354
+ column_dtypes: dict = NoDuplicateDict()
355
+ meta_columns: list = []
356
+ column_dtypes_later: dict = NoDuplicateDict()
357
+ meta_columns_later: list = []
358
+
359
+ if column_order_mode == ColumnOrderMode.per_category:
360
+ target_cds: dict = column_dtypes
361
+ target_mcs: list = meta_columns
362
+ elif column_order_mode == ColumnOrderMode.important_first:
363
+ target_cds: dict = column_dtypes_later
364
+ target_mcs: list = meta_columns_later
365
+ else:
366
+ assert False, f'Unknown {column_order_mode=}'
367
+
368
+ # noinspection PyUnresolvedReferences
369
+ keys = Record.__dataclass_fields__.copy().keys()
370
+ for key in keys:
371
+ # Note:
372
+ # as_df() で空欄になりうるカラムには
373
+ # Nan や '' を許容する dtype を指定すること
374
+ # 例えば、 trial に int を指定してはいけない
375
+ #
376
+ # Note:
377
+ # pandas は column_dtypes に str を受け付けない
378
+ # (object にキャストされる模様)
379
+
380
+ if key == 'x':
381
+ for prm_name in self.parameters.keys():
382
+
383
+ param = self.parameters[prm_name]
384
+
385
+ if isinstance(param, NumericParameter):
386
+ # important
387
+ column_dtypes.update({prm_name: float})
388
+ meta_columns.append('prm.num.value')
389
+
390
+ # later
391
+ f = CorrespondingColumnNameRuler.prm_lower_bound_name
392
+ target_cds.update({f(prm_name): float})
393
+ target_mcs.append('prm.num.lower_bound')
394
+
395
+ f = CorrespondingColumnNameRuler.prm_upper_bound_name
396
+ target_cds.update({f(prm_name): float})
397
+ target_mcs.append('prm.num.upper_bound')
398
+
399
+ f = CorrespondingColumnNameRuler.prm_step_name
400
+ target_cds.update({f(prm_name): float})
401
+ target_mcs.append('prm.num.step')
402
+
403
+ elif isinstance(param, CategoricalParameter):
404
+ # important
405
+ column_dtypes.update({prm_name: object})
406
+ meta_columns.append('prm.cat.value')
407
+
408
+ # later
409
+ f = CorrespondingColumnNameRuler.prm_choices_name
410
+ target_cds.update({f(prm_name): object})
411
+ target_mcs.append('prm.cat.choices')
412
+
413
+ else:
414
+ raise NotImplementedError
415
+
416
+ for extra_prm_name, extra_param in extra_parameters.items():
417
+
418
+ if isinstance(extra_param, NumericParameter):
419
+ # later
420
+ target_cds.update({extra_prm_name: float})
421
+ target_mcs.append('')
422
+
423
+ f = CorrespondingColumnNameRuler.prm_lower_bound_name
424
+ target_cds.update({f(extra_prm_name): object})
425
+ target_mcs.append('')
426
+
427
+ f = CorrespondingColumnNameRuler.prm_upper_bound_name
428
+ target_cds.update({f(extra_prm_name): object})
429
+ target_mcs.append('')
430
+
431
+ elif isinstance(extra_param, CategoricalParameter):
432
+ target_cds.update({extra_prm_name: object})
433
+ target_mcs.append('')
434
+
435
+ f = CorrespondingColumnNameRuler.prm_choices_name
436
+ target_cds.update({f(extra_prm_name): object})
437
+ target_mcs.append('')
438
+
439
+ else:
440
+ raise NotImplementedError
441
+
442
+ elif key == 'y':
443
+ f = CorrespondingColumnNameRuler.direction_name
444
+ for name in self.y_names:
445
+ # important
446
+ column_dtypes.update({name: float})
447
+ meta_columns.append('obj')
448
+
449
+ # later
450
+ target_cds.update({f(name): object}) # str | float
451
+ target_mcs.append(f('obj'))
452
+
453
+ for name in extra_y_names:
454
+ # later
455
+ target_cds.update({name: float})
456
+ target_mcs.append('')
457
+
458
+ # later
459
+ target_cds.update({f(name): object}) # str | float
460
+ target_mcs.append('')
461
+
462
+ elif key == 'c':
463
+
464
+ for name in self.c_names:
465
+ # important
466
+ column_dtypes.update({name: float})
467
+ meta_columns.append('cns')
468
+
469
+ for name in extra_c_names:
470
+ # later
471
+ target_cds.update({name: float})
472
+ target_mcs.append('')
473
+
474
+ # additional_data を入れる
475
+ elif key == self._get_additional_data_column():
476
+ # important
477
+ column_dtypes.update({key: object})
478
+ meta_columns.append(json.dumps(additional_data or dict()))
479
+
480
+ elif key in (
481
+ 'feasibility',
482
+ 'optimality',
483
+ 'sub_sampling',
484
+ 'sub_fidelity_name',
485
+ ):
486
+ # important
487
+ column_dtypes.update({key: object})
488
+ meta_columns.append('')
489
+
490
+ else:
491
+ # later
492
+ target_cds.update({key: object})
493
+ target_mcs.append('')
494
+
495
+ column_dtypes.update(column_dtypes_later)
496
+ meta_columns.extend(meta_columns_later)
497
+
498
+ self.column_dtypes = dict(**column_dtypes)
499
+ self.meta_columns = meta_columns
500
+
501
+ @staticmethod
502
+ def _get_additional_data_column():
503
+ return 'trial'
504
+
505
+ @classmethod
506
+ def _get_additional_data(cls, columns, meta_columns) -> dict:
507
+ for column, meta_column in zip(columns, meta_columns):
508
+ if column == cls._get_additional_data_column():
509
+ if meta_column:
510
+ return json.loads(meta_column)
511
+ else:
512
+ return json.loads('{}')
513
+ else:
514
+ raise RuntimeError(f'"{cls._get_additional_data_column()}" is not found in given columns.')
515
+
516
+ @staticmethod
517
+ def _filter_columns(meta_column, columns, meta_columns) -> list[str]:
518
+ out = []
519
+ assert len(columns) == len(meta_columns), f'{len(columns)=} and {len(meta_columns)=}'
520
+
521
+ for i, (column_, meta_column_) in enumerate(zip(columns, meta_columns)):
522
+ if meta_column_ == meta_column:
523
+ out.append(column_)
524
+ return out
525
+
526
+ @classmethod
527
+ def _filter_prm_names(cls, columns, meta_columns) -> list[str]:
528
+ return (
529
+ cls._filter_columns('prm.num.value', columns, meta_columns)
530
+ + cls._filter_columns('prm.cat.value', columns, meta_columns)
531
+ )
532
+
533
+ def filter_columns(self, meta_column) -> list[str]:
534
+ columns = list(self.column_dtypes.keys())
535
+ return self._filter_columns(meta_column, columns, self.meta_columns)
536
+
537
+ def get_prm_names(self) -> list[str]:
538
+ return (
539
+ self.filter_columns('prm.num.value')
540
+ + self.filter_columns('prm.cat.value')
541
+ )
542
+
543
+ def get_obj_names(self) -> list[str]:
544
+ return self.filter_columns('obj')
545
+
546
+ def get_cns_names(self) -> list[str]:
547
+ return self.filter_columns('cns')
548
+
549
+ @staticmethod
550
+ def _is_numerical_parameter(prm_name, columns):
551
+ prm_lb_name = CorrespondingColumnNameRuler.prm_lower_bound_name(prm_name)
552
+ return prm_lb_name in columns
553
+
554
+ @staticmethod
555
+ def _is_categorical_parameter(prm_name, columns):
556
+ prm_choices_name = CorrespondingColumnNameRuler.prm_choices_name(prm_name)
557
+ return prm_choices_name in columns
558
+
559
+ def is_numerical_parameter(self, prm_name) -> bool:
560
+ return self._is_numerical_parameter(prm_name, tuple(self.column_dtypes.keys()))
561
+
562
+ def is_categorical_parameter(self, prm_name) -> bool:
563
+ return self._is_categorical_parameter(prm_name, tuple(self.column_dtypes.keys()))
564
+
565
+ @staticmethod
566
+ def _get_parameter(prm_name: str, df: pd.DataFrame) -> Parameter:
567
+ if ColumnManager._is_numerical_parameter(prm_name, df.columns):
568
+ out = NumericParameter()
569
+ out.name = prm_name
570
+ out.value = float(df[prm_name].dropna().values[-1])
571
+
572
+ # lower_bound
573
+ key = CorrespondingColumnNameRuler.prm_lower_bound_name(prm_name)
574
+ if key in df.columns:
575
+ out.lower_bound = float(df[key].dropna().values[-1])
576
+ else:
577
+ out.lower_bound = None
578
+
579
+ # upper bound
580
+ key = CorrespondingColumnNameRuler.prm_upper_bound_name(prm_name)
581
+ if key in df.columns:
582
+ out.upper_bound = float(df[key].dropna().values[-1])
583
+ else:
584
+ out.upper_bound = None
585
+
586
+ # step
587
+ key = CorrespondingColumnNameRuler.prm_step_name(prm_name)
588
+ if key in df.columns:
589
+ out.step = float(df[key].dropna().values[-1])
590
+ else:
591
+ out.step = None
592
+
593
+ elif ColumnManager._is_categorical_parameter(prm_name, df.columns):
594
+ out = CategoricalParameter()
595
+ out.name = prm_name
596
+ out.value = str(df[prm_name].dropna().values[-1])
597
+ out.choices = df[CorrespondingColumnNameRuler.prm_choices_name(prm_name)].dropna().values[-1]
598
+
599
+ else:
600
+ raise NotImplementedError
601
+
602
+ return out
603
+
604
+ @staticmethod
605
+ def _reconvert_objects(df: pd.DataFrame, meta_columns: list[str]):
606
+ for column, meta_column in zip(df.columns, meta_columns):
607
+ # messages は df の段階で _RECORD_MESSAGE_DELIMITER
608
+ # separated な str なのでここで restore してはいけない
609
+
610
+ # choices list は csv を経由することで str になるので restore
611
+ if meta_column == 'prm.cat.choices':
612
+ df[column] = [ast.literal_eval(d) for d in df[column]]
613
+
614
+ @staticmethod
615
+ def _get_sub_fidelity_names(df: pd.DataFrame) -> list[str]:
616
+
617
+ if 'sub_fidelity_name' not in df.columns:
618
+ return [MAIN_FIDELITY_NAME]
619
+
620
+ else:
621
+ return np.unique(df['sub_fidelity_name'].values).tolist()
622
+
623
+
624
+ _RECORD_MESSAGE_DELIMITER = ' | '
625
+
626
+
627
+ @dataclasses.dataclass
628
+ class Record:
629
+ """:meta private:"""
630
+
631
+ # x, y, c のみ特殊で、データの展開や関連情報の
632
+ # 列への展開を必要とするが、他の field は
633
+ # ここに定義すればよい
634
+
635
+ trial: int = None
636
+ trial_id: int = None
637
+ sub_sampling: SubSampling | None = None
638
+ sub_fidelity_name: str = None
639
+ fidelity: Fidelity = None
640
+ x: TrialInput = dataclasses.field(default_factory=TrialInput)
641
+ y: TrialOutput = dataclasses.field(default_factory=TrialOutput)
642
+ c: TrialConstraintOutput = dataclasses.field(default_factory=TrialConstraintOutput)
643
+ state: TrialState = TrialState.undefined
644
+ datetime_start: datetime.datetime = dataclasses.field(default_factory=datetime.datetime.now)
645
+ datetime_end: datetime.datetime = dataclasses.field(default_factory=datetime.datetime.now)
646
+ messages: list = dataclasses.field(default_factory=list)
647
+ hypervolume: float | None = None
648
+ feasibility: bool | None = None
649
+ optimality: bool | None = None
650
+
651
+ def as_df(self, dtypes: dict = None):
652
+
653
+ # noinspection PyUnresolvedReferences
654
+ keys = self.__dataclass_fields__.copy().keys()
655
+ d = {key: getattr(self, key) for key in keys if getattr(self, key) is not None}
656
+
657
+ x: TrialInput = d.pop('x')
658
+ y: TrialOutput = d.pop('y')
659
+ c: TrialConstraintOutput = d.pop('c')
660
+
661
+ # prm
662
+ for prm_name, param in x.items():
663
+ d.update({prm_name: param.value})
664
+ if isinstance(param, NumericParameter):
665
+ f = CorrespondingColumnNameRuler.prm_lower_bound_name
666
+ d.update({f(prm_name): param.lower_bound})
667
+ f = CorrespondingColumnNameRuler.prm_upper_bound_name
668
+ d.update({f(prm_name): param.upper_bound})
669
+ f = CorrespondingColumnNameRuler.prm_step_name
670
+ d.update({f(prm_name): param.step})
671
+ elif isinstance(param, CategoricalParameter):
672
+ f = CorrespondingColumnNameRuler.prm_choices_name
673
+ d.update({f(prm_name): param.choices})
674
+ else:
675
+ raise NotImplementedError
676
+
677
+ # messages to str
678
+ messages_str = _RECORD_MESSAGE_DELIMITER.join(d['messages'])
679
+ d.update({'messages': messages_str})
680
+
681
+ d.update(**{k: v.value for k, v in y.items()})
682
+ d.update(**{f'{k}_direction': v.direction for k, v in y.items()})
683
+ d.update(**{k: v.value for k, v in c.items()})
684
+
685
+ df = pd.DataFrame(
686
+ {k: [v] for k, v in d.items()},
687
+ columns=tuple(dtypes.keys())
688
+ )
689
+
690
+ if dtypes:
691
+ df = df.astype(dtypes)
692
+
693
+ return df
694
+
695
+ @staticmethod
696
+ def get_state_str_from_series(row: pd.Series):
697
+ state: TrialState = TrialState.undefined
698
+ if 'state' in row:
699
+ state = row['state']
700
+ return state
701
+
702
+
703
+ class EntireDependentValuesCalculator:
704
+ """:meta private:"""
705
+
706
+ def __init__(
707
+ self,
708
+ records: Records,
709
+ equality_filters: dict,
710
+ entire_df: pd.DataFrame,
711
+ ):
712
+
713
+ self.records = records
714
+ self.equality_filters = equality_filters
715
+ self.entire_df: pd.DataFrame = entire_df
716
+ self.partial_df: pd.DataFrame = get_partial_df(entire_df, equality_filters)
717
+
718
+ assert self.records.df_wrapper.lock.locked()
719
+
720
+ # get column names
721
+ obj_names = self.records.column_manager.get_obj_names()
722
+ f = CorrespondingColumnNameRuler.direction_name
723
+ obj_direction_names = [f(name) for name in obj_names]
724
+
725
+ # get values
726
+ all_obj_values = self.partial_df[obj_names].values
727
+ all_obj_directions = self.partial_df[obj_direction_names].values
728
+ feasibility = self.partial_df['feasibility']
729
+
730
+ # convert values as minimization problem
731
+ y_internal = np.empty(all_obj_values.shape)
732
+ for i, (obj_values, obj_directions) \
733
+ in enumerate(zip(all_obj_values.T, all_obj_directions.T)):
734
+ y_internal[:, i] = np.array(
735
+ list(
736
+ map(
737
+ lambda args: Objective._convert(*args),
738
+ zip(obj_values, obj_directions)
739
+ )
740
+ )
741
+ )
742
+
743
+ self.partial_y_internal = y_internal
744
+ self.partial_feasibility = feasibility
745
+
746
+ def update_optimality(self):
747
+
748
+ assert self.records.df_wrapper.lock.locked()
749
+
750
+ # calc optimality
751
+ optimality = calc_optimality(
752
+ self.partial_y_internal,
753
+ self.partial_feasibility,
754
+ )
755
+
756
+ # update
757
+ self.partial_df.loc[:, 'optimality'] = optimality
758
+
759
+ def update_hypervolume(self):
760
+
761
+ assert self.records.df_wrapper.lock.locked()
762
+
763
+ # calc hypervolume
764
+ hv_values = calc_hypervolume(
765
+ self.partial_y_internal,
766
+ self.partial_feasibility,
767
+ ref_point='nadir-up-to-the-point',
768
+ )
769
+
770
+ # update
771
+ self.partial_df.loc[:, 'hypervolume'] = hv_values
772
+
773
+ def update_trial_number(self):
774
+
775
+ assert self.records.df_wrapper.lock.locked()
776
+
777
+ # calc trial
778
+ trial_number = 1 + np.arange(len(self.partial_df)).astype(int)
779
+
780
+ # update
781
+ self.partial_df.loc[:, 'trial'] = trial_number
782
+
783
+
784
+ class Records:
785
+ """:meta private:
786
+
787
+ 最適化の試行全体の情報を格納するモデルクラス
788
+ """
789
+ df_wrapper: DataFrameWrapper
790
+ column_manager: ColumnManager
791
+
792
+ def __init__(self):
793
+ self.df_wrapper = DataFrameWrapper(pd.DataFrame())
794
+ self.column_manager = ColumnManager()
795
+ self.loaded_meta_columns = None
796
+ self.loaded_df = None
797
+
798
+ def __str__(self):
799
+ return self.df_wrapper.__str__()
800
+
801
+ def __len__(self):
802
+ return len(self.df_wrapper)
803
+
804
+ def initialize(self):
805
+ with self.df_wrapper.lock:
806
+ # 新しく始まる場合に備えカラムを設定
807
+ # load の場合はあとで上書きされる
808
+ df = pd.DataFrame([], columns=list(self.column_manager.column_dtypes.keys()))
809
+ self.df_wrapper.set_df(df)
810
+
811
+ def load(self, path: str):
812
+
813
+ for encoding in (ENCODING, 'utf-8'):
814
+ try:
815
+ with open(path, 'r', encoding=encoding, newline='\n') as f:
816
+ reader = csv.reader(f, delimiter=',')
817
+ # load meta_column
818
+ loaded_meta_columns = reader.__next__()
819
+ reader.__next__() # empty line
820
+ # load df from line 3
821
+ loaded_df = pd.read_csv(f, encoding=encoding, header=0)
822
+ break
823
+
824
+ except UnicodeDecodeError:
825
+ continue
826
+
827
+ # df を csv にする過程で失われる list などのオブジェクトを restore
828
+ ColumnManager._reconvert_objects(loaded_df, loaded_meta_columns)
829
+
830
+ # この段階では column_dtypes が setup されていない可能性があるので
831
+ # compatibility check をしない。よって set_df しない。
832
+ self.loaded_meta_columns = loaded_meta_columns
833
+ self.loaded_df = loaded_df
834
+
835
+ def check_problem_compatibility(self):
836
+
837
+ # 読み込んだデータがないのであれば何もしない
838
+ if self.loaded_df is None:
839
+ return
840
+
841
+ # 順番が違ってもいいが、
842
+ # 構成に変更がないこと。
843
+ # ただし obj は減っていてもいい。
844
+ loaded_columns, loaded_meta_columns = self.loaded_df.columns, self.loaded_meta_columns
845
+
846
+ # prm_names が過不足ないか
847
+ loaded_prm_names = set(
848
+ self.column_manager._filter_prm_names(
849
+ loaded_columns, loaded_meta_columns
850
+ )
851
+ )
852
+ prm_names = set(self.column_manager.get_prm_names())
853
+ if not (len(loaded_prm_names - prm_names) == len(prm_names - loaded_prm_names) == 0):
854
+ raise RuntimeError('Incompatible parameter setting.')
855
+
856
+ # obj_names が増えていないか
857
+ loaded_obj_names = set(self.column_manager._filter_columns('obj', loaded_columns, loaded_meta_columns))
858
+ obj_names = set(self.column_manager.get_obj_names())
859
+ if len(obj_names - loaded_obj_names) > 0:
860
+ raise RuntimeError('Incompatible objective setting.')
861
+
862
+ # cns_names が過不足ないか
863
+ # TODO: cns の上下限は変更されてはならない。
864
+ loaded_cns_names = set(self.column_manager._filter_columns('cns', loaded_columns, loaded_meta_columns))
865
+ cns_names = set(self.column_manager.get_cns_names())
866
+ if not (len(loaded_cns_names - cns_names) == len(cns_names - loaded_cns_names) == 0):
867
+ raise RuntimeError('Incompatible constraint setting.')
868
+
869
+ def reinitialize_record_with_loaded_data(self, column_order_mode: str = ColumnOrderMode.per_category):
870
+
871
+ # 読み込んだデータがないのであれば何もしない
872
+ if self.loaded_df is None:
873
+ return
874
+
875
+ loaded_columns, loaded_meta_columns = self.loaded_df.columns, self.loaded_meta_columns
876
+ loaded_prm_names = set(self.column_manager._filter_prm_names(loaded_columns, loaded_meta_columns))
877
+ loaded_obj_names = set(self.column_manager._filter_columns('obj', loaded_columns, loaded_meta_columns))
878
+ loaded_cns_names = set(self.column_manager._filter_columns('cns', loaded_columns, loaded_meta_columns))
879
+
880
+ # loaded df に存在するが Record に存在しないカラムを Record に追加
881
+ extra_parameters = {}
882
+ extra_y_names = []
883
+ extra_c_names = []
884
+ for l_col, l_meta in zip(loaded_columns, loaded_meta_columns):
885
+
886
+ # 現在の Record に含まれないならば
887
+ if l_col not in self.column_manager.column_dtypes.keys():
888
+
889
+ # それが prm_name ならば
890
+ if l_col in loaded_prm_names:
891
+
892
+ # それが Categorical ならば
893
+ if CorrespondingColumnNameRuler.prm_choices_name(l_col) in loaded_columns:
894
+ param = CategoricalParameter()
895
+ param.name = l_col
896
+ param.value = ''
897
+ param.choices = []
898
+
899
+ # それが Numeric ならば
900
+ elif CorrespondingColumnNameRuler.prm_lower_bound_name(l_col) in loaded_columns:
901
+ param = NumericParameter()
902
+ param.name = l_col
903
+ param.value = np.nan
904
+ param.lower_bound = np.nan
905
+ param.upper_bound = np.nan
906
+
907
+ else:
908
+ raise NotImplementedError
909
+
910
+ extra_parameters.update({l_col: param})
911
+
912
+ # obj_name ならば
913
+ elif l_col in loaded_obj_names:
914
+ extra_y_names.append(l_col)
915
+
916
+ # cns_name ならば
917
+ elif l_col in loaded_cns_names:
918
+ extra_c_names.append(l_col)
919
+
920
+ # additional data を取得
921
+ a_data = self.column_manager._get_additional_data(loaded_columns, loaded_meta_columns)
922
+
923
+ self.column_manager.set_full_sorted_column_information(
924
+ extra_parameters=extra_parameters,
925
+ extra_y_names=extra_y_names,
926
+ extra_c_names=extra_c_names,
927
+ additional_data=a_data,
928
+ column_order_mode=column_order_mode,
929
+ )
930
+
931
+ # worker に影響しないように loaded_df のコピーを作成
932
+ df: pd.DataFrame = self.loaded_df.copy()
933
+
934
+ # loaded df に存在しないが Record に存在するカラムを追加
935
+ for col in self.column_manager.column_dtypes.keys():
936
+ if col not in df.columns:
937
+ # column ごとの default 値を追加
938
+ if col == 'sub_fidelity_name':
939
+ df[col] = MAIN_FIDELITY_NAME
940
+ else:
941
+ df[col] = np.nan
942
+
943
+ # column_dtypes を設定
944
+ # 与える column_dtypes のほうが多い場合
945
+ # エラーになるので余分なものを削除
946
+ # 与える column_dtypes が少ない分には
947
+ # (pandas としては) 問題ない
948
+ dtypes = {k: v for k, v in self.column_manager.column_dtypes.items() if k in self.loaded_df.columns}
949
+ df = df.astype(dtypes)
950
+
951
+ # 並べ替え
952
+ df = df[list(self.column_manager.column_dtypes.keys())].astype(self.column_manager.column_dtypes)
953
+
954
+ # OK なので読み込んだデータを set_df する
955
+ self.df_wrapper.set_df(df)
956
+
957
+ def remove_nan_columns(
958
+ self, df, meta_columns, columns_to_keep: str | list[str] = None
959
+ ) -> tuple[pd.DataFrame, tuple[str]]:
960
+ """
961
+
962
+ Args:
963
+ df:
964
+ meta_columns:
965
+ columns_to_keep: Allowing these columns to all NaN values.
966
+
967
+ Returns:
968
+ Removed DataFrame and corresponding meta_columns.
969
+
970
+ """
971
+
972
+ df = df.replace('', None)
973
+
974
+ nan_columns = df.isna().all(axis=0)
975
+ if columns_to_keep is None:
976
+ columns_to_keep = self.column_manager.columns_to_keep_even_if_nan()
977
+ nan_columns[columns_to_keep] = False
978
+
979
+ fdf = df.loc[:, ~nan_columns]
980
+ f_meta_columns = (np.array(meta_columns)[~nan_columns]).tolist()
981
+
982
+ return fdf, f_meta_columns
983
+
984
+ def save(self, path: str):
985
+
986
+ # filter NaN columns
987
+ df, meta_columns = self.remove_nan_columns(
988
+ self.df_wrapper.get_df(), self.column_manager.meta_columns,
989
+ )
990
+
991
+ try:
992
+ with open(path, 'w', encoding=ENCODING) as f:
993
+ writer = csv.writer(f, delimiter=',', lineterminator="\n")
994
+ # write meta_columns
995
+ writer.writerow(meta_columns)
996
+ writer.writerow([''] * len(meta_columns)) # empty line
997
+ # write df from line 3
998
+ df.to_csv(f, index=False, encoding=ENCODING, lineterminator='\n')
999
+ except PermissionError:
1000
+ logger.warning(
1001
+ _(
1002
+ en_message='History csv file ({path}) is in use and cannot be written to. '
1003
+ 'Please free this file before exiting the program, '
1004
+ 'otherwise history data will be lost.',
1005
+ jp_message='履歴のCSVファイル({path})が使用中のため書き込みできません。'
1006
+ 'プログラムを終了する前にこのファイルを閉じてください。'
1007
+ 'そうしない場合、履歴データが失われます。',
1008
+ path=path,
1009
+ )
1010
+ )
1011
+
1012
+ def append(self, record: Record) -> pd.Series:
1013
+
1014
+ # get row
1015
+ row = record.as_df(dtypes=self.column_manager.column_dtypes)
1016
+
1017
+ # concat
1018
+ dfw = self.df_wrapper
1019
+
1020
+ # append
1021
+ with dfw.lock:
1022
+
1023
+ df = dfw.get_df()
1024
+
1025
+ if len(df) == 0:
1026
+ # ここで空のカラムを削除しては
1027
+ # データの並びがおかしくなるケースが出る
1028
+ new_df = row
1029
+ else:
1030
+ # pandas の型推定の仕様変更対策で
1031
+ # 空のカラムは削除する
1032
+ row.dropna(axis=1, inplace=True, how='all')
1033
+
1034
+ new_df = pd.concat(
1035
+ [df, row],
1036
+ axis=0,
1037
+ ignore_index=True,
1038
+ )
1039
+
1040
+ # calc entire-dependent values
1041
+ # must be in with block to keep
1042
+ # the entire data compatibility
1043
+ # during processing.
1044
+ self.update_entire_dependent_values(new_df)
1045
+
1046
+ dfw.set_df(new_df)
1047
+
1048
+ # postprocess after recording で使うために
1049
+ # 計算済み最終行を返す
1050
+ return new_df.iloc[-1]
1051
+
1052
+ def update_entire_dependent_values(self, processing_df: pd.DataFrame):
1053
+
1054
+ with self.df_wrapper.lock_if_not_locked:
1055
+
1056
+ # update main fidelity
1057
+ equality_filters = MAIN_FILTER
1058
+ mgr = EntireDependentValuesCalculator(
1059
+ self,
1060
+ equality_filters,
1061
+ processing_df,
1062
+ )
1063
+ mgr.update_optimality()
1064
+ mgr.update_hypervolume()
1065
+ mgr.update_trial_number()
1066
+ pdf = mgr.partial_df
1067
+ apply_partial_df(df=processing_df, partial_df=pdf, equality_filters=equality_filters)
1068
+
1069
+ # update sub fidelity
1070
+ entire_df = self.df_wrapper.get_df()
1071
+ sub_fidelity_names: list = np.unique(entire_df['sub_fidelity_name']).tolist()
1072
+ if MAIN_FIDELITY_NAME in sub_fidelity_names:
1073
+ sub_fidelity_names.remove(MAIN_FIDELITY_NAME)
1074
+ for sub_fidelity_name in sub_fidelity_names:
1075
+ equality_filters = {'sub_fidelity_name': sub_fidelity_name}
1076
+ mgr = EntireDependentValuesCalculator(
1077
+ self,
1078
+ equality_filters,
1079
+ processing_df
1080
+ )
1081
+ mgr.update_trial_number()
1082
+ pdf = mgr.partial_df
1083
+ apply_partial_df(df=processing_df, partial_df=pdf, equality_filters=equality_filters)
1084
+
1085
+
1086
+ class History:
1087
+ """最適化の試行の履歴を管理します。"""
1088
+ _records: Records
1089
+ prm_names: list[str]
1090
+ obj_names: list[str]
1091
+ cns_names: list[str]
1092
+ sub_fidelity_names: list[str]
1093
+ is_restart: bool
1094
+ additional_data: dict
1095
+
1096
+ path: str
1097
+ """The existing or destination CSV path.
1098
+
1099
+ If not specified, the CSV file is saved in the format
1100
+ "pyfemtet.opt_%Y%m%d_%H%M%S.csv"
1101
+ when the optimization process starts.
1102
+ """
1103
+
1104
+ MAIN_FILTER = MAIN_FILTER
1105
+
1106
+ def __init__(self):
1107
+ self._records = Records()
1108
+ self.path: str | None = None
1109
+ self._finalized: bool = False
1110
+ self.is_restart = False
1111
+ self.additional_data = dict(version=pyfemtet.__version__)
1112
+ self.column_order_mode: ColumnOrderMode | ColumnOrderModeStr = ColumnOrderMode.per_category
1113
+
1114
+ def __str__(self):
1115
+ return self._records.__str__()
1116
+
1117
+ def __enter__(self):
1118
+ self._records.df_wrapper.start_dask()
1119
+
1120
+ def __exit__(self, exc_type, exc_val, exc_tb):
1121
+ self._records.df_wrapper.end_dask()
1122
+
1123
+ def load_csv(self, path, with_finalize=False):
1124
+ """:meta private:"""
1125
+
1126
+ self.is_restart = True
1127
+ self.path = path
1128
+ self._records.load(self.path)
1129
+
1130
+ if with_finalize:
1131
+ self._finalize_from_loaded_data()
1132
+
1133
+ def _finalize_from_loaded_data(self):
1134
+ assert self.is_restart
1135
+
1136
+ if not self._finalized:
1137
+
1138
+ df = self._records.loaded_df
1139
+ meta_columns = self._records.loaded_meta_columns
1140
+
1141
+ self.prm_names = ColumnManager._filter_prm_names(df.columns, meta_columns)
1142
+ self.obj_names = ColumnManager._filter_columns('obj', df.columns, meta_columns)
1143
+ self.cns_names = ColumnManager._filter_columns('cns', df.columns, meta_columns)
1144
+ self.sub_fidelity_names = ColumnManager._get_sub_fidelity_names(df)
1145
+ self.additional_data = ColumnManager._get_additional_data(df.columns, meta_columns)
1146
+
1147
+ parameters: TrialInput = {}
1148
+ for prm_name in self.prm_names:
1149
+ param = ColumnManager._get_parameter(prm_name, df)
1150
+ parameters.update({prm_name: param})
1151
+
1152
+ self.finalize(
1153
+ parameters,
1154
+ self.obj_names,
1155
+ self.cns_names,
1156
+ self.sub_fidelity_names,
1157
+ self.additional_data,
1158
+ )
1159
+
1160
+ def finalize(
1161
+ self,
1162
+ parameters: TrialInput,
1163
+ obj_names,
1164
+ cns_names,
1165
+ sub_fidelity_names,
1166
+ additional_data,
1167
+ ):
1168
+ """:meta private:"""
1169
+
1170
+ self.prm_names = list(parameters.keys())
1171
+ self.obj_names = list(obj_names)
1172
+ self.cns_names = list(cns_names)
1173
+ self.sub_fidelity_names = list(sub_fidelity_names)
1174
+ self.additional_data.update(additional_data)
1175
+
1176
+ if not self._finalized:
1177
+ # ここで column_dtypes が決定する
1178
+ self._records.column_manager.initialize(
1179
+ parameters, self.obj_names, self.cns_names, self.additional_data, self.column_order_mode
1180
+ )
1181
+
1182
+ # initialize
1183
+ self._records.initialize()
1184
+
1185
+ if self.path is None:
1186
+ self.path = datetime.datetime.now().strftime("pyfemtet.opt_%Y%m%d_%H%M%S.csv")
1187
+
1188
+ # load
1189
+ if os.path.isfile(self.path):
1190
+ self.load_csv(self.path)
1191
+ self._records.check_problem_compatibility()
1192
+ self._records.reinitialize_record_with_loaded_data(self.column_order_mode)
1193
+
1194
+ self._finalized = True
1195
+
1196
+ def get_df(self, equality_filters: dict = None) -> pd.DataFrame:
1197
+ """Returns the optimization history.
1198
+
1199
+ Args:
1200
+ equality_filters (dict, optional):
1201
+ The {column: value} というフォーマットの
1202
+ matching filter.
1203
+
1204
+ Returns: The optimization history.
1205
+
1206
+ """
1207
+ return self._records.df_wrapper.get_df(equality_filters)
1208
+
1209
+ @staticmethod
1210
+ def get_trial_name(trial=None, fidelity=None, sub_sampling=None, row: pd.Series = None):
1211
+ if row is not None:
1212
+ assert not math.isnan(row['trial'])
1213
+ trial = row['trial']
1214
+ fidelity = row['fidelity'] if not math.isnan(row['fidelity']) else None
1215
+ sub_sampling = row['sub_sampling'] if not math.isnan(row['sub_sampling']) else None
1216
+
1217
+ name_parts = ['trial']
1218
+ if fidelity is not None:
1219
+ fid = str(fidelity)
1220
+ if fid != MAIN_FIDELITY_NAME:
1221
+ name_parts.append(fid)
1222
+
1223
+ name_parts.append(str(trial))
1224
+
1225
+ if sub_sampling is not None:
1226
+ name_parts.append(str(sub_sampling))
1227
+
1228
+ trial_name = '_'.join(name_parts)
1229
+
1230
+ return trial_name
1231
+
1232
+ def recording(self, fem: AbstractFEMInterface):
1233
+ """:meta private:"""
1234
+
1235
+ # noinspection PyMethodParameters
1236
+ class RecordContext:
1237
+
1238
+ def __init__(self_):
1239
+ self_.record = Record()
1240
+ self_.record_as_df = None
1241
+
1242
+ def __enter__(self_):
1243
+ return self_.record
1244
+
1245
+ def append(self_):
1246
+ self_.record.datetime_end = self_.record.datetime_end \
1247
+ if self_.record.datetime_end is not None \
1248
+ else datetime.datetime.now()
1249
+ return self._records.append(self_.record)
1250
+
1251
+ @staticmethod
1252
+ def postprocess_after_recording(row):
1253
+
1254
+ client = get_client()
1255
+
1256
+ trial_name = self.get_trial_name(row=row)
1257
+
1258
+ # FIXME: メインフィデリティだけでなく、FEM に
1259
+ # 対応するフィデリティ又はサブサンプリングのみ
1260
+ # フィルタした情報を提供するようにする。
1261
+ # フィデリティの話は現在解析を実行している opt が
1262
+ # 必要なので、recording メソッドの引数に
1263
+ # それを追加する
1264
+ df = self.get_df(equality_filters=self.MAIN_FILTER)
1265
+
1266
+ if client is not None:
1267
+ client.run_on_scheduler(
1268
+ fem._postprocess_after_recording,
1269
+ trial_name=trial_name,
1270
+ df=df,
1271
+ **(fem._create_postprocess_args()),
1272
+ )
1273
+
1274
+ else:
1275
+ fem._postprocess_after_recording(
1276
+ dask_scheduler=None,
1277
+ trial_name=trial_name,
1278
+ df=df,
1279
+ **(fem._create_postprocess_args())
1280
+ )
1281
+
1282
+ def __exit__(self_, exc_type, exc_val, exc_tb):
1283
+
1284
+ row: pd.Series | None = None
1285
+
1286
+ # record feasibility
1287
+ # skipped -> None (empty)
1288
+ # succeeded -> True
1289
+ # else -> False
1290
+ if self_.record.state == TrialState.skipped:
1291
+ self_.record.feasibility = None
1292
+
1293
+ elif self_.record.state == TrialState.succeeded:
1294
+ self_.record.feasibility = True
1295
+
1296
+ else:
1297
+ self_.record.feasibility = False
1298
+
1299
+ # append
1300
+ if exc_type is None:
1301
+ row = self_.append()
1302
+ # 1st argument of issubclass cannot be None
1303
+ elif issubclass(exc_type, ExceptionDuringOptimization):
1304
+ row = self_.append()
1305
+
1306
+ # if append is succeeded,
1307
+ # do fem.post_processing
1308
+ if row is not None:
1309
+ self_.postprocess_after_recording(row)
1310
+
1311
+ # save history
1312
+ client = get_client()
1313
+ if client is None:
1314
+ self.save()
1315
+
1316
+ return RecordContext()
1317
+
1318
+ def save(self):
1319
+ """Export the optimization history.
1320
+
1321
+ The destination path is :class:`History.path`.
1322
+ """
1323
+ self._records.save(self.path)
1324
+
1325
+ def _create_optuna_study_for_visualization(self):
1326
+ import optuna
1327
+
1328
+ # create study
1329
+ kwargs: dict[str, ...] = dict(
1330
+ # storage='sqlite:///' + os.path.basename(self.path) + '_dummy.db',
1331
+ sampler=None, pruner=None, study_name='dummy',
1332
+ )
1333
+ if len(self.obj_names) == 1:
1334
+ kwargs.update(dict(direction='minimize'))
1335
+ else:
1336
+ kwargs.update(dict(directions=['minimize']*len(self.obj_names)))
1337
+ study = optuna.create_study(**kwargs)
1338
+
1339
+ # add trial to study
1340
+ df = self.get_df(equality_filters=MAIN_FILTER)
1341
+
1342
+ for i, row in df.iterrows():
1343
+
1344
+ # trial
1345
+ trial_kwargs: dict = dict()
1346
+
1347
+ # state
1348
+ state_str = Record.get_state_str_from_series(row)
1349
+ if state_str != TrialState.succeeded:
1350
+ continue
1351
+ state = optuna.trial.TrialState.COMPLETE
1352
+ trial_kwargs.update(dict(state=state))
1353
+
1354
+ # params
1355
+ params = {prm_name: row[prm_name] for prm_name in self.prm_names}
1356
+ trial_kwargs.update(dict(params=params))
1357
+
1358
+ # distribution
1359
+ distributions: dict[str, optuna.distributions.BaseDistribution] = dict()
1360
+ for prm_name in params.keys():
1361
+
1362
+ # float
1363
+ if self._records.column_manager.is_numerical_parameter(prm_name):
1364
+ lb_name = CorrespondingColumnNameRuler.prm_lower_bound_name(prm_name)
1365
+ ub_name = CorrespondingColumnNameRuler.prm_upper_bound_name(prm_name)
1366
+ dist = optuna.distributions.FloatDistribution(
1367
+ low=row[lb_name],
1368
+ high=row[ub_name],
1369
+ )
1370
+
1371
+ # categorical
1372
+ elif self._records.column_manager.is_categorical_parameter(prm_name):
1373
+ choices_name = CorrespondingColumnNameRuler.prm_choices_name(prm_name)
1374
+ dist = optuna.distributions.CategoricalDistribution(
1375
+ choices=row[choices_name]
1376
+ )
1377
+
1378
+ else:
1379
+ raise NotImplementedError
1380
+
1381
+ distributions.update(
1382
+ {prm_name: dist}
1383
+ )
1384
+ trial_kwargs.update(dict(distributions=distributions))
1385
+
1386
+ # objective
1387
+ if len(self.obj_names) == 1:
1388
+ trial_kwargs.update(dict(value=row[self.obj_names].values[0]))
1389
+ else:
1390
+ trial_kwargs.update(dict(values=row[self.obj_names].values))
1391
+
1392
+ # add to study
1393
+ trial = optuna.create_trial(**trial_kwargs)
1394
+ study.add_trial(trial)
1395
+
1396
+ return study
1397
+
1398
+ def is_numerical_parameter(self, prm_name: str) -> bool:
1399
+ """:meta private:"""
1400
+ return self._records.column_manager.is_numerical_parameter(prm_name)
1401
+
1402
+ def is_categorical_parameter(self, prm_name) -> bool:
1403
+ """:meta private:"""
1404
+ return self._records.column_manager.is_categorical_parameter(prm_name)