pyfemtet 1.0.0b0__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.
- pyfemtet/_util/df_util.py +18 -1
- pyfemtet/_util/helper.py +9 -0
- pyfemtet/opt/femopt.py +13 -0
- pyfemtet/opt/history/_history.py +20 -6
- pyfemtet/opt/interface/_excel_interface/excel_interface.py +0 -2
- pyfemtet/opt/interface/_femtet_interface/femtet_interface.py +3 -4
- pyfemtet/opt/interface/_femtet_with_solidworks/femtet_with_solidworks_interface.py +20 -0
- pyfemtet/opt/interface/_solidworks_interface/solidworks_interface.py +21 -0
- pyfemtet/opt/optimizer/_base_optimizer.py +48 -22
- pyfemtet/opt/optimizer/optuna_optimizer/_optuna_attribute.py +47 -57
- pyfemtet/opt/optimizer/optuna_optimizer/_optuna_optimizer.py +138 -20
- pyfemtet/opt/optimizer/optuna_optimizer/_pof_botorch/pof_botorch_sampler.py +3 -3
- pyfemtet/opt/optimizer/scipy_optimizer/_scipy_optimizer.py +19 -0
- pyfemtet/opt/problem/variable_manager/_variable_manager.py +84 -28
- {pyfemtet-1.0.0b0.dist-info → pyfemtet-1.0.1.dist-info}/METADATA +1 -1
- {pyfemtet-1.0.0b0.dist-info → pyfemtet-1.0.1.dist-info}/RECORD +20 -20
- {pyfemtet-1.0.0b0.dist-info → pyfemtet-1.0.1.dist-info}/LICENSE +0 -0
- {pyfemtet-1.0.0b0.dist-info → pyfemtet-1.0.1.dist-info}/LICENSE_THIRD_PARTY.txt +0 -0
- {pyfemtet-1.0.0b0.dist-info → pyfemtet-1.0.1.dist-info}/WHEEL +0 -0
- {pyfemtet-1.0.0b0.dist-info → pyfemtet-1.0.1.dist-info}/entry_points.txt +0 -0
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
|
-
|
|
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/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__(
|
pyfemtet/opt/history/_history.py
CHANGED
|
@@ -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,7 +44,10 @@ __all__ = [
|
|
|
43
44
|
'MAIN_FILTER',
|
|
44
45
|
]
|
|
45
46
|
|
|
46
|
-
MAIN_FILTER: dict = {
|
|
47
|
+
MAIN_FILTER: dict = {
|
|
48
|
+
'sub_fidelity_name': MAIN_FIDELITY_NAME,
|
|
49
|
+
'sub_sampling': float('nan')
|
|
50
|
+
}
|
|
47
51
|
|
|
48
52
|
|
|
49
53
|
logger = get_module_logger('opt.history', False)
|
|
@@ -126,9 +130,10 @@ class DataFrameWrapper:
|
|
|
126
130
|
|
|
127
131
|
__df: pd.DataFrame
|
|
128
132
|
_lock_name = 'edit-df'
|
|
129
|
-
_dataset_name
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -102,8 +102,6 @@ class FemtetInterface(COMInterface):
|
|
|
102
102
|
If you do not want to delete the swept table,
|
|
103
103
|
make a copy of the original file.
|
|
104
104
|
|
|
105
|
-
**kwargs: Additional arguments from inherited classes.
|
|
106
|
-
|
|
107
105
|
Warning:
|
|
108
106
|
Even if you specify ``strictly_pid_specify=True`` on the constructor,
|
|
109
107
|
**the connection behavior is like** ``strictly_pid_specify=False`` **in parallel processing**
|
|
@@ -119,6 +117,7 @@ class FemtetInterface(COMInterface):
|
|
|
119
117
|
|
|
120
118
|
com_members = {'Femtet': 'FemtetMacro.Femtet'}
|
|
121
119
|
_show_parametric_index_warning = True # for GUI
|
|
120
|
+
_femtet_connection_timeout = 10
|
|
122
121
|
|
|
123
122
|
def __init__(
|
|
124
123
|
self,
|
|
@@ -316,9 +315,9 @@ class FemtetInterface(COMInterface):
|
|
|
316
315
|
logger.info("└ Try to connect existing Femtet process.")
|
|
317
316
|
# 既存の Femtet を探して Dispatch する。
|
|
318
317
|
if pid is None:
|
|
319
|
-
self.Femtet, self.femtet_pid = dispatch_femtet(timeout=
|
|
318
|
+
self.Femtet, self.femtet_pid = dispatch_femtet(timeout=self._femtet_connection_timeout)
|
|
320
319
|
else:
|
|
321
|
-
self.Femtet, self.femtet_pid = dispatch_specific_femtet(pid, timeout=
|
|
320
|
+
self.Femtet, self.femtet_pid = dispatch_specific_femtet(pid, timeout=self._femtet_connection_timeout)
|
|
322
321
|
self.connected_method = "existing"
|
|
323
322
|
|
|
324
323
|
def connect_femtet(self, connect_method: str = "auto", pid: int 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,
|
|
@@ -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'}
|
|
@@ -434,6 +434,7 @@ class AbstractOptimizer:
|
|
|
434
434
|
|
|
435
435
|
def __init__(self, opt: AbstractOptimizer):
|
|
436
436
|
self.opt: AbstractOptimizer = opt
|
|
437
|
+
self.subsampling_idx: SubSampling | None = None
|
|
437
438
|
|
|
438
439
|
def _preprocess(self):
|
|
439
440
|
pass
|
|
@@ -460,6 +461,7 @@ class AbstractOptimizer:
|
|
|
460
461
|
variables_pass_to_fem: dict[str, SupportedVariableTypes],
|
|
461
462
|
history: History = None,
|
|
462
463
|
datetime_start=None,
|
|
464
|
+
trial_id=None,
|
|
463
465
|
) -> _FReturnValue:
|
|
464
466
|
|
|
465
467
|
# create context
|
|
@@ -486,6 +488,8 @@ class AbstractOptimizer:
|
|
|
486
488
|
{obj_name: ObjectiveResult(obj, opt_.fem, float('nan'))
|
|
487
489
|
for obj_name, obj in opt_.objectives.items()}
|
|
488
490
|
)
|
|
491
|
+
record.sub_sampling = self.subsampling_idx
|
|
492
|
+
record.trial_id = trial_id
|
|
489
493
|
record.sub_fidelity_name = opt_.sub_fidelity_name
|
|
490
494
|
record.fidelity = opt_.fidelity
|
|
491
495
|
record.datetime_start = datetime_start
|
|
@@ -498,7 +502,8 @@ class AbstractOptimizer:
|
|
|
498
502
|
raise SkipSolve
|
|
499
503
|
|
|
500
504
|
# start solve
|
|
501
|
-
if opt_.
|
|
505
|
+
if opt_.sub_fidelity_name != MAIN_FIDELITY_NAME:
|
|
506
|
+
logger.info('----------')
|
|
502
507
|
logger.info(_('fidelity: ({name})', name=opt_.sub_fidelity_name))
|
|
503
508
|
logger.info(_('input variables:'))
|
|
504
509
|
logger.info(parameters)
|
|
@@ -629,7 +634,9 @@ class AbstractOptimizer:
|
|
|
629
634
|
x: TrialInput,
|
|
630
635
|
x_pass_to_fem_: dict[str, SupportedVariableTypes],
|
|
631
636
|
opt_: AbstractOptimizer | None = None,
|
|
637
|
+
trial_id: str =None,
|
|
632
638
|
) -> _FReturnValue | None:
|
|
639
|
+
"""Nothing will be raised even if infeasible."""
|
|
633
640
|
|
|
634
641
|
vm = self.opt.variable_manager
|
|
635
642
|
|
|
@@ -643,32 +650,40 @@ class AbstractOptimizer:
|
|
|
643
650
|
# if opt_ is not self, update variable manager
|
|
644
651
|
opt_.variable_manager = vm
|
|
645
652
|
|
|
646
|
-
#
|
|
647
|
-
|
|
653
|
+
# noinspection PyMethodParameters
|
|
654
|
+
class Process:
|
|
655
|
+
def __enter__(self_):
|
|
656
|
+
# preprocess
|
|
657
|
+
self._preprocess()
|
|
648
658
|
|
|
649
|
-
|
|
650
|
-
|
|
659
|
+
def __exit__(self_, exc_type, exc_val, exc_tb):
|
|
660
|
+
# postprocess
|
|
661
|
+
self._postprocess()
|
|
651
662
|
|
|
652
|
-
|
|
653
|
-
datetime_start = datetime.datetime.now()
|
|
654
|
-
try:
|
|
655
|
-
f_return = self._solve_or_raise(
|
|
656
|
-
opt_, x, x_pass_to_fem_, self.opt.history, datetime_start
|
|
657
|
-
)
|
|
663
|
+
with Process():
|
|
658
664
|
|
|
659
|
-
|
|
660
|
-
|
|
665
|
+
# declare output
|
|
666
|
+
f_return = None
|
|
661
667
|
|
|
662
|
-
|
|
663
|
-
|
|
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
|
+
)
|
|
664
675
|
|
|
665
|
-
|
|
666
|
-
|
|
676
|
+
except HardConstraintViolation as e:
|
|
677
|
+
self._hard_constraint_handling(e)
|
|
667
678
|
|
|
668
|
-
|
|
669
|
-
|
|
679
|
+
except _HiddenConstraintViolation as e:
|
|
680
|
+
self._hidden_constraint_handling(e)
|
|
670
681
|
|
|
671
|
-
|
|
682
|
+
except SkipSolve as e:
|
|
683
|
+
self._skip_handling(e)
|
|
684
|
+
|
|
685
|
+
else:
|
|
686
|
+
self._if_succeeded(f_return)
|
|
672
687
|
|
|
673
688
|
# check interruption
|
|
674
689
|
self.opt._check_and_raise_interruption()
|
|
@@ -799,8 +814,19 @@ class AbstractOptimizer:
|
|
|
799
814
|
# noinspection PyMethodParameters
|
|
800
815
|
class LoggingOutput:
|
|
801
816
|
def __enter__(self_):
|
|
802
|
-
|
|
803
|
-
|
|
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 ▼▼▼▼▼')
|
|
804
830
|
|
|
805
831
|
def __exit__(self_, exc_type, exc_val, exc_tb):
|
|
806
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
|
-
|
|
12
|
-
|
|
15
|
+
By `set_user_attr_to_trial`,
|
|
16
|
+
key (str):
|
|
17
|
+
{sub_fidelity_name}(_{subsampling_idx})
|
|
18
|
+
value (dict):
|
|
13
19
|
fidelity: ...
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
20
|
+
internal_y_values: ...
|
|
21
|
+
violation_values: ...
|
|
22
|
+
pf_state: ...
|
|
17
23
|
|
|
18
24
|
"""
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
self.sub_fidelity_name = opt.sub_fidelity_name
|
|
32
|
-
self.
|
|
33
|
-
|
|
34
|
-
self.
|
|
35
|
-
self.
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
return
|
|
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
|
-
|
|
230
|
-
|
|
231
|
-
state =
|
|
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=(
|
|
591
|
-
_opt.add_objective('obj1', _parabola, args=(
|
|
592
|
-
# _opt.add_objective('obj2', _parabola2, args=(
|
|
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=(
|
|
599
|
-
# __opt.add_objective('obj2', _parabola2, args=(
|
|
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()
|
|
@@ -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
|
-
|
|
977
|
-
|
|
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
|
|
@@ -149,41 +149,59 @@ class VariableManager:
|
|
|
149
149
|
# fun を持つ場合
|
|
150
150
|
if isinstance(var, ExpressionFromFunction):
|
|
151
151
|
|
|
152
|
-
#
|
|
153
|
-
|
|
152
|
+
# 関数に渡す引数の初期化
|
|
153
|
+
final_args = []
|
|
154
|
+
final_kwargs = {}
|
|
154
155
|
|
|
155
|
-
#
|
|
156
|
-
|
|
157
|
-
kw_args = var.kwargs or {}
|
|
156
|
+
# 関数のシグネチャを取得
|
|
157
|
+
params = inspect.signature(var.fun).parameters
|
|
158
158
|
|
|
159
|
-
#
|
|
160
|
-
|
|
161
|
-
|
|
159
|
+
# 用意された引数をコピー
|
|
160
|
+
user_args = list(var.args)
|
|
161
|
+
user_kwargs = var.kwargs.copy()
|
|
162
162
|
|
|
163
|
-
|
|
163
|
+
# 関数に定義されている順に組み立て
|
|
164
|
+
for p in params.values():
|
|
165
|
+
|
|
166
|
+
# def sample(a, /, b, *c, d=None, **e):
|
|
167
|
+
# ...
|
|
168
|
+
# a: POSITIONAL_OR_KEYWORD
|
|
169
|
+
# b: POSITIONAL_OR_KEYWORD
|
|
170
|
+
# c: VAR_POSITIONAL
|
|
171
|
+
# d: KEYWORD_ONLY
|
|
172
|
+
# e: VAR_KEYWORD
|
|
173
|
+
|
|
174
|
+
# 位置引数で定義されているもの
|
|
164
175
|
if p.kind <= inspect.Parameter.POSITIONAL_OR_KEYWORD:
|
|
165
176
|
|
|
166
177
|
# 変数である
|
|
167
178
|
if p.name in self.variables:
|
|
168
179
|
# order 順に見ているのですでに value が正しいはず
|
|
169
|
-
|
|
180
|
+
final_args.append(self.variables[p.name].value)
|
|
170
181
|
|
|
171
|
-
#
|
|
182
|
+
# ユーザーが供給する変数である
|
|
172
183
|
else:
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
184
|
+
# user kwargs にあるかまず確認する
|
|
185
|
+
if p.name in user_kwargs:
|
|
186
|
+
# 該当物を抜き出して args に追加
|
|
187
|
+
final_args.append(user_kwargs.pop(p.name))
|
|
188
|
+
|
|
189
|
+
# user args がまだある
|
|
190
|
+
elif len(user_args) > 0:
|
|
191
|
+
# 先頭を抜き出して args に追加
|
|
192
|
+
final_args.append(user_args.pop(0))
|
|
193
|
+
|
|
194
|
+
# pos args が空であり、kwargs の中にもない
|
|
195
|
+
else:
|
|
176
196
|
msg = []
|
|
177
|
-
for p_ in
|
|
197
|
+
for p_ in params.values():
|
|
178
198
|
if p_.kind == inspect.Parameter.VAR_POSITIONAL:
|
|
179
199
|
msg.append(f'*{p_.name}')
|
|
180
200
|
elif p_.kind == inspect.Parameter.VAR_KEYWORD:
|
|
181
201
|
msg.append(f'**{p_.name}')
|
|
182
202
|
else:
|
|
183
203
|
msg.append(p_.name)
|
|
184
|
-
raise
|
|
185
|
-
*e.args,
|
|
186
|
-
_(
|
|
204
|
+
raise RuntimeError(_(
|
|
187
205
|
'Missing arguments! '
|
|
188
206
|
'The arguments specified by `args`: {var_args} / '
|
|
189
207
|
'The arguments specified by `kwargs`: {var_kwargs} / '
|
|
@@ -191,8 +209,7 @@ class VariableManager:
|
|
|
191
209
|
var_args=var.args,
|
|
192
210
|
var_kwargs=var.kwargs,
|
|
193
211
|
msg=msg
|
|
194
|
-
|
|
195
|
-
) from None
|
|
212
|
+
))
|
|
196
213
|
|
|
197
214
|
# *args である
|
|
198
215
|
elif p.kind == inspect.Parameter.VAR_POSITIONAL:
|
|
@@ -200,25 +217,64 @@ class VariableManager:
|
|
|
200
217
|
# ユーザー定義位置引数でないとおかしい
|
|
201
218
|
assert p.name not in self.variables, _('Extra positional argument name cannot be duplicated with a variable name.')
|
|
202
219
|
|
|
203
|
-
# *args
|
|
204
|
-
|
|
220
|
+
# *args なので残り全部を抜いて pos_args に入れる
|
|
221
|
+
final_args.extend(user_args)
|
|
222
|
+
user_args = []
|
|
205
223
|
|
|
206
|
-
#
|
|
224
|
+
# キーワード引数で定義されているもの
|
|
207
225
|
elif p.kind == inspect.Parameter.KEYWORD_ONLY:
|
|
208
226
|
|
|
209
227
|
# 変数である
|
|
210
228
|
if p.name in self.variables:
|
|
211
229
|
# order 順に見ているのですでに value が正しいはず
|
|
212
|
-
|
|
230
|
+
final_kwargs.update({p.name: self.variables[p.name].value})
|
|
213
231
|
|
|
214
|
-
|
|
215
|
-
|
|
232
|
+
# ユーザーが供給する変数である
|
|
233
|
+
else:
|
|
234
|
+
# user kwargs にあるかまず確認する
|
|
235
|
+
if p.name in user_kwargs:
|
|
236
|
+
# 該当物を抜き出して kwargs に追加
|
|
237
|
+
final_kwargs.update({p.name: user_kwargs.pop(p.name)})
|
|
238
|
+
|
|
239
|
+
# user args がまだある
|
|
240
|
+
elif len(user_args) > 0:
|
|
241
|
+
# 先頭を抜き出して args に追加
|
|
242
|
+
final_kwargs.update({p.name: user_args.pop(0)})
|
|
243
|
+
|
|
244
|
+
# pos args が空であり、kwargs の中にもない
|
|
245
|
+
else:
|
|
246
|
+
msg = []
|
|
247
|
+
for p_ in params.values():
|
|
248
|
+
if p_.kind == inspect.Parameter.VAR_POSITIONAL:
|
|
249
|
+
msg.append(f'*{p_.name}')
|
|
250
|
+
elif p_.kind == inspect.Parameter.VAR_KEYWORD:
|
|
251
|
+
msg.append(f'**{p_.name}')
|
|
252
|
+
else:
|
|
253
|
+
msg.append(p_.name)
|
|
254
|
+
raise RuntimeError(_(
|
|
255
|
+
'Missing arguments! '
|
|
256
|
+
'The arguments specified by `args`: {var_args} / '
|
|
257
|
+
'The arguments specified by `kwargs`: {var_kwargs} / '
|
|
258
|
+
'Required arguments: {msg}',
|
|
259
|
+
var_args=var.args,
|
|
260
|
+
var_kwargs=var.kwargs,
|
|
261
|
+
msg=msg
|
|
262
|
+
))
|
|
216
263
|
|
|
217
|
-
|
|
264
|
+
# **kwargs である
|
|
265
|
+
elif p.kind == inspect.Parameter.VAR_KEYWORD:
|
|
266
|
+
# 変数であってはいけない
|
|
218
267
|
assert p.name not in self.variables, _('Extra keyword argument name cannot be duplicated with a variable name.')
|
|
219
268
|
|
|
269
|
+
# 残りの kwargs を移す
|
|
270
|
+
final_kwargs.update(user_kwargs)
|
|
271
|
+
user_kwargs = {}
|
|
272
|
+
|
|
273
|
+
else:
|
|
274
|
+
raise NotImplementedError(f'Unknown argument type: {p.kind=}')
|
|
275
|
+
|
|
220
276
|
# fun を実行する
|
|
221
|
-
var.value = var.fun(*
|
|
277
|
+
var.value = var.fun(*final_args, **final_kwargs)
|
|
222
278
|
|
|
223
279
|
# string expression の場合
|
|
224
280
|
elif isinstance(var, ExpressionFromString):
|
|
@@ -11,14 +11,14 @@ pyfemtet/_i18n/messages.py,sha256=mzTARnpZPZ-Pmcq8RdOyy5a4XGCCSxwxuoHjN_HJ0TM,17
|
|
|
11
11
|
pyfemtet/_util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
12
|
pyfemtet/_util/closing.py,sha256=JPcgHDbW249YF20bMOX6UX-LquWoCWA14KhX-qFouR4,502
|
|
13
13
|
pyfemtet/_util/dask_util.py,sha256=Wm_1p_LR1yhhZNaGgazf-yQ-eialLfmqtgBjxha6qWk,2159
|
|
14
|
-
pyfemtet/_util/df_util.py,sha256=
|
|
14
|
+
pyfemtet/_util/df_util.py,sha256=hEae7YYhMnNmz8La2mmfAGlESqElxTdSSb-LVL2PUN4,1358
|
|
15
15
|
pyfemtet/_util/excel_macro_util.py,sha256=pUCpENpM1q6AbC7wH7rMXwnQ7nIFnabQ4xlbsli9VzM,8028
|
|
16
16
|
pyfemtet/_util/excel_parse_util.py,sha256=k8Y5EI9r0txwkieKUzlZqQ93b-9SMXldNIfq5ksWg28,5525
|
|
17
17
|
pyfemtet/_util/femtet_access_inspection.py,sha256=91E-7wzQTZum1UIsccW26717GnTM2jxf3HeOOR7-lv0,3999
|
|
18
18
|
pyfemtet/_util/femtet_autosave.py,sha256=-9DaYNsSK1QBnTOPo7EdLnMwqwAetCx_Ho5kOFMgwlk,2145
|
|
19
19
|
pyfemtet/_util/femtet_exit.py,sha256=sg3GJ2gqS_V7moOJQUr4T2AphOOat_kUnGF-0AHVB68,4256
|
|
20
20
|
pyfemtet/_util/femtet_version.py,sha256=9bWRMC_Exb-3rh5bg9eKfwBcgkxz_vg_eX99RPukcgA,580
|
|
21
|
-
pyfemtet/_util/helper.py,sha256=
|
|
21
|
+
pyfemtet/_util/helper.py,sha256=chlfSDQ764kn0mVgBXj8WXKBrje25AX6Riia8W5zkJo,2543
|
|
22
22
|
pyfemtet/_util/process_util.py,sha256=_hiRTZVZll8tnlBfXvz6GcBue4aE__jA0KnsVXe6m2A,3038
|
|
23
23
|
pyfemtet/_util/sample.xlsx,sha256=OU8mBY48YESJFQrdt4OkntlE1z-6WiyUyOV-PMr09DQ,9423
|
|
24
24
|
pyfemtet/_util/str_enum.py,sha256=b-mQRUsSZvk4zupYTxxSaOm3rtxfNyOajywZ82x9rmk,1441
|
|
@@ -29,28 +29,28 @@ pyfemtet/logger/__init__.py,sha256=lofBrZHr0P1hsxPUiPG1SQqKxCuSBk8zGnR7vUfCHYw,5
|
|
|
29
29
|
pyfemtet/logger/_impl.py,sha256=uJ9el3kR-A4W2DvO_G5A6k9z2u1lkyl7GSvZ68uPy-U,6321
|
|
30
30
|
pyfemtet/opt/__init__.py,sha256=1LcwTddtoi8plemxkzmX0YEKiNpAZvKn9OoNQysyDLE,339
|
|
31
31
|
pyfemtet/opt/exceptions.py,sha256=M_O7jm20Y4e_QxsKF6tnEl-OrAtErUOj6hNT7eEXCO4,1327
|
|
32
|
-
pyfemtet/opt/femopt.py,sha256=
|
|
32
|
+
pyfemtet/opt/femopt.py,sha256=Ufp9qJuJIEtP_lJtR-FeCkGtH_Us8imlJ1c-FVrVyLU,23186
|
|
33
33
|
pyfemtet/opt/history/__init__.py,sha256=pUp3SO4R7RGzmpNDLBg_pQH0X2yzBd-oqsHXWmB33os,201
|
|
34
|
-
pyfemtet/opt/history/_history.py,sha256=
|
|
34
|
+
pyfemtet/opt/history/_history.py,sha256=NwZvZTsWmZhvOZgC-ZqRtHPRaW6OFbXYTXUuDNjmOdQ,49299
|
|
35
35
|
pyfemtet/opt/history/_hypervolume.py,sha256=_IvGH71ZNreWvDQCG815Q2hS1OEvPFPQhUnNXf1UxRQ,4449
|
|
36
36
|
pyfemtet/opt/history/_optimality.py,sha256=6vLySZmrrklr04Qir0hGethTykf8NYFod88NDGrBrG0,2407
|
|
37
37
|
pyfemtet/opt/interface/__init__.py,sha256=D88d0yj-pQNvDij4HI2BG3OPXP5tvc5ZOomd_Jp4UE0,964
|
|
38
38
|
pyfemtet/opt/interface/_base_interface.py,sha256=A1J2junrjsFXUFulYLNhLEdTZgkZS8ev0trYRXtsSWc,6540
|
|
39
39
|
pyfemtet/opt/interface/_excel_interface/__init__.py,sha256=bFOmY18-j4RJc_dHQMe62GsxFwQSj7RWdvi6jxWuqyg,74
|
|
40
40
|
pyfemtet/opt/interface/_excel_interface/debug-excel-interface.xlsm,sha256=TM1CEOC5XtU7qYKNnHScO02kdtXT-gc5y29m2hatsm0,114259
|
|
41
|
-
pyfemtet/opt/interface/_excel_interface/excel_interface.py,sha256=
|
|
41
|
+
pyfemtet/opt/interface/_excel_interface/excel_interface.py,sha256=ihYbw_i3Bcz690AwSxVAPbRK08TVYJwyF66nFs7hMd0,39682
|
|
42
42
|
pyfemtet/opt/interface/_femtet_interface/__init__.py,sha256=snQruC8Rl_5rFeVmiqw9lmzdJ5mL42HpIlHIn5ytd8s,77
|
|
43
43
|
pyfemtet/opt/interface/_femtet_interface/_femtet_parametric.py,sha256=dEe6udq2CBpXAcKCye90sXC0UVKok14LORitobc8DK8,10282
|
|
44
|
-
pyfemtet/opt/interface/_femtet_interface/femtet_interface.py,sha256=
|
|
44
|
+
pyfemtet/opt/interface/_femtet_interface/femtet_interface.py,sha256=6EZ-7tCTfqj5NHi9G0HbOnmpPq9eYIVlAUrHtO_IiH4,44742
|
|
45
45
|
pyfemtet/opt/interface/_femtet_with_nx_interface/__init__.py,sha256=ppeoWVSmVsTmDNKpuFRVTnhjcoefQVEog3-FRiKpEe4,104
|
|
46
46
|
pyfemtet/opt/interface/_femtet_with_nx_interface/femtet_with_nx_interface.py,sha256=x1drM80W7gbsZMXWh1B2rFotyEPkDbbCQkxVnl12FcI,8359
|
|
47
47
|
pyfemtet/opt/interface/_femtet_with_nx_interface/model1.prt,sha256=cYVw2izr4_9PCPHOpi46XmDVOuNZ5ksuwKqzBtCZfNA,104833
|
|
48
48
|
pyfemtet/opt/interface/_femtet_with_nx_interface/model1.x_t,sha256=BHZJwc9gCCfleIkPMx7hZmNLZ3y6sJamFv6OSsHWhW0,6804
|
|
49
49
|
pyfemtet/opt/interface/_femtet_with_nx_interface/update_model.py,sha256=2U5SJWnG7Tyu6PP_7mtf6dOguNuub8c_1DBRtF0XuT0,3036
|
|
50
50
|
pyfemtet/opt/interface/_femtet_with_solidworks/__init__.py,sha256=5McSpy2uTmJNBylCrKt4xMSq90hlSpqyXYsjwZT3yGA,128
|
|
51
|
-
pyfemtet/opt/interface/_femtet_with_solidworks/femtet_with_solidworks_interface.py,sha256=
|
|
51
|
+
pyfemtet/opt/interface/_femtet_with_solidworks/femtet_with_solidworks_interface.py,sha256=TVFC9B6qJH61ipV2m8h8xFQWxYNpmqoLsLyosrqgePM,5888
|
|
52
52
|
pyfemtet/opt/interface/_solidworks_interface/__init__.py,sha256=2c52Hfme1xdJepewRGVkPT4yhrZMQgzlCuqvHzEZPVk,95
|
|
53
|
-
pyfemtet/opt/interface/_solidworks_interface/solidworks_interface.py,sha256=
|
|
53
|
+
pyfemtet/opt/interface/_solidworks_interface/solidworks_interface.py,sha256=f-DbgD_WymMjJl4tE6XrdRfmZ3RiuTcqrgb8EqhEZ94,8326
|
|
54
54
|
pyfemtet/opt/interface/_surrogate_model_interface/__init__.py,sha256=9sjzQjJsMLdpBSpURyl8x4VQHEO_PGYgAshCHoycAPI,252
|
|
55
55
|
pyfemtet/opt/interface/_surrogate_model_interface/base_surrogate_interface.py,sha256=vdYw8m09e6OCSYPsXUCXqoJcA_lpAOAYuEGmqDNgffM,6276
|
|
56
56
|
pyfemtet/opt/interface/_surrogate_model_interface/botorch_interface.py,sha256=bteAg6nPAIY8M9BoVycYRDtON7p0RLGT_RuPj0JNRtM,10279
|
|
@@ -63,17 +63,17 @@ pyfemtet/opt/meta_script/__main__.py,sha256=9-QM6eZOLpZ_CxERpRu3RAMqpudorSJdPCiK
|
|
|
63
63
|
pyfemtet/opt/meta_script/sample/sample.bas,sha256=2iuSYMgPDyAdiSDVGxRu3avjcZYnULz0l8e25YBa7SQ,27966
|
|
64
64
|
pyfemtet/opt/meta_script/sample/sample.femprj,sha256=6_0ywhgXxZjdzZzQFog8mgMUEjKNCFVNlEgAWoptovk,292885
|
|
65
65
|
pyfemtet/opt/optimizer/__init__.py,sha256=A4QYeF0KHEFdwoxLfkDND7ikDQ186Ryy3oXEGdakFSg,463
|
|
66
|
-
pyfemtet/opt/optimizer/_base_optimizer.py,sha256=
|
|
66
|
+
pyfemtet/opt/optimizer/_base_optimizer.py,sha256=zOoh0o0Um2zponfxWxeHx5MV46JrkC9eYC1JmVXbbVE,31685
|
|
67
67
|
pyfemtet/opt/optimizer/optuna_optimizer/__init__.py,sha256=u2Bwc79tkZTU5dMbhzzrPQi0RlFg22UgXc-m9K9G6wQ,242
|
|
68
|
-
pyfemtet/opt/optimizer/optuna_optimizer/_optuna_attribute.py,sha256=
|
|
69
|
-
pyfemtet/opt/optimizer/optuna_optimizer/_optuna_optimizer.py,sha256=
|
|
68
|
+
pyfemtet/opt/optimizer/optuna_optimizer/_optuna_attribute.py,sha256=7eZsruVCGgMlcnf3a9Vf55FOEE-D7V777MJQajI12Cw,1842
|
|
69
|
+
pyfemtet/opt/optimizer/optuna_optimizer/_optuna_optimizer.py,sha256=CAFqKdb12rud6_akWKgxvCifIcG6DjTi5Tt7Cr2OoCs,28919
|
|
70
70
|
pyfemtet/opt/optimizer/optuna_optimizer/_pof_botorch/__init__.py,sha256=BFbMNvdXqV9kl1h340pW2sq0-cwNFV5dfTo6UnNnX2M,179
|
|
71
71
|
pyfemtet/opt/optimizer/optuna_optimizer/_pof_botorch/debug-pof-botorch.reccsv,sha256=K6oI9jPi_5yayhBrI9Tm1RX3PoWWKo74TOdqnaPsIy8,1746
|
|
72
72
|
pyfemtet/opt/optimizer/optuna_optimizer/_pof_botorch/enable_nonlinear_constraint.py,sha256=FY0fIwiFvakEdeQNv13eb1YtTXKzu2lF67HL2Hfs06w,9814
|
|
73
|
-
pyfemtet/opt/optimizer/optuna_optimizer/_pof_botorch/pof_botorch_sampler.py,sha256=
|
|
73
|
+
pyfemtet/opt/optimizer/optuna_optimizer/_pof_botorch/pof_botorch_sampler.py,sha256=yVQJAD1yJ-Q2aCRuYCCifejjFS0eZmE2IoDTnKQgMko,46822
|
|
74
74
|
pyfemtet/opt/optimizer/optuna_optimizer/wat_ex14_parametric_jp.femprj,sha256=-M54MTNrV7muZWPm9Tjptd6HDdtgUFBsRroC6ytyqa0,180970
|
|
75
75
|
pyfemtet/opt/optimizer/scipy_optimizer/__init__.py,sha256=oXx2JAVLvgz0WwIXAknuV4p2MupaiutYYvjI8hXcFwc,45
|
|
76
|
-
pyfemtet/opt/optimizer/scipy_optimizer/_scipy_optimizer.py,sha256=
|
|
76
|
+
pyfemtet/opt/optimizer/scipy_optimizer/_scipy_optimizer.py,sha256=Og4DyD45nOmnBzrRYB8g_KSMyNH6tPNgItT3j7ThyMA,12901
|
|
77
77
|
pyfemtet/opt/prediction/__init__.py,sha256=-XYo-l5YFjExMtqMKj1YUAhmGSQq_0YITS0qizj2Xbs,104
|
|
78
78
|
pyfemtet/opt/prediction/_botorch_utils.py,sha256=KhPLY4JAPC6BDw_NA_KejzMYPCtGh-Yx1dc7PY1tYVA,3985
|
|
79
79
|
pyfemtet/opt/prediction/_gpytorch_modules_extension.py,sha256=B_qUtFn06dQENOmUOObbCpkeASUKI5JpXROx8zYeaq0,5224
|
|
@@ -83,7 +83,7 @@ pyfemtet/opt/problem/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
|
|
|
83
83
|
pyfemtet/opt/problem/problem.py,sha256=ohdZ450XH4YoFK4T9t52t8BSja6DpFTfMePQM1DdllU,9782
|
|
84
84
|
pyfemtet/opt/problem/variable_manager/__init__.py,sha256=uzuraWUZfLzB3uZHIQHFL7uMxWvv7Oaf940zEozXtNY,476
|
|
85
85
|
pyfemtet/opt/problem/variable_manager/_string_as_expression.py,sha256=aTJ9W9Gs6BS0Z_OsxWByJs9dAt32opD2_9913MCggPg,3626
|
|
86
|
-
pyfemtet/opt/problem/variable_manager/_variable_manager.py,sha256=
|
|
86
|
+
pyfemtet/opt/problem/variable_manager/_variable_manager.py,sha256=XrcmFIfE4J_5zkz4Yx6j4QyDAuJX3M2zJ4wESf8oE1Q,12697
|
|
87
87
|
pyfemtet/opt/visualization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
88
88
|
pyfemtet/opt/visualization/_create_wrapped_components.py,sha256=9AltJHr1DM6imZfpNp867rC-uAYqQ-emdgTLChKDrl8,2513
|
|
89
89
|
pyfemtet/opt/visualization/history_viewer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -164,9 +164,9 @@ pyfemtet/opt/visualization/plotter/main_figure_creator.py,sha256=9RXz6Wt52MiSz3H
|
|
|
164
164
|
pyfemtet/opt/visualization/plotter/pm_graph_creator.py,sha256=hUvvYeckMhkE1nH0FAOkXrA5K3A8PbfpxKYaYnBllB4,10864
|
|
165
165
|
pyfemtet/opt/wat_ex14_parametric_jp.femprj,sha256=dMwQMt6yok_PbZLyxPYdmg5wJQwgQDZ4RhS76zdGLGk,177944
|
|
166
166
|
pyfemtet/opt/worker_status.py,sha256=xSVW9lcw5jzYBwnmlVzk-1zCCyvmXVOH6EoRjqVbE9M,3605
|
|
167
|
-
pyfemtet-1.0.
|
|
168
|
-
pyfemtet-1.0.
|
|
169
|
-
pyfemtet-1.0.
|
|
170
|
-
pyfemtet-1.0.
|
|
171
|
-
pyfemtet-1.0.
|
|
172
|
-
pyfemtet-1.0.
|
|
167
|
+
pyfemtet-1.0.1.dist-info/LICENSE,sha256=LWUL5LlMGjSRTvsalS8_fFuwS4VMw18fJSNWFwDK8pc,1060
|
|
168
|
+
pyfemtet-1.0.1.dist-info/LICENSE_THIRD_PARTY.txt,sha256=8_9-cgzTpmeuCqItPZb9-lyAZcH2Qp9sZTU_hYuOZIQ,191
|
|
169
|
+
pyfemtet-1.0.1.dist-info/METADATA,sha256=8GuEhflweMHbq3Ap6e5ecnyPMj3eKxMgYxtEq7hegtw,3471
|
|
170
|
+
pyfemtet-1.0.1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
171
|
+
pyfemtet-1.0.1.dist-info/entry_points.txt,sha256=Tsb_l_8Z6pyyq2tRfuKiwfJUV3nq_cHoLS61foALtsg,134
|
|
172
|
+
pyfemtet-1.0.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|