pyfemtet 1.0.0a0__py3-none-any.whl → 1.0.1__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 (28) hide show
  1. pyfemtet/_i18n/locales/ja/LC_MESSAGES/messages.mo +0 -0
  2. pyfemtet/_i18n/locales/ja/LC_MESSAGES/messages.po +1 -1
  3. pyfemtet/_util/dask_util.py +1 -1
  4. pyfemtet/_util/df_util.py +18 -1
  5. pyfemtet/_util/helper.py +9 -0
  6. pyfemtet/opt/__init__.py +3 -1
  7. pyfemtet/opt/femopt.py +22 -3
  8. pyfemtet/opt/history/_history.py +21 -7
  9. pyfemtet/opt/interface/_excel_interface/excel_interface.py +0 -2
  10. pyfemtet/opt/interface/_femtet_interface/femtet_interface.py +24 -11
  11. pyfemtet/opt/interface/_femtet_with_nx_interface/femtet_with_nx_interface.py +1 -1
  12. pyfemtet/opt/interface/_femtet_with_solidworks/femtet_with_solidworks_interface.py +21 -1
  13. pyfemtet/opt/interface/_solidworks_interface/solidworks_interface.py +21 -0
  14. pyfemtet/opt/interface/_surrogate_model_interface/base_surrogate_interface.py +99 -8
  15. pyfemtet/opt/interface/_surrogate_model_interface/botorch_interface.py +30 -3
  16. pyfemtet/opt/optimizer/_base_optimizer.py +58 -23
  17. pyfemtet/opt/optimizer/optuna_optimizer/_optuna_attribute.py +47 -57
  18. pyfemtet/opt/optimizer/optuna_optimizer/_optuna_optimizer.py +138 -20
  19. pyfemtet/opt/optimizer/optuna_optimizer/_pof_botorch/pof_botorch_sampler.py +4 -4
  20. pyfemtet/opt/optimizer/scipy_optimizer/_scipy_optimizer.py +19 -0
  21. pyfemtet/opt/problem/variable_manager/_variable_manager.py +84 -28
  22. {pyfemtet-1.0.0a0.dist-info → pyfemtet-1.0.1.dist-info}/METADATA +3 -2
  23. {pyfemtet-1.0.0a0.dist-info → pyfemtet-1.0.1.dist-info}/RECORD +27 -28
  24. pyfemtet/opt/interface/_surrogate/_base.py +0 -0
  25. {pyfemtet-1.0.0a0.dist-info → pyfemtet-1.0.1.dist-info}/LICENSE +0 -0
  26. {pyfemtet-1.0.0a0.dist-info → pyfemtet-1.0.1.dist-info}/LICENSE_THIRD_PARTY.txt +0 -0
  27. {pyfemtet-1.0.0a0.dist-info → pyfemtet-1.0.1.dist-info}/WHEEL +0 -0
  28. {pyfemtet-1.0.0a0.dist-info → pyfemtet-1.0.1.dist-info}/entry_points.txt +0 -0
@@ -491,7 +491,7 @@ msgstr "グラフを自動更新"
491
491
 
492
492
  #: pyfemtet/_i18n/messages.py:242
493
493
  msgid "Interrupt Optimization"
494
- msgstr "最適化を中断しました"
494
+ msgstr "最適化を中断"
495
495
 
496
496
  #: pyfemtet/_i18n/messages.py:244
497
497
  msgid "Result"
@@ -24,7 +24,7 @@ warnings.filterwarnings('ignore', category=RuntimeWarning, message="Couldn't det
24
24
 
25
25
  cfg.set({'distributed.scheduler.worker-ttl': None})
26
26
 
27
- logger = get_module_logger('opt.dask', True)
27
+ logger = get_module_logger('opt.dask', False)
28
28
 
29
29
 
30
30
  __all__ = [
pyfemtet/_util/df_util.py CHANGED
@@ -1,3 +1,4 @@
1
+ from math import isnan
1
2
  import pandas as pd
2
3
 
3
4
 
@@ -9,8 +10,24 @@ __all__ = [
9
10
 
10
11
  def get_index(df, equality_filters):
11
12
  # フィルタ条件に一致する行のインデックスを取得
13
+
14
+ # na との == での比較は常に False なので別処理するために別リストを作る
15
+ want_na_keys = []
16
+ for key, value in equality_filters.items():
17
+ if isinstance(value, float):
18
+ if isnan(value):
19
+ want_na_keys.append(key)
20
+ [equality_filters.pop(key) for key in want_na_keys]
21
+
22
+ # na 以外の比較
12
23
  # noinspection PyUnresolvedReferences
13
- return (df[list(equality_filters.keys())] == pd.Series(equality_filters)).all(axis=1)
24
+ out: pd.Series = (df[list(equality_filters.keys())] == pd.Series(equality_filters)).all(axis=1)
25
+
26
+ # na との比較
27
+ for key in want_na_keys:
28
+ out = out & df[key].isna()
29
+
30
+ return out
14
31
 
15
32
 
16
33
  def get_partial_df(df: pd.DataFrame, equality_filters: dict):
pyfemtet/_util/helper.py CHANGED
@@ -1,6 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from typing import Callable
4
+
5
+ import string
6
+ import secrets
4
7
  import warnings
5
8
  from time import time, sleep
6
9
  from threading import Thread
@@ -10,6 +13,7 @@ from pyfemtet._i18n import _
10
13
  __all__ = [
11
14
  'float_',
12
15
  'time_counting',
16
+ 'generate_random_id',
13
17
  ]
14
18
 
15
19
 
@@ -92,3 +96,8 @@ def time_counting(
92
96
  warning_message,
93
97
  warning_fun,
94
98
  )
99
+
100
+
101
+ def generate_random_id(length: int = 16) -> str:
102
+ alphabet = string.ascii_letters + string.digits
103
+ return ''.join(secrets.choice(alphabet) for _ in range(length))
pyfemtet/opt/__init__.py CHANGED
@@ -1,11 +1,13 @@
1
1
  from .femopt import FEMOpt
2
- from .interface import FemtetInterface
2
+ from .interface import FemtetInterface, FemtetWithNXInterface, FemtetWithSolidworksInterface
3
3
  from .optimizer import OptunaOptimizer, ScipyOptimizer
4
4
 
5
5
 
6
6
  __all__ = [
7
7
  'FEMOpt',
8
8
  'FemtetInterface',
9
+ 'FemtetWithNXInterface',
10
+ 'FemtetWithSolidworksInterface',
9
11
  'OptunaOptimizer',
10
12
  'ScipyOptimizer',
11
13
  ]
pyfemtet/opt/femopt.py CHANGED
@@ -29,6 +29,19 @@ logger = get_module_logger('opt.femopt', False)
29
29
 
30
30
 
31
31
  class FEMOpt:
32
+ """
33
+ A class to manage finite element method (FEM) optimization using a specified optimizer and FEM interface.
34
+
35
+ Attributes:
36
+ opt (AbstractOptimizer): The optimizer instance to be used for optimization.
37
+ monitor_info (dict[str, str | int | None]): Dictionary to store monitoring information such as host and port.
38
+
39
+ Args:
40
+ fem (AbstractFEMInterface, optional): An instance of a FEM interface. Defaults to None, in which case a FemtetInterface is used.
41
+ opt (AbstractOptimizer, optional): An optimizer instance. Defaults to None, in which case OptunaOptimizer is used.
42
+
43
+ """
44
+
32
45
  opt: AbstractOptimizer
33
46
 
34
47
  def __init__(
@@ -71,16 +84,20 @@ class FEMOpt:
71
84
  name: str,
72
85
  expression_string: str,
73
86
  properties: dict[str, ...] | None = None,
87
+ *,
88
+ pass_to_fem: bool = True,
74
89
  ) -> None:
75
- self.opt.add_expression_string(name, expression_string, properties)
90
+ self.opt.add_expression_string(name, expression_string, properties, pass_to_fem=pass_to_fem)
76
91
 
77
92
  def add_expression_sympy(
78
93
  self,
79
94
  name: str,
80
95
  sympy_expr: sympy.Expr,
81
96
  properties: dict[str, ...] | None = None,
97
+ *,
98
+ pass_to_fem: bool = True,
82
99
  ) -> None:
83
- self.opt.add_expression_sympy(name, sympy_expr, properties)
100
+ self.opt.add_expression_sympy(name, sympy_expr, properties, pass_to_fem=pass_to_fem)
84
101
 
85
102
  def add_expression(
86
103
  self,
@@ -89,8 +106,10 @@ class FEMOpt:
89
106
  properties: dict[str, ...] | None = None,
90
107
  args: tuple | None = None,
91
108
  kwargs: dict | None = None,
109
+ *,
110
+ pass_to_fem: bool = True,
92
111
  ) -> None:
93
- self.opt.add_expression(name, fun, properties, args, kwargs)
112
+ self.opt.add_expression(name, fun, properties, args, kwargs, pass_to_fem=pass_to_fem)
94
113
 
95
114
  def add_categorical_parameter(
96
115
  self,
@@ -18,6 +18,7 @@ import pandas as pd
18
18
  import pyfemtet
19
19
 
20
20
  from pyfemtet._i18n import *
21
+ from pyfemtet._util.helper import generate_random_id
21
22
  from pyfemtet._util.df_util import *
22
23
  from pyfemtet._util.dask_util import *
23
24
  from pyfemtet._util.str_enum import StrEnum
@@ -43,10 +44,13 @@ __all__ = [
43
44
  'MAIN_FILTER',
44
45
  ]
45
46
 
46
- MAIN_FILTER: dict = {'sub_fidelity_name': MAIN_FIDELITY_NAME}
47
+ MAIN_FILTER: dict = {
48
+ 'sub_fidelity_name': MAIN_FIDELITY_NAME,
49
+ 'sub_sampling': float('nan')
50
+ }
47
51
 
48
52
 
49
- logger = get_module_logger('opt.history', True)
53
+ logger = get_module_logger('opt.history', False)
50
54
 
51
55
 
52
56
  def create_err_msg_from_exception(e: Exception):
@@ -126,9 +130,10 @@ class DataFrameWrapper:
126
130
 
127
131
  __df: pd.DataFrame
128
132
  _lock_name = 'edit-df'
129
- _dataset_name = 'df'
133
+ _dataset_name: str
130
134
 
131
135
  def __init__(self, df: pd.DataFrame):
136
+ self._dataset_name = 'df-' + generate_random_id()
132
137
  self.set_df(df)
133
138
 
134
139
  def __len__(self):
@@ -1053,6 +1058,14 @@ class Records:
1053
1058
 
1054
1059
  with self.df_wrapper.lock_if_not_locked:
1055
1060
 
1061
+ # check trial_id is filled
1062
+ trial_processed = False
1063
+ if processing_df['trial_id'].notna().all():
1064
+ id_to_n: dict = {tid: i + 1 for i, tid
1065
+ in enumerate(processing_df['trial_id'].unique())}
1066
+ processing_df['trial'] = processing_df['trial_id'].map(id_to_n)
1067
+ trial_processed = True
1068
+
1056
1069
  # update main fidelity
1057
1070
  equality_filters = MAIN_FILTER
1058
1071
  mgr = EntireDependentValuesCalculator(
@@ -1062,13 +1075,13 @@ class Records:
1062
1075
  )
1063
1076
  mgr.update_optimality()
1064
1077
  mgr.update_hypervolume()
1065
- mgr.update_trial_number()
1078
+ if not trial_processed:
1079
+ mgr.update_trial_number() # per_fidelity
1066
1080
  pdf = mgr.partial_df
1067
1081
  apply_partial_df(df=processing_df, partial_df=pdf, equality_filters=equality_filters)
1068
1082
 
1069
1083
  # update sub fidelity
1070
- entire_df = self.df_wrapper.get_df()
1071
- sub_fidelity_names: list = np.unique(entire_df['sub_fidelity_name']).tolist()
1084
+ sub_fidelity_names: list = np.unique(processing_df['sub_fidelity_name']).tolist()
1072
1085
  if MAIN_FIDELITY_NAME in sub_fidelity_names:
1073
1086
  sub_fidelity_names.remove(MAIN_FIDELITY_NAME)
1074
1087
  for sub_fidelity_name in sub_fidelity_names:
@@ -1078,7 +1091,8 @@ class Records:
1078
1091
  equality_filters,
1079
1092
  processing_df
1080
1093
  )
1081
- mgr.update_trial_number()
1094
+ if not trial_processed:
1095
+ mgr.update_trial_number() # per_fidelity
1082
1096
  pdf = mgr.partial_df
1083
1097
  apply_partial_df(df=processing_df, partial_df=pdf, equality_filters=equality_filters)
1084
1098
 
@@ -258,8 +258,6 @@ class ExcelInterface(COMInterface):
258
258
  force_override_when_load: bool = False,
259
259
  ):
260
260
 
261
- show_experimental_warning("ExcelInterface")
262
-
263
261
  def proc_path(path_):
264
262
  if path_ is None:
265
263
  return self._original_input_xlsm_path
@@ -4,7 +4,6 @@ from typing import TYPE_CHECKING
4
4
 
5
5
  import os
6
6
  import sys
7
- import warnings
8
7
  import subprocess
9
8
  from time import sleep
10
9
  from contextlib import nullcontext
@@ -103,8 +102,6 @@ class FemtetInterface(COMInterface):
103
102
  If you do not want to delete the swept table,
104
103
  make a copy of the original file.
105
104
 
106
- **kwargs: Additional arguments from inherited classes.
107
-
108
105
  Warning:
109
106
  Even if you specify ``strictly_pid_specify=True`` on the constructor,
110
107
  **the connection behavior is like** ``strictly_pid_specify=False`` **in parallel processing**
@@ -119,6 +116,8 @@ class FemtetInterface(COMInterface):
119
116
  """
120
117
 
121
118
  com_members = {'Femtet': 'FemtetMacro.Femtet'}
119
+ _show_parametric_index_warning = True # for GUI
120
+ _femtet_connection_timeout = 10
122
121
 
123
122
  def __init__(
124
123
  self,
@@ -129,14 +128,19 @@ class FemtetInterface(COMInterface):
129
128
  strictly_pid_specify: bool = True, # dask worker では True にしたいので super() の引数にしない。
130
129
  allow_without_project: bool = False, # main でのみ True を許容したいので super() の引数にしない。
131
130
  open_result_with_gui: bool = True,
132
- parametric_output_indexes_use_as_objective: dict[int, str or float] = None, # TODO: Remove this
133
131
  always_open_copy=False,
132
+ # ユーザーはメソッドを使うことを推奨。GUI などで使用。
133
+ parametric_output_indexes_use_as_objective: dict[int, str | float] = None,
134
134
  ):
135
- # warning
136
135
  if parametric_output_indexes_use_as_objective is not None:
137
- warnings.warn(
138
- "解析モデルに設定された既存のスイープテーブルは削除されます。"
139
- )
136
+ if FemtetInterface._show_parametric_index_warning:
137
+ logger.warning(_(
138
+ en_message='The argument `parametric_output_indexes_use_as_objective` is deprecated. '
139
+ 'Please use `FemtetInterface.use_parametric_output_as_objective()` instead.',
140
+ jp_message='`parametric_output_indexes_use_as_objective` は非推奨の引数です。'
141
+ '代わりに `FemtetInterface.use_parametric_output_as_objective()` '
142
+ 'を使ってください。',
143
+ ))
140
144
 
141
145
  # 引数の処理
142
146
  if femprj_path is None:
@@ -255,6 +259,13 @@ class FemtetInterface(COMInterface):
255
259
  None
256
260
 
257
261
  """
262
+
263
+ # warning
264
+ logger.warning(_(
265
+ en_message='The existing sweep table in the project will be removed.',
266
+ jp_message='解析モデルに設定された既存のスイープテーブルは削除されます。'
267
+ ))
268
+
258
269
  # check
259
270
  if isinstance(direction, str):
260
271
  if direction not in ("minimize", "maximize"):
@@ -304,9 +315,9 @@ class FemtetInterface(COMInterface):
304
315
  logger.info("└ Try to connect existing Femtet process.")
305
316
  # 既存の Femtet を探して Dispatch する。
306
317
  if pid is None:
307
- self.Femtet, self.femtet_pid = dispatch_femtet(timeout=5)
318
+ self.Femtet, self.femtet_pid = dispatch_femtet(timeout=self._femtet_connection_timeout)
308
319
  else:
309
- self.Femtet, self.femtet_pid = dispatch_specific_femtet(pid, timeout=5)
320
+ self.Femtet, self.femtet_pid = dispatch_specific_femtet(pid, timeout=self._femtet_connection_timeout)
310
321
  self.connected_method = "existing"
311
322
 
312
323
  def connect_femtet(self, connect_method: str = "auto", pid: int or None = None):
@@ -504,6 +515,8 @@ class FemtetInterface(COMInterface):
504
515
  name = fun.__name__
505
516
  if fun.__name__ == 'Solve':
506
517
  context = nullcontext()
518
+ elif fun.__name__ == 'solve_via_parametric_dll':
519
+ context = nullcontext()
507
520
 
508
521
  elif isinstance(fun, str):
509
522
 
@@ -525,7 +538,7 @@ class FemtetInterface(COMInterface):
525
538
  'If the optimization is hanging, the most reason is '
526
539
  'a dialog is opening in Femtet and it waits for your '
527
540
  'input. Please confirm there is no dialog in Femtet.',
528
- '{name} の実行に {warning_time_sec} 以上かかっています。'
541
+ '{name} の実行に {warning_time_sec} 秒以上かかっています。'
529
542
  'もし最適化がハングしているならば、考えられる理由として、'
530
543
  'Femtet で予期せずダイアログが開いてユーザーの入力待ちをしている場合があります。'
531
544
  'もし Femtet でダイアログが開いていれば、閉じてください。',
@@ -156,7 +156,7 @@ class FemtetWithNXInterface(FemtetInterface, _NXInterface):
156
156
  strictly_pid_specify: bool = True, # dask worker では True にしたいので super() の引数にしない。
157
157
  allow_without_project: bool = False, # main でのみ True を許容したいので super() の引数にしない。
158
158
  open_result_with_gui: bool = True,
159
- parametric_output_indexes_use_as_objective: dict[int, str or float] = None, # TODO: Remove this
159
+ parametric_output_indexes_use_as_objective: dict[int, str | float] = None,
160
160
  always_open_copy=False,
161
161
  export_curves: bool or None = None,
162
162
  export_surfaces: bool or None = None,
@@ -20,6 +20,26 @@ if TYPE_CHECKING:
20
20
 
21
21
 
22
22
  class FemtetWithSolidworksInterface(FemtetInterface, SolidworksInterface, AbstractFEMInterface):
23
+ """
24
+ Interface class integrating Femtet and SolidWorks operations.
25
+
26
+ This class combines the capabilities of both Femtet and SolidWorks interfaces, allowing
27
+ coordinated operation between simulation (Femtet) and CAD manipulation (SolidWorks).
28
+
29
+ Args:
30
+ sldprt_path (str): Path to the SolidWorks part file (.sldprt).
31
+ femprj_path (str, optional): Path to the Femtet project file (.femprj). Defaults to None.
32
+ model_name (str, optional): Name of the model in the project. Defaults to None.
33
+ connect_method (str, optional): Connection method for Femtet. Defaults to "auto".
34
+ save_pdt (str, optional): Specifies which data to save. Defaults to "all".
35
+ strictly_pid_specify (bool, optional): Whether to strictly specify parameter IDs. Defaults to True.
36
+ allow_without_project (bool, optional): If True, allows operation without a project file. Defaults to False.
37
+ open_result_with_gui (bool, optional): If True, open results with GUI. Defaults to True.
38
+ parametric_output_indexes_use_as_objective (dict[int, str | float], optional): Indexes for parametric output used as objectives. Defaults to None.
39
+ always_open_copy (bool, optional): If True, always open a copy of the project. Defaults to False.
40
+ close_solidworks_on_terminate (bool, optional): If True, SolidWorks will close when this object is destroyed. Defaults to False.
41
+ solidworks_visible (bool, optional): If True, SolidWorks will be started in visible mode. Defaults to True.
42
+ """
23
43
 
24
44
  def __init__(
25
45
  self,
@@ -31,7 +51,7 @@ class FemtetWithSolidworksInterface(FemtetInterface, SolidworksInterface, Abstra
31
51
  strictly_pid_specify: bool = True,
32
52
  allow_without_project: bool = False,
33
53
  open_result_with_gui: bool = True,
34
- parametric_output_indexes_use_as_objective: dict[int, str or float] = None,
54
+ parametric_output_indexes_use_as_objective: dict[int, str | float] = None,
35
55
  always_open_copy=False,
36
56
  close_solidworks_on_terminate=False,
37
57
  solidworks_visible=True,
@@ -41,6 +41,27 @@ class FileNotOpenedError(Exception):
41
41
 
42
42
  # noinspection PyPep8Naming
43
43
  class SolidworksInterface(COMInterface):
44
+ """
45
+ Interface class for interacting with SolidWorks through COM automation.
46
+
47
+ This class manages the connection and interaction with SolidWorks using its COM interface.
48
+ It handles initialization, visibility, and clean termination of the SolidWorks application.
49
+
50
+ Attributes:
51
+ swApp (CDispatch): The COM dispatch object for SolidWorks application.
52
+ com_members (dict): Mapping of COM member names to their interface strings.
53
+ sldprt_path (str): Absolute path to the SolidWorks part file (.sldprt).
54
+ quit_solidworks_on_terminate (bool): Whether to close SolidWorks upon object destruction.
55
+ solidworks_visible (bool): Whether the SolidWorks application window is visible.
56
+
57
+ Args:
58
+ sldprt_path (str): Path to the SolidWorks part file (.sldprt).
59
+ close_solidworks_on_terminate (bool, optional): If True, SolidWorks will close when this object is destroyed. Defaults to False.
60
+ visible (bool, optional): If True, SolidWorks will be started in visible mode. Defaults to True.
61
+
62
+ Raises:
63
+ AssertionError: If the specified part file does not exist.
64
+ """
44
65
 
45
66
  swApp: CDispatch
46
67
  com_members = {'swApp': 'SLDWORKS.Application'}
@@ -1,10 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING
3
+ from typing import TYPE_CHECKING, Sequence
4
4
 
5
5
  from pyfemtet.opt.history import *
6
6
 
7
7
  from pyfemtet.opt.interface import AbstractFEMInterface
8
+ from pyfemtet._i18n import _
8
9
 
9
10
  if TYPE_CHECKING:
10
11
  from pyfemtet.opt.optimizer import AbstractOptimizer
@@ -16,6 +17,7 @@ __all__ = [
16
17
 
17
18
 
18
19
  class AbstractSurrogateModelInterfaceBase(AbstractFEMInterface):
20
+ _load_problem_from_fem = True
19
21
  current_obj_values: dict[str, float]
20
22
  train_history: History
21
23
 
@@ -23,8 +25,15 @@ class AbstractSurrogateModelInterfaceBase(AbstractFEMInterface):
23
25
  self,
24
26
  history_path: str = None,
25
27
  train_history: History = None,
28
+ _output_directions: (
29
+ Sequence[str | float]
30
+ | dict[str, str | float]
31
+ | dict[int, str | float]
32
+ ) = None
26
33
  ):
27
34
 
35
+ self._output_directions = _output_directions
36
+
28
37
  # history_path が与えられた場合、train_history をコンストラクトする
29
38
  if history_path is not None:
30
39
  train_history = History()
@@ -37,13 +46,95 @@ class AbstractSurrogateModelInterfaceBase(AbstractFEMInterface):
37
46
  self.current_obj_values = {}
38
47
 
39
48
  def load_objectives(self, opt: AbstractOptimizer):
40
- # add_objective された目的のうち、
41
- # training data に含まれる名前ならば
42
- # fun を「その時点の current_obj_values を返す関数」で
43
- # 上書き
44
- for obj_name, obj in opt.objectives.items():
45
- if obj_name in self.train_history.obj_names:
46
- obj.fun = lambda obj_name_=obj_name: self.current_obj_values[obj_name_]
49
+
50
+ # output directions が与えられない場合、
51
+ # opt.add_objective との整合をチェックする
52
+ if self._output_directions is None:
53
+
54
+ # add_objective された目的のうち、
55
+ # training data に含まれる名前ならば
56
+ # fun を「その時点の current_obj_values を返す関数」で
57
+ # 上書き
58
+ obj_name: str
59
+ for obj_name, obj in opt.objectives.items():
60
+ # あれば上書き、なければ surrogate 最適化の際に
61
+ # 新しく追加した model を使わない目的関数と見做して何もしない
62
+ if obj_name in self.train_history.obj_names:
63
+ obj.fun = lambda _, obj_name_=obj_name: self.current_obj_values[obj_name_]
64
+
65
+ # dict で与えられた場合
66
+ elif isinstance(self._output_directions, dict):
67
+
68
+ # index 入力か str 入力かで統一されているか確認
69
+ keys = tuple(self._output_directions.keys())
70
+ assert all([isinstance(key, type(keys[0])) for key in keys]), _(
71
+ en_message='The keys of _output_directions must be '
72
+ 'all-int or all-str.',
73
+ jp_message='_output_directions のキーは int または str で'
74
+ '統一されていなければなりません。',
75
+ )
76
+
77
+ # index がキーである場合
78
+ if isinstance(keys[0], int):
79
+
80
+ for index, direction in self._output_directions.items():
81
+ obj_name = self.train_history.obj_names[index]
82
+
83
+ opt.add_objective(
84
+ name=obj_name,
85
+ fun=lambda _, obj_name_=obj_name: self.current_obj_values[obj_name_],
86
+ direction=direction,
87
+ args=(),
88
+ kwargs={},
89
+ )
90
+
91
+ # obj_name がキーである場合
92
+ if isinstance(keys[0], str):
93
+
94
+ for obj_name, direction in self._output_directions.items():
95
+ assert obj_name in self.train_history.obj_names, _(
96
+ en_message='The objective name passed as a key of '
97
+ '_output_direction must be one of the history\'s '
98
+ 'objective names. Passed name: {obj_name} / '
99
+ 'History\'s names: {obj_names}',
100
+ jp_message='_output_directions に目的関数名を与える場合は'
101
+ 'history に含まれる名前を指定しなければなりません。'
102
+ '与えられた目的名: {obj_name} / history に含まれる'
103
+ '目的名: {obj_names}',
104
+ obj_name=obj_name,
105
+ obj_names=', '.join(self.train_history.obj_names)
106
+ )
107
+
108
+ opt.add_objective(
109
+ name=obj_name,
110
+ fun=lambda obj_name_=obj_name: self.current_obj_values[obj_name_],
111
+ direction=direction,
112
+ args=(),
113
+ kwargs={},
114
+ )
115
+
116
+ # tuple で与えられた場合
117
+ elif isinstance(self._output_directions, list) \
118
+ or isinstance(self._output_directions, tuple):
119
+
120
+ obj_names = self.train_history.obj_names
121
+ assert len(self._output_directions) == len(obj_names), _(
122
+ en_message='The length of _output_directions passed as a list '
123
+ 'must be same with that of the history\'s objective '
124
+ 'names.',
125
+ jp_message='_output_directions をリストで渡す場合は'
126
+ 'その長さが history の目的関数数と一致して'
127
+ 'いなければなりません。'
128
+ )
129
+
130
+ for obj_name, direction in zip(obj_names, self._output_directions):
131
+ opt.add_objective(
132
+ name=obj_name,
133
+ fun=lambda _, obj_name_=obj_name: self.current_obj_values[obj_name_],
134
+ direction=direction,
135
+ args=(),
136
+ kwargs={},
137
+ )
47
138
 
48
139
  def load_variables(self, opt: AbstractOptimizer):
49
140
  # opt の変数が充分であるかのチェックのみ
@@ -1,3 +1,5 @@
1
+ from typing import Sequence
2
+
1
3
  import numpy as np
2
4
  from scipy.stats.distributions import norm
3
5
 
@@ -24,8 +26,22 @@ __all__ = [
24
26
 
25
27
  class BoTorchInterface(AbstractSurrogateModelInterfaceBase):
26
28
 
27
- def __init__(self, history_path: str = None, train_history: History = None):
28
- AbstractSurrogateModelInterfaceBase.__init__(self, history_path, train_history)
29
+ def __init__(
30
+ self,
31
+ history_path: str = None,
32
+ train_history: History = None,
33
+ _output_directions: (
34
+ Sequence[str | float]
35
+ | dict[str, str | float]
36
+ | dict[int, str | float]
37
+ ) = None
38
+ ):
39
+ AbstractSurrogateModelInterfaceBase.__init__(
40
+ self,
41
+ history_path,
42
+ train_history,
43
+ _output_directions
44
+ )
29
45
 
30
46
  self.model = SingleTaskGPModel()
31
47
  self.pyfemtet_model = PyFemtetModel()
@@ -62,11 +78,22 @@ class PoFBoTorchInterface(BoTorchInterface, AbstractSurrogateModelInterfaceBase)
62
78
  def __init__(
63
79
  self,
64
80
  history_path: str,
81
+ train_history: History = None,
65
82
  observation_noise: float | str | None = None,
66
83
  feasibility_noise: float | str | None = None,
67
84
  feasibility_cdf_threshold: float | str = 0.5, # or 'sample_mean'
85
+ _output_directions: (
86
+ Sequence[str | float]
87
+ | dict[str, str | float]
88
+ | dict[int, str | float]
89
+ ) = None
68
90
  ):
69
- AbstractSurrogateModelInterfaceBase.__init__(self, history_path, None)
91
+ AbstractSurrogateModelInterfaceBase.__init__(
92
+ self,
93
+ history_path,
94
+ train_history,
95
+ _output_directions
96
+ )
70
97
 
71
98
  self.model = SingleTaskGPModel()
72
99
  self.pyfemtet_model = PyFemtetModel()