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
@@ -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)