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
@@ -32,7 +32,7 @@ DIRECTION: TypeAlias = (
32
32
  ]
33
33
  )
34
34
 
35
- logger = get_module_logger('opt.optimizer', True)
35
+ logger = get_module_logger('opt.optimizer', False)
36
36
 
37
37
 
38
38
  def _log_hidden_constraint(e: Exception):
@@ -177,11 +177,14 @@ class AbstractOptimizer:
177
177
  name: str,
178
178
  expression_string: str,
179
179
  properties: dict[str, ...] | None = None,
180
+ *,
181
+ pass_to_fem: bool = True,
180
182
  ) -> None:
181
183
  var = ExpressionFromString()
182
184
  var.name = name
183
185
  var._expr = ExpressionFromString.InternalClass(expression_string=expression_string)
184
186
  var.properties = properties or dict()
187
+ var.pass_to_fem = pass_to_fem
185
188
  _duplicated_name_check(name, self.variable_manager.variables.keys())
186
189
  self.variable_manager.variables.update({name: var})
187
190
 
@@ -190,11 +193,14 @@ class AbstractOptimizer:
190
193
  name: str,
191
194
  sympy_expr: sympy.Expr,
192
195
  properties: dict[str, ...] | None = None,
196
+ *,
197
+ pass_to_fem: bool = True,
193
198
  ) -> None:
194
199
  var = ExpressionFromString()
195
200
  var.name = name
196
201
  var._expr = ExpressionFromString.InternalClass(sympy_expr=sympy_expr)
197
202
  var.properties = properties or dict()
203
+ var.pass_to_fem = pass_to_fem
198
204
  _duplicated_name_check(name, self.variable_manager.variables.keys())
199
205
  self.variable_manager.variables.update({name: var})
200
206
 
@@ -205,6 +211,8 @@ class AbstractOptimizer:
205
211
  properties: dict[str, ...] | None = None,
206
212
  args: tuple | None = None,
207
213
  kwargs: dict | None = None,
214
+ *,
215
+ pass_to_fem: bool = True,
208
216
  ) -> None:
209
217
  var = ExpressionFromFunction()
210
218
  var.name = name
@@ -212,6 +220,7 @@ class AbstractOptimizer:
212
220
  var.args = args or tuple()
213
221
  var.kwargs = kwargs or dict()
214
222
  var.properties = properties or dict()
223
+ var.pass_to_fem = pass_to_fem
215
224
  _duplicated_name_check(name, self.variable_manager.variables.keys())
216
225
  self.variable_manager.variables.update({name: var})
217
226
 
@@ -425,6 +434,7 @@ class AbstractOptimizer:
425
434
 
426
435
  def __init__(self, opt: AbstractOptimizer):
427
436
  self.opt: AbstractOptimizer = opt
437
+ self.subsampling_idx: SubSampling | None = None
428
438
 
429
439
  def _preprocess(self):
430
440
  pass
@@ -451,6 +461,7 @@ class AbstractOptimizer:
451
461
  variables_pass_to_fem: dict[str, SupportedVariableTypes],
452
462
  history: History = None,
453
463
  datetime_start=None,
464
+ trial_id=None,
454
465
  ) -> _FReturnValue:
455
466
 
456
467
  # create context
@@ -477,6 +488,8 @@ class AbstractOptimizer:
477
488
  {obj_name: ObjectiveResult(obj, opt_.fem, float('nan'))
478
489
  for obj_name, obj in opt_.objectives.items()}
479
490
  )
491
+ record.sub_sampling = self.subsampling_idx
492
+ record.trial_id = trial_id
480
493
  record.sub_fidelity_name = opt_.sub_fidelity_name
481
494
  record.fidelity = opt_.fidelity
482
495
  record.datetime_start = datetime_start
@@ -489,7 +502,8 @@ class AbstractOptimizer:
489
502
  raise SkipSolve
490
503
 
491
504
  # start solve
492
- if opt_.sub_fidelity_models != MAIN_FIDELITY_NAME:
505
+ if opt_.sub_fidelity_name != MAIN_FIDELITY_NAME:
506
+ logger.info('----------')
493
507
  logger.info(_('fidelity: ({name})', name=opt_.sub_fidelity_name))
494
508
  logger.info(_('input variables:'))
495
509
  logger.info(parameters)
@@ -620,7 +634,9 @@ class AbstractOptimizer:
620
634
  x: TrialInput,
621
635
  x_pass_to_fem_: dict[str, SupportedVariableTypes],
622
636
  opt_: AbstractOptimizer | None = None,
637
+ trial_id: str =None,
623
638
  ) -> _FReturnValue | None:
639
+ """Nothing will be raised even if infeasible."""
624
640
 
625
641
  vm = self.opt.variable_manager
626
642
 
@@ -634,32 +650,40 @@ class AbstractOptimizer:
634
650
  # if opt_ is not self, update variable manager
635
651
  opt_.variable_manager = vm
636
652
 
637
- # preprocess
638
- self._preprocess()
653
+ # noinspection PyMethodParameters
654
+ class Process:
655
+ def __enter__(self_):
656
+ # preprocess
657
+ self._preprocess()
639
658
 
640
- # declare output
641
- f_return = None
659
+ def __exit__(self_, exc_type, exc_val, exc_tb):
660
+ # postprocess
661
+ self._postprocess()
642
662
 
643
- # start solve
644
- datetime_start = datetime.datetime.now()
645
- try:
646
- f_return = self._solve_or_raise(
647
- opt_, x, x_pass_to_fem_, self.opt.history, datetime_start
648
- )
663
+ with Process():
649
664
 
650
- except HardConstraintViolation as e:
651
- self._hard_constraint_handling(e)
665
+ # declare output
666
+ f_return = None
652
667
 
653
- except _HiddenConstraintViolation as e:
654
- self._hidden_constraint_handling(e)
668
+ # start solve
669
+ datetime_start = datetime.datetime.now()
670
+ try:
671
+ f_return = self._solve_or_raise(
672
+ opt_, x, x_pass_to_fem_, self.opt.history,
673
+ datetime_start, trial_id
674
+ )
655
675
 
656
- except SkipSolve as e:
657
- self._skip_handling(e)
676
+ except HardConstraintViolation as e:
677
+ self._hard_constraint_handling(e)
658
678
 
659
- else:
660
- self._if_succeeded(f_return)
679
+ except _HiddenConstraintViolation as e:
680
+ self._hidden_constraint_handling(e)
661
681
 
662
- self._postprocess()
682
+ except SkipSolve as e:
683
+ self._skip_handling(e)
684
+
685
+ else:
686
+ self._if_succeeded(f_return)
663
687
 
664
688
  # check interruption
665
689
  self.opt._check_and_raise_interruption()
@@ -790,8 +814,19 @@ class AbstractOptimizer:
790
814
  # noinspection PyMethodParameters
791
815
  class LoggingOutput:
792
816
  def __enter__(self_):
793
- self_.count = len(self.history.get_df()) + 1
794
- logger.info(f'▼▼▼▼▼ solve {self_.count} start ▼▼▼▼▼')
817
+ df = self.history.get_df(
818
+ equality_filters=MAIN_FILTER
819
+ )
820
+ self_.count = len(df) + 1
821
+
822
+ succeeded_count = len(df[df['state'] == TrialState.succeeded])
823
+ succeeded_text = _(
824
+ en_message='{succeeded_count} succeeded trials',
825
+ jp_message='成功した試行数: {succeeded_count}',
826
+ succeeded_count=succeeded_count,
827
+ )
828
+
829
+ logger.info(f'▼▼▼▼▼ solve {self_.count} ({succeeded_text}) start ▼▼▼▼▼')
795
830
 
796
831
  def __exit__(self_, exc_type, exc_val, exc_tb):
797
832
  logger.info(f'▲▲▲▲▲ solve {self_.count} end ▲▲▲▲▲\n')
@@ -1,73 +1,63 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from typing import TypedDict, Sequence
4
+
5
+ import optuna.trial
6
+
3
7
  from pyfemtet.opt.problem.problem import *
4
8
  from pyfemtet.opt.history import *
5
9
  from pyfemtet.opt.optimizer._base_optimizer import *
6
10
 
7
11
 
8
12
  class OptunaAttribute:
9
- """Manage optuna user attribute
13
+ """Manage optuna user attribute.
10
14
 
11
- user attributes are:
12
- sub_fidelity_name:
15
+ By `set_user_attr_to_trial`,
16
+ key (str):
17
+ {sub_fidelity_name}(_{subsampling_idx})
18
+ value (dict):
13
19
  fidelity: ...
14
- OBJECTIVE_ATTR_KEY: ...
15
- PYFEMTET_STATE_ATTR_KEY: ...
16
- CONSTRAINT_ATTR_KEY: ...
20
+ internal_y_values: ...
21
+ violation_values: ...
22
+ pf_state: ...
17
23
 
18
24
  """
19
-
20
- OBJECTIVE_KEY = 'internal_objective'
21
- CONSTRAINT_KEY = 'constraint'
22
- PYFEMTET_TRIAL_STATE_KEY = 'pyfemtet_trial_state'
23
-
24
- sub_fidelity_name: str # key
25
- fidelity: Fidelity | None
26
- v_values: tuple | None # violation
27
- y_values: tuple | None # internal objective
28
- pf_state: TrialState | None # PyFemtet state
29
-
30
- def __init__(self, opt: AbstractOptimizer):
31
- self.sub_fidelity_name = opt.sub_fidelity_name
32
- self.fidelity = None
33
- self.v_values = None
34
- self.y_values = None
35
- self.pf_state = None
36
-
37
- # noinspection PyPropertyDefinition
38
- @classmethod
39
- def main_fidelity_key(cls):
40
- return MAIN_FIDELITY_NAME
25
+ class AttributeStructure(TypedDict):
26
+ fidelity: Fidelity | None
27
+ internal_y_values: Sequence[float] | None
28
+ violation_values: Sequence[float] | None
29
+ pf_state: TrialState | None
30
+
31
+ def __init__(
32
+ self,
33
+ opt: AbstractOptimizer,
34
+ subsampling_idx: SubSampling | None = None,
35
+ ):
36
+ # key
37
+ self.sub_fidelity_name: str = opt.sub_fidelity_name
38
+ self.subsampling_idx: SubSampling | None = subsampling_idx
39
+ # value
40
+ self.fidelity: Fidelity = opt.fidelity
41
+ self.y_values: Sequence[float] | None = None
42
+ self.v_values: Sequence[float] | None = None
43
+ self.pf_state: TrialState | None = None
41
44
 
42
45
  @property
43
- def key(self):
44
- return self.sub_fidelity_name
46
+ def key(self) -> str:
47
+ key = self.sub_fidelity_name
48
+ if self.subsampling_idx is not None:
49
+ key += f'_{self.subsampling_idx}'
50
+ return key
45
51
 
46
52
  @property
47
- def value(self):
48
- d = {}
49
- if self.fidelity:
50
- d.update({'fidelity': self.fidelity})
51
- if self.y_values:
52
- d.update({self.OBJECTIVE_KEY: self.y_values})
53
- if self.v_values:
54
- d.update({self.CONSTRAINT_KEY: self.v_values})
55
- if self.pf_state:
56
- d.update({self.PYFEMTET_TRIAL_STATE_KEY: self.pf_state})
57
- return d
58
-
59
- @staticmethod
60
- def get_fidelity(optuna_attribute: OptunaAttribute):
61
- return optuna_attribute.value['fidelity']
62
-
63
- @staticmethod
64
- def get_violation(optuna_attribute: OptunaAttribute):
65
- return optuna_attribute.value[OptunaAttribute.CONSTRAINT_KEY]
66
-
67
- @staticmethod
68
- def get_violation_from_trial_attr(trial_attr: dict): # value is OptunaAttribute.value
69
- return trial_attr[OptunaAttribute.CONSTRAINT_KEY]
70
-
71
- @staticmethod
72
- def get_pf_state_from_trial_attr(trial_attr: dict): # value is OptunaAttribute.value
73
- return trial_attr[OptunaAttribute.PYFEMTET_TRIAL_STATE_KEY]
53
+ def value(self) -> AttributeStructure:
54
+ out = self.AttributeStructure(
55
+ fidelity=self.fidelity,
56
+ internal_y_values=self.y_values,
57
+ violation_values=self.v_values,
58
+ pf_state=self.pf_state,
59
+ )
60
+ return out
61
+
62
+ def set_user_attr_to_trial(self, trial: optuna.trial.Trial):
63
+ trial.set_user_attr(self.key, self.value)
@@ -39,6 +39,37 @@ warnings.filterwarnings('ignore', 'Argument ``constraints_func`` is an experimen
39
39
 
40
40
 
41
41
  class OptunaOptimizer(AbstractOptimizer):
42
+ """
43
+ An optimizer class utilizing Optuna for hyperparameter optimization.
44
+
45
+ This class provides an interface to conduct optimization studies using Optuna.
46
+ It manages the study lifecycle, sampler configuration, and trial execution.
47
+
48
+ Attributes:
49
+ study_name (str): Name of the Optuna study.
50
+ storage (str | optuna.storages.BaseStorage): Storage URL or object for the Optuna study.
51
+ storage_path (str): Path to the Optuna study storage.
52
+ current_trial (optuna.trial.Trial | None): The current Optuna trial being evaluated.
53
+ sampler_class (type[optuna.samplers.BaseSampler]): The class of the Optuna sampler to use.
54
+ sampler_kwargs (dict): Keyword arguments to initialize the sampler.
55
+ n_trials (int | None): Number of trials to run in the study.
56
+ timeout (float | None): Maximum time allowed for the optimization.
57
+ callbacks (list): List of callback functions to invoke during optimization.
58
+
59
+ Args:
60
+ sampler_class (type[optuna.samplers.BaseSampler], optional): The sampler class for suggesting parameter values. Defaults to TPESampler if None.
61
+ sampler_kwargs (dict[str, ...], optional): Dictionary of keyword arguments for the sampler. Defaults to an empty dictionary.
62
+
63
+ Raises:
64
+ None
65
+
66
+ Examples:
67
+ >>> optimizer = OptunaOptimizer()
68
+ >>> optimizer.n_trials = 100
69
+ >>> optimizer.timeout = 600
70
+ >>> # Further configuration and usage...
71
+ """
72
+
42
73
  # system
43
74
  study_name = 'pyfemtet-study'
44
75
  storage: str | optuna.storages.BaseStorage
@@ -122,7 +153,7 @@ class OptunaOptimizer(AbstractOptimizer):
122
153
 
123
154
  y, dict_y_internal, c, record = f_return
124
155
 
125
- # convert constraint to **sorted** violation
156
+ # convert constraint to **sorted 1-d array** violation
126
157
  assert len(c) == len(self.opt_.constraints)
127
158
  v = {}
128
159
  for cns_name, cns in self.opt_.constraints.items():
@@ -139,10 +170,7 @@ class OptunaOptimizer(AbstractOptimizer):
139
170
 
140
171
  def _postprocess(self):
141
172
  # update trial attribute
142
- self.opt.current_trial.set_user_attr(
143
- self.optuna_attr.key,
144
- self.optuna_attr.value,
145
- )
173
+ self.optuna_attr.set_user_attr_to_trial(self.opt.current_trial)
146
174
 
147
175
  def _create_infeasible_constraints(self, opt_: AbstractOptimizer = None) -> tuple:
148
176
  opt_ = opt_ if opt_ is not None else self
@@ -155,9 +183,9 @@ class OptunaOptimizer(AbstractOptimizer):
155
183
  return tuple(1e9 * np.ones(count, dtype=np.float64))
156
184
 
157
185
  def _constraint(self, trial: optuna.trial.FrozenTrial):
158
- key = OptunaAttribute(self).key
159
- value = trial.user_attrs[key]
160
- return OptunaAttribute.get_violation_from_trial_attr(value)
186
+ main_key = OptunaAttribute(self).key
187
+ user_attribute: OptunaAttribute.AttributeStructure = trial.user_attrs[main_key]
188
+ return user_attribute['violation_values']
161
189
 
162
190
  def _objective(self, trial: optuna.trial.Trial):
163
191
 
@@ -226,9 +254,9 @@ class OptunaOptimizer(AbstractOptimizer):
226
254
 
227
255
  # To avoid trial FAILED with hard constraint
228
256
  # violation, check pf_state and raise TrialPruned.
229
- key = OptunaAttribute(self).key
230
- value = trial.user_attrs[key]
231
- state = OptunaAttribute.get_pf_state_from_trial_attr(value)
257
+ main_key = OptunaAttribute(self).key
258
+ user_attribute: OptunaAttribute.AttributeStructure = trial.user_attrs[main_key]
259
+ state: TrialState = user_attribute['pf_state']
232
260
  if state in [
233
261
  TrialState.hard_constraint_violation,
234
262
  TrialState.model_error,
@@ -245,9 +273,6 @@ class OptunaOptimizer(AbstractOptimizer):
245
273
 
246
274
  return y_internal
247
275
 
248
- if get_client() is None:
249
- self.history.save()
250
-
251
276
  def _get_callback(self, n_trials: int):
252
277
 
253
278
  # restart である場合、追加 N 回と見做す
@@ -587,16 +612,16 @@ def debug_1():
587
612
  _opt.add_parameter('x1', 1, -1, 1, step=0.1)
588
613
  _opt.add_parameter('x2', 1, -1, 1, step=0.1)
589
614
  _opt.add_categorical_parameter('x3', 'a', choices=['a', 'b', 'c'])
590
- _opt.add_constraint('cns', _cns, lower_bound=-0.9, args=(_fem, _opt))
591
- _opt.add_objective('obj1', _parabola, args=(_fem, _opt))
592
- # _opt.add_objective('obj2', _parabola2, args=(_fem, _opt))
615
+ # _opt.add_constraint('cns', _cns, lower_bound=-0.9, args=(_opt,))
616
+ _opt.add_objective('obj1', _parabola, args=(_opt,))
617
+ # _opt.add_objective('obj2', _parabola2, args=(_opt,))
593
618
 
594
619
  # # ===== sub-fidelity =====
595
620
  # __fem = NoFEM()
596
621
  # __opt = SubFidelityModel()
597
622
  # __opt.fem = __fem
598
- # __opt.add_objective('obj1', _parabola, args=(__fem, __opt))
599
- # __opt.add_objective('obj2', _parabola2, args=(__fem, __opt))
623
+ # __opt.add_objective('obj1', _parabola, args=(__opt,))
624
+ # # __opt.add_objective('obj2', _parabola2, args=(__opt,))
600
625
  #
601
626
  # _opt.add_sub_fidelity_model(name='low-fidelity', sub_fidelity_model=__opt, fidelity=0.5)
602
627
  #
@@ -622,6 +647,96 @@ def debug_1():
622
647
  _opt.history.save()
623
648
 
624
649
 
650
+ def debug_1s():
651
+ # from pyfemtet.opt.optimizer.optuna_optimizer.pof_botorch.pof_botorch_sampler import
652
+ # sampler = PoFBoTorchSampler(
653
+ # n_startup_trials=5,
654
+ # seed=42,
655
+ # constraints_func=self._constraint,
656
+ # pof_config=PoFConfig(
657
+ # # consider_pof=False,
658
+ # # feasibility_cdf_threshold='mean',
659
+ # ),
660
+ # partial_optimize_acqf_kwargs=PartialOptimizeACQFConfig(
661
+ # # gen_candidates='scipy',
662
+ # timeout_sec=5.,
663
+ # # method='SLSQP' # 'COBYLA, COBYQA, SLSQP or trust-constr
664
+ # tol=0.1,
665
+ # # scipy_minimize_kwargs=dict(),
666
+ # ),
667
+ # )
668
+ # from optuna_integration import BoTorchSampler
669
+ # sampler = BoTorchSampler(n_startup_trials=5)
670
+
671
+ os.chdir(os.path.dirname(__file__))
672
+
673
+ def _parabola(_fem: AbstractFEMInterface, _opt: AbstractOptimizer) -> float:
674
+ d = _opt.get_variables()
675
+ x1 = d['x1']
676
+ x2 = d['x2']
677
+ # if _cns(_fem, _opt) < 0:
678
+ # raise PostProcessError
679
+ return x1 ** 2 + x2 ** 2
680
+
681
+ def _parabola2(_fem: AbstractFEMInterface, _opt: AbstractOptimizer) -> float:
682
+ x = _opt.get_variables('values')
683
+ return ((x - 0.1) ** 2).sum()
684
+
685
+ def _cns(_fem: AbstractFEMInterface, _opt: AbstractOptimizer) -> float:
686
+ x = _opt.get_variables('values')
687
+ return x[0]
688
+
689
+ _fem = NoFEM()
690
+ _opt = OptunaOptimizer()
691
+ _opt.fem = _fem
692
+
693
+ # _opt.sampler = optuna.samplers.RandomSampler(seed=42)
694
+ _opt.seed = 42
695
+ _opt.sampler_class = optuna.samplers.TPESampler
696
+ # _opt.sampler_class = optuna.samplers.RandomSampler
697
+ _opt.sampler_kwargs = dict(
698
+ n_startup_trials=5,
699
+ )
700
+ _opt.n_trials = 10
701
+
702
+ _opt.add_parameter('x1', 1, -1, 1, step=0.1)
703
+ _opt.add_parameter('x2', 1, -1, 1, step=0.1)
704
+ _opt.add_categorical_parameter('x3', 'a', choices=['a', 'b', 'c'])
705
+ # _opt.add_constraint('cns', _cns, lower_bound=-0.9, args=(_opt,))
706
+ _opt.add_objective('obj1', _parabola, args=(_opt,))
707
+ # _opt.add_objective('obj2', _parabola2, args=(_opt,))
708
+
709
+ # ===== sub-fidelity =====
710
+ __fem = NoFEM()
711
+ __opt = SubFidelityModel()
712
+ __opt.fem = __fem
713
+ __opt.add_objective('obj1', _parabola, args=(__opt,))
714
+ # __opt.add_objective('obj2', _parabola2, args=(__opt,))
715
+
716
+ _opt.add_sub_fidelity_model(name='low-fidelity', sub_fidelity_model=__opt, fidelity=0.5)
717
+
718
+ def _solve_condition(_history: History):
719
+
720
+ sub_fidelity_df = _history.get_df(
721
+ {'sub_fidelity_name': 'low-fidelity'}
722
+ )
723
+ idx = sub_fidelity_df['state'] == TrialState.succeeded
724
+ pdf = sub_fidelity_df[idx]
725
+
726
+ return len(pdf) % 5 == 0
727
+
728
+ _opt.set_solve_condition(_solve_condition)
729
+
730
+ # _opt.history.path = 'restart-test.csv'
731
+ _opt.run()
732
+
733
+ # import plotly.express as px
734
+ # _df = _opt.history.get_df()
735
+ # px.scatter_3d(_df, x='x1', y='x2', z='obj', color='fidelity', opacity=0.5).show()
736
+
737
+ _opt.history.save()
738
+
739
+
625
740
  def substrate_size(Femtet):
626
741
  """基板のXY平面上での専有面積を計算します。"""
627
742
  substrate_w = Femtet.GetVariableValue('substrate_w')
@@ -669,10 +784,13 @@ def debug_3():
669
784
  opt.add_parameter(name="substrate_d", initial_value=60, lower_bound=34, upper_bound=60)
670
785
 
671
786
  opt.n_trials = 5
672
- opt.history.path = os.path.join(os.path.dirname(__file__), 'femtet-test.csv')
787
+ opt.history.path = os.path.join(os.path.dirname(__file__), 'femtet-test-2.csv')
673
788
 
674
789
  opt.run()
675
790
 
676
791
 
677
792
  if __name__ == '__main__':
793
+ debug_1()
794
+ debug_1s()
795
+ debug_2()
678
796
  debug_3()
@@ -138,7 +138,7 @@ if TYPE_CHECKING:
138
138
  # noinspection PyTypeChecker
139
139
  _logger = get_logger(False)
140
140
 
141
- DEBUG = True
141
+ DEBUG = False
142
142
  logger = get_module_logger('opt.PoFBoTorchSampler', DEBUG)
143
143
 
144
144
  warnings.filterwarnings('ignore', category=InputDataWarning)
@@ -973,9 +973,9 @@ class PoFBoTorchSampler(BoTorchSampler):
973
973
  feasible_trials: list[FrozenTrial] = []
974
974
  infeasible_trials: list[FrozenTrial] = []
975
975
  for trial in (completed_trials + pruned_trials):
976
- state: PFTrialState = OptunaAttribute.get_pf_state_from_trial_attr(
977
- trial.user_attrs[OptunaAttribute.main_fidelity_key()]
978
- )
976
+ main_key = OptunaAttribute(self.pyfemtet_optimizer).key
977
+ user_attribute: OptunaAttribute.AttributeStructure = trial.user_attrs[main_key]
978
+ state: PFTrialState = user_attribute['pf_state']
979
979
 
980
980
  if state in self.pof_config._states_to_consider_pof:
981
981
  infeasible_trials.append(trial)
@@ -36,6 +36,25 @@ class _ScipyCallback:
36
36
 
37
37
 
38
38
  class ScipyOptimizer(AbstractOptimizer):
39
+ """
40
+ Optimizer class that utilizes SciPy optimization methods.
41
+
42
+ This class serves as a wrapper around SciPy's optimization routines,
43
+ allowing customization of the optimization method, tolerance, and options.
44
+ It also provides mechanisms for handling constraints with enhancement and scaling.
45
+
46
+ Attributes:
47
+ method (str): The optimization method to use (e.g., 'BFGS', 'Nelder-Mead').
48
+ tol (float or None): Tolerance for termination.
49
+ options (dict): Additional options to pass to the SciPy optimizer.
50
+ constraint_enhancement (float): Small value added to enhance constraint handling.
51
+ constraint_scaling (float): Scaling factor applied to constraints.
52
+
53
+ Args:
54
+ method (str): The optimization method to use (e.g., 'BFGS', 'Nelder-Mead').
55
+ tol (float or None): Tolerance for termination.
56
+
57
+ """
39
58
 
40
59
  _timeout: None = None
41
60
  _n_trials: None = None