@@ 20-259 (lines=240) @@ | ||
17 | from .search_space import SearchSpace |
|
18 | ||
19 | ||
20 | class Hyperactive: |
|
21 | """ |
|
22 | Initialize the Hyperactive class to manage optimization processes. |
|
23 | ||
24 | Parameters: |
|
25 | - verbosity: List of verbosity levels (default: ["progress_bar", "print_results", "print_times"]) |
|
26 | - distribution: String indicating the distribution method (default: "multiprocessing") |
|
27 | - n_processes: Number of processes to run in parallel or "auto" to determine automatically (default: "auto") |
|
28 | ||
29 | Methods: |
|
30 | - add_search: Add a new optimization search process with specified parameters |
|
31 | - run: Execute the optimization searches |
|
32 | - best_para: Get the best parameters for a specific search |
|
33 | - best_score: Get the best score for a specific search |
|
34 | - search_data: Get the search data for a specific search |
|
35 | """ |
|
36 | ||
37 | def __init__( |
|
38 | self, |
|
39 | verbosity: list = ["progress_bar", "print_results", "print_times"], |
|
40 | distribution: str = "multiprocessing", |
|
41 | n_processes: Union[str, int] = "auto", |
|
42 | ): |
|
43 | super().__init__() |
|
44 | if verbosity is False: |
|
45 | verbosity = [] |
|
46 | ||
47 | self.verbosity = verbosity |
|
48 | self.distribution = distribution |
|
49 | self.n_processes = n_processes |
|
50 | ||
51 | self.opt_pros = {} |
|
52 | ||
53 | def _create_shared_memory(self): |
|
54 | _bundle_opt_processes = {} |
|
55 | ||
56 | for opt_pros in self.opt_pros.values(): |
|
57 | if opt_pros.memory != "share": |
|
58 | continue |
|
59 | name = opt_pros.objective_function.__name__ |
|
60 | ||
61 | _bundle_opt_processes.setdefault(name, []).append(opt_pros) |
|
62 | ||
63 | for opt_pros_l in _bundle_opt_processes.values(): |
|
64 | # Check if the lengths of the search spaces of all optimizers in the list are the same. |
|
65 | if ( |
|
66 | len(set(len(opt_pros.s_space()) for opt_pros in opt_pros_l)) |
|
67 | == 1 |
|
68 | ): |
|
69 | manager = mp.Manager() # get new manager.dict |
|
70 | shared_memory = manager.dict() |
|
71 | for opt_pros in opt_pros_l: |
|
72 | opt_pros.memory = shared_memory |
|
73 | else: |
|
74 | for opt_pros in opt_pros_l: |
|
75 | opt_pros.memory = opt_pros_l[ |
|
76 | 0 |
|
77 | ].memory # get same manager.dict |
|
78 | ||
79 | @staticmethod |
|
80 | def _default_opt(optimizer): |
|
81 | if isinstance(optimizer, str): |
|
82 | if optimizer == "default": |
|
83 | optimizer = RandomSearchOptimizer() |
|
84 | return copy.deepcopy(optimizer) |
|
85 | ||
86 | @staticmethod |
|
87 | def _default_search_id(search_id, objective_function): |
|
88 | if not search_id: |
|
89 | search_id = objective_function.__name__ |
|
90 | return search_id |
|
91 | ||
92 | @staticmethod |
|
93 | def check_list(search_space): |
|
94 | for key in search_space.keys(): |
|
95 | search_dim = search_space[key] |
|
96 | ||
97 | error_msg = "Value in '{}' of search space dictionary must be of type list".format( |
|
98 | key |
|
99 | ) |
|
100 | if not isinstance(search_dim, list): |
|
101 | print("Warning", error_msg) |
|
102 | # raise ValueError(error_msg) |
|
103 | ||
104 | def add_search( |
|
105 | self, |
|
106 | objective_function: callable, |
|
107 | search_space: Dict[str, list], |
|
108 | n_iter: int, |
|
109 | search_id=None, |
|
110 | optimizer: Union[str, Type[RandomSearchOptimizer]] = "default", |
|
111 | n_jobs: int = 1, |
|
112 | initialize: Dict[str, int] = {"grid": 4, "random": 2, "vertices": 4}, |
|
113 | constraints: List[callable] = None, |
|
114 | pass_through: Dict = None, |
|
115 | callbacks: Dict[str, callable] = None, |
|
116 | catch: Dict = None, |
|
117 | max_score: float = None, |
|
118 | early_stopping: Dict = None, |
|
119 | random_state: int = None, |
|
120 | memory: Union[str, bool] = "share", |
|
121 | memory_warm_start: pd.DataFrame = None, |
|
122 | ): |
|
123 | """ |
|
124 | Add a new optimization search process with specified parameters. |
|
125 | ||
126 | Parameters: |
|
127 | - objective_function: The objective function to optimize. |
|
128 | - search_space: Dictionary defining the search space for optimization. |
|
129 | - n_iter: Number of iterations for the optimization process. |
|
130 | - search_id: Identifier for the search process (default: None). |
|
131 | - optimizer: The optimizer to use for the search process (default: "default"). |
|
132 | - n_jobs: Number of parallel jobs to run (default: 1). |
|
133 | - initialize: Dictionary specifying initialization parameters (default: {"grid": 4, "random": 2, "vertices": 4}). |
|
134 | - constraints: List of constraint functions (default: None). |
|
135 | - pass_through: Dictionary of additional parameters to pass through (default: None). |
|
136 | - callbacks: Dictionary of callback functions (default: None). |
|
137 | - catch: Dictionary of exceptions to catch during optimization (default: None). |
|
138 | - max_score: Maximum score to achieve (default: None). |
|
139 | - early_stopping: Dictionary specifying early stopping criteria (default: None). |
|
140 | - random_state: Seed for random number generation (default: None). |
|
141 | - memory: Option to share memory between processes (default: "share"). |
|
142 | - memory_warm_start: DataFrame containing warm start memory (default: None). |
|
143 | """ |
|
144 | ||
145 | self.check_list(search_space) |
|
146 | ||
147 | constraints = constraints or [] |
|
148 | pass_through = pass_through or {} |
|
149 | callbacks = callbacks or {} |
|
150 | catch = catch or {} |
|
151 | early_stopping = early_stopping or {} |
|
152 | ||
153 | optimizer = self._default_opt(optimizer) |
|
154 | search_id = self._default_search_id(search_id, objective_function) |
|
155 | s_space = SearchSpace(search_space) |
|
156 | ||
157 | optimizer.setup_search( |
|
158 | objective_function=objective_function, |
|
159 | s_space=s_space, |
|
160 | n_iter=n_iter, |
|
161 | initialize=initialize, |
|
162 | constraints=constraints, |
|
163 | pass_through=pass_through, |
|
164 | callbacks=callbacks, |
|
165 | catch=catch, |
|
166 | max_score=max_score, |
|
167 | early_stopping=early_stopping, |
|
168 | random_state=random_state, |
|
169 | memory=memory, |
|
170 | memory_warm_start=memory_warm_start, |
|
171 | verbosity=self.verbosity, |
|
172 | ) |
|
173 | ||
174 | n_jobs = mp.cpu_count() if n_jobs == -1 else n_jobs |
|
175 | ||
176 | for _ in range(n_jobs): |
|
177 | nth_process = len(self.opt_pros) |
|
178 | self.opt_pros[nth_process] = optimizer |
|
179 | ||
180 | def _print_info(self): |
|
181 | print_res = PrintResults(self.opt_pros, self.verbosity) |
|
182 | ||
183 | if self.verbosity: |
|
184 | for _ in range(len(self.opt_pros)): |
|
185 | print("") |
|
186 | ||
187 | for results in self.results_list: |
|
188 | nth_process = results["nth_process"] |
|
189 | print_res.print_process(results, nth_process) |
|
190 | ||
191 | def run(self, max_time: float = None): |
|
192 | """ |
|
193 | Run the optimization process with an optional maximum time limit. |
|
194 | ||
195 | Args: |
|
196 | max_time (float, optional): Maximum time limit for the optimization process. Defaults to None. |
|
197 | """ |
|
198 | ||
199 | self._create_shared_memory() |
|
200 | ||
201 | for opt in self.opt_pros.values(): |
|
202 | opt.max_time = max_time |
|
203 | ||
204 | self.results_list = run_search( |
|
205 | self.opt_pros, self.distribution, self.n_processes |
|
206 | ) |
|
207 | ||
208 | self.results_ = Results(self.results_list, self.opt_pros) |
|
209 | ||
210 | self._print_info() |
|
211 | ||
212 | def best_para(self, id_): |
|
213 | """ |
|
214 | Retrieve the best parameters for a specific ID from the results. |
|
215 | ||
216 | Parameters: |
|
217 | - id_ (int): The ID of the parameters to retrieve. |
|
218 | ||
219 | Returns: |
|
220 | - Union[Dict[str, Union[int, float]], None]: The best parameters for the specified ID if found, otherwise None. |
|
221 | ||
222 | Raises: |
|
223 | - ValueError: If the objective function name is not recognized. |
|
224 | """ |
|
225 | ||
226 | return self.results_.best_para(id_) |
|
227 | ||
228 | def best_score(self, id_): |
|
229 | """ |
|
230 | Return the best score for a specific ID from the results. |
|
231 | ||
232 | Parameters: |
|
233 | - id_ (int): The ID for which the best score is requested. |
|
234 | """ |
|
235 | ||
236 | return self.results_.best_score(id_) |
|
237 | ||
238 | def search_data(self, id_, times=False): |
|
239 | """ |
|
240 | Retrieve search data for a specific ID from the results. Optionally exclude evaluation and iteration times if 'times' is set to False. |
|
241 | ||
242 | Parameters: |
|
243 | - id_ (int): The ID of the search data to retrieve. |
|
244 | - times (bool, optional): Whether to exclude evaluation and iteration times. Defaults to False. |
|
245 | ||
246 | Returns: |
|
247 | - pd.DataFrame: The search data for the specified ID. |
|
248 | """ |
|
249 | ||
250 | search_data_ = self.results_.search_data(id_) |
|
251 | ||
252 | if times == False: |
|
253 | search_data_.drop( |
|
254 | labels=["eval_times", "iter_times"], |
|
255 | axis=1, |
|
256 | inplace=True, |
|
257 | errors="ignore", |
|
258 | ) |
|
259 | return search_data_ |
|
260 |
@@ 20-259 (lines=240) @@ | ||
17 | from .search_space import SearchSpace |
|
18 | ||
19 | ||
20 | class Hyperactive: |
|
21 | """ |
|
22 | Initialize the Hyperactive class to manage optimization processes. |
|
23 | ||
24 | Parameters: |
|
25 | - verbosity: List of verbosity levels (default: ["progress_bar", "print_results", "print_times"]) |
|
26 | - distribution: String indicating the distribution method (default: "multiprocessing") |
|
27 | - n_processes: Number of processes to run in parallel or "auto" to determine automatically (default: "auto") |
|
28 | ||
29 | Methods: |
|
30 | - add_search: Add a new optimization search process with specified parameters |
|
31 | - run: Execute the optimization searches |
|
32 | - best_para: Get the best parameters for a specific search |
|
33 | - best_score: Get the best score for a specific search |
|
34 | - search_data: Get the search data for a specific search |
|
35 | """ |
|
36 | ||
37 | def __init__( |
|
38 | self, |
|
39 | verbosity: list = ["progress_bar", "print_results", "print_times"], |
|
40 | distribution: str = "multiprocessing", |
|
41 | n_processes: Union[str, int] = "auto", |
|
42 | ): |
|
43 | super().__init__() |
|
44 | if verbosity is False: |
|
45 | verbosity = [] |
|
46 | ||
47 | self.verbosity = verbosity |
|
48 | self.distribution = distribution |
|
49 | self.n_processes = n_processes |
|
50 | ||
51 | self.opt_pros = {} |
|
52 | ||
53 | def _create_shared_memory(self): |
|
54 | _bundle_opt_processes = {} |
|
55 | ||
56 | for opt_pros in self.opt_pros.values(): |
|
57 | if opt_pros.memory != "share": |
|
58 | continue |
|
59 | name = opt_pros.objective_function.__name__ |
|
60 | ||
61 | _bundle_opt_processes.setdefault(name, []).append(opt_pros) |
|
62 | ||
63 | for opt_pros_l in _bundle_opt_processes.values(): |
|
64 | # Check if the lengths of the search spaces of all optimizers in the list are the same. |
|
65 | if ( |
|
66 | len(set(len(opt_pros.s_space()) for opt_pros in opt_pros_l)) |
|
67 | == 1 |
|
68 | ): |
|
69 | manager = mp.Manager() # get new manager.dict |
|
70 | shared_memory = manager.dict() |
|
71 | for opt_pros in opt_pros_l: |
|
72 | opt_pros.memory = shared_memory |
|
73 | else: |
|
74 | for opt_pros in opt_pros_l: |
|
75 | opt_pros.memory = opt_pros_l[ |
|
76 | 0 |
|
77 | ].memory # get same manager.dict |
|
78 | ||
79 | @staticmethod |
|
80 | def _default_opt(optimizer): |
|
81 | if isinstance(optimizer, str): |
|
82 | if optimizer == "default": |
|
83 | optimizer = RandomSearchOptimizer() |
|
84 | return copy.deepcopy(optimizer) |
|
85 | ||
86 | @staticmethod |
|
87 | def _default_search_id(search_id, objective_function): |
|
88 | if not search_id: |
|
89 | search_id = objective_function.__name__ |
|
90 | return search_id |
|
91 | ||
92 | @staticmethod |
|
93 | def check_list(search_space): |
|
94 | for key in search_space.keys(): |
|
95 | search_dim = search_space[key] |
|
96 | ||
97 | error_msg = "Value in '{}' of search space dictionary must be of type list".format( |
|
98 | key |
|
99 | ) |
|
100 | if not isinstance(search_dim, list): |
|
101 | print("Warning", error_msg) |
|
102 | # raise ValueError(error_msg) |
|
103 | ||
104 | def add_search( |
|
105 | self, |
|
106 | objective_function: callable, |
|
107 | search_space: Dict[str, list], |
|
108 | n_iter: int, |
|
109 | search_id=None, |
|
110 | optimizer: Union[str, Type[RandomSearchOptimizer]] = "default", |
|
111 | n_jobs: int = 1, |
|
112 | initialize: Dict[str, int] = {"grid": 4, "random": 2, "vertices": 4}, |
|
113 | constraints: List[callable] = None, |
|
114 | pass_through: Dict = None, |
|
115 | callbacks: Dict[str, callable] = None, |
|
116 | catch: Dict = None, |
|
117 | max_score: float = None, |
|
118 | early_stopping: Dict = None, |
|
119 | random_state: int = None, |
|
120 | memory: Union[str, bool] = "share", |
|
121 | memory_warm_start: pd.DataFrame = None, |
|
122 | ): |
|
123 | """ |
|
124 | Add a new optimization search process with specified parameters. |
|
125 | ||
126 | Parameters: |
|
127 | - objective_function: The objective function to optimize. |
|
128 | - search_space: Dictionary defining the search space for optimization. |
|
129 | - n_iter: Number of iterations for the optimization process. |
|
130 | - search_id: Identifier for the search process (default: None). |
|
131 | - optimizer: The optimizer to use for the search process (default: "default"). |
|
132 | - n_jobs: Number of parallel jobs to run (default: 1). |
|
133 | - initialize: Dictionary specifying initialization parameters (default: {"grid": 4, "random": 2, "vertices": 4}). |
|
134 | - constraints: List of constraint functions (default: None). |
|
135 | - pass_through: Dictionary of additional parameters to pass through (default: None). |
|
136 | - callbacks: Dictionary of callback functions (default: None). |
|
137 | - catch: Dictionary of exceptions to catch during optimization (default: None). |
|
138 | - max_score: Maximum score to achieve (default: None). |
|
139 | - early_stopping: Dictionary specifying early stopping criteria (default: None). |
|
140 | - random_state: Seed for random number generation (default: None). |
|
141 | - memory: Option to share memory between processes (default: "share"). |
|
142 | - memory_warm_start: DataFrame containing warm start memory (default: None). |
|
143 | """ |
|
144 | ||
145 | self.check_list(search_space) |
|
146 | ||
147 | constraints = constraints or [] |
|
148 | pass_through = pass_through or {} |
|
149 | callbacks = callbacks or {} |
|
150 | catch = catch or {} |
|
151 | early_stopping = early_stopping or {} |
|
152 | ||
153 | optimizer = self._default_opt(optimizer) |
|
154 | search_id = self._default_search_id(search_id, objective_function) |
|
155 | s_space = SearchSpace(search_space) |
|
156 | ||
157 | optimizer.setup_search( |
|
158 | objective_function=objective_function, |
|
159 | s_space=s_space, |
|
160 | n_iter=n_iter, |
|
161 | initialize=initialize, |
|
162 | constraints=constraints, |
|
163 | pass_through=pass_through, |
|
164 | callbacks=callbacks, |
|
165 | catch=catch, |
|
166 | max_score=max_score, |
|
167 | early_stopping=early_stopping, |
|
168 | random_state=random_state, |
|
169 | memory=memory, |
|
170 | memory_warm_start=memory_warm_start, |
|
171 | verbosity=self.verbosity, |
|
172 | ) |
|
173 | ||
174 | n_jobs = mp.cpu_count() if n_jobs == -1 else n_jobs |
|
175 | ||
176 | for _ in range(n_jobs): |
|
177 | nth_process = len(self.opt_pros) |
|
178 | self.opt_pros[nth_process] = optimizer |
|
179 | ||
180 | def _print_info(self): |
|
181 | print_res = PrintResults(self.opt_pros, self.verbosity) |
|
182 | ||
183 | if self.verbosity: |
|
184 | for _ in range(len(self.opt_pros)): |
|
185 | print("") |
|
186 | ||
187 | for results in self.results_list: |
|
188 | nth_process = results["nth_process"] |
|
189 | print_res.print_process(results, nth_process) |
|
190 | ||
191 | def run(self, max_time: float = None): |
|
192 | """ |
|
193 | Run the optimization process with an optional maximum time limit. |
|
194 | ||
195 | Args: |
|
196 | max_time (float, optional): Maximum time limit for the optimization process. Defaults to None. |
|
197 | """ |
|
198 | ||
199 | self._create_shared_memory() |
|
200 | ||
201 | for opt in self.opt_pros.values(): |
|
202 | opt.max_time = max_time |
|
203 | ||
204 | self.results_list = run_search( |
|
205 | self.opt_pros, self.distribution, self.n_processes |
|
206 | ) |
|
207 | ||
208 | self.results_ = Results(self.results_list, self.opt_pros) |
|
209 | ||
210 | self._print_info() |
|
211 | ||
212 | def best_para(self, id_): |
|
213 | """ |
|
214 | Retrieve the best parameters for a specific ID from the results. |
|
215 | ||
216 | Parameters: |
|
217 | - id_ (int): The ID of the parameters to retrieve. |
|
218 | ||
219 | Returns: |
|
220 | - Union[Dict[str, Union[int, float]], None]: The best parameters for the specified ID if found, otherwise None. |
|
221 | ||
222 | Raises: |
|
223 | - ValueError: If the objective function name is not recognized. |
|
224 | """ |
|
225 | ||
226 | return self.results_.best_para(id_) |
|
227 | ||
228 | def best_score(self, id_): |
|
229 | """ |
|
230 | Return the best score for a specific ID from the results. |
|
231 | ||
232 | Parameters: |
|
233 | - id_ (int): The ID for which the best score is requested. |
|
234 | """ |
|
235 | ||
236 | return self.results_.best_score(id_) |
|
237 | ||
238 | def search_data(self, id_, times=False): |
|
239 | """ |
|
240 | Retrieve search data for a specific ID from the results. Optionally exclude evaluation and iteration times if 'times' is set to False. |
|
241 | ||
242 | Parameters: |
|
243 | - id_ (int): The ID of the search data to retrieve. |
|
244 | - times (bool, optional): Whether to exclude evaluation and iteration times. Defaults to False. |
|
245 | ||
246 | Returns: |
|
247 | - pd.DataFrame: The search data for the specified ID. |
|
248 | """ |
|
249 | ||
250 | search_data_ = self.results_.search_data(id_) |
|
251 | ||
252 | if times == False: |
|
253 | search_data_.drop( |
|
254 | labels=["eval_times", "iter_times"], |
|
255 | axis=1, |
|
256 | inplace=True, |
|
257 | errors="ignore", |
|
258 | ) |
|
259 | return search_data_ |
|
260 |