pyfemtet 0.5.4__tar.gz → 0.6.1__tar.gz

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 (120) hide show
  1. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/PKG-INFO +1 -1
  2. pyfemtet-0.6.1/pyfemtet/__init__.py +1 -0
  3. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/_message/locales/ja/LC_MESSAGES/messages.po +1 -1
  4. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/__init__.py +3 -0
  5. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/_femopt.py +73 -14
  6. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/_femopt_core.py +172 -63
  7. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/interface/_femtet.py +15 -4
  8. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/optimizer/__init__.py +5 -1
  9. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/optimizer/_base.py +5 -13
  10. pyfemtet-0.5.4/pyfemtet/opt/optimizer/_optuna_botorchsampler_parameter_constraint_helper.py → pyfemtet-0.6.1/pyfemtet/opt/optimizer/_optuna/_botorch_patch/enable_nonlinear_constraint.py +9 -126
  11. {pyfemtet-0.5.4/pyfemtet/opt/optimizer → pyfemtet-0.6.1/pyfemtet/opt/optimizer/_optuna}/_optuna.py +89 -18
  12. pyfemtet-0.6.1/pyfemtet/opt/optimizer/_optuna/_pof_botorch.py +1872 -0
  13. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/constrained_pipe.py +2 -2
  14. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/her_ex40_parametric.py +2 -2
  15. pyfemtet-0.6.1/pyfemtet/opt/visualization/_wrapped_components/__init__.py +0 -0
  16. pyfemtet-0.6.1/pyfemtet/opt/visualization/result_viewer/__init__.py +0 -0
  17. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyproject.toml +7 -1
  18. pyfemtet-0.5.4/pyfemtet/__init__.py +0 -1
  19. pyfemtet-0.5.4/pyfemtet/opt/samples/femprj_sample/.gitignore +0 -2
  20. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/LICENSE +0 -0
  21. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/README.md +0 -0
  22. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/_message/1. make_pot.bat +0 -0
  23. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/_message/2. make_mo.bat +0 -0
  24. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/_message/__init__.py +0 -0
  25. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/_message/babel.cfg +0 -0
  26. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/_message/locales/messages.pot +0 -0
  27. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/_message/messages.py +0 -0
  28. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/_warning.py +0 -0
  29. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/core.py +0 -0
  30. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/dispatch_extensions/__init__.py +0 -0
  31. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/dispatch_extensions/_impl.py +0 -0
  32. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/logger/__init__.py +0 -0
  33. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/logger/_impl.py +0 -0
  34. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/_test_utils/__init__.py +0 -0
  35. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/_test_utils/control_femtet.py +0 -0
  36. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/_test_utils/hyper_sphere.py +0 -0
  37. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/_test_utils/record_history.py +0 -0
  38. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/interface/__init__.py +0 -0
  39. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/interface/_base.py +0 -0
  40. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/interface/_femtet_parametric.py +0 -0
  41. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/interface/_femtet_with_nx/__init__.py +0 -0
  42. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/interface/_femtet_with_nx/_interface.py +0 -0
  43. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/interface/_femtet_with_nx/update_model.py +0 -0
  44. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/interface/_femtet_with_sldworks.py +0 -0
  45. {pyfemtet-0.5.4/pyfemtet/opt/prediction → pyfemtet-0.6.1/pyfemtet/opt/optimizer/_optuna}/__init__.py +0 -0
  46. {pyfemtet-0.5.4/pyfemtet/opt/visualization → pyfemtet-0.6.1/pyfemtet/opt/optimizer/_optuna/_botorch_patch}/__init__.py +0 -0
  47. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/optimizer/_scipy.py +0 -0
  48. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/optimizer/_scipy_scalar.py +0 -0
  49. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/optimizer/parameter.py +0 -0
  50. {pyfemtet-0.5.4/pyfemtet/opt/visualization/_complex_components → pyfemtet-0.6.1/pyfemtet/opt/prediction}/__init__.py +0 -0
  51. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/prediction/_base.py +0 -0
  52. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/prediction/single_task_gp.py +0 -0
  53. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/ParametricIF.femprj +0 -0
  54. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/ParametricIF.py +0 -0
  55. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/ParametricIF_test_result.reccsv +0 -0
  56. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/cad_ex01_NX.femprj +0 -0
  57. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/cad_ex01_NX.prt +0 -0
  58. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/cad_ex01_NX.py +0 -0
  59. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/cad_ex01_NX_test_result.reccsv +0 -0
  60. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/cad_ex01_SW.SLDPRT +0 -0
  61. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/cad_ex01_SW.femprj +0 -0
  62. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/cad_ex01_SW.py +0 -0
  63. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/cad_ex01_SW_test_result.reccsv +0 -0
  64. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/constrained_pipe.femprj +0 -0
  65. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/constrained_pipe_test_result.reccsv +0 -0
  66. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/gal_ex58_parametric.femprj +0 -0
  67. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/gal_ex58_parametric.py +0 -0
  68. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/gal_ex58_parametric_test_result.reccsv +0 -0
  69. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/gau_ex08_parametric.femprj +0 -0
  70. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/gau_ex08_parametric.py +0 -0
  71. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/gau_ex08_parametric_test_result.reccsv +0 -0
  72. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/her_ex40_parametric.femprj +0 -0
  73. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/her_ex40_parametric_test_result.reccsv +0 -0
  74. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric.femprj +0 -0
  75. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric.py +0 -0
  76. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric_parallel.py +0 -0
  77. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/paswat_ex1_parametric_test_result.reccsv +0 -0
  78. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric.femprj +0 -0
  79. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric.py +0 -0
  80. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric_parallel.py +0 -0
  81. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample/wat_ex14_parametric_test_result.reccsv +0 -0
  82. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample_jp/ParametricIF_jp.femprj +0 -0
  83. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample_jp/ParametricIF_jp.py +0 -0
  84. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_NX_jp.femprj +0 -0
  85. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_NX_jp.py +0 -0
  86. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_SW_jp.femprj +0 -0
  87. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample_jp/cad_ex01_SW_jp.py +0 -0
  88. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample_jp/constrained_pipe_jp.py +0 -0
  89. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample_jp/gal_ex58_parametric_jp.femprj +0 -0
  90. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample_jp/gal_ex58_parametric_jp.py +0 -0
  91. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample_jp/gau_ex08_parametric_jp.femprj +0 -0
  92. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample_jp/gau_ex08_parametric_jp.py +0 -0
  93. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample_jp/her_ex40_parametric_jp.femprj +0 -0
  94. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample_jp/her_ex40_parametric_jp.py +0 -0
  95. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample_jp/paswat_ex1_parametric_jp.femprj +0 -0
  96. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample_jp/paswat_ex1_parametric_jp.py +0 -0
  97. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample_jp/paswat_ex1_parametric_parallel_jp.py +0 -0
  98. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample_jp/wat_ex14_parametric_jp.femprj +0 -0
  99. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample_jp/wat_ex14_parametric_jp.py +0 -0
  100. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/samples/femprj_sample_jp/wat_ex14_parametric_parallel_jp.py +0 -0
  101. {pyfemtet-0.5.4/pyfemtet/opt/visualization/_process_monitor → pyfemtet-0.6.1/pyfemtet/opt/visualization}/__init__.py +0 -0
  102. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/visualization/_base.py +0 -0
  103. {pyfemtet-0.5.4/pyfemtet/opt/visualization/_wrapped_components → pyfemtet-0.6.1/pyfemtet/opt/visualization/_complex_components}/__init__.py +0 -0
  104. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/visualization/_complex_components/alert_region.py +0 -0
  105. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/visualization/_complex_components/control_femtet.py +0 -0
  106. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/visualization/_complex_components/main_figure_creator.py +0 -0
  107. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/visualization/_complex_components/main_graph.py +0 -0
  108. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/visualization/_complex_components/pm_graph.py +0 -0
  109. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/visualization/_complex_components/pm_graph_creator.py +0 -0
  110. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/visualization/_create_wrapped_components.py +0 -0
  111. {pyfemtet-0.5.4/pyfemtet/opt/visualization/result_viewer → pyfemtet-0.6.1/pyfemtet/opt/visualization/_process_monitor}/__init__.py +0 -0
  112. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/visualization/_process_monitor/application.py +0 -0
  113. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/visualization/_process_monitor/pages.py +0 -0
  114. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/visualization/_wrapped_components/dbc.py +0 -0
  115. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/visualization/_wrapped_components/dcc.py +0 -0
  116. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/visualization/_wrapped_components/html.py +0 -0
  117. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/visualization/_wrapped_components/str_enum.py +0 -0
  118. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/visualization/result_viewer/.gitignore +0 -0
  119. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/visualization/result_viewer/application.py +0 -0
  120. {pyfemtet-0.5.4 → pyfemtet-0.6.1}/pyfemtet/opt/visualization/result_viewer/pages.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyfemtet
3
- Version: 0.5.4
3
+ Version: 0.6.1
4
4
  Summary: Design parameter optimization using Femtet.
5
5
  Home-page: https://github.com/pyfemtet/pyfemtet
6
6
  License: BSD-3-Clause
@@ -0,0 +1 @@
1
+ __version__ = "0.6.1"
@@ -80,7 +80,7 @@ msgstr "モデル更新に失敗しました。"
80
80
 
81
81
  #: pyfemtet/message/messages.py:60
82
82
  msgid "It was detected that the configuration of Femtet python macro constants has not been completed. The configuration was done automatically (python -m win32com.client.makepy FemtetMacro). Please restart the program."
83
- msgstr "Femtet マクロの定数の設定が未完了であることが検出され、設定コマンド(python -m win23com.client.makepy FemtetMacro)が自動実行されました。プログラムを再実行してください。"
83
+ msgstr "Femtet マクロの定数の設定が未完了であることが検出され、設定コマンド(python -m win32com.client.makepy FemtetMacro)が自動実行されました。プログラムを再実行してください。"
84
84
 
85
85
  #: pyfemtet/message/messages.py:61
86
86
  msgid "Failed to connect to Femtet."
@@ -9,6 +9,8 @@ from pyfemtet.opt.optimizer import AbstractOptimizer
9
9
 
10
10
  from pyfemtet.opt._femopt import FEMOpt
11
11
 
12
+ from pyfemtet.opt._femopt_core import History
13
+
12
14
 
13
15
  __all__ = [
14
16
  'FEMOpt',
@@ -21,4 +23,5 @@ __all__ = [
21
23
  'OptunaOptimizer',
22
24
  'ScipyScalarOptimizer',
23
25
  'ScipyOptimizer',
26
+ 'History',
24
27
  ]
@@ -12,7 +12,7 @@ from traceback import print_exception
12
12
  # 3rd-party
13
13
  import numpy as np
14
14
  import pandas as pd
15
- from dask.distributed import LocalCluster, Client, Worker
15
+ from dask.distributed import LocalCluster, Client
16
16
 
17
17
  # pyfemtet relative
18
18
  from pyfemtet.opt.interface import FEMInterface, FemtetInterface
@@ -32,10 +32,13 @@ from pyfemtet._message import Msg, encoding
32
32
  from pyfemtet.opt.optimizer.parameter import Parameter, Expression
33
33
  from pyfemtet._warning import experimental_feature
34
34
 
35
+ from dask import config as cfg
36
+ cfg.set({'distributed.scheduler.worker-ttl': None})
35
37
 
36
- def add_worker(client, worker_name):
38
+
39
+ def add_worker(client, worker_name, n_workers=1):
37
40
  import sys
38
- from subprocess import Popen, DEVNULL
41
+ from subprocess import Popen, DEVNULL, PIPE
39
42
 
40
43
  current_n_workers = len(client.nthreads().keys())
41
44
 
@@ -43,16 +46,16 @@ def add_worker(client, worker_name):
43
46
  f'{sys.executable} -m dask worker '
44
47
  f'{client.scheduler.address} '
45
48
  f'--nthreads 1 '
46
- f'--nworkers 1 '
47
- f'--name {worker_name} '
48
- f'--no-nanny',
49
+ f'--nworkers {n_workers} '
50
+ f'--name {worker_name} ', # A unique name for this worker like ‘worker-1’. If used with -nworkers then the process number will be appended like name-0, name-1, name-2, …
51
+ # --no-nanny option は --nworkers と併用できない
49
52
  shell=True,
50
53
  stderr=DEVNULL,
51
54
  stdout=DEVNULL,
52
55
  )
53
56
 
54
57
  # worker が増えるまで待つ
55
- client.wait_for_workers(n_workers=current_n_workers + 1)
58
+ client.wait_for_workers(n_workers=current_n_workers + n_workers)
56
59
 
57
60
 
58
61
  class FEMOpt:
@@ -132,6 +135,7 @@ class FEMOpt:
132
135
  self.monitor_process_future = None
133
136
  self.monitor_server_kwargs = dict()
134
137
  self.monitor_process_worker_name = None
138
+ self._hv_reference = None
135
139
 
136
140
  # multiprocess 時に pickle できないオブジェクト参照の削除
137
141
  def __getstate__(self):
@@ -359,6 +363,46 @@ class FEMOpt:
359
363
 
360
364
  self.opt.objectives[name] = Objective(fun, name, direction, args, kwargs)
361
365
 
366
+ def add_objectives(
367
+ self,
368
+ fun: Callable[[Any], "Sequence"["SupportsFloat"]],
369
+ n_return: int,
370
+ names: str or "Sequence"[str] or None = None,
371
+ directions: str or "Sequence"[str] or None = None,
372
+ args: tuple or None = None,
373
+ kwargs: dict or None = None,
374
+ ):
375
+ from pyfemtet.opt._femopt_core import ObjectivesFunc
376
+ components = ObjectivesFunc(fun, n_return)
377
+
378
+ if names is not None:
379
+ if isinstance(names, str):
380
+ names = [f'name_{i}' for i in range(n_return)]
381
+ else:
382
+ # names = names
383
+ pass
384
+ else:
385
+ names = [None for _ in range(n_return)]
386
+
387
+ if directions is not None:
388
+ if isinstance(directions, str):
389
+ directions = [directions for _ in range(n_return)]
390
+ else:
391
+ # directions = directions
392
+ pass
393
+ else:
394
+ directions = ['minimize' for _ in range(n_return)]
395
+
396
+ for name, component, direction in zip(names, components, directions):
397
+ self.add_objective(
398
+ fun=component,
399
+ name=name,
400
+ direction=direction,
401
+ args=args,
402
+ kwargs=kwargs,
403
+ )
404
+
405
+
362
406
  def add_constraint(
363
407
  self,
364
408
  fun,
@@ -385,9 +429,9 @@ class FEMOpt:
385
429
 
386
430
  Warnings:
387
431
 
388
- When ```strict``` == True and using OptunaOptimizer along with BoTorchSampler,
389
- Pyfemtet will solve an optimization subproblem to propose new variables.
390
- During this process, the constraint function fun will be executed at each
432
+ When ```strict``` == True and using OptunaOptimizer along with :class:`PoFBoTorchSampler`,
433
+ PyFemtet will solve an optimization subproblem to propose new variables.
434
+ During this process, the constraint function ```fun``` will be executed at each
391
435
  iteration of the subproblem, which may include time-consuming operations such
392
436
  as retrieving 3D model information via FEMInterface.
393
437
  As a result, it may not be feasible to complete the overall optimization within
@@ -406,7 +450,7 @@ class FEMOpt:
406
450
 
407
451
  Instead, please do the following.
408
452
 
409
- >>> def bottom_area(_, opt): # 第一引数 Femtet にはアクセスしないようにします。 # doctest: +SKIP
453
+ >>> def bottom_area(_, opt): # Not to access the 1st argument. # doctest: +SKIP
410
454
  ... params = opt.get_parameter() # doctest: +SKIP
411
455
  ... w, d = params['width'], params['depth'] # doctest: +SKIP
412
456
  ... return w * d # doctest: +SKIP
@@ -623,14 +667,21 @@ class FEMOpt:
623
667
 
624
668
  else:
625
669
  # ローカルクラスターを構築
626
- logger.info('Launching single machine cluster. This may take tens of seconds.')
670
+ logger.info('Launching single machine cluster... This may take tens of seconds.')
671
+
672
+ # Fixed:
673
+ # Nanny の管理機能は必要ないが、Python API では worker_class を Worker にすると
674
+ # processes 引数が無視されて Thread worker が立てられる。
675
+ # これは CLI の --no-nanny オプションも同様らしい。
676
+
677
+ # クラスターの構築
627
678
  cluster = LocalCluster(
628
679
  processes=True,
629
- n_workers=n_parallel, # n_parallel = n_parallel - 1 + 1; main 分減らし、monitor 分増やす
680
+ n_workers=n_parallel,
630
681
  threads_per_worker=1,
631
- worker_class=Worker,
632
682
  )
633
683
  logger.info('LocalCluster launched successfully.')
684
+
634
685
  self.client = Client(cluster, direct_to_workers=False)
635
686
  self.scheduler_address = self.client.scheduler.address
636
687
  logger.info('Client launched successfully.')
@@ -655,6 +706,7 @@ class FEMOpt:
655
706
  model_name=self.fem.model_name
656
707
  )
657
708
  )
709
+
658
710
  # Femtet の parametric 設定を目的関数に用いるかどうか
659
711
  if self.fem.parametric_output_indexes_use_as_objective is not None:
660
712
  from pyfemtet.opt.interface._femtet_parametric import add_parametric_results_as_objectives
@@ -665,8 +717,14 @@ class FEMOpt:
665
717
  indexes,
666
718
  directions,
667
719
  )
720
+
721
+ # Femtet の confirm_before_exit のセット
722
+ self.fem.confirm_before_exit = confirm_before_exit
723
+ self.fem.kwargs['confirm_before_exit'] = confirm_before_exit
724
+
668
725
  logger.info('Femtet loaded successfully.')
669
726
 
727
+
670
728
  # actor の設定
671
729
  self.status = OptimizationStatus(_client)
672
730
  self.worker_status_list = [OptimizationStatus(_client, name) for name in worker_addresses] # tqdm 検討
@@ -678,6 +736,7 @@ class FEMOpt:
678
736
  list(self.opt.constraints.keys()),
679
737
  _client,
680
738
  metadata,
739
+ self._hv_reference
681
740
  )
682
741
  logger.info('Status Actor initialized successfully.')
683
742
 
@@ -430,6 +430,79 @@ class Constraint(Function):
430
430
  super().__init__(fun, name, args, kwargs)
431
431
 
432
432
 
433
+ class ObjectivesFunc:
434
+ """複数の値を返す関数を objective として扱うためのクラス
435
+
436
+ 複数の値を返す関数を受け取る。
437
+
438
+ 最初に評価されたときに計算を実行し
439
+ そうでない場合は保持した値を返す
440
+ callable のリストを提供する。
441
+
442
+ """
443
+
444
+ def __init__(self, fun, n_return):
445
+ self._evaluated = [False for _ in range(n_return)]
446
+ self._values = [None for _ in range(n_return)]
447
+ self._i = 0
448
+ self.fun = fun
449
+ self.n_return = n_return
450
+
451
+ def __iter__(self):
452
+ return self
453
+
454
+ def __next__(self):
455
+
456
+ # iter の長さ
457
+ if self._i == self.n_return:
458
+ self._i = 0
459
+ raise StopIteration
460
+
461
+ # iter として提供する callable オブジェクト
462
+ # self の情報にもアクセスする必要があり
463
+ # それぞれが iter された時点での i 番目という
464
+ # 情報も必要なのでこのスコープで定義する必要がある
465
+ class NthFunc:
466
+ def __init__(self_, i):
467
+ # 何番目の要素であるかを保持
468
+ # self._i を直接参照すると
469
+ # 実行時点での ObjectiveFunc の
470
+ # 値を参照してしまう
471
+ self_.i = i
472
+
473
+ def __call__(self_, *args, **kwargs):
474
+ # 何番目の要素であるか
475
+ i = self_.i
476
+
477
+ # 一度も評価されていなければ評価する
478
+ if not any(self._evaluated):
479
+ self._values = tuple(self.fun(*args, **kwargs))
480
+ assert len(self._values) == self.n_return, '予期しない戻り値の数'
481
+
482
+ # 評価したらフラグを立てる
483
+ self._evaluated[i] = True
484
+
485
+ # すべてのフラグが立ったらクリアする
486
+ if all(self._evaluated):
487
+ self._evaluated = [False for _ in range(self.n_return)]
488
+
489
+ # 値を返す
490
+ return self._values[i]
491
+
492
+ @property
493
+ def __globals__(self_):
494
+ # ScapeGoat 実装への対処
495
+ return self.fun.__globals__
496
+
497
+ # callable を作成
498
+ f = NthFunc(self._i)
499
+
500
+ # index を更新
501
+ self._i += 1
502
+
503
+ return f
504
+
505
+
433
506
  class _HistoryDfCore:
434
507
  """Class for managing a DataFrame object in a distributed manner."""
435
508
 
@@ -453,18 +526,12 @@ class History:
453
526
  cns_names (List[str], optional): The names of constraints. Defaults to None.
454
527
  client (dask.distributed.Client): Dask client.
455
528
  additional_metadata (str, optional): metadata of optimization process.
456
-
457
- Raises:
458
- FileNotFoundError: If the csv file is not found.
459
-
460
- Attributes:
461
- HEADER_ROW (int): Header row number of csv file. Must be grater than 0. Default to 2.
462
- ENCODING (str): Encoding of csv file. Default to 'cp932'.
463
- prm_names (str): User defined names of parameters.
464
- obj_names (str): User defined names of objectives.
465
- cns_names (str): User defined names of constraints.
466
- is_restart (bool): If the optimization process is a continuation of another process or not.
467
- is_processing (bool): The optimization is running or not.
529
+ hv_reference (str or list[float or np.ndarray, optional):
530
+ The method to calculate hypervolume or
531
+ the reference point itself.
532
+ Valid values are 'dynamic-pareto' or
533
+ 'dynamic-nadir' or 'nadir' or 'pareto'
534
+ or fixed point (in objective function space).
468
535
 
469
536
  """
470
537
 
@@ -487,7 +554,10 @@ class History:
487
554
  cns_names=None,
488
555
  client=None,
489
556
  additional_metadata=None,
557
+ hv_reference=None,
490
558
  ):
559
+ # hypervolume 計算メソッド
560
+ self._hv_reference = hv_reference or 'dynamic-pareto'
491
561
 
492
562
  # 引数の処理
493
563
  self.path = history_path # .csv
@@ -739,59 +809,98 @@ class History:
739
809
 
740
810
  def _calc_hypervolume(self, objectives, df):
741
811
 
742
- # filter non-dominated and feasible solutions
743
- idx = df['non_domi'].values
744
- idx = idx * df['feasible'].values # *= を使うと non_domi 列の値が変わる
745
- pdf = df[idx]
746
- pareto_set = pdf[self.obj_names].values
747
- n = len(pareto_set) # 集合の要素数
748
- m = len(pareto_set.T) # 目的変数数
749
- # 多目的でないと計算できない
750
- if m <= 1:
751
- return None
752
- # 長さが 2 以上でないと計算できない
753
- if n <= 1:
754
- return None
755
- # 最小化問題に convert
756
- for i, (_, objective) in enumerate(objectives.items()):
757
- for j in range(n):
758
- pareto_set[j, i] = objective.convert(pareto_set[j, i])
759
- #### reference point の計算[1]
760
- # 逆正規化のための範囲計算
761
- maximum = pareto_set.max(axis=0)
762
- minimum = pareto_set.min(axis=0)
763
-
764
- r = 1.01
765
-
766
- # r を逆正規化
767
- reference_point = r * (maximum - minimum) + minimum
768
-
769
- #### hv 履歴の計算
770
- hvs = []
771
- for i in range(n):
772
- hv = compute_hypervolume(pareto_set[:i], reference_point)
773
- if np.isnan(hv):
774
- hv = 0
775
- hvs.append(hv)
776
-
777
- # 計算結果を履歴の一部に割り当て
778
- # idx: [False, True, False, True, True, False, ...]
779
- # hvs: [ 1, 2, 3, ...]
780
- # want:[ 0, 1, 1, 2, 3, 3, ...]
781
- hvs_index = -1
782
- for i in range(len(df)):
783
-
784
- # process hvs index
785
- if idx[i]:
786
- hvs_index += 1
787
-
788
- # get hv
789
- if hvs_index < 0:
790
- hypervolume = 0.
812
+ if len(objectives) < 2:
813
+ df.loc[len(df) - 1, 'hypervolume'] = 0.
814
+ return
815
+
816
+ # 最小化問題に変換された objective values を取得
817
+ raw_objective_values = df[self.obj_names].values
818
+ objective_values = np.empty_like(raw_objective_values)
819
+ for n_trial in range(len(raw_objective_values)):
820
+ for obj_idx, (_, objective) in enumerate(objectives.items()):
821
+ objective_values[n_trial, obj_idx] = objective.convert(raw_objective_values[n_trial, obj_idx])
822
+
823
+ # pareto front を取得
824
+ def get_pareto(objective_values_, with_partial=False):
825
+ ret = None
826
+ if with_partial:
827
+ ret = []
828
+
829
+ pareto_set_ = np.empty((0, len(self.obj_names)))
830
+ for i in range(len(objective_values_)):
831
+ target = objective_values_[i]
832
+ dominated = False
833
+ # TODO: Array の計算に直して高速化する
834
+ for j in range(len(objective_values_)):
835
+ compare = objective_values_[j]
836
+ if all(target > compare):
837
+ dominated = True
838
+ break
839
+ if not dominated:
840
+ pareto_set_ = np.concatenate([pareto_set_, [target]], axis=0)
841
+
842
+ if ret is not None:
843
+ ret.append(np.array(pareto_set_))
844
+
845
+ if ret is not None:
846
+ return pareto_set_, ret
791
847
  else:
792
- hypervolume = hvs[hvs_index]
848
+ return pareto_set_
849
+
850
+ if self._hv_reference == 'dynamic-pareto':
851
+ pareto_set, pareto_set_list = get_pareto(objective_values, with_partial=True)
852
+ for i, partial_pareto_set in enumerate(pareto_set_list):
853
+ ref_point = pareto_set.max(axis=0) + 1e-8
854
+ hv = compute_hypervolume(partial_pareto_set, ref_point)
855
+ df.loc[i, 'hypervolume'] = hv
856
+ return
857
+
858
+ elif self._hv_reference == 'dynamic-nadir':
859
+ _, pareto_set_list = get_pareto(objective_values, with_partial=True)
860
+ for i, partial_pareto_set in enumerate(pareto_set_list):
861
+ ref_point = objective_values.max(axis=0) + 1e-8
862
+ hv = compute_hypervolume(partial_pareto_set, ref_point)
863
+ df.loc[i, 'hypervolume'] = hv
864
+ return
865
+
866
+ elif self._hv_reference == 'nadir':
867
+ pareto_set = get_pareto(objective_values)
868
+ ref_point = objective_values.max(axis=0) + 1e-8
869
+ hv = compute_hypervolume(pareto_set, ref_point)
870
+ df.loc[len(df) - 1, 'hypervolume'] = hv
871
+ return
872
+
873
+ elif self._hv_reference == 'pareto':
874
+ pareto_set = get_pareto(objective_values)
875
+ ref_point = pareto_set.max(axis=0) + 1e-8
876
+ hv = compute_hypervolume(pareto_set, ref_point)
877
+ df.loc[len(df) - 1, 'hypervolume'] = hv
878
+ return
879
+
880
+ elif (
881
+ isinstance(self._hv_reference, np.ndarray)
882
+ or isinstance(self._hv_reference, list)
883
+ ):
884
+ _buff = np.array(self._hv_reference)
885
+ assert _buff.shape == (len(self.obj_names),)
886
+
887
+ ref_point = np.array(
888
+ [obj.convert(raw_value) for obj, raw_value in zip(objectives.values(), _buff)]
889
+ )
890
+
891
+ _buff = get_pareto(objective_values)
793
892
 
794
- df.loc[i, 'hypervolume'] = hypervolume
893
+ pareto_set = np.empty((0, len(objectives)))
894
+ for pareto_sol in _buff:
895
+ if all(pareto_sol < ref_point):
896
+ pareto_set = np.concatenate([pareto_set, [pareto_sol]], axis=0)
897
+
898
+ hv = compute_hypervolume(pareto_set, ref_point)
899
+ df.loc[len(df) - 1, 'hypervolume'] = hv
900
+ return
901
+
902
+ else:
903
+ raise NotImplementedError(f'Invalid Hypervolume reference point calculation method: {self._hv_reference}')
795
904
 
796
905
  def save(self, _f=None):
797
906
  """Save csv file."""
@@ -78,6 +78,10 @@ class FemtetInterface(FEMInterface):
78
78
  it will be None and no parametric outputs are used
79
79
  as objectives.
80
80
 
81
+ confirm_before_exit (bool):
82
+ Whether to confirm before (abnormal) termination.
83
+ Default is True.
84
+
81
85
  **kwargs: Additional arguments from inherited classes.
82
86
 
83
87
  Warning:
@@ -103,6 +107,7 @@ class FemtetInterface(FEMInterface):
103
107
  allow_without_project: bool = False, # main でのみ True を許容したいので super() の引数にしない。
104
108
  open_result_with_gui: bool = True,
105
109
  parametric_output_indexes_use_as_objective: dict[int, str or float] = None,
110
+ confirm_before_exit: bool = True,
106
111
  **kwargs # 継承されたクラスからの引数
107
112
  ):
108
113
 
@@ -132,6 +137,7 @@ class FemtetInterface(FEMInterface):
132
137
  self.parametric_output_indexes_use_as_objective = parametric_output_indexes_use_as_objective
133
138
  self._original_autosave_enabled = _get_autosave_enabled()
134
139
  _set_autosave_enabled(False)
140
+ self.confirm_before_exit = confirm_before_exit
135
141
 
136
142
  # dask サブプロセスのときは femprj を更新し connect_method を new にする
137
143
  try:
@@ -169,6 +175,7 @@ class FemtetInterface(FEMInterface):
169
175
  open_result_with_gui=self.open_result_with_gui,
170
176
  parametric_output_indexes_use_as_objective=self.parametric_output_indexes_use_as_objective,
171
177
  save_pdt=self.save_pdt,
178
+ confirm_before_exit=self.confirm_before_exit,
172
179
  **kwargs
173
180
  )
174
181
 
@@ -241,7 +248,8 @@ class FemtetInterface(FEMInterface):
241
248
  print('================')
242
249
  print(message)
243
250
  print('================')
244
- input('Press enter to finish...')
251
+ if self.confirm_before_exit:
252
+ input('Press enter to finish...')
245
253
  raise RuntimeError(message)
246
254
 
247
255
  if self.Femtet is None:
@@ -532,7 +540,8 @@ class FemtetInterface(FEMInterface):
532
540
  logger.error(Msg.ERR_CANNOT_ACCESS_API + 'GetVariableNames_py')
533
541
  logger.error(Msg.CERTIFY_MACRO_VERSION)
534
542
  print('================')
535
- input(Msg.ENTER_TO_QUIT)
543
+ if self.confirm_before_exit:
544
+ input(Msg.ENTER_TO_QUIT)
536
545
  raise e
537
546
 
538
547
  if variable_names is not None:
@@ -543,7 +552,8 @@ class FemtetInterface(FEMInterface):
543
552
  logger.error(message)
544
553
  logger.error(f'`{param_name}` not in {variable_names}')
545
554
  print('================')
546
- input(Msg.ENTER_TO_QUIT)
555
+ if self.confirm_before_exit:
556
+ input(Msg.ENTER_TO_QUIT)
547
557
  raise RuntimeError(message)
548
558
  else:
549
559
  return None
@@ -736,7 +746,8 @@ class FemtetInterface(FEMInterface):
736
746
  logger.error(Msg.ERR_CANNOT_ACCESS_API + 'Femtet.Exit()')
737
747
  logger.error(Msg.CERTIFY_MACRO_VERSION)
738
748
  print('================')
739
- input(Msg.ENTER_TO_QUIT)
749
+ if self.confirm_before_exit:
750
+ input(Msg.ENTER_TO_QUIT)
740
751
  raise e
741
752
 
742
753
  else:
@@ -1,12 +1,16 @@
1
1
  from pyfemtet.opt.optimizer._base import AbstractOptimizer, logger, OptimizationMethodChecker
2
- from pyfemtet.opt.optimizer._optuna import OptunaOptimizer
2
+ from pyfemtet.opt.optimizer._optuna._optuna import OptunaOptimizer
3
3
  from pyfemtet.opt.optimizer._scipy import ScipyOptimizer
4
4
  from pyfemtet.opt.optimizer._scipy_scalar import ScipyScalarOptimizer
5
5
 
6
+ from pyfemtet.opt.optimizer._optuna._pof_botorch import PoFBoTorchSampler, PoFConfig
7
+
6
8
  __all__ = [
7
9
  'ScipyScalarOptimizer',
8
10
  'ScipyOptimizer',
9
11
  'OptunaOptimizer',
10
12
  'AbstractOptimizer',
11
13
  'logger',
14
+ 'PoFBoTorchSampler',
15
+ 'PoFConfig',
12
16
  ]
@@ -142,6 +142,7 @@ class AbstractOptimizer(ABC):
142
142
  self.subprocess_idx = None
143
143
  self._exception = None
144
144
  self.method_checker: OptimizationMethodChecker = OptimizationMethodChecker(self)
145
+ self._retry_counter = 0
145
146
 
146
147
  # ===== algorithm specific methods =====
147
148
  @abstractmethod
@@ -176,13 +177,11 @@ class AbstractOptimizer(ABC):
176
177
 
177
178
  # Optimizer の x の更新
178
179
  self.set_parameter_values(x)
179
-
180
- logger.info('---------------------')
181
180
  logger.info(f'input: {x}')
182
181
 
183
182
  # FEM の更新
184
- logger.debug('fem.update() start')
185
183
  try:
184
+ logger.info(f'Solving FEM...')
186
185
  df_to_fem = self.variables.get_variables(
187
186
  format='df',
188
187
  filter_pass_to_fem=True
@@ -193,25 +192,20 @@ class AbstractOptimizer(ABC):
193
192
  logger.info(f'{type(e).__name__} : {e}')
194
193
  logger.info(Msg.INFO_EXCEPTION_DURING_FEM_ANALYSIS)
195
194
  logger.info(x)
196
- raise e # may be just a ModelError, etc.
195
+ raise e # may be just a ModelError, etc. Handling them in Concrete classes.
197
196
 
198
197
  # y, _y, c の更新
199
- logger.debug('calculate y start')
200
198
  y = [obj.calc(self.fem) for obj in self.objectives.values()]
201
199
 
202
- logger.debug('calculate _y start')
203
200
  _y = [obj.convert(value) for obj, value in zip(self.objectives.values(), y)]
204
201
 
205
- logger.debug('calculate c start')
206
202
  c = [cns.calc(self.fem) for cns in self.constraints.values()]
207
203
 
208
- logger.debug('history.record start')
209
-
204
+ # register to history
210
205
  df_to_opt = self.variables.get_variables(
211
206
  format='df',
212
207
  filter_parameter=True,
213
208
  )
214
-
215
209
  self.history.record(
216
210
  df_to_opt,
217
211
  self.objectives,
@@ -223,9 +217,7 @@ class AbstractOptimizer(ABC):
223
217
  postprocess_args=self.fem._create_postprocess_args(),
224
218
  )
225
219
 
226
- logger.debug('history.record end')
227
-
228
- logger.info(f'output: {_y}')
220
+ logger.info(f'output: {y}')
229
221
 
230
222
  return np.array(y), np.array(_y), np.array(c)
231
223